KT-11960 Fix for data classes

This commit is contained in:
Alexey Andreev
2016-05-13 14:02:22 +03:00
parent 170a671a1b
commit 6f363a71be
7 changed files with 44 additions and 22 deletions
@@ -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"
}
@@ -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");
@@ -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");
@@ -213,7 +213,7 @@ object ConstructorCallCase : FunctionCallCase() {
val invocationArguments = mutableListOf<JsExpression>()
val constructorDescriptor = callableDescriptor as ConstructorDescriptor
if (context.isDeferred(constructorDescriptor)) {
if (context.shouldBeDeferred(constructorDescriptor)) {
context.deferConstructorCall(constructorDescriptor, invocationArguments)
}
else {
@@ -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<JsExpression> invocationArgs) {
ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
List<DeferredCallSite> 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));
}
@@ -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)
}
}
@@ -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<? super JsPropertyInitializer> output;
private final List<JsName> closureFieldNames = new ArrayList<JsName>();
JsDataClassGenerator(KtClassOrObject klass, TranslationContext context, List<? super JsPropertyInitializer> 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<JsExpression> constructorArguments = new ArrayList<JsExpression>(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));
}