JS backend: Add api for call builder and CallCaseDispatcher

This commit is contained in:
Erokhin Stanislav
2013-12-27 17:33:21 +04:00
parent 21f56365b8
commit ee54e50138
2 changed files with 180 additions and 9 deletions
@@ -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")
}
}