diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/CallInterceptor.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/CallInterceptor.kt index 80cdcffa06f..7835561cabb 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/CallInterceptor.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/CallInterceptor.kt @@ -86,9 +86,10 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : when { Wrapper.mustBeHandledWithWrapper(irClass) || irClass.fqNameWhenAvailable!!.startsWith(Name.identifier("java")) -> { Wrapper.getConstructorMethod(irConstructor).invokeMethod(irConstructor, args) - when (constructorCall) { - is IrConstructorCall -> callStack.setState(constructorCall.getThisReceiver(), callStack.popState()) - else -> (receiver as Common).superWrapperClass = callStack.popState() as Wrapper + when { + irClass.isSubclassOfThrowable() -> (receiver as ExceptionState).copyFieldsFrom(callStack.popState() as Wrapper) + constructorCall is IrConstructorCall -> callStack.setState(constructorCall.getThisReceiver(), callStack.popState()) + else -> (receiver as Complex).superWrapperClass = callStack.popState() as Wrapper } } irClass.defaultType.isArray() || irClass.defaultType.isPrimitiveArray() -> { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt index 7eab2586d6c..172aaf0ce52 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt @@ -37,9 +37,9 @@ internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEn when (element) { null -> return is IrSimpleFunction -> unfoldFunction(element, environment) - is IrConstructor -> unfoldConstructor(element, environment) + is IrConstructor -> unfoldConstructor(element, callStack) is IrCall -> unfoldCall(element, callStack) - is IrConstructorCall -> unfoldConstructorCall(element, callStack) + is IrConstructorCall -> unfoldConstructorCall(element, environment) is IrEnumConstructorCall -> unfoldEnumConstructorCall(element, callStack) is IrDelegatingConstructorCall -> unfoldDelegatingConstructorCall(element, callStack) is IrInstanceInitializerCall -> unfoldInstanceInitializerCall(element, callStack) @@ -88,15 +88,17 @@ private fun unfoldFunction(function: IrSimpleFunction, environment: IrInterprete ?: throw InterpreterError("Ir function must be with body") } -private fun unfoldConstructor(constructor: IrConstructor, environment: IrInterpreterEnvironment) { - val callStack = environment.callStack +private fun unfoldConstructor(constructor: IrConstructor, callStack: CallStack) { when (constructor.fqNameWhenAvailable?.asString()) { - "kotlin.Enum." -> { + "kotlin.Enum.", "kotlin.Throwable." -> { val irClass = constructor.parentAsClass - val receiver = irClass.thisReceiver!!.symbol - val receiverState = callStack.getState(receiver) - irClass.declarations.filterIsInstance().forEachIndexed { index, property -> - receiverState.setField(Variable(property.symbol, callStack.getState(constructor.valueParameters[index].symbol))) + val receiverSymbol = irClass.thisReceiver!!.symbol + val receiverState = callStack.getState(receiverSymbol) + + irClass.declarations.filterIsInstance().forEach { property -> + val parameter = constructor.valueParameters.singleOrNull { it.name == property.name } + val state = parameter?.let { callStack.getState(it.symbol) } ?: Primitive.nullStateOfType(property.getter!!.returnType) + receiverState.setField(Variable(property.symbol, state)) } } else -> { @@ -111,11 +113,15 @@ private fun unfoldCall(call: IrCall, callStack: CallStack) { unfoldValueParameters(call, callStack) } -private fun unfoldConstructorCall(constructorCall: IrFunctionAccessExpression, callStack: CallStack) { +private fun unfoldConstructorCall(constructorCall: IrFunctionAccessExpression, environment: IrInterpreterEnvironment) { + val callStack = environment.callStack val constructor = constructorCall.symbol.owner + val irClass = constructor.parentAsClass unfoldValueParameters(constructorCall, callStack) - // this variable is used to create object once - callStack.addVariable(Variable(constructorCall.getThisReceiver(), Common(constructor.parentAsClass))) + // this state is used to create object once + val state = if (irClass.isSubclassOfThrowable()) ExceptionState(irClass, callStack.getStackTrace()) else Common(irClass) + if (irClass.isObject) environment.mapOfObjects[irClass.symbol] = state // must set object's state here to avoid cyclic evaluation + callStack.addVariable(Variable(constructorCall.getThisReceiver(), state)) } private fun unfoldEnumConstructorCall(enumConstructorCall: IrEnumConstructorCall, callStack: CallStack) { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index 62a85c6b590..92cc3121541 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -385,18 +385,18 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal private fun interpretGetObjectValue(expression: IrGetObjectValue) { callInterceptor.interceptGetObjectValue(expression) { val objectClass = expression.symbol.owner - val state = Common(objectClass) - environment.mapOfObjects[objectClass.symbol] = state // must set object's state here to avoid cyclic evaluation - callStack.addVariable(Variable(objectClass.thisReceiver!!.symbol, state)) // non compile time objects can be used in interpreter, for example, to evaluate const properties or in tests - if (!objectClass.hasAnnotation(compileTimeAnnotation) && !environment.configuration.createNonCompileTimeObjects) + val buildObject = objectClass.hasAnnotation(compileTimeAnnotation) || environment.configuration.createNonCompileTimeObjects + if (objectClass.constructors.none() || !buildObject) { + val state = Common(objectClass) + environment.mapOfObjects[objectClass.symbol] = state return@interceptGetObjectValue callStack.pushState(state) + } - val constructor = objectClass.constructors.firstOrNull() ?: return@interceptGetObjectValue callStack.pushState(state) + val constructor = objectClass.constructors.first() val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol) - callStack.newSubFrame(constructorCall) - callStack.addInstruction(SimpleInstruction(constructorCall)) + callStack.addInstruction(CompoundInstruction(constructorCall)) } } @@ -564,15 +564,8 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal element.finallyExpression?.handleAndDropResult(callStack) } + @Suppress("UNUSED_PARAMETER") private fun interpretThrow(expression: IrThrow) { - val exception = callStack.popState() - callStack.newSubFrame(expression) // temporary frame to get correct stack trace - when (exception) { - is Common -> callStack.pushState(ExceptionState(exception, callStack.getStackTrace())) - is Wrapper -> callStack.pushState(ExceptionState(exception, callStack.getStackTrace())) - is ExceptionState -> callStack.pushState(exception) - else -> throw InterpreterError("${exception::class} cannot be used as exception state") - } callStack.dropFramesUntilTryCatch() } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt index d44214e3f98..bb479ff463f 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.Variance -import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import java.lang.invoke.MethodType @@ -77,7 +76,7 @@ internal fun Any?.toState(irType: IrType): State { is State -> this is Boolean, is Char, is Byte, is Short, is Int, is Long, is String, is Float, is Double, is Array<*>, is ByteArray, is CharArray, is ShortArray, is IntArray, is LongArray, is FloatArray, is DoubleArray, is BooleanArray -> Primitive(this, irType) - null -> Primitive(this, irType) + null -> Primitive.nullStateOfType(irType) else -> irType.classOrNull?.owner?.let { Wrapper(this, it) } ?: Wrapper(this) } } @@ -200,6 +199,12 @@ internal fun IrType.isFunction() = this.getClass()?.fqNameWhenAvailable?.asStrin internal fun IrType.isKFunction() = this.getClass()?.fqNameWhenAvailable?.asString()?.startsWith("kotlin.reflect.KFunction") ?: false internal fun IrType.isTypeParameter() = classifierOrNull is IrTypeParameterSymbol internal fun IrType.isThrowable() = this.getClass()?.fqNameWhenAvailable?.asString() == "kotlin.Throwable" +internal fun IrClass.isSubclassOfThrowable(): Boolean { + return generateSequence(this) { irClass -> + if (irClass.defaultType.isAny()) return@generateSequence null + irClass.superTypes.mapNotNull { it.classOrNull?.owner }.singleOrNull { it.isClass } + }.any { it.defaultType.isThrowable() } +} internal fun IrType.isUnsignedArray(): Boolean { if (this !is IrSimpleType || classifier !is IrClassSymbol) return false diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/ExceptionState.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/ExceptionState.kt index 20bb81db8c0..d49788eac0b 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/ExceptionState.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/ExceptionState.kt @@ -5,12 +5,12 @@ package org.jetbrains.kotlin.ir.interpreter.state -import org.jetbrains.kotlin.ir.interpreter.getLastOverridden -import org.jetbrains.kotlin.ir.interpreter.stack.Variable -import org.jetbrains.kotlin.ir.interpreter.toState import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.interpreter.getLastOverridden +import org.jetbrains.kotlin.ir.interpreter.stack.Variable +import org.jetbrains.kotlin.ir.interpreter.toState import org.jetbrains.kotlin.ir.util.isSubclassOf import org.jetbrains.kotlin.ir.util.nameForIrSerialization import kotlin.math.min @@ -24,8 +24,8 @@ internal class ExceptionState private constructor( override val message: String? get() = getField(messageProperty.symbol)?.asStringOrNull() - override val cause: Throwable? - get() = getField(causeProperty.symbol)?.let { if (it is ExceptionState) it else null } + override val cause: ExceptionState? + get() = getField(causeProperty.symbol) as? ExceptionState private lateinit var exceptionFqName: String private val exceptionHierarchy = mutableListOf() @@ -36,20 +36,11 @@ internal class ExceptionState private constructor( init { if (!this::exceptionFqName.isInitialized) this.exceptionFqName = irClassFqName() - - if (fields.none { it.symbol == messageProperty.symbol }) { - setMessage() - } + if (fields.none { it.symbol == messageProperty.symbol }) setMessage(null) + if (fields.none { it.symbol == causeProperty.symbol }) setCause(null) } - constructor(common: Common, stackTrace: List) : this(common.irClass, common.fields, stackTrace) { - (common.superWrapperClass?.value as? Throwable)?.let { setMessage(it.message) } - setUpCauseIfNeeded(common.superWrapperClass) - } - - constructor(wrapper: Wrapper, stackTrace: List) : this(wrapper.value as Throwable, wrapper.irClass, stackTrace) { - setUpCauseIfNeeded(wrapper) - } + constructor(irClass: IrClass, stackTrace: List) : this(irClass, mutableListOf(), stackTrace) constructor( exception: Throwable, irClass: IrClass, stackTrace: List @@ -64,11 +55,21 @@ internal class ExceptionState private constructor( } } - private fun setUpCauseIfNeeded(wrapper: Wrapper?) { - val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionState - setCause(cause) + fun copyFieldsFrom(wrapper: Wrapper) { + (wrapper.value as? Throwable)?.let { + setMessage(it.message) + setCause(it.cause as? ExceptionState) + } + } + + override fun setField(newVar: Variable) { + super.setField(newVar) + recalculateCauseAndMessage() + } + + private fun recalculateCauseAndMessage() { if (message == null && cause != null) { - val causeMessage = cause.exceptionFqName + (cause.message?.let { ": $it" } ?: "") + val causeMessage = cause!!.exceptionFqName + (cause!!.message?.let { ": $it" } ?: "") setMessage(causeMessage) } } @@ -80,12 +81,12 @@ internal class ExceptionState private constructor( return irClass.isSubclassOf(ancestor) } - private fun setMessage(messageValue: String? = null) { + private fun setMessage(messageValue: String?) { setField(Variable(messageProperty.symbol, Primitive(messageValue, messageProperty.getter!!.returnType))) } private fun setCause(causeValue: State?) { - setField(Variable(causeProperty.symbol, causeValue ?: Primitive(null, causeProperty.getter!!.returnType))) + setField(Variable(causeProperty.symbol, causeValue ?: Primitive.nullStateOfType(causeProperty.getter!!.returnType))) } fun getFullDescription(): String { @@ -93,7 +94,7 @@ internal class ExceptionState private constructor( val message = message.let { if (it?.isNotEmpty() == true) ": $it" else "" } val prefix = if (stackTrace.isNotEmpty()) "\n\t" else "" val postfix = if (stackTrace.size > 10) "\n\t..." else "" - val causeMessage = (cause as? ExceptionState)?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: "" + val causeMessage = cause?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: "" return "Exception $exceptionFqName$message" + stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) + causeMessage diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Primitive.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Primitive.kt index c5c0ed41ca9..2f55328a2a6 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Primitive.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Primitive.kt @@ -29,4 +29,10 @@ internal class Primitive(val value: T, val type: IrType) : State { override fun toString(): String { return "Primitive(value=$value, type=${irClass.defaultType})" } + + companion object { + fun nullStateOfType(irType: IrType): Primitive<*> { + return Primitive(null, irType) + } + } }