diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt index f9dd914b47b..82be7607067 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -18,10 +18,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.isArray -import org.jetbrains.kotlin.ir.types.isSubtypeOf +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import java.lang.invoke.MethodHandle @@ -36,9 +33,12 @@ class IrInterpreter(irModule: IrModuleFragment) { private val irExceptions = irModule.files.flatMap { it.declarations }.filterIsInstance() .filter { it.isSubclassOf(irBuiltIns.throwableClass.owner) } private val classCastException = irExceptions.first { it.name.asString() == ClassCastException::class.java.simpleName } + private val illegalArgumentException = irExceptions.first { it.name.asString() == IllegalArgumentException::class.java.simpleName } private val stackTrace = mutableListOf() + private val mapOfEnums = mutableMapOf, Common>() + private fun Any?.getType(defaultType: IrType): IrType { return when (this) { is Boolean -> irBuiltIns.booleanType @@ -83,6 +83,8 @@ class IrInterpreter(irModule: IrModuleFragment) { is IrGetField -> interpretGetField(this, data) is IrGetValue -> interpretGetValue(this, data) is IrGetObjectValue -> interpretGetObjectValue(this, data) + is IrGetEnumValue -> interpretGetEnumValue(this, data) + is IrEnumEntry -> interpretEnumEntry(this, data) is IrConst<*> -> interpretConst(this, data) is IrVariable -> interpretVariable(this, data) is IrSetVariable -> interpretSetVariable(this, data) @@ -178,7 +180,36 @@ class IrInterpreter(irModule: IrModuleFragment) { throw StackOverflowError("") } - irFunction.body?.interpret(data) ?: throw AssertionError("Ir function must be with body") + when (val kind = (irFunction.body as? IrSyntheticBody)?.kind) { + IrSyntheticBodyKind.ENUM_VALUES -> { + val enumClass = irFunction.parentAsClass + val enumEntries = enumClass.declarations + .filterIsInstance() + .map { entry -> + entry.interpret(data).also { if (it != Code.NEXT) return it } + data.popReturnValue() as Common + } + data.pushReturnValue(enumEntries.toTypedArray().toState(irBuiltIns.arrayClass.defaultType)) + Code.NEXT + } + IrSyntheticBodyKind.ENUM_VALUEOF -> { + val enumClass = irFunction.parentAsClass + val enumEntryName = (data.getAll().single().state as Primitive<*>).value.toString() + val enumEntry = enumClass.declarations + .filterIsInstance() + .singleOrNull { it.name.asString() == enumEntryName } + if (enumEntry == null) { + val message = "No enum constant ${enumClass.fqNameForIrSerialization}.$enumEntryName" + data.pushReturnValue(ExceptionState(IllegalArgumentException(message), illegalArgumentException, stackTrace)) + Code.EXCEPTION + } else { + enumEntry.interpret(data).also { if (it != Code.NEXT) return it } + Code.NEXT + } + } + null -> irFunction.body?.interpret(data) ?: throw AssertionError("Ir function must be with body") + else -> throw AssertionError("Unsupported IrSyntheticBodyKind $kind") + } } finally { if (irFunction.fileOrNull != null) stackTrace.removeAt(stackTrace.lastIndex) } @@ -228,6 +259,7 @@ class IrInterpreter(irModule: IrModuleFragment) { val overriddenOwner = overridden.owner as IrFunctionImpl return when { + superQualifier.irClassFqName() == "kotlin.Enum" && owner.name.asString() == "hashCode" -> calculateOverridden(overriddenOwner, newStates) overriddenOwner.body != null -> overriddenOwner.interpret(newStates) superQualifier.superType == null -> calculateBuiltIns(overriddenOwner, newStates) else -> calculateOverridden(overriddenOwner, newStates) @@ -551,6 +583,23 @@ class IrInterpreter(irModule: IrModuleFragment) { return Code.NEXT } + private suspend fun interpretGetEnumValue(expression: IrGetEnumValue, data: Frame): Code { + return interpretEnumEntry(expression.symbol.owner, data) + } + + private suspend fun interpretEnumEntry(enumEntry: IrEnumEntry, data: Frame): Code { + val enumSignature = Pair(enumEntry.parentAsClass, enumEntry.name.asString()) + mapOfEnums[enumSignature]?.let { + data.pushReturnValue(it) + return Code.NEXT + } + + val code = enumEntry.initializerExpression?.interpret(data)?.also { if (it != Code.NEXT) return it } + code ?: throw AssertionError("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null") + mapOfEnums[enumSignature] = data.peekReturnValue() as Common + return code + } + private suspend fun interpretTypeOperatorCall(expression: IrTypeOperatorCall, data: Frame): Code { val code = expression.argument.interpret(data).also { if (it != Code.NEXT) return it } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt index 2a0de75a21a..018ee57d79e 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt @@ -118,6 +118,10 @@ abstract class Complex(override val irClass: IrClass, override val fields: Mutab return irClass.thisReceiver!!.descriptor } + fun irClassFqName(): String { + return irClass.fqNameForIrSerialization.toString() + } + override fun setState(newVar: Variable) { when (val oldState = fields.firstOrNull { it.descriptor == newVar.descriptor }) { null -> fields.add(newVar) // newVar isn't present in value list