From 1103de4bd586859b69cf424ae701640eec6cc4c2 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Wed, 10 Jun 2020 23:30:28 +0300 Subject: [PATCH] Prepare ir interpreter for receiving ir bodies in separate map This change will be useful when starts work with jvm klib --- .../common/interpreter/IrInterpreter.kt | 37 +++++++++++++++---- .../backend/common/interpreter/Utils.kt | 2 + .../intrinsics/IntrinsicImplementations.kt | 2 +- 3 files changed, 32 insertions(+), 9 deletions(-) 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 9b389e3f1fd..29c33093ff2 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 @@ -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 = emptyMap()) { private val irBuiltIns = irModule.irBuiltins private val irExceptions = irModule.files.flatMap { it.declarations }.filterIsInstance() .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") } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt index 5d03205c1f2..9f7a29070f5 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt @@ -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() } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/intrinsics/IntrinsicImplementations.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/intrinsics/IntrinsicImplementations.kt index 49a044a09e9..f5c30f23d00 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/intrinsics/IntrinsicImplementations.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/intrinsics/IntrinsicImplementations.kt @@ -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(