diff --git a/compiler/testData/codegen/box/innerNested/passingOuterRef.kt b/compiler/testData/codegen/box/innerNested/passingOuterRef.kt new file mode 100644 index 00000000000..64ed58c7f8d --- /dev/null +++ b/compiler/testData/codegen/box/innerNested/passingOuterRef.kt @@ -0,0 +1,47 @@ +open class A1(y: String) { + val x = "A1.x,$y" +} + +open class A2(y: String) { + val x = "A2.x,$y" + + inner open class B1 : A1 { + constructor(p: String) : super("B1.param,$p") + } + + inner open class B2 : A2 { + constructor(p: String) : super("B2.param,$p") + } + + inner class B3 : B1 { + constructor(p: String) : super("B3.param,$p") + } + + fun foo(): String { + return B1("q").x + ";" + B2("w").x + ";" + B3("e").x + ";" + x + } +} + +open class A3(y: String) { + val x = "A3.x,$y" + + inner open class B1(p: String) : A1("B1.param,$p") + + inner open class B2(p: String) : A3("B2.param,$p") + + inner class B3(p: String) : B1("B3.param,$p") + + fun foo(): String { + return B1("q").x + ";" + B2("w").x + ";" + B3("e").x + ";" + x + } +} + +fun box(): String { + val r1 = A2("c").foo() + if (r1 != "A1.x,B1.param,q;A2.x,B2.param,w;A1.x,B1.param,B3.param,e;A2.x,c") return "fail1: $r1" + + val r2 = A3("d").foo() + if (r2 != "A1.x,B1.param,q;A3.x,B2.param,w;A1.x,B1.param,B3.param,e;A3.x,d") return "fail2: $r2" + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 7cd532b6d9b..1124c391897 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -10151,6 +10151,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("passingOuterRef.kt") + public void testPassingOuterRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/passingOuterRef.kt"); + doTest(fileName); + } + @TestMetadata("protectedNestedClass.kt") public void testProtectedNestedClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 6db5a649145..e879650d311 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -10151,6 +10151,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("passingOuterRef.kt") + public void testPassingOuterRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/passingOuterRef.kt"); + doTest(fileName); + } + @TestMetadata("protectedNestedClass.kt") public void testProtectedNestedClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 12569f860f3..c614b0b3c9c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -10151,6 +10151,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("passingOuterRef.kt") + public void testPassingOuterRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/passingOuterRef.kt"); + doTest(fileName); + } + @TestMetadata("protectedNestedClass.kt") public void testProtectedNestedClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index d853e452b9b..e78a6b64a67 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -11075,6 +11075,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("passingOuterRef.kt") + public void testPassingOuterRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/passingOuterRef.kt"); + doTest(fileName); + } + @TestMetadata("protectedNestedClass.kt") public void testProtectedNestedClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt"); 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 79fd2da80a5..0f61a1c8db5 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 @@ -476,11 +476,6 @@ public class TranslationContext { return tempVar; } - public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) { - assert expression == temporaryConstVariable.assignmentExpression(); - expressionToTempConstVariableCache.put(expression, temporaryConstVariable); - } - @NotNull public Namer namer() { return staticContext.getNamer(); @@ -586,14 +581,8 @@ public class TranslationContext { assert classifier instanceof ClassDescriptor; ClassDescriptor cls = (ClassDescriptor) classifier; - - assert classDescriptor != null : "Can't get ReceiverParameterDescriptor in top level"; - JsExpression receiver = getAliasForDescriptor(classDescriptor.getThisAsReceiverParameter()); - if (receiver == null) { - receiver = new JsThisRef(); - } - - return getDispatchReceiverPath(cls, receiver); + assert classDescriptor != null; + return getDispatchReceiverPath(classDescriptor, cls, new JsThisRef()); } private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) { @@ -601,24 +590,25 @@ public class TranslationContext { } @NotNull - private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression) { - if (cls != null) { - JsExpression alias = getAliasForDescriptor(cls); + private JsExpression getDispatchReceiverPath( + @NotNull ClassDescriptor from, @NotNull ClassDescriptor to, + @NotNull JsExpression qualifier + ) { + while (true) { + JsExpression alias = getAliasForDescriptor(from.getThisAsReceiverParameter()); if (alias != null) { - return alias; + qualifier = alias; } + + if (from == to || !from.isInner() || !(from.getContainingDeclaration() instanceof ClassDescriptor)) { + break; + } + + qualifier = new JsNameRef(Namer.OUTER_FIELD_NAME, qualifier); + from = (ClassDescriptor) from.getContainingDeclaration(); } - if (classDescriptor == cls || parent == null) { - return thisExpression; - } - - if (classDescriptor != parent.classDescriptor) { - return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression)); - } - else { - return parent.getDispatchReceiverPath(cls, thisExpression); - } + return qualifier; } @Nullable diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index 00e2917e9b9..9759b5c81cb 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -268,14 +268,16 @@ class ClassTranslator private constructor( superCallGenerators += { it += instanceVar } // Add parameter for outer instance - val leadingArgs = mutableListOf() + val commonLeadingArgs = mutableListOf() if (descriptor.kind == ClassKind.ENUM_CLASS) { val nameParamName = JsScope.declareTemporaryName("name") val ordinalParamName = JsScope.declareTemporaryName("ordinal") constructorInitializer.parameters.addAll(0, listOf(JsParameter(nameParamName), JsParameter(ordinalParamName))) - leadingArgs += listOf(nameParamName.makeRef().withDefaultLocation(), ordinalParamName.makeRef().withDefaultLocation()) + commonLeadingArgs += listOf(nameParamName.makeRef().withDefaultLocation(), ordinalParamName.makeRef().withDefaultLocation()) } + + val leadingArgs = commonLeadingArgs.toMutableList() if (outerClassName != null) { constructorInitializer.parameters.add(0, JsParameter(outerClassName)) leadingArgs += outerClassName.makeRef() @@ -301,8 +303,15 @@ class ClassTranslator private constructor( superCallGenerators += { val delegationConstructor = resolvedCall.resultingDescriptor val innerContext = context.innerBlock() + val delegatedLeadingArgs = commonLeadingArgs.toMutableList() + val delegateClass = resolvedCall.resultingDescriptor.constructedClass + if (delegateClass.isInner) { + val delegatedOuterDescriptor = (delegateClass.containingDeclaration as ClassDescriptor).thisAsReceiverParameter + delegatedLeadingArgs += context.getDispatchReceiver(delegatedOuterDescriptor) + } + val statement = CallTranslator.translate(innerContext, resolvedCall).toInvocationWith( - leadingArgs, delegationConstructor.valueParameters.size, thisNameRef.deepCopy()) + delegatedLeadingArgs, delegationConstructor.valueParameters.size, thisNameRef.deepCopy()) .source(resolvedCall.call.callElement) .makeStmt() it += innerContext.currentBlock.statements