From 6f363a71be23f22e2909b515dcc874c82c72b507 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 13 May 2016 14:02:22 +0300 Subject: [PATCH] KT-11960 Fix for data classes --- .../secondaryConstructors/localDataClass.kt | 20 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++++ .../SecondaryConstructorTestGenerated.java | 6 ++++++ .../callTranslator/FunctionCallCases.kt | 2 +- .../translate/context/TranslationContext.java | 4 ++-- .../translate/declaration/ClassTranslator.kt | 16 ++++++--------- .../declaration/JsDataClassGenerator.java | 12 +++-------- 7 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 compiler/testData/codegen/box/secondaryConstructors/localDataClass.kt diff --git a/compiler/testData/codegen/box/secondaryConstructors/localDataClass.kt b/compiler/testData/codegen/box/secondaryConstructors/localDataClass.kt new file mode 100644 index 00000000000..552e12329bd --- /dev/null +++ b/compiler/testData/codegen/box/secondaryConstructors/localDataClass.kt @@ -0,0 +1,20 @@ +// Enable for JVM backend when KT-8120 gets fixed +// TARGET_BACKEND: JS + +fun box(): String { + val capturedInConstructor = 1 + + data class A(var x: Int) { + var y = 0 + + init { + y += x + capturedInConstructor + } + } + + val a = A(100).copy() + if (a.y != 101) return "fail1a: ${a.y}" + if (a.x != 100) return "fail1b: ${a.x}" + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 60225f3968c..f7066c00479 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -13015,6 +13015,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("localDataClass.kt") + public void testLocalDataClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/localDataClass.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.tests/test/org/jetbrains/kotlin/js/test/semantics/SecondaryConstructorTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/SecondaryConstructorTestGenerated.java index 92832938801..88720ade084 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 @@ -167,6 +167,12 @@ public class SecondaryConstructorTestGenerated extends AbstractSecondaryConstruc doTest(fileName); } + @TestMetadata("localDataClass.kt") + public void testLocalDataClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/localDataClass.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/callTranslator/FunctionCallCases.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt index 64a2c2850d4..289eb2d72d0 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/FunctionCallCases.kt @@ -213,7 +213,7 @@ object ConstructorCallCase : FunctionCallCase() { val invocationArguments = mutableListOf() val constructorDescriptor = callableDescriptor as ConstructorDescriptor - if (context.isDeferred(constructorDescriptor)) { + if (context.shouldBeDeferred(constructorDescriptor)) { context.deferConstructorCall(constructorDescriptor, invocationArguments) } else { 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 144f1148ce3..abd8e996c88 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 @@ -530,7 +530,7 @@ public class TranslationContext { return result; } - public boolean isDeferred(@NotNull ConstructorDescriptor constructor) { + public boolean shouldBeDeferred(@NotNull ConstructorDescriptor constructor) { ClassDescriptor classDescriptor = constructor.getContainingDeclaration(); return staticContext.getDeferredCallSites().containsKey(classDescriptor); } @@ -538,7 +538,7 @@ public class TranslationContext { public void deferConstructorCall(@NotNull ConstructorDescriptor constructor, @NotNull List invocationArgs) { ClassDescriptor classDescriptor = constructor.getContainingDeclaration(); List callSites = staticContext.getDeferredCallSites().get(classDescriptor); - if (callSites == null) throw new IllegalStateException("This method should be call only when `isDeferred` method " + + if (callSites == null) throw new IllegalStateException("This method should be call only when `shouldBeDeferred` method " + "reports true for given constructor: " + constructor); callSites.add(new DeferredCallSite(constructor, invocationArgs)); } 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 49ddd8e6dde..cb36023add3 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 @@ -99,20 +99,18 @@ class ClassTranslator private constructor( classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) } generatedBridgeMethods(properties) + if (descriptor.isData) { + JsDataClassGenerator(classDeclaration, context, properties).generate() + } + if (isEnumClass(descriptor)) { val enumEntries = JsObjectLiteral(bodyVisitor.enumEntryList, true) invocationArguments += simpleReturnFunction(nonConstructorContext.getScopeForDescriptor(descriptor), enumEntries) } - val dataClassGenerator = JsDataClassGenerator(classDeclaration, context, properties) - emitConstructors(nonConstructorContext, nonConstructorContext.endDeclaration()) for (constructor in allConstructors) { - addClosureParameters(constructor, nonConstructorContext, dataClassGenerator) - } - - if (descriptor.isData) { - dataClassGenerator.generate() + addClosureParameters(constructor, nonConstructorContext) } // ExpressionVisitor.visitObjectLiteralExpression uses DefinitionPlace of the translated class to generate call to @@ -295,8 +293,7 @@ class ClassTranslator private constructor( } } - private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext, - dataClassGenerator: JsDataClassGenerator) { + private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext) { val usageTracker = constructor.context.usageTracker()!! val capturedVars = context().getClassOrConstructorClosure(constructor.descriptor) ?: return val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!! @@ -310,7 +307,6 @@ class ClassTranslator private constructor( function.parameters.add(i, JsParameter(name)) if (fieldName != null && constructor == primaryConstructor) { additionalStatements += JsAstUtils.defineSimpleProperty(fieldName.ident, name.makeRef()) - dataClassGenerator.addClosureVariable(fieldName) } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java index 9fddfc97885..b9acc88e5bf 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsDataClassGenerator.java @@ -38,7 +38,6 @@ import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.or; class JsDataClassGenerator extends DataClassMethodGenerator { private final TranslationContext context; private final List output; - private final List closureFieldNames = new ArrayList(); JsDataClassGenerator(KtClassOrObject klass, TranslationContext context, List output) { super(klass, context.bindingContext()); @@ -46,10 +45,6 @@ class JsDataClassGenerator extends DataClassMethodGenerator { this.output = output; } - public void addClosureVariable(@NotNull JsName name) { - closureFieldNames.add(name); - } - @Override public void generateComponentFunction(@NotNull FunctionDescriptor function, @NotNull ValueParameterDescriptor parameter) { PropertyDescriptor propertyDescriptor = context.bindingContext().get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter); @@ -68,10 +63,6 @@ class JsDataClassGenerator extends DataClassMethodGenerator { List constructorArguments = new ArrayList(constructorParameters.size()); - for (JsName closureFieldName : closureFieldNames) { - constructorArguments.add(JsAstUtils.fqnWithoutSideEffects(closureFieldName, JsLiteral.THIS)); - } - for (int i = 0; i < constructorParameters.size(); i++) { KtParameter constructorParam = constructorParameters.get(i); @@ -106,6 +97,9 @@ class JsDataClassGenerator extends DataClassMethodGenerator { JsExpression constructorRef = context.getQualifiedReference(constructor); JsExpression returnExpression = new JsNew(constructorRef, constructorArguments); + if (context.shouldBeDeferred(constructor)) { + context.deferConstructorCall(constructor, constructorArguments); + } functionObj.getBody().getStatements().add(new JsReturn(returnExpression)); }