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);
|
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")
|
@TestMetadata("superCallPrimary.kt")
|
||||||
public void testSuperCallPrimary() throws Exception {
|
public void testSuperCallPrimary() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt");
|
||||||
|
|||||||
+6
@@ -167,6 +167,12 @@ public class SecondaryConstructorTestGenerated extends AbstractSecondaryConstruc
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("superCallPrimary.kt")
|
||||||
public void testSuperCallPrimary() throws Exception {
|
public void testSuperCallPrimary() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/superCallPrimary.kt");
|
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 invocationArguments = mutableListOf<JsExpression>()
|
||||||
|
|
||||||
val constructorDescriptor = callableDescriptor as ConstructorDescriptor
|
val constructorDescriptor = callableDescriptor as ConstructorDescriptor
|
||||||
if (context.isDeferred(constructorDescriptor)) {
|
if (context.shouldBeDeferred(constructorDescriptor)) {
|
||||||
context.deferConstructorCall(constructorDescriptor, invocationArguments)
|
context.deferConstructorCall(constructorDescriptor, invocationArguments)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+2
-2
@@ -530,7 +530,7 @@ public class TranslationContext {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDeferred(@NotNull ConstructorDescriptor constructor) {
|
public boolean shouldBeDeferred(@NotNull ConstructorDescriptor constructor) {
|
||||||
ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
|
ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
|
||||||
return staticContext.getDeferredCallSites().containsKey(classDescriptor);
|
return staticContext.getDeferredCallSites().containsKey(classDescriptor);
|
||||||
}
|
}
|
||||||
@@ -538,7 +538,7 @@ public class TranslationContext {
|
|||||||
public void deferConstructorCall(@NotNull ConstructorDescriptor constructor, @NotNull List<JsExpression> invocationArgs) {
|
public void deferConstructorCall(@NotNull ConstructorDescriptor constructor, @NotNull List<JsExpression> invocationArgs) {
|
||||||
ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
|
ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
|
||||||
List<DeferredCallSite> callSites = staticContext.getDeferredCallSites().get(classDescriptor);
|
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);
|
"reports true for given constructor: " + constructor);
|
||||||
callSites.add(new DeferredCallSite(constructor, invocationArgs));
|
callSites.add(new DeferredCallSite(constructor, invocationArgs));
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-10
@@ -99,20 +99,18 @@ class ClassTranslator private constructor(
|
|||||||
classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) }
|
classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) }
|
||||||
generatedBridgeMethods(properties)
|
generatedBridgeMethods(properties)
|
||||||
|
|
||||||
|
if (descriptor.isData) {
|
||||||
|
JsDataClassGenerator(classDeclaration, context, properties).generate()
|
||||||
|
}
|
||||||
|
|
||||||
if (isEnumClass(descriptor)) {
|
if (isEnumClass(descriptor)) {
|
||||||
val enumEntries = JsObjectLiteral(bodyVisitor.enumEntryList, true)
|
val enumEntries = JsObjectLiteral(bodyVisitor.enumEntryList, true)
|
||||||
invocationArguments += simpleReturnFunction(nonConstructorContext.getScopeForDescriptor(descriptor), enumEntries)
|
invocationArguments += simpleReturnFunction(nonConstructorContext.getScopeForDescriptor(descriptor), enumEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataClassGenerator = JsDataClassGenerator(classDeclaration, context, properties)
|
|
||||||
|
|
||||||
emitConstructors(nonConstructorContext, nonConstructorContext.endDeclaration())
|
emitConstructors(nonConstructorContext, nonConstructorContext.endDeclaration())
|
||||||
for (constructor in allConstructors) {
|
for (constructor in allConstructors) {
|
||||||
addClosureParameters(constructor, nonConstructorContext, dataClassGenerator)
|
addClosureParameters(constructor, nonConstructorContext)
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.isData) {
|
|
||||||
dataClassGenerator.generate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpressionVisitor.visitObjectLiteralExpression uses DefinitionPlace of the translated class to generate call to
|
// 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,
|
private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext) {
|
||||||
dataClassGenerator: JsDataClassGenerator) {
|
|
||||||
val usageTracker = constructor.context.usageTracker()!!
|
val usageTracker = constructor.context.usageTracker()!!
|
||||||
val capturedVars = context().getClassOrConstructorClosure(constructor.descriptor) ?: return
|
val capturedVars = context().getClassOrConstructorClosure(constructor.descriptor) ?: return
|
||||||
val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!!
|
val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!!
|
||||||
@@ -310,7 +307,6 @@ class ClassTranslator private constructor(
|
|||||||
function.parameters.add(i, JsParameter(name))
|
function.parameters.add(i, JsParameter(name))
|
||||||
if (fieldName != null && constructor == primaryConstructor) {
|
if (fieldName != null && constructor == primaryConstructor) {
|
||||||
additionalStatements += JsAstUtils.defineSimpleProperty(fieldName.ident, name.makeRef())
|
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 {
|
class JsDataClassGenerator extends DataClassMethodGenerator {
|
||||||
private final TranslationContext context;
|
private final TranslationContext context;
|
||||||
private final List<? super JsPropertyInitializer> output;
|
private final List<? super JsPropertyInitializer> output;
|
||||||
private final List<JsName> closureFieldNames = new ArrayList<JsName>();
|
|
||||||
|
|
||||||
JsDataClassGenerator(KtClassOrObject klass, TranslationContext context, List<? super JsPropertyInitializer> output) {
|
JsDataClassGenerator(KtClassOrObject klass, TranslationContext context, List<? super JsPropertyInitializer> output) {
|
||||||
super(klass, context.bindingContext());
|
super(klass, context.bindingContext());
|
||||||
@@ -46,10 +45,6 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
|
|||||||
this.output = output;
|
this.output = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addClosureVariable(@NotNull JsName name) {
|
|
||||||
closureFieldNames.add(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateComponentFunction(@NotNull FunctionDescriptor function, @NotNull ValueParameterDescriptor parameter) {
|
public void generateComponentFunction(@NotNull FunctionDescriptor function, @NotNull ValueParameterDescriptor parameter) {
|
||||||
PropertyDescriptor propertyDescriptor = context.bindingContext().get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, 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());
|
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++) {
|
for (int i = 0; i < constructorParameters.size(); i++) {
|
||||||
KtParameter constructorParam = constructorParameters.get(i);
|
KtParameter constructorParam = constructorParameters.get(i);
|
||||||
|
|
||||||
@@ -106,6 +97,9 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
|
|||||||
JsExpression constructorRef = context.getQualifiedReference(constructor);
|
JsExpression constructorRef = context.getQualifiedReference(constructor);
|
||||||
|
|
||||||
JsExpression returnExpression = new JsNew(constructorRef, constructorArguments);
|
JsExpression returnExpression = new JsNew(constructorRef, constructorArguments);
|
||||||
|
if (context.shouldBeDeferred(constructor)) {
|
||||||
|
context.deferConstructorCall(constructor, constructorArguments);
|
||||||
|
}
|
||||||
functionObj.getBody().getStatements().add(new JsReturn(returnExpression));
|
functionObj.getBody().getStatements().add(new JsReturn(returnExpression));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user