KT-11030 Fix translation of secondary constructors of inner classes
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
// TODO: Enable for JS when it supports local classes.
|
||||
// TARGET_BACKEND: JVM
|
||||
// WITH_RUNTIME
|
||||
|
||||
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) {
|
||||
object B {
|
||||
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 {
|
||||
val outerProp: String
|
||||
constructor(x: String) {
|
||||
|
||||
-3
@@ -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) {
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
@@ -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");
|
||||
|
||||
+24
-24
@@ -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");
|
||||
|
||||
+30
-30
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+27
-8
@@ -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<JsExpression>()
|
||||
|
||||
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<JsStatement> =
|
||||
with(arrayListOf<JsStatement>()) {
|
||||
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
|
||||
|
||||
+4
-6
@@ -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.<JsExpression>emptyList(),
|
||||
JsLiteral.THIS);
|
||||
initializerStatements.add(0, fixedInvocation.makeStmt());
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -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<JsExpression>, thisExpr: JsExpression): JsExpression {
|
||||
val qualifier: JsExpression
|
||||
val arguments: MutableList<JsExpression>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user