From b39c16cb6cd645b808965d152830ff936a5dd49f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 24 Mar 2016 14:17:24 +0300 Subject: [PATCH] KT-11030 Fix translation of secondary constructors of inner classes --- .../testData/codegen/box/classes/kt8011.kt | 2 - .../accessToNestedObject.kt | 3 - .../box/secondaryConstructors/innerClasses.kt | 3 - .../innerClassesInheritance.kt | 3 - .../box/secondaryConstructors/localClasses.kt | 3 - .../test/semantics/ClassesTestGenerated.java | 12 ++++ .../SecondaryConstructorTestGenerated.java | 48 +++++++-------- .../translate/context/TranslationContext.java | 60 +++++++++---------- .../translate/declaration/ClassTranslator.kt | 35 ++++++++--- .../ClassInitializerTranslator.java | 10 ++-- .../kotlin/js/translate/utils/astUtils.kt | 6 +- 11 files changed, 100 insertions(+), 85 deletions(-) diff --git a/compiler/testData/codegen/box/classes/kt8011.kt b/compiler/testData/codegen/box/classes/kt8011.kt index 602a4a2894a..e4ab453aa33 100644 --- a/compiler/testData/codegen/box/classes/kt8011.kt +++ b/compiler/testData/codegen/box/classes/kt8011.kt @@ -1,5 +1,3 @@ -// TODO: Enable for JS when it supports local classes. -// TARGET_BACKEND: JVM // WITH_RUNTIME fun testFun1(str: String): String { diff --git a/compiler/testData/codegen/box/secondaryConstructors/accessToNestedObject.kt b/compiler/testData/codegen/box/secondaryConstructors/accessToNestedObject.kt index 2a093d7f477..3093181e4bd 100644 --- a/compiler/testData/codegen/box/secondaryConstructors/accessToNestedObject.kt +++ b/compiler/testData/codegen/box/secondaryConstructors/accessToNestedObject.kt @@ -1,6 +1,3 @@ -// TODO enable for JS backend too when KT-6203 will be fixed -// TARGET_BACKEND: JVM - class A(val result: Int) { object B { fun bar(): Int = 4 diff --git a/compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt b/compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt index e4abfb6eeb7..9d0851c426b 100644 --- a/compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt +++ b/compiler/testData/codegen/box/secondaryConstructors/innerClasses.kt @@ -1,6 +1,3 @@ -// TODO enable for JS backend too when KT-4124 will be fixed -// TARGET_BACKEND: JVM - class Outer { val outerProp: String constructor(x: String) { diff --git a/compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt b/compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt index 5aaf8f6d88e..f68b538d5ef 100644 --- a/compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt +++ b/compiler/testData/codegen/box/secondaryConstructors/innerClassesInheritance.kt @@ -1,6 +1,3 @@ -// TODO enable for JS backend too when KT-4124 will be fixed -// TARGET_BACKEND: JVM - class Outer { val outerProp: String constructor(x: String) { diff --git a/compiler/testData/codegen/box/secondaryConstructors/localClasses.kt b/compiler/testData/codegen/box/secondaryConstructors/localClasses.kt index 31c83c1b990..fb26a6ba6aa 100644 --- a/compiler/testData/codegen/box/secondaryConstructors/localClasses.kt +++ b/compiler/testData/codegen/box/secondaryConstructors/localClasses.kt @@ -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) fun box(): String { var sideEffects: String = "" diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClassesTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClassesTestGenerated.java index e8b45ae22fe..c729c0d6dbc 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClassesTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClassesTestGenerated.java @@ -31,6 +31,12 @@ import java.util.regex.Pattern; @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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") public void ignoredClassObjectAsStaticInitializer() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/classObjectAsStaticInitializer.kt"); @@ -569,6 +575,12 @@ public class ClassesTestGenerated extends AbstractClassesTest { 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") public void testKt8011a() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/kt8011a.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/SecondaryConstructorTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/SecondaryConstructorTestGenerated.java index c64132e4a32..d5cb1ee0992 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/SecondaryConstructorTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/SecondaryConstructorTestGenerated.java @@ -31,30 +31,6 @@ import java.util.regex.Pattern; @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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") public void ignoredWithGenerics() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/withGenerics.kt"); @@ -91,6 +67,12 @@ public class SecondaryConstructorTestGenerated extends AbstractSecondaryConstruc 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 { 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); } + @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") public void testSuperCallPrimary() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.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 f65e6377c0b..4512f05a232 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 @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import java.util.HashMap; import java.util.List; @@ -58,8 +59,6 @@ public class TranslationContext { private final DeclarationDescriptor declarationDescriptor; @Nullable private final ClassDescriptor classDescriptor; - @Nullable - private final ClassDescriptor objectDescriptor; @NotNull public static TranslationContext rootContext(@NotNull StaticContext staticContext, JsFunction rootFunction) { @@ -86,20 +85,12 @@ public class TranslationContext { this.usageTracker = usageTracker; this.definitionPlace = definitionPlace; this.declarationDescriptor = declarationDescriptor; - if (declarationDescriptor instanceof ClassDescriptor - && !DescriptorUtils.isAnonymousObject(declarationDescriptor) - && !DescriptorUtils.isObject(declarationDescriptor)) { + if (declarationDescriptor instanceof ClassDescriptor) { this.classDescriptor = (ClassDescriptor) declarationDescriptor; } else { 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 @@ -361,7 +352,20 @@ public class TranslationContext { 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) { @@ -374,7 +378,7 @@ public class TranslationContext { } @NotNull - private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls) { + private JsExpression getDispatchReceiverPath(@Nullable ClassDescriptor cls, JsExpression thisExpression) { if (cls != null) { JsExpression alias = getAliasForDescriptor(cls); if (alias != null) { @@ -385,14 +389,14 @@ public class TranslationContext { (classDescriptor != null && cls != null && DescriptorUtils.isSubclass(classDescriptor, cls)) || parent == null) { - return JsLiteral.THIS; + return thisExpression; } ClassDescriptor parentDescriptor = parent.classDescriptor; 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 { - return parent.getDispatchReceiverPath(cls); + return parent.getDispatchReceiverPath(cls, thisExpression); } } @@ -442,20 +446,6 @@ public class TranslationContext { 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 public DeclarationDescriptor getDeclarationDescriptor() { return declarationDescriptor; @@ -482,4 +472,14 @@ public class TranslationContext { public JsScope 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); + } } 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 23b07db952b..50a394be9df 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 @@ -301,35 +301,54 @@ class ClassTranslator private constructor( val constructorScope = context.getScopeForDescriptor(constructorDescriptor) val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME) val thisNameRef = thisName.makeRef() - val receiverDescriptor = JsDescriptorUtils.getReceiverParameterForDeclaration(classDescriptor) - val translationContext = context.innerContextWithAliased(receiverDescriptor, thisNameRef) + val receiverDescriptor = classDescriptor.thisAsReceiverParameter + + 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 constructorFunction = constructorInitializer.valueExpr as JsFunction + val leadingArgs = mutableListOf() + + if (outerName != null) { + constructorFunction.parameters.add(0, JsParameter(outerName)) + leadingArgs.add(outerName.makeRef()) + } constructorFunction.parameters.add(JsParameter(thisName)) - val referenceToClass = context.getQualifiedReference(classDescriptor) + val referenceToClass = translationContext.getQualifiedReference(classDescriptor) val forAddToBeginning: List = with(arrayListOf()) { - addAll(FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, context)) + addAll(FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, translationContext)) val createInstance = Namer.createObjectWithPrototypeFrom(referenceToClass) val instanceVar = JsAstUtils.assignment(thisNameRef, JsAstUtils.or(thisNameRef, createInstance)).makeStmt() add(instanceVar) - val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context.bindingContext(), constructorDescriptor) + val resolvedCall = BindingContextUtils.getDelegationConstructorCall(translationContext.bindingContext(), + constructorDescriptor) val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) { - val superCall = CallTranslator.translate(context, resolvedCall) - add(superCall.toInvocationWith(thisNameRef).makeStmt()) + val superCall = CallTranslator.translate(translationContext, resolvedCall) + add(superCall.toInvocationWith(leadingArgs, thisNameRef).makeStmt()) } val delegationCtorInTheSameClass = delegationClassDescriptor == classDescriptor if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) { - add(JsInvocation(Namer.getFunctionCallRef(referenceToClass), thisNameRef).makeStmt()) + add(JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + leadingArgs).makeStmt()) } this diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java index b01959cca9d..d75b0890f30 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java @@ -120,12 +120,9 @@ public final class ClassInitializerTranslator extends AbstractTranslator { } private void addOuterClassReference(ClassDescriptor classDescriptor) { - DeclarationDescriptor container = classDescriptor.getContainingDeclaration(); - if (!(container instanceof ClassDescriptor) || !classDescriptor.isInner()) { - return; - } + JsName outerName = context.getOuterClassReference(classDescriptor); + if (outerName == null) return; - JsName outerName = initFunction.getScope().declareFreshName(Namer.OUTER_FIELD_NAME); initFunction.getParameters().add(0, new JsParameter(outerName)); JsExpression paramRef = fqnWithoutSideEffects(outerName, null); @@ -157,7 +154,8 @@ public final class ClassInitializerTranslator extends AbstractTranslator { if (classDeclaration instanceof KtEnumEntry) { JsExpression expression = CallTranslator.translate(context(), superCall, null); - JsExpression fixedInvocation = AstUtilsKt.toInvocationWith(expression, JsLiteral.THIS); + JsExpression fixedInvocation = AstUtilsKt.toInvocationWith(expression, Collections.emptyList(), + JsLiteral.THIS); initializerStatements.add(0, fixedInvocation.makeStmt()); } else { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt index f2ac827d543..4e1db7ee50c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt @@ -56,7 +56,7 @@ fun JsNode.any(predicate: (JsNode) -> Boolean): Boolean { return visitor.matched } -fun JsExpression.toInvocationWith(thisExpr: JsExpression): JsExpression { +fun JsExpression.toInvocationWith(leadingExtraArgs: List, thisExpr: JsExpression): JsExpression { val qualifier: JsExpression val arguments: MutableList @@ -65,13 +65,13 @@ fun JsExpression.toInvocationWith(thisExpr: JsExpression): JsExpression { qualifier = Namer.getFunctionCallRef(constructorExpression) arguments = getArguments() // `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 -> { qualifier = getQualifier() arguments = getArguments() // `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) }