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:
+29
-8
@@ -19,18 +19,20 @@ import org.jetbrains.kotlin.ir.IrElement
|
|||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
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.*
|
||||||
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.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import java.lang.invoke.MethodHandle
|
import java.lang.invoke.MethodHandle
|
||||||
|
|
||||||
private const val MAX_STACK_SIZE = 10_000
|
private const val MAX_STACK_SIZE = 10_000
|
||||||
private const val MAX_COMMANDS = 500_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 irBuiltIns = irModule.irBuiltins
|
||||||
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) }
|
||||||
@@ -91,6 +93,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
incrementAndCheckCommands()
|
incrementAndCheckCommands()
|
||||||
val executionResult = when (this) {
|
val executionResult = when (this) {
|
||||||
is IrFunctionImpl -> interpretFunction(this)
|
is IrFunctionImpl -> interpretFunction(this)
|
||||||
|
is IrLazyFunction -> interpretFunction(this as IrSimpleFunction)
|
||||||
is IrCall -> interpretCall(this)
|
is IrCall -> interpretCall(this)
|
||||||
is IrConstructorCall -> interpretConstructorCall(this)
|
is IrConstructorCall -> interpretConstructorCall(this)
|
||||||
is IrEnumConstructorCall -> interpretEnumConstructorCall(this)
|
is IrEnumConstructorCall -> interpretEnumConstructorCall(this)
|
||||||
@@ -141,7 +144,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this method is used to get stack trace after exception
|
// 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()
|
yield()
|
||||||
|
|
||||||
if (irFunction.fileOrNull != null) stack.setCurrentFrameName(irFunction)
|
if (irFunction.fileOrNull != null) stack.setCurrentFrameName(irFunction)
|
||||||
@@ -274,6 +277,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
val functionReceiver = dispatchReceiver.getCorrectReceiverByFunction(irFunction)
|
val functionReceiver = dispatchReceiver.getCorrectReceiverByFunction(irFunction)
|
||||||
|
|
||||||
// it is important firstly to add receiver, then arguments; this order is used in builtin method call
|
// 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.getDispatchReceiver()?.let { functionReceiver?.let { receiver -> valueArguments.add(Variable(it, receiver)) } }
|
||||||
irFunction.getExtensionReceiver()?.let { extensionReceiver?.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 {
|
return@newFrame when {
|
||||||
dispatchReceiver is Wrapper && !isInlineOnly -> dispatchReceiver.getMethod(irFunction).invokeMethod(irFunction)
|
dispatchReceiver is Wrapper && !isInlineOnly -> dispatchReceiver.getMethod(irFunction).invokeMethod(irFunction)
|
||||||
irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getStaticMethod(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()
|
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 {
|
private suspend fun interpretInstanceInitializerCall(call: IrInstanceInitializerCall): ExecutionResult {
|
||||||
val irClass = call.classSymbol.owner
|
val irClass = call.classSymbol.owner
|
||||||
|
|
||||||
@@ -332,7 +350,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
|
|
||||||
val irClass = owner.parent as IrClass
|
val irClass = owner.parent as IrClass
|
||||||
val typeArguments = getTypeArguments(irClass, constructorCall) { stack.getVariableState(it) } + stack.getAllTypeArguments()
|
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) }
|
return stack.newFrame(initPool = valueArguments) { Wrapper.getConstructorMethod(owner).invokeMethod(owner) }
|
||||||
.apply { (stack.peekReturnValue() as? Wrapper)?.typeArguments?.addAll(typeArguments) } // 'as?' because can be exception
|
.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
|
// coercion to unit means that return value isn't used
|
||||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> stack.popReturnValue()
|
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> stack.popReturnValue()
|
||||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
|
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
|
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
|
||||||
throw ClassCastException("$convertibleClassName cannot be cast to ${typeOperandClass.fqNameWhenAvailable}")
|
throw ClassCastException("$convertibleClassName cannot be cast to ${typeOperandClass.fqNameWhenAvailable}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IrTypeOperator.SAFE_CAST -> {
|
IrTypeOperator.SAFE_CAST -> {
|
||||||
if (!stack.peekReturnValue().irClass.isSubclassOf(typeOperandClass)) {
|
if (!stack.peekReturnValue().irClass.let { it.isSubclassOf(typeOperandClass) || it.defaultType.isNothing() }) {
|
||||||
stack.popReturnValue()
|
stack.popReturnValue()
|
||||||
stack.pushReturnValue(null.toState(irBuiltIns.nothingType))
|
stack.pushReturnValue(null.toState(irBuiltIns.nothingType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IrTypeOperator.INSTANCEOF -> {
|
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))
|
stack.pushReturnValue(isInstance.toState(irBuiltIns.nothingType))
|
||||||
}
|
}
|
||||||
IrTypeOperator.NOT_INSTANCEOF -> {
|
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))
|
stack.pushReturnValue((!isInstance).toState(irBuiltIns.nothingType))
|
||||||
|
}
|
||||||
|
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
||||||
|
|
||||||
}
|
}
|
||||||
else -> TODO("${expression.operator} not implemented")
|
else -> TODO("${expression.operator} not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
|||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||||
@@ -144,6 +145,7 @@ fun IrAnnotationContainer.getAnnotation(annotation: FqName): IrConstructorCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun IrAnnotationContainer.getEvaluateIntrinsicValue(): String? {
|
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
|
if (!this.hasAnnotation(evaluateIntrinsicAnnotation)) return null
|
||||||
return (this.getAnnotation(evaluateIntrinsicAnnotation).getValueArgument(0) as IrConst<*>).value.toString()
|
return (this.getAnnotation(evaluateIntrinsicAnnotation).getValueArgument(0) as IrConst<*>).value.toString()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ sealed class IntrinsicBase {
|
|||||||
object EmptyArray : IntrinsicBase() {
|
object EmptyArray : IntrinsicBase() {
|
||||||
override fun equalTo(irFunction: IrFunction): Boolean {
|
override fun equalTo(irFunction: IrFunction): Boolean {
|
||||||
val fqName = irFunction.fqNameWhenAvailable.toString()
|
val fqName = irFunction.fqNameWhenAvailable.toString()
|
||||||
return fqName == "kotlin.emptyArray"
|
return fqName in setOf("kotlin.emptyArray", "kotlin.ArrayIntrinsicsKt.emptyArray")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun evaluate(
|
override suspend fun evaluate(
|
||||||
|
|||||||
Reference in New Issue
Block a user