Prepare ir interpreter for receiving ir bodies in separate map

This change will be useful when starts work with jvm klib
This commit is contained in:
Ivan Kylchik
2020-06-10 23:30:28 +03:00
parent 9542eb36ec
commit 1103de4bd5
3 changed files with 32 additions and 9 deletions
@@ -19,18 +19,20 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction
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.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import java.lang.invoke.MethodHandle
private const val MAX_STACK_SIZE = 10_000
private const val MAX_COMMANDS = 500_000
class IrInterpreter(irModule: IrModuleFragment) {
class IrInterpreter(irModule: IrModuleFragment, private val declarationsMap: Map<IdSignature, IrBody> = emptyMap()) {
private val irBuiltIns = irModule.irBuiltins
private val irExceptions = irModule.files.flatMap { it.declarations }.filterIsInstance<IrClass>()
.filter { it.isSubclassOf(irBuiltIns.throwableClass.owner) }
@@ -91,6 +93,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
incrementAndCheckCommands()
val executionResult = when (this) {
is IrFunctionImpl -> interpretFunction(this)
is IrLazyFunction -> interpretFunction(this as IrSimpleFunction)
is IrCall -> interpretCall(this)
is IrConstructorCall -> interpretConstructorCall(this)
is IrEnumConstructorCall -> interpretEnumConstructorCall(this)
@@ -141,7 +144,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
}
// this method is used to get stack trace after exception
private suspend fun interpretFunction(irFunction: IrFunctionImpl): ExecutionResult {
private suspend fun interpretFunction(irFunction: IrSimpleFunction): ExecutionResult {
yield()
if (irFunction.fileOrNull != null) stack.setCurrentFrameName(irFunction)
@@ -274,6 +277,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
val functionReceiver = dispatchReceiver.getCorrectReceiverByFunction(irFunction)
// it is important firstly to add receiver, then arguments; this order is used in builtin method call
// TODO what if receiver is null
irFunction.getDispatchReceiver()?.let { functionReceiver?.let { receiver -> valueArguments.add(Variable(it, receiver)) } }
irFunction.getExtensionReceiver()?.let { extensionReceiver?.let { receiver -> valueArguments.add(Variable(it, receiver)) } }
@@ -296,12 +300,26 @@ class IrInterpreter(irModule: IrModuleFragment) {
return@newFrame when {
dispatchReceiver is Wrapper && !isInlineOnly -> dispatchReceiver.getMethod(irFunction).invokeMethod(irFunction)
irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction)
irFunction.body == null || dispatchReceiver is Primitive<*> -> calculateBuiltIns(irFunction) // 'is Primitive' because of js char and long
dispatchReceiver is Primitive<*> -> calculateBuiltIns(irFunction) // 'is Primitive' check for js char and js long
irFunction.body == null -> irFunction.substituteFunctionBody()
else -> irFunction.interpret()
}
}
}
private suspend fun IrFunction.substituteFunctionBody(): ExecutionResult {
val body = declarationsMap[this.symbol.signature]
return body?.let {
try {
this.body = it
this.interpret()
} finally {
this.body = null
}
} ?: calculateBuiltIns(this)
}
private suspend fun interpretInstanceInitializerCall(call: IrInstanceInitializerCall): ExecutionResult {
val irClass = call.classSymbol.owner
@@ -332,7 +350,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
val irClass = owner.parent as IrClass
val typeArguments = getTypeArguments(irClass, constructorCall) { stack.getVariableState(it) } + stack.getAllTypeArguments()
if (irClass.hasAnnotation(evaluateIntrinsicAnnotation)) {
if (irClass.hasAnnotation(evaluateIntrinsicAnnotation) || irClass.fqNameWhenAvailable!!.startsWith(Name.identifier("java"))) {
return stack.newFrame(initPool = valueArguments) { Wrapper.getConstructorMethod(owner).invokeMethod(owner) }
.apply { (stack.peekReturnValue() as? Wrapper)?.typeArguments?.addAll(typeArguments) } // 'as?' because can be exception
}
@@ -582,24 +600,27 @@ class IrInterpreter(irModule: IrModuleFragment) {
// coercion to unit means that return value isn't used
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> stack.popReturnValue()
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
if (!stack.peekReturnValue().irClass.isSubclassOf(typeOperandClass)) {
if (!stack.peekReturnValue().irClass.let { it.isSubclassOf(typeOperandClass) || it.defaultType.isNothing() }) {
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
throw ClassCastException("$convertibleClassName cannot be cast to ${typeOperandClass.fqNameWhenAvailable}")
}
}
IrTypeOperator.SAFE_CAST -> {
if (!stack.peekReturnValue().irClass.isSubclassOf(typeOperandClass)) {
if (!stack.peekReturnValue().irClass.let { it.isSubclassOf(typeOperandClass) || it.defaultType.isNothing() }) {
stack.popReturnValue()
stack.pushReturnValue(null.toState(irBuiltIns.nothingType))
}
}
IrTypeOperator.INSTANCEOF -> {
val isInstance = stack.popReturnValue().irClass.isSubclassOf(typeOperandClass)
val isInstance = stack.popReturnValue().irClass.let { it.isSubclassOf(typeOperandClass) || it.defaultType.isNothing() }
stack.pushReturnValue(isInstance.toState(irBuiltIns.nothingType))
}
IrTypeOperator.NOT_INSTANCEOF -> {
val isInstance = stack.popReturnValue().irClass.isSubclassOf(typeOperandClass)
val isInstance = stack.popReturnValue().irClass.let { it.isSubclassOf(typeOperandClass) || it.defaultType.isNothing() }
stack.pushReturnValue((!isInstance).toState(irBuiltIns.nothingType))
}
IrTypeOperator.IMPLICIT_NOTNULL -> {
}
else -> TODO("${expression.operator} not implemented")
}
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
@@ -144,6 +145,7 @@ fun IrAnnotationContainer.getAnnotation(annotation: FqName): IrConstructorCall {
}
fun IrAnnotationContainer.getEvaluateIntrinsicValue(): String? {
if (this is IrClass && this.fqNameWhenAvailable?.startsWith(Name.identifier("java")) == true) return this.fqNameWhenAvailable?.asString()
if (!this.hasAnnotation(evaluateIntrinsicAnnotation)) return null
return (this.getAnnotation(evaluateIntrinsicAnnotation).getValueArgument(0) as IrConst<*>).value.toString()
}
@@ -25,7 +25,7 @@ sealed class IntrinsicBase {
object EmptyArray : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.emptyArray"
return fqName in setOf("kotlin.emptyArray", "kotlin.ArrayIntrinsicsKt.emptyArray")
}
override suspend fun evaluate(