JS: fix referencing outer class from secondary constructor

See KT-21041
This commit is contained in:
Alexey Andreev
2017-11-08 20:42:19 +03:00
parent 88441da131
commit ec8adfe7c4
7 changed files with 100 additions and 30 deletions
@@ -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"
}
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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
@@ -268,14 +268,16 @@ class ClassTranslator private constructor(
superCallGenerators += { it += instanceVar }
// Add parameter for outer instance
val leadingArgs = mutableListOf<JsExpression>()
val commonLeadingArgs = mutableListOf<JsExpression>()
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