Implement enum interpretation
This commit is contained in:
+54
-5
@@ -18,10 +18,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
|||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
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.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import java.lang.invoke.MethodHandle
|
import java.lang.invoke.MethodHandle
|
||||||
|
|
||||||
@@ -36,9 +33,12 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
private val irExceptions = irModule.files.flatMap { it.declarations }.filterIsInstance<IrClass>()
|
private val irExceptions = irModule.files.flatMap { it.declarations }.filterIsInstance<IrClass>()
|
||||||
.filter { it.isSubclassOf(irBuiltIns.throwableClass.owner) }
|
.filter { it.isSubclassOf(irBuiltIns.throwableClass.owner) }
|
||||||
private val classCastException = irExceptions.first { it.name.asString() == ClassCastException::class.java.simpleName }
|
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<String>()
|
private val stackTrace = mutableListOf<String>()
|
||||||
|
|
||||||
|
private val mapOfEnums = mutableMapOf<Pair<IrClass, String>, Common>()
|
||||||
|
|
||||||
private fun Any?.getType(defaultType: IrType): IrType {
|
private fun Any?.getType(defaultType: IrType): IrType {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is Boolean -> irBuiltIns.booleanType
|
is Boolean -> irBuiltIns.booleanType
|
||||||
@@ -83,6 +83,8 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
is IrGetField -> interpretGetField(this, data)
|
is IrGetField -> interpretGetField(this, data)
|
||||||
is IrGetValue -> interpretGetValue(this, data)
|
is IrGetValue -> interpretGetValue(this, data)
|
||||||
is IrGetObjectValue -> interpretGetObjectValue(this, data)
|
is IrGetObjectValue -> interpretGetObjectValue(this, data)
|
||||||
|
is IrGetEnumValue -> interpretGetEnumValue(this, data)
|
||||||
|
is IrEnumEntry -> interpretEnumEntry(this, data)
|
||||||
is IrConst<*> -> interpretConst(this, data)
|
is IrConst<*> -> interpretConst(this, data)
|
||||||
is IrVariable -> interpretVariable(this, data)
|
is IrVariable -> interpretVariable(this, data)
|
||||||
is IrSetVariable -> interpretSetVariable(this, data)
|
is IrSetVariable -> interpretSetVariable(this, data)
|
||||||
@@ -178,7 +180,36 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
throw StackOverflowError("")
|
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<IrEnumEntry>()
|
||||||
|
.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<IrEnumEntry>()
|
||||||
|
.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 {
|
} finally {
|
||||||
if (irFunction.fileOrNull != null) stackTrace.removeAt(stackTrace.lastIndex)
|
if (irFunction.fileOrNull != null) stackTrace.removeAt(stackTrace.lastIndex)
|
||||||
}
|
}
|
||||||
@@ -228,6 +259,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
|
|
||||||
val overriddenOwner = overridden.owner as IrFunctionImpl
|
val overriddenOwner = overridden.owner as IrFunctionImpl
|
||||||
return when {
|
return when {
|
||||||
|
superQualifier.irClassFqName() == "kotlin.Enum" && owner.name.asString() == "hashCode" -> calculateOverridden(overriddenOwner, newStates)
|
||||||
overriddenOwner.body != null -> overriddenOwner.interpret(newStates)
|
overriddenOwner.body != null -> overriddenOwner.interpret(newStates)
|
||||||
superQualifier.superType == null -> calculateBuiltIns(overriddenOwner, newStates)
|
superQualifier.superType == null -> calculateBuiltIns(overriddenOwner, newStates)
|
||||||
else -> calculateOverridden(overriddenOwner, newStates)
|
else -> calculateOverridden(overriddenOwner, newStates)
|
||||||
@@ -551,6 +583,23 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
return Code.NEXT
|
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 {
|
private suspend fun interpretTypeOperatorCall(expression: IrTypeOperatorCall, data: Frame): Code {
|
||||||
val code = expression.argument.interpret(data).also { if (it != Code.NEXT) return it }
|
val code = expression.argument.interpret(data).also { if (it != Code.NEXT) return it }
|
||||||
|
|
||||||
|
|||||||
+4
@@ -118,6 +118,10 @@ abstract class Complex(override val irClass: IrClass, override val fields: Mutab
|
|||||||
return irClass.thisReceiver!!.descriptor
|
return irClass.thisReceiver!!.descriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun irClassFqName(): String {
|
||||||
|
return irClass.fqNameForIrSerialization.toString()
|
||||||
|
}
|
||||||
|
|
||||||
override fun setState(newVar: Variable) {
|
override fun setState(newVar: Variable) {
|
||||||
when (val oldState = fields.firstOrNull { it.descriptor == newVar.descriptor }) {
|
when (val oldState = fields.firstOrNull { it.descriptor == newVar.descriptor }) {
|
||||||
null -> fields.add(newVar) // newVar isn't present in value list
|
null -> fields.add(newVar) // newVar isn't present in value list
|
||||||
|
|||||||
Reference in New Issue
Block a user