diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NestedTypesTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NestedTypesTest.java index dc64e100719..dcb6294a707 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NestedTypesTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NestedTypesTest.java @@ -94,6 +94,10 @@ public class NestedTypesTest extends SingleFileTranslationTest { checkFooBoxIsOk(); } + public void testReceivers() throws Exception { + checkFooBoxIsOk(); + } + @NotNull @Override protected List additionalJsFiles(@NotNull EcmaVersion ecmaVersion) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfo.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfo.kt index a22ba45d10f..2fc6ce058f0 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfo.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfo.kt @@ -21,7 +21,6 @@ import com.google.dart.compiler.backend.js.ast.JsConditional import com.google.dart.compiler.backend.js.ast.JsExpression import com.google.dart.compiler.backend.js.ast.JsLiteral import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.js.translate.context.TranslationContext @@ -32,7 +31,6 @@ import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* -import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue interface CallInfo { @@ -101,8 +99,8 @@ fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall, explicitReceivers: ExplicitReceivers): CallInfo { @@ -111,17 +109,11 @@ private fun TranslationContext.createCallInfo(resolvedCall: ResolvedCall explicitReceivers.extensionOrDispatchReceiver - else -> this.getDispatchReceiver(receiverValue, isConstructorCall()) + else -> getDispatchReceiver(receiverValue) } } @@ -130,7 +122,7 @@ private fun TranslationContext.createCallInfo(resolvedCall: ResolvedCall explicitReceivers.extensionOrDispatchReceiver BOTH_RECEIVERS -> explicitReceivers.extensionReceiver - else -> this.getDispatchReceiver(receiverValue, isConstructorCall()) + else -> getDispatchReceiver(receiverValue) } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index 300450981fb..6123862781c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -339,17 +339,7 @@ public class TranslationContext { } @NotNull - public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor, boolean allowSuperCall) { - // I don't see any reason for descriptor being treated inconsistently for different cases of call. - // descriptor should always point on the exact class. I.e., in the code - // - // class A { inner class B : A { foo() } } - // - // implicit receiver for `foo` must always point to either `A` or `B` depending on where `foo()` is picked by resolver. - // However, it's not always true. According to ExpressionCodegen, this rule is violated in the case of constructor call. - // It's reasonable since there won't be any ambiguity in this case, but it's simply inconsistent. - // TODO: avoid `allowSuperCall` by convincing people to alter behaviour of frontend - + public JsExpression getDispatchReceiver(@NotNull ReceiverParameterDescriptor descriptor) { JsExpression alias = getAliasForDescriptor(descriptor); if (alias != null) { return alias; @@ -378,7 +368,7 @@ public class TranslationContext { receiver = JsLiteral.THIS; } - return getDispatchReceiverPath(cls, receiver, allowSuperCall); + return getDispatchReceiverPath(cls, receiver); } private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) { @@ -391,24 +381,24 @@ public class TranslationContext { } @NotNull - private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression, boolean allowSuperCall) { + private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression) { if (cls != null) { JsExpression alias = getAliasForDescriptor(cls); if (alias != null) { return alias; } } - if (classDescriptor == cls || - (allowSuperCall && classDescriptor != null && cls != null && DescriptorUtils.isSubclass(classDescriptor, cls)) || - parent == null) { + + if (classDescriptor == cls || parent == null) { return thisExpression; } + ClassDescriptor parentDescriptor = parent.classDescriptor; if (classDescriptor != parentDescriptor) { - return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression, allowSuperCall)); + return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression)); } else { - return parent.getDispatchReceiverPath(cls, thisExpression, allowSuperCall); + return parent.getDispatchReceiverPath(cls, thisExpression); } } @@ -502,7 +492,7 @@ public class TranslationContext { JsExpression alias = getAliasForDescriptor(descriptor); if (alias != null) return alias; if (descriptor instanceof ReceiverParameterDescriptor) { - return getDispatchReceiver((ReceiverParameterDescriptor) descriptor, false); + return getDispatchReceiver((ReceiverParameterDescriptor) descriptor); } return getNameForDescriptor(descriptor).makeRef(); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index 2eb6d546daa..f4c279ad1cb 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -23,11 +23,6 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.ClassifierDescriptor; -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; -import org.jetbrains.kotlin.descriptors.FunctionDescriptor; -import org.jetbrains.kotlin.descriptors.VariableDescriptor; -import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention; @@ -69,8 +64,10 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatem import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.newVar; import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration; import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateInitializerForProperty; -import static org.jetbrains.kotlin.resolve.BindingContext.*; +import static org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR; +import static org.jetbrains.kotlin.resolve.BindingContext.LABEL_TARGET; import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure; +import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt.getResolvedCallWithAssert; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionExpression; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral; @@ -489,8 +486,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference()); assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText(); - // TODO: not sure if `false` is a proper argument here, revisit this code later - return context.getDispatchReceiver(getReceiverParameterForDeclaration(thisExpression), false).source(expression); + return context.getDispatchReceiver(getReceiverParameterForDeclaration(thisExpression)).source(expression); } @Override @@ -503,11 +499,8 @@ public final class ExpressionVisitor extends TranslatorVisitor { @Override @NotNull public JsNode visitSuperExpression(@NotNull KtSuperExpression expression, @NotNull TranslationContext context) { - DeclarationDescriptor superTarget = getSuperTarget(context, expression); - ReceiverParameterDescriptor receiver = getReceiverParameterForDeclaration(superTarget); - - // TODO: not sure if `true` is a proper argument here, revisit this code later - return context.getDispatchReceiver(receiver, true); + ResolvedCall resolvedCall = getResolvedCallWithAssert(expression, context.bindingContext()); + return context.getDispatchReceiver((ReceiverParameterDescriptor) resolvedCall.getResultingDescriptor()); } @Override @@ -600,18 +593,6 @@ public final class ExpressionVisitor extends TranslatorVisitor { return super.visitAnnotatedExpression(expression, context); } - @NotNull - private static DeclarationDescriptor getSuperTarget(TranslationContext context, KtSuperExpression expression) { - BindingContext bindingContext = context.bindingContext(); - PsiElement labelPsi = bindingContext.get(LABEL_TARGET, expression.getTargetLabel()); - ClassDescriptor labelTarget = (ClassDescriptor) bindingContext.get(DECLARATION_TO_DESCRIPTOR, labelPsi); - if (labelTarget != null) return labelTarget; - - DeclarationDescriptor descriptor = bindingContext.get(REFERENCE_TARGET, expression.getInstanceReference()); - assert descriptor != null : "Missing declaration descriptor: " + PsiUtilsKt.getTextWithLocation(expression); - return descriptor; - } - @Override public JsNode visitClass(@NotNull KtClass klass, TranslationContext context) { ClassDescriptor descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), klass); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index 2051812abb1..326770a9026 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -165,7 +165,7 @@ public final class TranslationUtils { receiver = JsLiteral.THIS; } else { - receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor), false); + receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor)); } return new JsNameRef(backingFieldName, receiver); } diff --git a/js/js.translator/testData/nestedTypes/cases/receivers.kt b/js/js.translator/testData/nestedTypes/cases/receivers.kt new file mode 100644 index 00000000000..3a9ddba5ce3 --- /dev/null +++ b/js/js.translator/testData/nestedTypes/cases/receivers.kt @@ -0,0 +1,35 @@ +package foo + +open class D { + open val name = "D" + fun boo() = "D[$name]::boo;" +} + +open class Z : D() { + override val name = "Z" + fun bar() = "Z[$name]::bar;" + private fun baz() = "Z[$name]::baz;" + + inner class X : Z() { + override val name = "X" + fun foo() = "X[$name]::foo;" + + fun test() { + assertEquals("X[X]::foo;", this.foo()) + assertEquals("Z[Z]::bar;", this@Z.bar()) + + assertEquals("Z[X]::bar;", super.bar()) + assertEquals("Z[X]::bar;", super.bar()) + assertEquals("D[Z]::boo;", super@Z.boo()) + + assertEquals("X[X]::foo;", foo()) + assertEquals("Z[X]::bar;", bar()) + assertEquals("Z[Z]::baz;", baz()) + } + } +} + +fun box(): String { + Z().X().test() + return "OK" +}