Init enum entries whenever we access companion object or accessing valueOf

Fixes https://youtrack.jetbrains.com/issue/KT-43987
Fixes https://youtrack.jetbrains.com/issue/KT-43989
This commit is contained in:
Shagen Ogandzhanian
2020-12-30 14:52:21 +01:00
parent 7fa04afda2
commit e7dc199ad7
14 changed files with 284 additions and 18 deletions
@@ -11182,6 +11182,21 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/enum/inclassobj.kt");
}
@TestMetadata("initEntriesInCompanionObject.kt")
public void testInitEntriesInCompanionObject() throws Exception {
runTest("compiler/testData/codegen/box/enum/initEntriesInCompanionObject.kt");
}
@TestMetadata("initEntriesInValueOf.kt")
public void testInitEntriesInValueOf() throws Exception {
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("inner.kt")
public void testInner() throws Exception {
runTest("compiler/testData/codegen/box/enum/inner.kt");
@@ -304,7 +304,7 @@ private val enumSyntheticFunsLoweringPhase = makeDeclarationTransformerPhase(
::EnumSyntheticFunctionsLowering,
name = "EnumSyntheticFunctionsLowering",
description = "Implement `valueOf` and `values`",
prerequisite = setOf(enumClassConstructorLoweringPhase)
prerequisite = setOf(enumClassConstructorLoweringPhase, enumClassCreateInitializerLoweringPhase)
)
private val enumUsageLoweringPhase = makeBodyLoweringPhase(
@@ -410,7 +410,7 @@ class EnumEntryCreateGetInstancesFunsLowering(val context: JsCommonBackendContex
val irClass = declaration.parentAsClass
if (irClass.isInstantiableEnum) {
// Create entry instance getters. These are used to lower `IrGetEnumValue`.
val entryGetInstanceFun = createGetEntryInstanceFun(irClass, declaration, irClass.initEntryInstancesFun!!)
val entryGetInstanceFun = createGetEntryInstanceFun(irClass, declaration)
// TODO prettify
entryGetInstanceFun.parent = irClass.parent
@@ -426,7 +426,7 @@ class EnumEntryCreateGetInstancesFunsLowering(val context: JsCommonBackendContex
}
private fun createGetEntryInstanceFun(
irClass: IrClass, enumEntry: IrEnumEntry, initEntryInstancesFun: IrSimpleFunction
irClass: IrClass, enumEntry: IrEnumEntry
): IrSimpleFunction =
context.mapping.enumEntryToGetInstanceFun.getOrPut(enumEntry) {
context.irFactory.buildFun {
@@ -439,7 +439,7 @@ class EnumEntryCreateGetInstancesFunsLowering(val context: JsCommonBackendContex
}.also {
it.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(it.symbol).irBlockBody(it) {
+irCall(initEntryInstancesFun)
+irCall(irClass.initEntryInstancesFun!!)
+irReturn(irGetField(null, enumEntry.correspondingField!!))
}.statements
}
@@ -449,20 +449,40 @@ class EnumEntryCreateGetInstancesFunsLowering(val context: JsCommonBackendContex
private val IrClass.isInstantiableEnum: Boolean
get() = isEnumClass && !isExpect && !isEffectivelyExternal()
private val IrDeclaration.parentEnumClassOrNull: IrClass?
get() = parents.filterIsInstance<IrClass>().firstOrNull { it.isInstantiableEnum }
class EnumSyntheticFunctionsLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
private val IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
private val IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrConstructor && declaration.isPrimary) {
declaration.parentEnumClassOrNull?.let { enumClass ->
if (declaration.parentClassOrNull?.isCompanion == true) {
(declaration.body as? IrSyntheticBody)?.let { originalBody ->
declaration.parentEnumClassOrNull?.let { enumClass ->
declaration.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(declaration.symbol).irBlockBody {
+irCall(enumClass.initEntryInstancesFun!!.symbol)
}.statements + originalBody.statements
}
}
}
}
}
}
if (declaration is IrSimpleFunction) {
(declaration.body as? IrSyntheticBody)?.let { body ->
val kind = body.kind
declaration.parents.filterIsInstance<IrClass>().firstOrNull { it.isInstantiableEnum }?.let { irClass ->
declaration.parentEnumClassOrNull?.let { enumClass ->
declaration.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += when (kind) {
IrSyntheticBodyKind.ENUM_VALUES -> createEnumValuesBody(declaration, irClass)
IrSyntheticBodyKind.ENUM_VALUEOF -> createEnumValueOfBody(declaration, irClass)
IrSyntheticBodyKind.ENUM_VALUES -> createEnumValuesBody(declaration, enumClass)
IrSyntheticBodyKind.ENUM_VALUEOF -> createEnumValueOfBody(declaration, enumClass)
}.statements
}
}
@@ -479,15 +499,16 @@ class EnumSyntheticFunctionsLowering(val context: JsCommonBackendContext) : Decl
return context.createIrBuilder(valueOfFun.symbol).run {
irBlockBody {
+irReturn(
irWhen(
irClass.defaultType,
irClass.enumEntries.map {
irBranch(
irEquals(irString(it.name.identifier), irGet(nameParameter)), irCall(it.getInstanceFun!!)
)
} + irElseBranch(irCall(throwISESymbol))
)
+irWhen(
irClass.defaultType,
irClass.enumEntries.map {
irBranch(
irEquals(irString(it.name.identifier), irGet(nameParameter)), irReturn(irCall(it.getInstanceFun!!))
)
} + irElseBranch(irBlock {
+irCall(irClass.initEntryInstancesFun!!)
+irCall(throwISESymbol)
})
)
}
}
@@ -154,7 +154,7 @@ private val enumSyntheticFunsLoweringPhase = makeWasmModulePhase(
::EnumSyntheticFunctionsLowering,
name = "EnumSyntheticFunctionsLowering",
description = "Implement `valueOf` and `values`",
prerequisite = setOf(enumClassConstructorLoweringPhase)
prerequisite = setOf(enumClassConstructorLoweringPhase, enumClassCreateInitializerLoweringPhase)
)
private val enumUsageLoweringPhase = makeWasmModulePhase(
@@ -0,0 +1,53 @@
// IGNORE_BACKEND: JS
// IGNORE_BACKEND: WASM
var l = ""
enum class Foo {
FOO,
BAR;
init {
l += "Foo.$name;"
}
companion object {
init {
l += "Foo.CO;"
}
val boo = 22
}
}
enum class Foo2 {
FOO,
BAR;
init {
l += "Foo2.$name;"
}
companion object {
init {
l += "Foo2.CO;"
}
val boo = 22
}
}
fun box(): String {
try {
Foo.valueOf("NO")
} catch (e: Throwable) {
l += "caught;"
}
if (l != "Foo.FOO;Foo.BAR;Foo.CO;caught;") return "Failure 0: l = $l"
l = ""
Foo2.valueOf("BAR")
if (l != "Foo2.FOO;Foo2.BAR;Foo2.CO;") return "Failure 1: l = $l"
return "OK"
}
@@ -0,0 +1,53 @@
// IGNORE_BACKEND: JS
// IGNORE_BACKEND: WASM
var l = ""
enum class Foo {
FOO,
BAR;
init {
l += "Foo.$name;"
}
companion object {
init {
l += "Foo.CO;"
}
val boo = 22
}
}
enum class Foo2 {
FOO,
BAR;
init {
l += "Foo2.$name;"
}
companion object {
init {
l += "Foo2.CO;"
}
val boo = 22
}
}
fun box(): String {
try {
Foo.valueOf("NO")
} catch (e: Throwable) {
l += "caught;"
}
if (l != "Foo.FOO;Foo.BAR;Foo.CO;caught;") return "Failure 0: l = $l"
l = ""
Foo2.valueOf("BAR")
if (l != "Foo2.FOO;Foo2.BAR;Foo2.CO;") return "Failure 1: l = $l"
return "OK"
}
@@ -0,0 +1,19 @@
// IGNORE_BACKEND: JS
var l = ""
enum class Foo {
F;
init {
l += "Foo;"
}
object L {
init {
l += "Foo.CO;"
}
}
}
fun box(): String {
Foo.L
return if (l != "Foo.CO;") "FAIL: ${l}" else "OK"
}
@@ -11182,6 +11182,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/enum/inclassobj.kt");
}
@TestMetadata("initEntriesInCompanionObject.kt")
public void testInitEntriesInCompanionObject() throws Exception {
runTest("compiler/testData/codegen/box/enum/initEntriesInCompanionObject.kt");
}
@TestMetadata("initEntriesInValueOf.kt")
public void testInitEntriesInValueOf() throws Exception {
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("inner.kt")
public void testInner() throws Exception {
runTest("compiler/testData/codegen/box/enum/inner.kt");
@@ -11182,6 +11182,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/enum/inclassobj.kt");
}
@TestMetadata("initEntriesInCompanionObject.kt")
public void testInitEntriesInCompanionObject() throws Exception {
runTest("compiler/testData/codegen/box/enum/initEntriesInCompanionObject.kt");
}
@TestMetadata("initEntriesInValueOf.kt")
public void testInitEntriesInValueOf() throws Exception {
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("inner.kt")
public void testInner() throws Exception {
runTest("compiler/testData/codegen/box/enum/inner.kt");
@@ -11182,6 +11182,21 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/enum/inclassobj.kt");
}
@TestMetadata("initEntriesInCompanionObject.kt")
public void testInitEntriesInCompanionObject() throws Exception {
runTest("compiler/testData/codegen/box/enum/initEntriesInCompanionObject.kt");
}
@TestMetadata("initEntriesInValueOf.kt")
public void testInitEntriesInValueOf() throws Exception {
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("inner.kt")
public void testInner() throws Exception {
runTest("compiler/testData/codegen/box/enum/inner.kt");
@@ -9567,6 +9567,21 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/enum/inclassobj.kt");
}
@TestMetadata("initEntriesInCompanionObject.kt")
public void testInitEntriesInCompanionObject() throws Exception {
runTest("compiler/testData/codegen/box/enum/initEntriesInCompanionObject.kt");
}
@TestMetadata("initEntriesInValueOf.kt")
public void testInitEntriesInValueOf() throws Exception {
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("inner.kt")
public void testInner() throws Exception {
runTest("compiler/testData/codegen/box/enum/inner.kt");
@@ -9567,6 +9567,21 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/enum/inclassobj.kt");
}
@TestMetadata("initEntriesInCompanionObject.kt")
public void testInitEntriesInCompanionObject() throws Exception {
runTest("compiler/testData/codegen/box/enum/initEntriesInCompanionObject.kt");
}
@TestMetadata("initEntriesInValueOf.kt")
public void testInitEntriesInValueOf() throws Exception {
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("inner.kt")
public void testInner() throws Exception {
runTest("compiler/testData/codegen/box/enum/inner.kt");
@@ -9567,6 +9567,21 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/enum/inclassobj.kt");
}
@TestMetadata("initEntriesInCompanionObject.kt")
public void testInitEntriesInCompanionObject() throws Exception {
runTest("compiler/testData/codegen/box/enum/initEntriesInCompanionObject.kt");
}
@TestMetadata("initEntriesInValueOf.kt")
public void testInitEntriesInValueOf() throws Exception {
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("inner.kt")
public void testInner() throws Exception {
runTest("compiler/testData/codegen/box/enum/inner.kt");
@@ -4651,6 +4651,21 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/enum/inclassobj.kt");
}
@TestMetadata("initEntriesInCompanionObject.kt")
public void testInitEntriesInCompanionObject() throws Exception {
runTest("compiler/testData/codegen/box/enum/initEntriesInCompanionObject.kt");
}
@TestMetadata("initEntriesInValueOf.kt")
public void testInitEntriesInValueOf() throws Exception {
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("inner.kt")
public void testInner() throws Exception {
runTest("compiler/testData/codegen/box/enum/inner.kt");