From 9e5520bba8bda3a30e05af63c94276bbedfb822e Mon Sep 17 00:00:00 2001 From: Sergej Jaskiewicz Date: Wed, 13 Oct 2021 17:06:32 +0300 Subject: [PATCH] [JS IR & WASM] Fix executing init blocks for enums with nested objects --- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 3 +- .../ir/backend/js/lower/ObjectLowering.kt | 10 +++ .../kotlin/backend/wasm/WasmLoweringPhases.kt | 3 +- .../codegen/box/enum/enumCompanionInit.kt | 33 +++++++-- .../box/enum/enumCompanionInitJsAndWasm.kt | 72 +++++++++++++++++++ .../box/enum/initEnumAfterObjectAccess.kt | 7 +- .../initEnumAfterObjectAccessJsAndWasm.kt | 22 ++++++ .../IrJsCodegenBoxES6TestGenerated.java | 12 ++-- .../IrJsCodegenBoxTestGenerated.java | 12 ++-- .../semantics/JsCodegenBoxTestGenerated.java | 12 ++-- .../IrCodegenBoxWasmTestGenerated.java | 12 ++-- 11 files changed, 164 insertions(+), 34 deletions(-) create mode 100644 compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt create mode 100644 compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 3802c0d414a..bdae36031d3 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -755,7 +755,8 @@ private val staticMembersLoweringPhase = makeDeclarationTransformerPhase( private val objectDeclarationLoweringPhase = makeDeclarationTransformerPhase( ::ObjectDeclarationLowering, name = "ObjectDeclarationLowering", - description = "Create lazy object instance generator functions" + description = "Create lazy object instance generator functions", + prerequisite = setOf(enumClassCreateInitializerLoweringPhase) ) private val invokeStaticInitializersPhase = makeBodyLoweringPhase( diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ObjectLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ObjectLowering.kt index eb297a41f3f..fdb184cd67f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ObjectLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ObjectLowering.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class ObjectDeclarationLowering( val context: JsCommonBackendContext @@ -34,6 +35,11 @@ class ObjectDeclarationLowering( private var IrClass.instanceField by context.mapping.objectToInstanceField 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? { if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal()) return null @@ -54,8 +60,12 @@ class ObjectDeclarationLowering( val primaryConstructor = declaration.primaryConstructor ?: declaration.syntheticPrimaryConstructor!! + val initEntryInstancesFun = declaration.parent.safeAs()?.initEntryInstancesFun + getInstanceFun.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) { statements += context.createIrBuilder(getInstanceFun.symbol).irBlockBody(getInstanceFun) { + if (initEntryInstancesFun != null) + +irCall(initEntryInstancesFun) +irIfThen( irEqualsNull(irGetField(null, instanceField)), // Instance field initialized inside constructor diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt index e32c4eccf8b..b79cbe3090f 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt @@ -421,7 +421,8 @@ private val builtInsLoweringPhase = makeWasmModulePhase( private val objectDeclarationLoweringPhase = makeWasmModulePhase( ::ObjectDeclarationLowering, name = "ObjectDeclarationLowering", - description = "Create lazy object instance generator functions" + description = "Create lazy object instance generator functions", + prerequisite = setOf(enumClassCreateInitializerLoweringPhase) ) private val objectUsageLoweringPhase = makeWasmModulePhase( diff --git a/compiler/testData/codegen/box/enum/enumCompanionInit.kt b/compiler/testData/codegen/box/enum/enumCompanionInit.kt index f303341f41f..f7d2b29b8b9 100644 --- a/compiler/testData/codegen/box/enum/enumCompanionInit.kt +++ b/compiler/testData/codegen/box/enum/enumCompanionInit.kt @@ -1,7 +1,7 @@ -// IGNORE_BACKEND: WASM -// WASM_MUTE_REASON: IGNORED_IN_JS -// IGNORE_BACKEND: JS_IR -// IGNORE_BACKEND: JS_IR_ES6 +// DONT_TARGET_EXACT_BACKEND: JS +// DONT_TARGET_EXACT_BACKEND: JS_IR +// DONT_TARGET_EXACT_BACKEND: JS_IR_ES6 +// DONT_TARGET_EXACT_BACKEND: WASM // IGNORE_BACKEND: NATIVE 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 { val y = E.Y result += "${y.name};" 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" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt b/compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt new file mode 100644 index 00000000000..1e85380c6eb --- /dev/null +++ b/compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt @@ -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" +} diff --git a/compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt b/compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt index 6f9304c62e5..4426245fc4f 100644 --- a/compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt +++ b/compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt @@ -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 = "" enum class Foo { @@ -16,4 +19,4 @@ enum class Foo { fun box(): String { Foo.L return if (l != "Foo.CO;") "FAIL: ${l}" else "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt b/compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt new file mode 100644 index 00000000000..ea97107ba06 --- /dev/null +++ b/compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt @@ -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" +} diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index e6a88925cbc..cb98a76f910 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -10991,9 +10991,9 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt"); } - @TestMetadata("enumCompanionInit.kt") - public void testEnumCompanionInit() throws Exception { - runTest("compiler/testData/codegen/box/enum/enumCompanionInit.kt"); + @TestMetadata("enumCompanionInitJsAndWasm.kt") + public void testEnumCompanionInitJsAndWasm() throws Exception { + runTest("compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt"); } @TestMetadata("enumConstructorParameterClashWithDefaults.kt") @@ -11076,9 +11076,9 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt"); } - @TestMetadata("initEnumAfterObjectAccess.kt") - public void testInitEnumAfterObjectAccess() throws Exception { - runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt"); + @TestMetadata("initEnumAfterObjectAccessJsAndWasm.kt") + public void testInitEnumAfterObjectAccessJsAndWasm() throws Exception { + runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt"); } @TestMetadata("inner.kt") diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 82f8f00eda0..46432a78971 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -10397,9 +10397,9 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt"); } - @TestMetadata("enumCompanionInit.kt") - public void testEnumCompanionInit() throws Exception { - runTest("compiler/testData/codegen/box/enum/enumCompanionInit.kt"); + @TestMetadata("enumCompanionInitJsAndWasm.kt") + public void testEnumCompanionInitJsAndWasm() throws Exception { + runTest("compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt"); } @TestMetadata("enumConstructorParameterClashWithDefaults.kt") @@ -10482,9 +10482,9 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt"); } - @TestMetadata("initEnumAfterObjectAccess.kt") - public void testInitEnumAfterObjectAccess() throws Exception { - runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt"); + @TestMetadata("initEnumAfterObjectAccessJsAndWasm.kt") + public void testInitEnumAfterObjectAccessJsAndWasm() throws Exception { + runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt"); } @TestMetadata("inner.kt") diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index c0636a23e82..743b72c308b 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -10362,9 +10362,9 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt"); } - @TestMetadata("enumCompanionInit.kt") - public void testEnumCompanionInit() throws Exception { - runTest("compiler/testData/codegen/box/enum/enumCompanionInit.kt"); + @TestMetadata("enumCompanionInitJsAndWasm.kt") + public void testEnumCompanionInitJsAndWasm() throws Exception { + runTest("compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt"); } @TestMetadata("enumConstructorParameterClashWithDefaults.kt") @@ -10447,9 +10447,9 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt"); } - @TestMetadata("initEnumAfterObjectAccess.kt") - public void testInitEnumAfterObjectAccess() throws Exception { - runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt"); + @TestMetadata("initEnumAfterObjectAccessJsAndWasm.kt") + public void testInitEnumAfterObjectAccessJsAndWasm() throws Exception { + runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt"); } @TestMetadata("inner.kt") diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 52327f6d432..f0f3179f63d 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -9658,9 +9658,9 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/enum/emptyEnumValuesValueOf.kt"); } - @TestMetadata("enumCompanionInit.kt") - public void testEnumCompanionInit() throws Exception { - runTest("compiler/testData/codegen/box/enum/enumCompanionInit.kt"); + @TestMetadata("enumCompanionInitJsAndWasm.kt") + public void testEnumCompanionInitJsAndWasm() throws Exception { + runTest("compiler/testData/codegen/box/enum/enumCompanionInitJsAndWasm.kt"); } @TestMetadata("enumConstructorParameterClashWithDefaults.kt") @@ -9743,9 +9743,9 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest runTest("compiler/testData/codegen/box/enum/initEntriesInValueOf.kt"); } - @TestMetadata("initEnumAfterObjectAccess.kt") - public void testInitEnumAfterObjectAccess() throws Exception { - runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccess.kt"); + @TestMetadata("initEnumAfterObjectAccessJsAndWasm.kt") + public void testInitEnumAfterObjectAccessJsAndWasm() throws Exception { + runTest("compiler/testData/codegen/box/enum/initEnumAfterObjectAccessJsAndWasm.kt"); } @TestMetadata("inner.kt")