Fix initialization order of enum with companion object in JS BE
Make enum entries initialize before companion object. This helps in situation when companion object initializer refers to enum fields. JVM be generates <clinit> method which first initializes all enum fields and then runs companion object initializer. This commit introduces the similar behaviour in JS BE. The old behaviour was: initialize companion object in constructor. In enum, constructor is called to initialize enum fields, so previously companion object was initialized first, which is incorrect. See KT-16745
This commit is contained in:
@@ -8154,6 +8154,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumCompanionInit.kt")
|
||||
public void testEnumCompanionInit() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumCompanionInit.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumInheritedFromTrait.kt")
|
||||
public void testEnumInheritedFromTrait() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/enum/enumInheritedFromTrait.kt");
|
||||
|
||||
+5
-1
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isOverridable
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters
|
||||
import org.jetbrains.kotlin.js.translate.expression.translateFunction
|
||||
@@ -34,8 +35,11 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
|
||||
abstract class AbstractDeclarationVisitor : TranslatorVisitor<Unit>() {
|
||||
override fun emptyResult(context: TranslationContext) { }
|
||||
|
||||
open val enumInitializerName: JsName?
|
||||
get() = null
|
||||
|
||||
override fun visitClassOrObject(classOrObject: KtClassOrObject, context: TranslationContext) {
|
||||
ClassTranslator.translate(classOrObject, context)
|
||||
ClassTranslator.translate(classOrObject, context, enumInitializerName)
|
||||
val descriptor = BindingUtils.getClassDescriptor(context.bindingContext(), classOrObject)
|
||||
context.export(descriptor)
|
||||
}
|
||||
|
||||
+12
-6
@@ -92,6 +92,12 @@ class ClassTranslator private constructor(
|
||||
constructorFunction.body.statements += bodyVisitor.initializerStatements
|
||||
delegationTranslator.generateDelegated()
|
||||
|
||||
val companionDescriptor = descriptor.companionObjectDescriptor
|
||||
if (enumInitFunction != null && companionDescriptor != null) {
|
||||
val initInvocation = JsInvocation(JsAstUtils.pureFqn(context().getNameForObjectInstance(companionDescriptor), null))
|
||||
enumInitFunction.body.statements += JsAstUtils.asSyntheticStatement(initInvocation)
|
||||
}
|
||||
|
||||
translatePrimaryConstructor(constructorFunction, context, delegationTranslator)
|
||||
addMetadataObject()
|
||||
addMetadataType()
|
||||
@@ -446,7 +452,10 @@ class ClassTranslator private constructor(
|
||||
val instanceFun = context().createRootScopedFunction("Instance function: " + descriptor)
|
||||
instanceFun.name = context().getNameForObjectInstance(descriptor)
|
||||
|
||||
if (enumInitializerName == null) {
|
||||
if (enumInitializerName != null) {
|
||||
instanceFun.body.statements += JsInvocation(pureFqn(enumInitializerName, null)).makeStmt()
|
||||
}
|
||||
if (descriptor.kind != ClassKind.ENUM_ENTRY) {
|
||||
val instanceCreatedCondition = JsAstUtils.equality(cachedInstanceName.makeRef(), JsLiteral.NULL)
|
||||
val instanceCreationBlock = JsBlock()
|
||||
val instanceCreatedGuard = JsIf(instanceCreatedCondition, instanceCreationBlock)
|
||||
@@ -455,9 +464,6 @@ class ClassTranslator private constructor(
|
||||
val objectRef = context().getInnerReference(descriptor)
|
||||
instanceCreationBlock.statements += JsNew(objectRef).makeStmt()
|
||||
}
|
||||
else {
|
||||
instanceFun.body.statements += JsInvocation(pureFqn(enumInitializerName, null)).makeStmt()
|
||||
}
|
||||
|
||||
instanceFun.body.statements += JsReturn(cachedInstanceName.makeRef())
|
||||
|
||||
@@ -534,8 +540,8 @@ class ClassTranslator private constructor(
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic fun translate(classDeclaration: KtClassOrObject, context: TranslationContext) {
|
||||
return ClassTranslator(classDeclaration, context, null, null).translate()
|
||||
@JvmStatic fun translate(classDeclaration: KtClassOrObject, context: TranslationContext, enumInitializerName: JsName?) {
|
||||
return ClassTranslator(classDeclaration, context, enumInitializerName, null).translate()
|
||||
}
|
||||
|
||||
@JvmStatic fun translate(classDeclaration: KtEnumEntry, context: TranslationContext, enumInitializerName: JsName, ordinal: Int) {
|
||||
|
||||
+4
-1
@@ -40,11 +40,14 @@ class DeclarationBodyVisitor(
|
||||
val initializerStatements = mutableListOf<JsStatement>()
|
||||
val enumEntries = mutableListOf<ClassDescriptor>()
|
||||
|
||||
override val enumInitializerName: JsName?
|
||||
get() = enumInitializer?.name
|
||||
|
||||
override fun visitClassOrObject(classOrObject: KtClassOrObject, context: TranslationContext) {
|
||||
super.visitClassOrObject(classOrObject, context)
|
||||
|
||||
if (classOrObject is KtObjectDeclaration) {
|
||||
if (classOrObject.isCompanion()) {
|
||||
if (classOrObject.isCompanion() && containingClass.kind != ClassKind.ENUM_CLASS) {
|
||||
val descriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), classOrObject) as ClassDescriptor
|
||||
addInitializerStatement(JsInvocation(context.getNameForObjectInstance(descriptor).makeRef()).makeStmt())
|
||||
}
|
||||
|
||||
+1
-1
@@ -604,6 +604,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
||||
) {
|
||||
JsScope scope = context.getScopeForDescriptor(descriptor);
|
||||
TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor);
|
||||
ClassTranslator.translate(declaration, classContext);
|
||||
ClassTranslator.translate(declaration, classContext, null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user