diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt index 973904b28d4..4a4a2b0d1fa 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt @@ -37,7 +37,7 @@ internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEn is IrConstructor -> unfoldConstructor(element, callStack) is IrCall -> unfoldValueParameters(element, environment) is IrConstructorCall -> unfoldValueParameters(element, environment) - is IrEnumConstructorCall -> unfoldValueParameters(element, environment) + is IrEnumConstructorCall -> unfoldEnumConstructorCall(element, environment) is IrDelegatingConstructorCall -> unfoldValueParameters(element, environment) is IrInstanceInitializerCall -> unfoldInstanceInitializerCall(element, callStack) is IrField -> unfoldField(element, callStack) @@ -49,6 +49,7 @@ internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEn is IrGetValue -> unfoldGetValue(element, environment) is IrGetObjectValue -> unfoldGetObjectValue(element, environment) is IrGetEnumValue -> unfoldGetEnumValue(element, environment) + is IrEnumEntry -> unfoldEnumEntry(element, environment) is IrConst<*> -> callStack.pushSimpleInstruction(element) is IrVariable -> unfoldVariable(element, callStack) is IrSetValue -> unfoldSetValue(element, callStack) @@ -177,6 +178,21 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro } } +private fun unfoldEnumConstructorCall(element: IrEnumConstructorCall, environment: IrInterpreterEnvironment) { + val parentName = element.symbol.owner.parentClassOrNull?.fqName + if (parentName == "kotlin.Enum") { + // must create a copy here to avoid original data corruption + val constructorCallCopy = element.shallowCopy() + val enumObject = environment.callStack.loadState(element.getThisReceiver()) + environment.irBuiltIns.enumClass.owner.declarations.filterIsInstance().forEachIndexed { index, it -> + val field = enumObject.getField(it.symbol) as Primitive<*> + constructorCallCopy.putValueArgument(index, field.value.toIrConst(field.type)) + } + return unfoldValueParameters(constructorCallCopy, environment) + } + unfoldValueParameters(element, environment) +} + private fun unfoldInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall, callStack: CallStack) { val irClass = instanceInitializerCall.classSymbol.owner val toInitialize = irClass.declarations.filter { it is IrProperty || it is IrAnonymousInitializer } @@ -264,6 +280,13 @@ private fun unfoldGetEnumValue(expression: IrGetEnumValue, environment: IrInterp } } +@Suppress("UNUSED_PARAMETER") +private fun unfoldEnumEntry(enumEntry: IrEnumEntry, environment: IrInterpreterEnvironment) { + // a little hak and misconception here; we are not creating simple instructions from this and just do the cleaning after interpretation + environment.callStack.popState() + environment.callStack.dropSubFrame() +} + private fun unfoldVariable(variable: IrVariable, callStack: CallStack) { when (variable.initializer) { null -> callStack.storeState(variable.symbol, null) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index bfa431b51ad..e3881b92f88 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -396,28 +396,24 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal ?: throw InterpreterError("Initializer at enum entry ${enumEntry.fqName} is null") val enumConstructorCall = enumInitializer as? IrEnumConstructorCall ?: (enumInitializer as IrBlock).statements.filterIsInstance().single() - val enumSuperCall = enumConstructorCall.getSuperEnumCall() - - // TODO must call this function even after exception has occurred or else data will be corrupted - val cleanEnumSuperCall = fun() { - enumSuperCall.apply { (0 until this.valueArgumentsCount).forEach { putValueArgument(it, null) } } // restore to null - callStack.popState() // result of constructor must be dropped, because next instruction will be `IrGetEnumValue` - callStack.dropSubFrame() - } - - if (enumEntries.isNotEmpty()) { - val valueArguments = listOf( - enumEntry.name.asString().toIrConst(irBuiltIns.stringType), - enumEntries.indexOf(enumEntry).toIrConst(irBuiltIns.intType) - ) - valueArguments.forEachIndexed { index, irConst -> enumSuperCall.putValueArgument(index, irConst) } - } val enumClassObject = Common(enumEntry.correspondingClass ?: enumClass) environment.mapOfEnums[enumEntry.symbol] = enumClassObject + if (enumEntries.isNotEmpty()) { + // these fields will be evaluated during interpretation of constructor call + // but we need them right away to create correct call to Enum's constructor + val valueArguments = listOf( + Primitive(enumEntry.name.asString(), irBuiltIns.stringType), + Primitive(enumEntries.indexOf(enumEntry), irBuiltIns.intType) + ) + irBuiltIns.enumClass.owner.declarations.filterIsInstance().zip(valueArguments).forEach { (property, argument) -> + enumClassObject.setField(property.symbol, argument) + } + } + callStack.newSubFrame(enumEntry) - callStack.pushInstruction(CustomInstruction(cleanEnumSuperCall)) + callStack.pushCompoundInstruction(enumEntry) // not really a compound instruction callStack.pushCompoundInstruction(enumInitializer) callStack.storeState(enumConstructorCall.getThisReceiver(), enumClassObject) } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt index 5cce7de824f..bd1103bdea9 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt @@ -285,16 +285,6 @@ internal fun IrInterpreterEnvironment.loadReifiedTypeArguments(expression: IrFun } } -internal fun IrFunctionAccessExpression.getSuperEnumCall(): IrEnumConstructorCall { - val name = this.symbol.owner.parentClassOrNull?.fqName - if (this is IrEnumConstructorCall && name == "kotlin.Enum") return this - return when (val delegatingCall = this.symbol.owner.body?.statements?.get(0)) { - is IrFunctionAccessExpression -> delegatingCall.getSuperEnumCall() - is IrTypeOperatorCall -> (delegatingCall.argument as IrFunctionAccessExpression).getSuperEnumCall() - else -> TODO("$delegatingCall is unexpected") - } -} - internal fun IrFunction.hasFunInterfaceParent(): Boolean { return this.parentClassOrNull?.isFun == true }