diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallParameters.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallParameters.java new file mode 100644 index 00000000000..14c09db8dde --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallParameters.java @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2012 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 com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Pavel Talanov + */ +public final class CallParameters { + + @Nullable + private JsExpression receiver; + + @NotNull + private JsExpression functionReference; + + @Nullable + private JsExpression thisObject; + + public CallParameters(@Nullable JsExpression receiver, + @NotNull JsExpression functionReference, + @Nullable JsExpression thisObject) { + this.receiver = receiver; + this.functionReference = functionReference; + this.thisObject = thisObject; + } + + @NotNull + public JsExpression getFunctionReference() { + return functionReference; + } + + @Nullable + public JsExpression getThisObject() { + return thisObject; + } + + @Nullable + public JsExpression getReceiver() { + return receiver; + } +} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallParametersResolver.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallParametersResolver.java new file mode 100644 index 00000000000..82a96c0466e --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallParametersResolver.java @@ -0,0 +1,120 @@ +/* + * Copyright 2010-2012 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 com.google.dart.compiler.backend.js.ast.JsExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.utils.TranslationUtils; + +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*; + +/** + * @author Pavel Talanov + */ +public final class CallParametersResolver { + + public static CallParameters resolveCallParameters(@Nullable JsExpression qualifier, + @Nullable JsExpression callee, + @NotNull CallableDescriptor descriptor, + @NotNull ResolvedCall call, + @NotNull TranslationContext context) { + return (new CallParametersResolver(qualifier, callee, descriptor, call, context)).resolve(); + } + + // the actual qualifier for the call at the call site + @Nullable + private final JsExpression qualifier; + @Nullable + private final JsExpression callee; + @NotNull + private final CallableDescriptor descriptor; + @NotNull + private final TranslationContext context; + @NotNull + private final ResolvedCall resolvedCall; + private final boolean isExtensionCall; + + public CallParametersResolver(@Nullable JsExpression qualifier, + @Nullable JsExpression callee, + @NotNull CallableDescriptor descriptor, + @NotNull ResolvedCall call, + @NotNull TranslationContext context) { + this.qualifier = qualifier; + this.callee = callee; + this.descriptor = descriptor; + this.context = context; + this.resolvedCall = call; + this.isExtensionCall = resolvedCall.getReceiverArgument().exists(); + } + + @NotNull + private CallParameters resolve() { + JsExpression receiver = isExtensionCall ? getExtensionFunctionCallReceiver() : null; + JsExpression functionReference = getFunctionReference(); + JsExpression thisObject = getThisObject(); + return new CallParameters(receiver, functionReference, thisObject); + } + + @NotNull + private JsExpression getFunctionReference() { + if (callee != null) { + return callee; + } + if (!isVariableAsFunction(descriptor)) { + return ReferenceTranslator.translateAsLocalNameReference(descriptor, context); + } + VariableDescriptor variableDescriptor = + getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor)descriptor); + if (variableDescriptor instanceof PropertyDescriptor) { + return getterCall((PropertyDescriptor)variableDescriptor); + } + return ReferenceTranslator.translateAsLocalNameReference(variableDescriptor, context); + } + + //TODO: inspect + @NotNull + private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) { + //TODO: call type? + return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context); + } + + @Nullable + private JsExpression getThisObject() { + if (qualifier != null && !isExtensionCall) { + return qualifier; + } + JsExpression result = TranslationUtils.resolveThisObjectForResolvedCall(resolvedCall, context); + if (result != null) { + return result; + } + return null; + } + + @NotNull + private JsExpression getExtensionFunctionCallReceiver() { + if (qualifier != null) { + return qualifier; + } + DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor); + assert expectedReceiverDescriptor != null; + return TranslationUtils.getThisObject(context, expectedReceiverDescriptor); + } +} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java index 657bbe4a274..3dac6201225 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java @@ -23,22 +23,23 @@ import com.google.dart.compiler.backend.js.ast.JsNew; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.VariableAsFunctionDescriptor; import org.jetbrains.jet.lang.resolve.calls.ExpressionAsFunctionDescriptor; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; -import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.intrinsic.Intrinsic; import org.jetbrains.k2js.translate.utils.AnnotationsUtils; -import org.jetbrains.k2js.translate.utils.TranslationUtils; import java.util.ArrayList; import java.util.List; -import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*; +import static org.jetbrains.k2js.translate.reference.CallParametersResolver.resolveCallParameters; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isConstructorDescriptor; import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; -import static org.jetbrains.k2js.translate.utils.TranslationUtils.getThisObject; /** * @author Pavel Talanov @@ -46,24 +47,6 @@ import static org.jetbrains.k2js.translate.utils.TranslationUtils.getThisObject; //TODO: write tests on calling backing fields as functions //TODO: think of the refactoring the class public final class CallTranslator extends AbstractTranslator { - - private static class CallParameters { - - public CallParameters(@Nullable JsExpression receiver, @NotNull JsExpression functionReference) { - this.receiver = receiver; - this.functionReference = functionReference; - } - - @Nullable - public final JsExpression receiver; - @NotNull - public final JsExpression functionReference; - } - - //NOTE: receiver may mean this object as well - @Nullable - private /*var*/ JsExpression receiver; - @Nullable private final JsExpression callee; @@ -79,6 +62,9 @@ public final class CallTranslator extends AbstractTranslator { @NotNull private final CallType callType; + @NotNull + private final CallParameters callParameters; + /*package*/ CallTranslator(@Nullable JsExpression receiver, @Nullable JsExpression callee, @NotNull List arguments, @NotNull ResolvedCall resolvedCall, @@ -86,12 +72,12 @@ public final class CallTranslator extends AbstractTranslator { @NotNull CallType callType, @NotNull TranslationContext context) { super(context); - this.receiver = receiver; this.arguments = arguments; this.resolvedCall = resolvedCall; this.callType = callType; this.descriptor = descriptorToCall; this.callee = callee; + this.callParameters = resolveCallParameters(receiver, callee, descriptor, resolvedCall, context); } @NotNull @@ -114,17 +100,16 @@ public final class CallTranslator extends AbstractTranslator { if (isExpressionAsFunction()) { return expressionAsFunctionCall(); } - return methodCall(callParameters(resolveThisObject(/*just get qualifier if null*/ true))); + return methodCall(getThisObjectOrQualifier()); } private boolean isExpressionAsFunction() { return callee != null; } + @NotNull private JsExpression expressionAsFunctionCall() { - assert callee != null; - CallParameters expressionAsFunctionParameters = new CallParameters(null, callee); - return methodCall(expressionAsFunctionParameters); + return methodCall(null, callee); } private boolean isIntrinsic() { @@ -134,10 +119,8 @@ public final class CallTranslator extends AbstractTranslator { @NotNull private JsExpression intrinsicInvocation() { assert descriptor instanceof FunctionDescriptor; - Intrinsic intrinsic = - context().intrinsics().getFunctionIntrinsic((FunctionDescriptor) descriptor); - JsExpression receiverExpression = resolveThisObject(/*do not get qualifier*/false); - return intrinsic.apply(receiverExpression, arguments, context()); + Intrinsic intrinsic = context().intrinsics().getFunctionIntrinsic((FunctionDescriptor)descriptor); + return intrinsic.apply(callParameters.getThisObject(), arguments, context()); } private boolean isConstructor() { @@ -163,8 +146,7 @@ public final class CallTranslator extends AbstractTranslator { @NotNull private JsExpression nativeExtensionCall() { - receiver = getExtensionFunctionCallReceiver(); - return methodCall(callParameters(resolveThisObject(/*just get qualifier if null = */ true))); + return methodCall(callParameters.getReceiver()); } private boolean isExtensionFunctionLiteral() { @@ -175,8 +157,7 @@ public final class CallTranslator extends AbstractTranslator { @NotNull private JsExpression extensionFunctionLiteralCall() { - JsExpression realReceiver = getExtensionFunctionCallReceiver(); - return callType.constructCall(realReceiver, new CallType.CallConstructor() { + return callType.constructCall(callParameters.getReceiver(), new CallType.CallConstructor() { @NotNull @Override public JsExpression construct(@Nullable JsExpression receiver) { @@ -199,7 +180,7 @@ public final class CallTranslator extends AbstractTranslator { JsNameRef callMethodNameRef = AstUtil.newQualifiedNameRef("call"); JsInvocation callMethodInvocation = new JsInvocation(); callMethodInvocation.setQualifier(callMethodNameRef); - setQualifier(callMethodInvocation, callParameters(resolveThisObject(/*just get qualifier if null*/ true)).functionReference); + setQualifier(callMethodInvocation, callParameters.getFunctionReference()); return callMethodInvocation; } @@ -211,8 +192,7 @@ public final class CallTranslator extends AbstractTranslator { @NotNull private JsExpression extensionFunctionCall() { - JsExpression realReceiver = getExtensionFunctionCallReceiver(); - return callType.constructCall(realReceiver, new CallType.CallConstructor() { + return callType.constructCall(callParameters.getReceiver(), new CallType.CallConstructor() { @NotNull @Override public JsExpression construct(@Nullable JsExpression receiver) { @@ -222,92 +202,47 @@ public final class CallTranslator extends AbstractTranslator { }, context()); } - @NotNull - private JsExpression getExtensionFunctionCallReceiver() { - if (receiver != null) { - JsExpression result = receiver; - //Now the rest of the code can work as if it was simple method invocation - receiver = null; - return result; - } - DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor); - assert expectedReceiverDescriptor != null; - return getThisObject(context(), expectedReceiverDescriptor); - } - @NotNull private JsExpression constructExtensionFunctionCall(@NotNull JsExpression receiver) { List argumentList = generateExtensionCallArgumentList(receiver); - CallParameters callParameters = callParameters(resolveThisObject(/*just get qualifier if null*/ true)); - JsExpression functionReference = callParameters.functionReference; - setQualifier(functionReference, callParameters.receiver); + JsExpression functionReference = callParameters.getFunctionReference(); + setQualifier(functionReference, getThisObjectOrQualifier()); return newInvocation(functionReference, argumentList); } @NotNull private List generateExtensionCallArgumentList(@NotNull JsExpression receiver) { List argumentList = new ArrayList(); - assert this.receiver == null : "Should be null at that point"; argumentList.add(receiver); argumentList.addAll(arguments); return argumentList; } @NotNull - private JsExpression methodCall(@NotNull final CallParameters callParameters) { - return callType.constructCall(callParameters.receiver, new CallType.CallConstructor() { + private JsExpression methodCall(@Nullable JsExpression receiver) { + return methodCall(receiver, callParameters.getFunctionReference()); + } + + @NotNull + private JsExpression methodCall(@Nullable JsExpression receiver, final JsExpression callee) { + return callType.constructCall(receiver, new CallType.CallConstructor() { @NotNull @Override public JsExpression construct(@Nullable JsExpression receiver) { - JsExpression functionReference = callParameters.functionReference; if (receiver != null) { - setQualifier(functionReference, receiver); + setQualifier(callee, receiver); } - return newInvocation(functionReference, arguments); + return newInvocation(callee, arguments); } }, context()); } - @NotNull - private CallParameters callParameters(@Nullable JsExpression receiver) { - JsExpression functionReference = functionReference(); - return new CallParameters(receiver, functionReference); - } - - //TODO: inspect - @NotNull - private JsExpression functionReference() { - if (!isVariableAsFunction(descriptor)) { - return ReferenceTranslator.translateAsLocalNameReference(descriptor, context()); - } - VariableDescriptor variableDescriptor = - getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor) descriptor); - if (variableDescriptor instanceof PropertyDescriptor) { - return getterCall((PropertyDescriptor) variableDescriptor); - } - return ReferenceTranslator.translateAsLocalNameReference(variableDescriptor, context()); - } - - @NotNull - private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) { - //TODO: call type? - return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context()); - } - - //TODO: refactor @Nullable - private JsExpression resolveThisObject(boolean getQualifierIfNull) { - if (receiver != null) { - return receiver; + JsExpression getThisObjectOrQualifier() { + JsExpression thisObject = callParameters.getThisObject(); + if (thisObject != null) { + return thisObject; } - ReceiverDescriptor thisObject = resolvedCall.getThisObject(); - if (thisObject.exists()) { - DeclarationDescriptor expectedThisDescriptor = getDeclarationDescriptorForReceiver(thisObject); - return TranslationUtils.getThisObject(context(), expectedThisDescriptor); - } - if (getQualifierIfNull) { - return context().getQualifierForDescriptor(descriptor); - } - return null; + return context().getQualifierForDescriptor(descriptor); } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/InlinedCallExpressionTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/InlinedCallExpressionTranslator.java index d6e9f9c2a2b..55e00e8f735 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/InlinedCallExpressionTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/InlinedCallExpressionTranslator.java @@ -32,6 +32,7 @@ import org.jetbrains.k2js.translate.context.TemporaryVariable; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.utils.DescriptorUtils; import org.jetbrains.k2js.translate.utils.JsAstUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator; import org.jetbrains.k2js.translate.utils.mutator.Mutator; @@ -115,14 +116,23 @@ public final class InlinedCallExpressionTranslator extends AbstractCallExpressio if (classDescriptorForMethod == null) { return contextForInlining; } - //TODO TranslationContext contextWithAliasForThisExpression = contextForInlining; - TemporaryVariable aliasForThis = contextForInlining.declareTemporary(receiver); + TemporaryVariable aliasForThis = contextWithAliasForThisExpression.declareTemporary(getReceiver()); contextWithAliasForThisExpression = contextForInlining.innerContextWithThisAliased(classDescriptorForMethod, aliasForThis.name()); contextWithAliasForThisExpression.addStatementToCurrentBlock(aliasForThis.assignmentExpression().makeStmt()); return contextWithAliasForThisExpression; } + @NotNull + private JsExpression getReceiver() { + if (receiver != null) { + return receiver; + } + JsExpression result = TranslationUtils.resolveThisObjectForResolvedCall(resolvedCall, context()); + assert result != null; + return result; + } + @NotNull private TemporaryVariable createAliasForArgument(@NotNull ValueParameterDescriptor parameterDescriptor) { ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().get(parameterDescriptor.getIndex()); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index 50dad2a11c1..c036b836989 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -21,6 +21,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.intrinsic.Intrinsic; @@ -32,6 +34,7 @@ import java.util.List; import static com.google.dart.compiler.util.AstUtil.newAssignment; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression; import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptor; +import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getDeclarationDescriptorForReceiver; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getExpectedReceiverDescriptor; import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; @@ -207,4 +210,15 @@ public final class TranslationUtils { PropertyDescriptor propertyDescriptor = getPropertyDescriptor(context.bindingContext(), property); return assignmentToBackingField(context, propertyDescriptor, initExpression); } + + @Nullable + public static JsExpression resolveThisObjectForResolvedCall(@NotNull ResolvedCall call, + @NotNull TranslationContext context) { + ReceiverDescriptor thisObject = call.getThisObject(); + if (!thisObject.exists()) { + return null; + } + DeclarationDescriptor expectedThisDescriptor = getDeclarationDescriptorForReceiver(thisObject); + return TranslationUtils.getThisObject(context, expectedThisDescriptor); + } } diff --git a/js/js.translator/testFiles/inline/cases/methodWithIndirectlyReferencedThis.kt b/js/js.translator/testFiles/inline/cases/methodWithIndirectlyReferencedThis.kt new file mode 100644 index 00000000000..002a31b8932 --- /dev/null +++ b/js/js.translator/testFiles/inline/cases/methodWithIndirectlyReferencedThis.kt @@ -0,0 +1,13 @@ +package foo + +fun box() : Boolean { + return !(A(false).wrap()) and A(true).wrap() +} + +class A(val a : Boolean) { + inline fun myInlineMethod() : Boolean { + return a + } + + fun wrap() = myInlineMethod() +} \ No newline at end of file