KT-11960 Fix for data classes
This commit is contained in:
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+2
-2
@@ -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));
|
||||
}
|
||||
|
||||
+6
-10
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-9
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user