KT-11030 Fix translation of secondary constructors of inner classes

This commit is contained in:
Alexey Andreev
2016-03-24 14:17:24 +03:00
parent 7af4b2fdb2
commit b39c16cb6c
11 changed files with 100 additions and 85 deletions
-2
View File
@@ -1,5 +1,3 @@
// TODO: Enable for JS when it supports local classes.
// TARGET_BACKEND: JVM
// WITH_RUNTIME // WITH_RUNTIME
fun testFun1(str: String): String { fun testFun1(str: String): String {
@@ -1,6 +1,3 @@
// TODO enable for JS backend too when KT-6203 will be fixed
// TARGET_BACKEND: JVM
class A(val result: Int) { class A(val result: Int) {
object B { object B {
fun bar(): Int = 4 fun bar(): Int = 4
@@ -1,6 +1,3 @@
// TODO enable for JS backend too when KT-4124 will be fixed
// TARGET_BACKEND: JVM
class Outer { class Outer {
val outerProp: String val outerProp: String
constructor(x: String) { constructor(x: String) {
@@ -1,6 +1,3 @@
// TODO enable for JS backend too when KT-4124 will be fixed
// TARGET_BACKEND: JVM
class Outer { class Outer {
val outerProp: String val outerProp: String
constructor(x: String) { constructor(x: String) {
@@ -1,6 +1,3 @@
// TODO enable for JS backend too when KT-4124 will be fixed
// TARGET_BACKEND: JVM
open class C(val grandParentProp: String) open class C(val grandParentProp: String)
fun box(): String { fun box(): String {
var sideEffects: String = "" var sideEffects: String = ""
@@ -31,6 +31,12 @@ import java.util.regex.Pattern;
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public class ClassesTestGenerated extends AbstractClassesTest { public class ClassesTestGenerated extends AbstractClassesTest {
@TestMetadata("classCompanionInitializationWithJava.kt")
public void ignoredClassCompanionInitializationWithJava() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/classCompanionInitializationWithJava.kt");
doTest(fileName);
}
@TestMetadata("classObjectAsStaticInitializer.kt") @TestMetadata("classObjectAsStaticInitializer.kt")
public void ignoredClassObjectAsStaticInitializer() throws Exception { public void ignoredClassObjectAsStaticInitializer() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/classObjectAsStaticInitializer.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/classObjectAsStaticInitializer.kt");
@@ -569,6 +575,12 @@ public class ClassesTestGenerated extends AbstractClassesTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kt8011.kt")
public void testKt8011() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/kt8011.kt");
doTest(fileName);
}
@TestMetadata("kt8011a.kt") @TestMetadata("kt8011a.kt")
public void testKt8011a() throws Exception { public void testKt8011a() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/kt8011a.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/kt8011a.kt");
@@ -31,30 +31,6 @@ import java.util.regex.Pattern;
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public class SecondaryConstructorTestGenerated extends AbstractSecondaryConstructorTest { public class SecondaryConstructorTestGenerated extends AbstractSecondaryConstructorTest {
@TestMetadata("accessToNestedObject.kt")
public void ignoredAccessToNestedObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/accessToNestedObject.kt");
doTest(fileName);
}
@TestMetadata("innerClasses.kt")
public void ignoredInnerClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt");
doTest(fileName);
}
@TestMetadata("innerClassesInheritance.kt")
public void ignoredInnerClassesInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt");
doTest(fileName);
}
@TestMetadata("localClasses.kt")
public void ignoredLocalClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/localClasses.kt");
doTest(fileName);
}
@TestMetadata("withGenerics.kt") @TestMetadata("withGenerics.kt")
public void ignoredWithGenerics() throws Exception { public void ignoredWithGenerics() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/withGenerics.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/withGenerics.kt");
@@ -91,6 +67,12 @@ public class SecondaryConstructorTestGenerated extends AbstractSecondaryConstruc
doTest(fileName); doTest(fileName);
} }
@TestMetadata("accessToNestedObject.kt")
public void testAccessToNestedObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/accessToNestedObject.kt");
doTest(fileName);
}
public void testAllFilesPresentInSecondaryConstructors() throws Exception { public void testAllFilesPresentInSecondaryConstructors() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/secondaryConstructors"), Pattern.compile("^(.+)\\.kt$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/secondaryConstructors"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@@ -149,6 +131,24 @@ public class SecondaryConstructorTestGenerated extends AbstractSecondaryConstruc
doTest(fileName); doTest(fileName);
} }
@TestMetadata("innerClasses.kt")
public void testInnerClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt");
doTest(fileName);
}
@TestMetadata("innerClassesInheritance.kt")
public void testInnerClassesInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt");
doTest(fileName);
}
@TestMetadata("localClasses.kt")
public void testLocalClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/localClasses.kt");
doTest(fileName);
}
@TestMetadata("superCallPrimary.kt") @TestMetadata("superCallPrimary.kt")
public void testSuperCallPrimary() throws Exception { public void testSuperCallPrimary() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt");
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -58,8 +59,6 @@ public class TranslationContext {
private final DeclarationDescriptor declarationDescriptor; private final DeclarationDescriptor declarationDescriptor;
@Nullable @Nullable
private final ClassDescriptor classDescriptor; private final ClassDescriptor classDescriptor;
@Nullable
private final ClassDescriptor objectDescriptor;
@NotNull @NotNull
public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) { public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) {
@@ -86,20 +85,12 @@ public class TranslationContext {
this.usageTracker = usageTracker; this.usageTracker = usageTracker;
this.definitionPlace = definitionPlace; this.definitionPlace = definitionPlace;
this.declarationDescriptor = declarationDescriptor; this.declarationDescriptor = declarationDescriptor;
if (declarationDescriptor instanceof ClassDescriptor if (declarationDescriptor instanceof ClassDescriptor) {
&& !DescriptorUtils.isAnonymousObject(declarationDescriptor)
&& !DescriptorUtils.isObject(declarationDescriptor)) {
this.classDescriptor = (ClassDescriptor) declarationDescriptor; this.classDescriptor = (ClassDescriptor) declarationDescriptor;
} }
else { else {
this.classDescriptor = parent != null ? parent.classDescriptor : null; this.classDescriptor = parent != null ? parent.classDescriptor : null;
} }
if (declarationDescriptor instanceof ClassDescriptor && DescriptorUtils.isObject(declarationDescriptor)) {
this.objectDescriptor = (ClassDescriptor) declarationDescriptor;
}
else {
this.objectDescriptor = parent != null ? parent.objectDescriptor : null;
}
} }
@Nullable @Nullable
@@ -361,7 +352,20 @@ public class TranslationContext {
return getQualifiedReference(descriptor.getContainingDeclaration()); return getQualifiedReference(descriptor.getContainingDeclaration());
} }
} }
return getDispatchReceiverPath(getNearestClass(descriptor));
if (descriptor.getValue() instanceof ExtensionReceiver) return JsLiteral.THIS;
ClassifierDescriptor classifier = descriptor.getValue().getType().getConstructor().getDeclarationDescriptor();
if (!(classifier instanceof ClassDescriptor)) return JsLiteral.THIS;
ClassDescriptor cls = (ClassDescriptor) classifier;
JsExpression receiver = getAliasForDescriptor(cls.getThisAsReceiverParameter());
if (receiver == null) {
receiver = JsLiteral.THIS;
}
return getDispatchReceiverPath(cls, receiver);
} }
private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) { private boolean isConstructorOrDirectScope(DeclarationDescriptor descriptor) {
@@ -374,7 +378,7 @@ public class TranslationContext {
} }
@NotNull @NotNull
private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls) { private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression) {
if (cls != null) { if (cls != null) {
JsExpression alias = getAliasForDescriptor(cls); JsExpression alias = getAliasForDescriptor(cls);
if (alias != null) { if (alias != null) {
@@ -385,14 +389,14 @@ public class TranslationContext {
(classDescriptor != null && (classDescriptor != null &&
cls != null && DescriptorUtils.isSubclass(classDescriptor, cls)) || cls != null && DescriptorUtils.isSubclass(classDescriptor, cls)) ||
parent == null) { parent == null) {
return JsLiteral.THIS; return thisExpression;
} }
ClassDescriptor parentDescriptor = parent.classDescriptor; ClassDescriptor parentDescriptor = parent.classDescriptor;
if (classDescriptor != parentDescriptor) { if (classDescriptor != parentDescriptor) {
return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls)); return new JsNameRef(Namer.OUTER_FIELD_NAME, parent.getDispatchReceiverPath(cls, thisExpression));
} }
else { else {
return parent.getDispatchReceiverPath(cls); return parent.getDispatchReceiverPath(cls, thisExpression);
} }
} }
@@ -442,20 +446,6 @@ public class TranslationContext {
return true; return true;
} }
@Nullable
private static ClassDescriptor getNearestClass(@NotNull DeclarationDescriptor declaration) {
DeclarationDescriptor decl = declaration;
while (decl != null) {
if (decl instanceof ClassDescriptor) {
if (!DescriptorUtils.isAnonymousObject(decl) && !DescriptorUtils.isObject(decl)) {
return (ClassDescriptor) decl;
}
}
decl = decl.getContainingDeclaration();
}
return null;
}
@Nullable @Nullable
public DeclarationDescriptor getDeclarationDescriptor() { public DeclarationDescriptor getDeclarationDescriptor() {
return declarationDescriptor; return declarationDescriptor;
@@ -482,4 +472,14 @@ public class TranslationContext {
public JsScope getRootScope() { public JsScope getRootScope() {
return staticContext.getRootScope(); return staticContext.getRootScope();
} }
@Nullable
public JsName getOuterClassReference(ClassDescriptor descriptor) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (!(container instanceof ClassDescriptor) || !descriptor.isInner()) {
return null;
}
return staticContext.getScopeForDescriptor(descriptor).declareName(Namer.OUTER_FIELD_NAME);
}
} }
@@ -301,35 +301,54 @@ class ClassTranslator private constructor(
val constructorScope = context.getScopeForDescriptor(constructorDescriptor) val constructorScope = context.getScopeForDescriptor(constructorDescriptor)
val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME) val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME)
val thisNameRef = thisName.makeRef() val thisNameRef = thisName.makeRef()
val receiverDescriptor = JsDescriptorUtils.getReceiverParameterForDeclaration(classDescriptor) val receiverDescriptor = classDescriptor.thisAsReceiverParameter
val translationContext = context.innerContextWithAliased(receiverDescriptor, thisNameRef)
var translationContext = context
translationContext = translationContext.newDeclaration(constructorDescriptor.containingDeclaration,
translationContext.definitionPlace)
translationContext = translationContext.newDeclaration(constructorDescriptor, translationContext.definitionPlace)
translationContext = translationContext.innerContextWithAliased(receiverDescriptor, thisNameRef)
val outerName = translationContext.getOuterClassReference(classDescriptor);
val outerClass = DescriptorUtils.getContainingClass(classDescriptor)
if (outerClass != null && outerName != null) {
val outerClassRef = outerClass.thisAsReceiverParameter
translationContext = translationContext.innerContextWithAliased(outerClassRef, outerName.makeRef())
}
val constructorInitializer = FunctionTranslator.newInstance(constructor, translationContext).translateAsMethod() val constructorInitializer = FunctionTranslator.newInstance(constructor, translationContext).translateAsMethod()
val constructorFunction = constructorInitializer.valueExpr as JsFunction val constructorFunction = constructorInitializer.valueExpr as JsFunction
val leadingArgs = mutableListOf<JsExpression>()
if (outerName != null) {
constructorFunction.parameters.add(0, JsParameter(outerName))
leadingArgs.add(outerName.makeRef())
}
constructorFunction.parameters.add(JsParameter(thisName)) constructorFunction.parameters.add(JsParameter(thisName))
val referenceToClass = context.getQualifiedReference(classDescriptor) val referenceToClass = translationContext.getQualifiedReference(classDescriptor)
val forAddToBeginning: List<JsStatement> = val forAddToBeginning: List<JsStatement> =
with(arrayListOf<JsStatement>()) { with(arrayListOf<JsStatement>()) {
addAll(FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, context)) addAll(FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, translationContext))
val createInstance = Namer.createObjectWithPrototypeFrom(referenceToClass) val createInstance = Namer.createObjectWithPrototypeFrom(referenceToClass)
val instanceVar = JsAstUtils.assignment(thisNameRef, JsAstUtils.or(thisNameRef, createInstance)).makeStmt() val instanceVar = JsAstUtils.assignment(thisNameRef, JsAstUtils.or(thisNameRef, createInstance)).makeStmt()
add(instanceVar) add(instanceVar)
val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context.bindingContext(), constructorDescriptor) val resolvedCall = BindingContextUtils.getDelegationConstructorCall(translationContext.bindingContext(),
constructorDescriptor)
val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration
if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) { if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) {
val superCall = CallTranslator.translate(context, resolvedCall) val superCall = CallTranslator.translate(translationContext, resolvedCall)
add(superCall.toInvocationWith(thisNameRef).makeStmt()) add(superCall.toInvocationWith(leadingArgs, thisNameRef).makeStmt())
} }
val delegationCtorInTheSameClass = delegationClassDescriptor == classDescriptor val delegationCtorInTheSameClass = delegationClassDescriptor == classDescriptor
if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) { if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) {
add(JsInvocation(Namer.getFunctionCallRef(referenceToClass), thisNameRef).makeStmt()) add(JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + leadingArgs).makeStmt())
} }
this this
@@ -120,12 +120,9 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
} }
private void addOuterClassReference(ClassDescriptor classDescriptor) { private void addOuterClassReference(ClassDescriptor classDescriptor) {
DeclarationDescriptor container = classDescriptor.getContainingDeclaration(); JsName outerName = context.getOuterClassReference(classDescriptor);
if (!(container instanceof ClassDescriptor) || !classDescriptor.isInner()) { if (outerName == null) return;
return;
}
JsName outerName = initFunction.getScope().declareFreshName(Namer.OUTER_FIELD_NAME);
initFunction.getParameters().add(0, new JsParameter(outerName)); initFunction.getParameters().add(0, new JsParameter(outerName));
JsExpression paramRef = fqnWithoutSideEffects(outerName, null); JsExpression paramRef = fqnWithoutSideEffects(outerName, null);
@@ -157,7 +154,8 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
if (classDeclaration instanceof KtEnumEntry) { if (classDeclaration instanceof KtEnumEntry) {
JsExpression expression = CallTranslator.translate(context(), superCall, null); JsExpression expression = CallTranslator.translate(context(), superCall, null);
JsExpression fixedInvocation = AstUtilsKt.toInvocationWith(expression, JsLiteral.THIS); JsExpression fixedInvocation = AstUtilsKt.toInvocationWith(expression, Collections.<JsExpression>emptyList(),
JsLiteral.THIS);
initializerStatements.add(0, fixedInvocation.makeStmt()); initializerStatements.add(0, fixedInvocation.makeStmt());
} }
else { else {
@@ -56,7 +56,7 @@ fun JsNode.any(predicate: (JsNode) -> Boolean): Boolean {
return visitor.matched return visitor.matched
} }
fun JsExpression.toInvocationWith(thisExpr: JsExpression): JsExpression { fun JsExpression.toInvocationWith(leadingExtraArgs: List<JsExpression>, thisExpr: JsExpression): JsExpression {
val qualifier: JsExpression val qualifier: JsExpression
val arguments: MutableList<JsExpression> val arguments: MutableList<JsExpression>
@@ -65,13 +65,13 @@ fun JsExpression.toInvocationWith(thisExpr: JsExpression): JsExpression {
qualifier = Namer.getFunctionCallRef(constructorExpression) qualifier = Namer.getFunctionCallRef(constructorExpression)
arguments = getArguments() arguments = getArguments()
// `new A(a, b, c)` -> `A.call($this, a, b, c)` // `new A(a, b, c)` -> `A.call($this, a, b, c)`
return JsInvocation(qualifier, listOf(thisExpr) + arguments) return JsInvocation(qualifier, listOf(thisExpr) + leadingExtraArgs + arguments)
} }
is JsInvocation -> { is JsInvocation -> {
qualifier = getQualifier() qualifier = getQualifier()
arguments = getArguments() arguments = getArguments()
// `A(a, b, c)` -> `A(a, b, c, $this)` // `A(a, b, c)` -> `A(a, b, c, $this)`
return JsInvocation(qualifier, arguments + thisExpr) return JsInvocation(qualifier, leadingExtraArgs + arguments + thisExpr)
} }
else -> throw IllegalStateException("Unexpected node type: " + javaClass) else -> throw IllegalStateException("Unexpected node type: " + javaClass)
} }