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
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) {
@@ -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");
@@ -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,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);
}
}
@@ -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
@@ -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)
}