diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallInfo.kt b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallInfo.kt index cde9901da7c..ab4f2f41fcd 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallInfo.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallInfo.kt @@ -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, receiver: JsExpression?): BaseFunctionCallInfo { +fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall, receiver: JsExpression?): FunctionCallInfo { return getCallInfo(resolvedCall, receiver, null); } // two receiver need only for FunctionCall in VariableAsFunctionResolvedCall -fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall, receiver1: JsExpression?, receiver2: JsExpression?): BaseFunctionCallInfo { +fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall, 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) } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/ClearCallTranslator.kt b/js/js.translator/src/org/jetbrains/k2js/translate/reference/ClearCallTranslator.kt new file mode 100644 index 00000000000..2b25e15de11 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/ClearCallTranslator.kt @@ -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() +val variableAccessCases = CallCaseDispatcher() + +fun TranslationContext.buildCall(resolvedCall: ResolvedCall, receiver: JsExpression? = null): JsExpression { + return buildCall(resolvedCall, receiver, null) +} + +fun TranslationContext.buildGet(resolvedCall: ResolvedCall, receiver: JsExpression? = null): JsExpression { + val variableAccessInfo = VariableAccessInfo(getCallInfo(resolvedCall, receiver), null); + return variableAccessCases.translate(variableAccessInfo) +} + +fun TranslationContext.buildSet(resolvedCall: ResolvedCall, setTo: JsExpression, receiver: JsExpression? = null): JsExpression { + val variableAccessInfo = VariableAccessInfo(getCallInfo(resolvedCall, receiver), setTo); + return variableAccessCases.translate(variableAccessInfo) +} + +private fun TranslationContext.buildCall(resolvedCall: ResolvedCall, 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 { + 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 +open class VariableAccessCase(override val callInfo: VariableAccessInfo) : CallCase + +class CallCaseDispatcher, 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") + } +} +