JS: fix referencing outer class from secondary constructor
See KT-21041
This commit is contained in:
@@ -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"
|
||||||
|
}
|
||||||
+6
@@ -10151,6 +10151,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("protectedNestedClass.kt")
|
||||||
public void testProtectedNestedClass() throws Exception {
|
public void testProtectedNestedClass() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt");
|
||||||
|
|||||||
@@ -10151,6 +10151,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("protectedNestedClass.kt")
|
||||||
public void testProtectedNestedClass() throws Exception {
|
public void testProtectedNestedClass() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt");
|
||||||
|
|||||||
@@ -10151,6 +10151,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("protectedNestedClass.kt")
|
||||||
public void testProtectedNestedClass() throws Exception {
|
public void testProtectedNestedClass() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt");
|
||||||
|
|||||||
@@ -11075,6 +11075,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("protectedNestedClass.kt")
|
||||||
public void testProtectedNestedClass() throws Exception {
|
public void testProtectedNestedClass() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/innerNested/protectedNestedClass.kt");
|
||||||
|
|||||||
+17
-27
@@ -476,11 +476,6 @@ public class TranslationContext {
|
|||||||
return tempVar;
|
return tempVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) {
|
|
||||||
assert expression == temporaryConstVariable.assignmentExpression();
|
|
||||||
expressionToTempConstVariableCache.put(expression, temporaryConstVariable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Namer namer() {
|
public Namer namer() {
|
||||||
return staticContext.getNamer();
|
return staticContext.getNamer();
|
||||||
@@ -586,14 +581,8 @@ public class TranslationContext {
|
|||||||
assert classifier instanceof ClassDescriptor;
|
assert classifier instanceof ClassDescriptor;
|
||||||
|
|
||||||
ClassDescriptor cls = (ClassDescriptor) classifier;
|
ClassDescriptor cls = (ClassDescriptor) classifier;
|
||||||
|
assert classDescriptor != null;
|
||||||
assert classDescriptor != null : "Can't get ReceiverParameterDescriptor in top level";
|
return getDispatchReceiverPath(classDescriptor, cls, new JsThisRef());
|
||||||
JsExpression receiver = getAliasForDescriptor(classDescriptor.getThisAsReceiverParameter());
|
|
||||||
if (receiver == null) {
|
|
||||||
receiver = new JsThisRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
return getDispatchReceiverPath(cls, receiver);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
|
private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
|
||||||
@@ -601,24 +590,25 @@ public class TranslationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression) {
|
private JsExpression getDispatchReceiverPath(
|
||||||
if (cls != null) {
|
@NotNull ClassDescriptor from, @NotNull ClassDescriptor to,
|
||||||
JsExpression alias = getAliasForDescriptor(cls);
|
@NotNull JsExpression qualifier
|
||||||
|
) {
|
||||||
|
while (true) {
|
||||||
|
JsExpression alias = getAliasForDescriptor(from.getThisAsReceiverParameter());
|
||||||
if (alias != null) {
|
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 qualifier;
|
||||||
return thisExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (classDescriptor != parent.classDescriptor) {
|
|
||||||
return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return parent.getDispatchReceiverPath(cls, thisExpression);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
+12
-3
@@ -268,14 +268,16 @@ class ClassTranslator private constructor(
|
|||||||
superCallGenerators += { it += instanceVar }
|
superCallGenerators += { it += instanceVar }
|
||||||
|
|
||||||
// Add parameter for outer instance
|
// Add parameter for outer instance
|
||||||
val leadingArgs = mutableListOf<JsExpression>()
|
val commonLeadingArgs = mutableListOf<JsExpression>()
|
||||||
|
|
||||||
if (descriptor.kind == ClassKind.ENUM_CLASS) {
|
if (descriptor.kind == ClassKind.ENUM_CLASS) {
|
||||||
val nameParamName = JsScope.declareTemporaryName("name")
|
val nameParamName = JsScope.declareTemporaryName("name")
|
||||||
val ordinalParamName = JsScope.declareTemporaryName("ordinal")
|
val ordinalParamName = JsScope.declareTemporaryName("ordinal")
|
||||||
constructorInitializer.parameters.addAll(0, listOf(JsParameter(nameParamName), JsParameter(ordinalParamName)))
|
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) {
|
if (outerClassName != null) {
|
||||||
constructorInitializer.parameters.add(0, JsParameter(outerClassName))
|
constructorInitializer.parameters.add(0, JsParameter(outerClassName))
|
||||||
leadingArgs += outerClassName.makeRef()
|
leadingArgs += outerClassName.makeRef()
|
||||||
@@ -301,8 +303,15 @@ class ClassTranslator private constructor(
|
|||||||
superCallGenerators += {
|
superCallGenerators += {
|
||||||
val delegationConstructor = resolvedCall.resultingDescriptor
|
val delegationConstructor = resolvedCall.resultingDescriptor
|
||||||
val innerContext = context.innerBlock()
|
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(
|
val statement = CallTranslator.translate(innerContext, resolvedCall).toInvocationWith(
|
||||||
leadingArgs, delegationConstructor.valueParameters.size, thisNameRef.deepCopy())
|
delegatedLeadingArgs, delegationConstructor.valueParameters.size, thisNameRef.deepCopy())
|
||||||
.source(resolvedCall.call.callElement)
|
.source(resolvedCall.call.callElement)
|
||||||
.makeStmt()
|
.makeStmt()
|
||||||
it += innerContext.currentBlock.statements
|
it += innerContext.currentBlock.statements
|
||||||
|
|||||||
Reference in New Issue
Block a user