JS backend: Add api for call builder and CallCaseDispatcher
This commit is contained in:
@@ -27,6 +27,9 @@ import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind.*
|
||||
import org.jetbrains.k2js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.jet.lang.psi.JetSuperExpression
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
|
||||
import com.google.dart.compiler.backend.js.ast.JsName
|
||||
|
||||
|
||||
open class BaseCallInfo(
|
||||
@@ -38,7 +41,6 @@ open class BaseCallInfo(
|
||||
val nullableReceiverForSafeCall: JsExpression?
|
||||
) {
|
||||
val callableDescriptor = resolvedCall.getResultingDescriptor().getOriginal()
|
||||
val functionName = context.getNameForDescriptor(callableDescriptor)
|
||||
|
||||
fun isExtension(): Boolean = receiverObject != null
|
||||
fun isMemberCall(): Boolean = thisObject != null
|
||||
@@ -53,10 +55,37 @@ open class BaseCallInfo(
|
||||
// TODO: toString for debug
|
||||
}
|
||||
|
||||
class BaseFunctionCallInfo(
|
||||
callInfo: BaseCallInfo,
|
||||
val argumentsInfo: CallArgumentTranslator.ArgumentsInfo
|
||||
) : BaseCallInfo(callInfo.context, callInfo.resolvedCall, callInfo.thisObject, callInfo.receiverObject, callInfo.nullableReceiverForSafeCall)
|
||||
private open class CallInfoWrapper(callInfo: BaseCallInfo) :
|
||||
BaseCallInfo(callInfo.context, callInfo.resolvedCall, callInfo.thisObject, callInfo.receiverObject, callInfo.nullableReceiverForSafeCall)
|
||||
|
||||
// if setTo == null, it is get access
|
||||
class VariableAccessInfo(callInfo: BaseCallInfo, private val setTo: JsExpression? = null): CallInfoWrapper(callInfo) {
|
||||
val variableDescriptor = super.callableDescriptor as VariableDescriptor
|
||||
val propertyName : JsName
|
||||
get() {
|
||||
return context.getNameForDescriptor(variableDescriptor)
|
||||
}
|
||||
|
||||
fun isGetAccess(): Boolean = setTo == null
|
||||
|
||||
fun getSetToExpression(): JsExpression {
|
||||
if (isGetAccess()) {
|
||||
throw IllegalStateException("This is get, setTo is null. callInfo: $this")
|
||||
}
|
||||
return setTo!!
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionCallInfo(callInfo: BaseCallInfo, val argumentsInfo: CallArgumentTranslator.ArgumentsInfo) : CallInfoWrapper(callInfo) {
|
||||
val functionName : JsName
|
||||
get() { // getter, because for several descriptors name is undefined. Example: {(a) -> a+1}(3)
|
||||
return context.getNameForDescriptor(callableDescriptor)
|
||||
}
|
||||
|
||||
fun hasSpreadOperator() : Boolean {
|
||||
return argumentsInfo.isHasSpreadOperator()
|
||||
}
|
||||
}
|
||||
|
||||
private fun TranslationContext.getThisObject(receiverValue: ReceiverValue): JsExpression {
|
||||
assert(receiverValue.exists(), "receiverValue must be exist here")
|
||||
@@ -117,13 +146,18 @@ fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out CallableDescri
|
||||
return mainGetCallInfo(resolvedCall, receiver, null)
|
||||
}
|
||||
|
||||
fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescriptor>, receiver: JsExpression?): BaseFunctionCallInfo {
|
||||
fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescriptor>, receiver: JsExpression?): FunctionCallInfo {
|
||||
return getCallInfo(resolvedCall, receiver, null);
|
||||
}
|
||||
|
||||
// two receiver need only for FunctionCall in VariableAsFunctionResolvedCall
|
||||
fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescriptor>, receiver1: JsExpression?, receiver2: JsExpression?): BaseFunctionCallInfo {
|
||||
fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescriptor>, receiver1: JsExpression?, receiver2: JsExpression?): FunctionCallInfo {
|
||||
val callInfo = mainGetCallInfo(resolvedCall, receiver1, receiver2)
|
||||
val argumentsInfo = CallArgumentTranslator.translate(resolvedCall, receiver1, this)
|
||||
return BaseFunctionCallInfo(callInfo, argumentsInfo)
|
||||
|
||||
val receiverForArgsTranslator = if (resolvedCall.getExplicitReceiverKind() == BOTH_RECEIVERS) // TODO: remove this hack
|
||||
receiver2
|
||||
else
|
||||
receiver1
|
||||
val argumentsInfo = CallArgumentTranslator.translate(resolvedCall, receiverForArgsTranslator, this)
|
||||
return FunctionCallInfo(callInfo, argumentsInfo)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2010-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.k2js.translate.reference
|
||||
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException
|
||||
|
||||
|
||||
val functionCallCases = CallCaseDispatcher<FunctionCallCase, FunctionCallInfo>()
|
||||
val variableAccessCases = CallCaseDispatcher<VariableAccessCase, VariableAccessInfo>()
|
||||
|
||||
fun TranslationContext.buildCall(resolvedCall: ResolvedCall<out FunctionDescriptor>, receiver: JsExpression? = null): JsExpression {
|
||||
return buildCall(resolvedCall, receiver, null)
|
||||
}
|
||||
|
||||
fun TranslationContext.buildGet(resolvedCall: ResolvedCall<out VariableDescriptor>, receiver: JsExpression? = null): JsExpression {
|
||||
val variableAccessInfo = VariableAccessInfo(getCallInfo(resolvedCall, receiver), null);
|
||||
return variableAccessCases.translate(variableAccessInfo)
|
||||
}
|
||||
|
||||
fun TranslationContext.buildSet(resolvedCall: ResolvedCall<out VariableDescriptor>, setTo: JsExpression, receiver: JsExpression? = null): JsExpression {
|
||||
val variableAccessInfo = VariableAccessInfo(getCallInfo(resolvedCall, receiver), setTo);
|
||||
return variableAccessCases.translate(variableAccessInfo)
|
||||
}
|
||||
|
||||
private fun TranslationContext.buildCall(resolvedCall: ResolvedCall<out FunctionDescriptor>, receiver1: JsExpression?, receiver2: JsExpression?): JsExpression {
|
||||
if (resolvedCall is VariableAsFunctionResolvedCall) {
|
||||
assert(receiver2 == null, "receiver2 for VariableAsFunctionResolvedCall must be null") // TODO: add debug info
|
||||
val variableCall = resolvedCall.getVariableCall()
|
||||
if (variableCall.expectedReceivers()) {
|
||||
val newReceiver = buildGet(variableCall, receiver1)
|
||||
return buildCall(resolvedCall.getFunctionCall(), newReceiver, null)
|
||||
} else {
|
||||
val newReceiver2 = buildGet(variableCall, null)
|
||||
if (receiver1 == null)
|
||||
return buildCall(resolvedCall.getFunctionCall(), newReceiver2)
|
||||
else
|
||||
return buildCall(resolvedCall.getFunctionCall(), receiver1, newReceiver2)
|
||||
}
|
||||
}
|
||||
|
||||
val functionCallInfo = getCallInfo(resolvedCall, receiver1, receiver2)
|
||||
return functionCallCases.translate(functionCallInfo)
|
||||
}
|
||||
|
||||
|
||||
trait CallCase<I : BaseCallInfo> {
|
||||
val callInfo: I
|
||||
|
||||
protected fun unsupported(message: String = "") : Exception {
|
||||
val stackTrace = Thread.currentThread().getStackTrace()
|
||||
val methodName = stackTrace.get(stackTrace.lastIndex - 1).getMethodName()
|
||||
val caseName = javaClass.getName()
|
||||
return UnsupportedOperationException("this case unsopported: $message [$methodName; $caseName; $callInfo]")
|
||||
}
|
||||
|
||||
protected fun I.noReceivers(): JsExpression {
|
||||
throw unsupported()
|
||||
}
|
||||
|
||||
protected fun I.thisObject(): JsExpression {
|
||||
throw unsupported()
|
||||
}
|
||||
|
||||
protected fun I.receiverArgument(): JsExpression {
|
||||
throw unsupported()
|
||||
}
|
||||
|
||||
protected fun I.bothReceivers(): JsExpression {
|
||||
throw unsupported()
|
||||
}
|
||||
|
||||
final fun translate(): JsExpression {
|
||||
return if (callInfo.thisObject == null) {
|
||||
if (callInfo.receiverObject == null)
|
||||
callInfo.noReceivers()
|
||||
else
|
||||
callInfo.receiverArgument()
|
||||
} else {
|
||||
if (callInfo.receiverObject == null)
|
||||
callInfo.thisObject()
|
||||
else
|
||||
callInfo.bothReceivers()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class FunctionCallCase(override val callInfo: FunctionCallInfo) : CallCase<FunctionCallInfo>
|
||||
open class VariableAccessCase(override val callInfo: VariableAccessInfo) : CallCase<VariableAccessInfo>
|
||||
|
||||
class CallCaseDispatcher<C : CallCase<I>, I : BaseCallInfo> {
|
||||
private val cases: MutableList<(I) -> JsExpression?> = ArrayList()
|
||||
|
||||
fun addCase(canBeApplyCase: (I) -> JsExpression?) {
|
||||
cases.add(canBeApplyCase)
|
||||
}
|
||||
|
||||
fun addCase(caseConstructor: (I) -> C, canApply: (I) -> Boolean) {
|
||||
cases.add({
|
||||
if (canApply(it))
|
||||
caseConstructor(it).translate()
|
||||
else
|
||||
null
|
||||
})
|
||||
}
|
||||
|
||||
fun translate(callInfo: I): JsExpression {
|
||||
for (case in cases) {
|
||||
val result = case(callInfo)
|
||||
if (result != null)
|
||||
return result
|
||||
}
|
||||
throw UnsupportedOperationException("This case of call unsopported. CallInfo: $callInfo")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user