[JS IR & WASM] Fix executing init blocks for enums with nested objects
This commit is contained in:
@@ -755,7 +755,8 @@ private val staticMembersLoweringPhase = makeDeclarationTransformerPhase(
|
|||||||
private val objectDeclarationLoweringPhase = makeDeclarationTransformerPhase(
|
private val objectDeclarationLoweringPhase = makeDeclarationTransformerPhase(
|
||||||
::ObjectDeclarationLowering,
|
::ObjectDeclarationLowering,
|
||||||
name = "ObjectDeclarationLowering",
|
name = "ObjectDeclarationLowering",
|
||||||
description = "Create lazy object instance generator functions"
|
description = "Create lazy object instance generator functions",
|
||||||
|
prerequisite = setOf(enumClassCreateInitializerLoweringPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val invokeStaticInitializersPhase = makeBodyLoweringPhase(
|
private val invokeStaticInitializersPhase = makeBodyLoweringPhase(
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.ir.util.*
|
|||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
class ObjectDeclarationLowering(
|
class ObjectDeclarationLowering(
|
||||||
val context: JsCommonBackendContext
|
val context: JsCommonBackendContext
|
||||||
@@ -34,6 +35,11 @@ class ObjectDeclarationLowering(
|
|||||||
private var IrClass.instanceField by context.mapping.objectToInstanceField
|
private var IrClass.instanceField by context.mapping.objectToInstanceField
|
||||||
private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor
|
private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the object being lowered is nested inside an enum class, we want to also initialize the enum entries when initializing the object.
|
||||||
|
*/
|
||||||
|
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
|
||||||
|
|
||||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||||
if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal())
|
if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal())
|
||||||
return null
|
return null
|
||||||
@@ -54,8 +60,12 @@ class ObjectDeclarationLowering(
|
|||||||
|
|
||||||
val primaryConstructor = declaration.primaryConstructor ?: declaration.syntheticPrimaryConstructor!!
|
val primaryConstructor = declaration.primaryConstructor ?: declaration.syntheticPrimaryConstructor!!
|
||||||
|
|
||||||
|
val initEntryInstancesFun = declaration.parent.safeAs<IrClass>()?.initEntryInstancesFun
|
||||||
|
|
||||||
getInstanceFun.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
|
getInstanceFun.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
|
||||||
statements += context.createIrBuilder(getInstanceFun.symbol).irBlockBody(getInstanceFun) {
|
statements += context.createIrBuilder(getInstanceFun.symbol).irBlockBody(getInstanceFun) {
|
||||||
|
if (initEntryInstancesFun != null)
|
||||||
|
+irCall(initEntryInstancesFun)
|
||||||
+irIfThen(
|
+irIfThen(
|
||||||
irEqualsNull(irGetField(null, instanceField)),
|
irEqualsNull(irGetField(null, instanceField)),
|
||||||
// Instance field initialized inside constructor
|
// Instance field initialized inside constructor
|
||||||
|
|||||||
+2
-1
@@ -421,7 +421,8 @@ private val builtInsLoweringPhase = makeWasmModulePhase(
|
|||||||
private val objectDeclarationLoweringPhase = makeWasmModulePhase(
|
private val objectDeclarationLoweringPhase = makeWasmModulePhase(
|
||||||
::ObjectDeclarationLowering,
|
::ObjectDeclarationLowering,
|
||||||
name = "ObjectDeclarationLowering",
|
name = "ObjectDeclarationLowering",
|
||||||
description = "Create lazy object instance generator functions"
|
description = "Create lazy object instance generator functions",
|
||||||
|
prerequisite = setOf(enumClassCreateInitializerLoweringPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val objectUsageLoweringPhase = makeWasmModulePhase(
|
private val objectUsageLoweringPhase = makeWasmModulePhase(
|
||||||
|
|||||||
+26
-5
@@ -1,7 +1,7 @@
|
|||||||
// IGNORE_BACKEND: WASM
|
// DONT_TARGET_EXACT_BACKEND: JS
|
||||||
// WASM_MUTE_REASON: IGNORED_IN_JS
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR_ES6
|
||||||
// IGNORE_BACKEND: JS_IR_ES6
|
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||||
// IGNORE_BACKEND: NATIVE
|
// IGNORE_BACKEND: NATIVE
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
@@ -41,11 +41,32 @@ enum class F(a: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class G(a: String) {
|
||||||
|
X("x"),
|
||||||
|
Y("y");
|
||||||
|
|
||||||
|
init {
|
||||||
|
result += "G.init($a);"
|
||||||
|
}
|
||||||
|
|
||||||
|
object O {
|
||||||
|
init {
|
||||||
|
result += "G.O.init;"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
result += "G.O.foo();$X;"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val y = E.Y
|
val y = E.Y
|
||||||
result += "${y.name};"
|
result += "${y.name};"
|
||||||
F.foo()
|
F.foo()
|
||||||
if (result != "E.init(x);E.init(y);E.companion.init;X;Y;F.init(x);F.init(y);F.companion.init;F.foo();X;") return "fail: $result"
|
G.O.foo()
|
||||||
|
if (result != "E.init(x);E.init(y);E.companion.init;X;Y;F.init(x);F.init(y);F.companion.init;F.foo();X;G.O.init;G.O.foo();X;")
|
||||||
|
return "fail: $result"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// TARGET_BACKEND: JS
|
||||||
|
// TARGET_BACKEND: JS_IR
|
||||||
|
// TARGET_BACKEND: JS_IR_ES6
|
||||||
|
// TARGET_BACKEND: WASM
|
||||||
|
// WITH_RUNTIME
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
enum class E(a: String) {
|
||||||
|
X("x"),
|
||||||
|
Y("y");
|
||||||
|
|
||||||
|
init {
|
||||||
|
result += "E.init($a);"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
result += "E.companion.init;"
|
||||||
|
val value = E.values()[0].name
|
||||||
|
result += "$value;"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class F(a: String) {
|
||||||
|
X("x"),
|
||||||
|
Y("y");
|
||||||
|
|
||||||
|
init {
|
||||||
|
result += "F.init($a);"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
result += "F.companion.init;"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
result += "F.foo();$X;"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class G(a: String) {
|
||||||
|
X("x"),
|
||||||
|
Y("y");
|
||||||
|
|
||||||
|
init {
|
||||||
|
result += "G.init($a);"
|
||||||
|
}
|
||||||
|
|
||||||
|
object O {
|
||||||
|
init {
|
||||||
|
result += "G.O.init;"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
result += "G.O.foo();$X;"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val y = E.Y
|
||||||
|
result += "${y.name};"
|
||||||
|
F.foo()
|
||||||
|
G.O.foo()
|
||||||
|
if (result != "E.init(x);E.init(y);E.companion.init;X;Y;F.init(x);F.init(y);F.companion.init;F.foo();X;G.init(x);G.init(y);G.O.init;G.O.foo();X;")
|
||||||
|
return "fail: $result"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
// IGNORE_BACKEND: JS
|
// DONT_TARGET_EXACT_BACKEND: JS
|
||||||
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
// DONT_TARGET_EXACT_BACKEND: JS_IR_ES6
|
||||||
|
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||||
|
|
||||||
var l = ""
|
var l = ""
|
||||||
enum class Foo {
|
enum class Foo {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// TARGET_BACKEND: JS
|
||||||
|
// TARGET_BACKEND: JS_IR
|
||||||
|
// TARGET_BACKEND: JS_IR_ES6
|
||||||
|
// TARGET_BACKEND: WASM
|
||||||
|
|
||||||
|
var l = ""
|
||||||
|
enum class Foo {
|
||||||
|
F;
|
||||||
|
init {
|
||||||
|
l += "Foo;"
|
||||||
|
}
|
||||||
|
object L {
|
||||||
|
init {
|
||||||
|
l += "Foo.CO;"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
Foo.L
|
||||||
|
return if (l != "Foo;Foo.CO;") "FAIL: ${l}" else "OK"
|
||||||
|
}
|
||||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java
Generated
+6
-6
@@ -10991,9 +10991,9 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
|||||||
runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("enumCompanionInit.kt")
|
@TestMetadata("enumCompanionInitJsAndWasm.kt")
|
||||||
public void testEnumCompanionInit() throws Exception {
|
public void testEnumCompanionInitJsAndWasm() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/enumCompanionInit.kt");
|
runTest("compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("enumConstructorParameterClashWithDefaults.kt")
|
@TestMetadata("enumConstructorParameterClashWithDefaults.kt")
|
||||||
@@ -11076,9 +11076,9 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
|||||||
runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt");
|
runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("initEnumAfterObjectAccess.kt")
|
@TestMetadata("initEnumAfterObjectAccessJsAndWasm.kt")
|
||||||
public void testInitEnumAfterObjectAccess() throws Exception {
|
public void testInitEnumAfterObjectAccessJsAndWasm() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt");
|
runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("inner.kt")
|
@TestMetadata("inner.kt")
|
||||||
|
|||||||
Generated
+6
-6
@@ -10397,9 +10397,9 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("enumCompanionInit.kt")
|
@TestMetadata("enumCompanionInitJsAndWasm.kt")
|
||||||
public void testEnumCompanionInit() throws Exception {
|
public void testEnumCompanionInitJsAndWasm() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/enumCompanionInit.kt");
|
runTest("compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("enumConstructorParameterClashWithDefaults.kt")
|
@TestMetadata("enumConstructorParameterClashWithDefaults.kt")
|
||||||
@@ -10482,9 +10482,9 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt");
|
runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("initEnumAfterObjectAccess.kt")
|
@TestMetadata("initEnumAfterObjectAccessJsAndWasm.kt")
|
||||||
public void testInitEnumAfterObjectAccess() throws Exception {
|
public void testInitEnumAfterObjectAccessJsAndWasm() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt");
|
runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("inner.kt")
|
@TestMetadata("inner.kt")
|
||||||
|
|||||||
Generated
+6
-6
@@ -10362,9 +10362,9 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("enumCompanionInit.kt")
|
@TestMetadata("enumCompanionInitJsAndWasm.kt")
|
||||||
public void testEnumCompanionInit() throws Exception {
|
public void testEnumCompanionInitJsAndWasm() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/enumCompanionInit.kt");
|
runTest("compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("enumConstructorParameterClashWithDefaults.kt")
|
@TestMetadata("enumConstructorParameterClashWithDefaults.kt")
|
||||||
@@ -10447,9 +10447,9 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt");
|
runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("initEnumAfterObjectAccess.kt")
|
@TestMetadata("initEnumAfterObjectAccessJsAndWasm.kt")
|
||||||
public void testInitEnumAfterObjectAccess() throws Exception {
|
public void testInitEnumAfterObjectAccessJsAndWasm() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt");
|
runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("inner.kt")
|
@TestMetadata("inner.kt")
|
||||||
|
|||||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
Generated
+6
-6
@@ -9658,9 +9658,9 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
|||||||
runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("enumCompanionInit.kt")
|
@TestMetadata("enumCompanionInitJsAndWasm.kt")
|
||||||
public void testEnumCompanionInit() throws Exception {
|
public void testEnumCompanionInitJsAndWasm() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/enumCompanionInit.kt");
|
runTest("compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("enumConstructorParameterClashWithDefaults.kt")
|
@TestMetadata("enumConstructorParameterClashWithDefaults.kt")
|
||||||
@@ -9743,9 +9743,9 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
|||||||
runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt");
|
runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("initEnumAfterObjectAccess.kt")
|
@TestMetadata("initEnumAfterObjectAccessJsAndWasm.kt")
|
||||||
public void testInitEnumAfterObjectAccess() throws Exception {
|
public void testInitEnumAfterObjectAccessJsAndWasm() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt");
|
runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("inner.kt")
|
@TestMetadata("inner.kt")
|
||||||
|
|||||||
Reference in New Issue
Block a user