Optimize some methods of CallStack and Frame

This commit is contained in:
Ivan Kylchik
2021-04-15 16:45:10 +03:00
committed by TeamCityServer
parent e0438d1123
commit 877832ef8c
12 changed files with 193 additions and 170 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.interpreter
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
@@ -54,7 +55,9 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
private val bodyMap: Map<IdSignature, IrBody> = interpreter.bodyMap
override fun interceptProxy(irFunction: IrFunction, valueArguments: List<Variable>, expectedResultClass: Class<*>): Any? {
val irCall = IrCallImpl.fromSymbolOwner(0, 0, irFunction.returnType, irFunction.symbol as IrSimpleFunctionSymbol)
val irCall = IrCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, irFunction.returnType, irFunction.symbol as IrSimpleFunctionSymbol
)
return interpreter.withNewCallStack(irCall) {
this@withNewCallStack.environment.callStack.addInstruction(SimpleInstruction(irCall))
valueArguments.forEach { this@withNewCallStack.environment.callStack.addVariable(it) }
@@ -81,22 +84,26 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
val invokedFunction = functionState.irFunction
var index = 1 // drop first arg that is reference to function
val dispatchReceiver = invokedFunction.dispatchReceiverParameter?.let { functionState.getState(it.symbol) ?: args[index++] }
val extensionReceiver = invokedFunction.extensionReceiverParameter?.let { functionState.getState(it.symbol) ?: args[index++] }
val dispatchReceiver = invokedFunction.dispatchReceiverParameter?.let { functionState.getField(it.symbol) ?: args[index++] }
val extensionReceiver = invokedFunction.extensionReceiverParameter?.let { functionState.getField(it.symbol) ?: args[index++] }
val valueArguments = invokedFunction.valueParameters.map { args[index++] }
val function = when (val symbol = invokedFunction.symbol) {
is IrSimpleFunctionSymbol -> {
val irCall = IrCallImpl.fromSymbolOwner(0, 0, invokedFunction.returnType, invokedFunction.symbol as IrSimpleFunctionSymbol)
val irCall = IrCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, invokedFunction.returnType, invokedFunction.symbol as IrSimpleFunctionSymbol
)
val actualFunction = dispatchReceiver?.getIrFunctionByIrCall(irCall) ?: invokedFunction
callStack.newFrame(actualFunction, listOf(SimpleInstruction(actualFunction)))
callStack.newFrame(actualFunction)
callStack.addInstruction(SimpleInstruction(actualFunction))
callStack.addVariable(Variable(actualFunction.symbol, KTypeState(call.type, irBuiltIns.anyClass.owner)))
actualFunction
}
is IrConstructorSymbol -> {
val irConstructorCall = IrConstructorCallImpl.fromSymbolOwner(invokedFunction.returnType, symbol)
callStack.newSubFrame(irConstructorCall, listOf(SimpleInstruction(irConstructorCall)))
callStack.newSubFrame(irConstructorCall)
callStack.addInstruction(SimpleInstruction(irConstructorCall))
callStack.addVariable(Variable(invokedFunction.parentAsClass.thisReceiver!!.symbol, Common(invokedFunction.parentAsClass)))
invokedFunction
@@ -104,7 +104,8 @@ private fun unfoldDelegatingConstructorCall(delegatingConstructorCall: IrFunctio
private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callStack: CallStack) {
val irFunction = expression.symbol.owner
// new sub frame is used to store value arguments, in case then they are used in default args evaluation
callStack.newSubFrame(expression, listOf(SimpleInstruction(expression)))
callStack.newSubFrame(expression)
callStack.addInstruction(SimpleInstruction(expression))
fun getDefaultForParameterAt(index: Int): IrExpression? {
fun IrValueParameter.getDefault(): IrExpressionBody? {
@@ -169,7 +170,7 @@ private fun unfoldBody(body: IrBody, callStack: CallStack) {
}
private fun unfoldBlock(block: IrBlock, callStack: CallStack) {
callStack.newSubFrame(block, listOf())
callStack.newSubFrame(block)
callStack.addInstruction(SimpleInstruction(block))
unfoldStatements(block.statements, callStack)
}
@@ -211,9 +212,10 @@ private fun unfoldGetValue(expression: IrGetValue, environment: IrInterpreterEnv
// used to evaluate constants inside object
if (expectedClass != null && expectedClass.isObject && expression.symbol.owner.origin == IrDeclarationOrigin.INSTANCE_RECEIVER) {
// TODO is this correct behaviour?
return unfoldGetObjectValue(IrGetObjectValueImpl(0, 0, expectedClass.defaultType, expectedClass.symbol), environment)
val irGetObject = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, expectedClass.defaultType, expectedClass.symbol)
return unfoldGetObjectValue(irGetObject, environment)
}
environment.callStack.pushState(environment.callStack.getVariable(expression.symbol).state)
environment.callStack.pushState(environment.callStack.getState(expression.symbol))
}
private fun unfoldGetObjectValue(expression: IrGetObjectValue, environment: IrInterpreterEnvironment) {
@@ -257,18 +259,18 @@ private fun unfoldTypeOperatorCall(element: IrTypeOperatorCall, callStack: CallS
}
private fun unfoldBranch(branch: IrBranch, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(branch)) //2
callStack.addInstruction(CompoundInstruction(branch.condition)) //1
callStack.addInstruction(SimpleInstruction(branch))
callStack.addInstruction(CompoundInstruction(branch.condition))
}
private fun unfoldWhileLoop(loop: IrWhileLoop, callStack: CallStack) {
callStack.newSubFrame(loop, listOf())
callStack.newSubFrame(loop)
callStack.addInstruction(SimpleInstruction(loop))
callStack.addInstruction(CompoundInstruction(loop.condition))
}
private fun unfoldDoWhileLoop(loop: IrDoWhileLoop, callStack: CallStack) {
callStack.newSubFrame(loop, listOf())
callStack.newSubFrame(loop)
callStack.addInstruction(SimpleInstruction(loop))
callStack.addInstruction(CompoundInstruction(loop.condition))
callStack.addInstruction(CompoundInstruction(loop.body))
@@ -276,7 +278,9 @@ private fun unfoldDoWhileLoop(loop: IrDoWhileLoop, callStack: CallStack) {
private fun unfoldWhen(element: IrWhen, callStack: CallStack) {
// new sub frame to drop it after
callStack.newSubFrame(element, element.branches.map { CompoundInstruction(it) } + listOf(SimpleInstruction(element)))
callStack.newSubFrame(element)
callStack.addInstruction(SimpleInstruction(element))
element.branches.reversed().forEach { callStack.addInstruction(CompoundInstruction(it)) }
}
private fun unfoldContinue(element: IrContinue, callStack: CallStack) {
@@ -293,7 +297,7 @@ private fun unfoldVararg(element: IrVararg, callStack: CallStack) {
}
private fun unfoldTry(element: IrTry, callStack: CallStack) {
callStack.newSubFrame(element, listOf())
callStack.newSubFrame(element)
callStack.addInstruction(SimpleInstruction(element))
callStack.addInstruction(CompoundInstruction(element.tryResult))
}
@@ -302,9 +306,9 @@ private fun unfoldCatch(element: IrCatch, callStack: CallStack) {
val exceptionState = callStack.peekState() as? ExceptionState ?: return
if (exceptionState.isSubtypeOf(element.catchParameter.type)) {
callStack.popState()
val frameOwner = callStack.getCurrentFrameOwner() as IrTry
val frameOwner = callStack.currentFrameOwner as IrTry
callStack.dropSubFrame() // drop other catch blocks
callStack.newSubFrame(element, listOf()) // new frame with IrTry instruction to interpret finally block at the end
callStack.newSubFrame(element) // new frame with IrTry instruction to interpret finally block at the end
callStack.addVariable(Variable(element.catchParameter.symbol, exceptionState))
callStack.addInstruction(SimpleInstruction(frameOwner))
callStack.addInstruction(CompoundInstruction(element.result))
@@ -318,7 +322,7 @@ private fun unfoldThrow(expression: IrThrow, callStack: CallStack) {
private fun unfoldStringConcatenation(expression: IrStringConcatenation, environment: IrInterpreterEnvironment) {
val callStack = environment.callStack
callStack.newSubFrame(expression, listOf())
callStack.newSubFrame(expression)
callStack.addInstruction(SimpleInstruction(expression))
// this callback is used to check the need for an explicit toString call
@@ -339,10 +343,11 @@ private fun unfoldStringConcatenation(expression: IrStringConcatenation, environ
}
val toStringFun = state.getToStringFunction()
val receiver = toStringFun.dispatchReceiverParameter!!
val toStringCall = IrCallImpl.fromSymbolOwner(0, 0, environment.irBuiltIns.stringType, toStringFun.symbol)
toStringCall.dispatchReceiver = IrConstImpl.constNull(0, 0, receiver.type) // just stub receiver
val toStringCall =
IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, environment.irBuiltIns.stringType, toStringFun.symbol)
callStack.newSubFrame(toStringCall, listOf(SimpleInstruction(toStringCall)))
callStack.newSubFrame(toStringCall)
callStack.addInstruction(SimpleInstruction(toStringCall))
callStack.addVariable(Variable(receiver.symbol, state))
}
}
@@ -365,7 +370,8 @@ private fun unfoldComposite(element: IrComposite, callStack: CallStack) {
private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: CallStack) {
val function = reference.symbol.owner
callStack.newSubFrame(reference, listOf(SimpleInstruction(reference)))
callStack.newSubFrame(reference)
callStack.addInstruction(SimpleInstruction(reference))
reference.dispatchReceiver?.let { callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!)) }
reference.extensionReceiver?.let { callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!)) }
@@ -376,7 +382,8 @@ private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: C
private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) {
val getter = propertyReference.getter!!.owner
callStack.newSubFrame(propertyReference, listOf(SimpleInstruction(propertyReference)))
callStack.newSubFrame(propertyReference)
callStack.addInstruction(SimpleInstruction(propertyReference))
propertyReference.dispatchReceiver?.let { callStack.addInstruction(SimpleInstruction(getter.dispatchReceiverParameter!!)) }
propertyReference.extensionReceiver?.let { callStack.addInstruction(SimpleInstruction(getter.extensionReceiverParameter!!)) }
@@ -62,18 +62,18 @@ class IrInterpreter private constructor(
private fun Instruction.handle() {
when (this) {
is CompoundInstruction -> unfoldInstruction(this.element, environment)
is SimpleInstruction -> interpret(this.element)
is SimpleInstruction -> interpret(this.element).also { incrementAndCheckCommands() }
is CustomInstruction -> this.evaluate()
}
}
fun interpret(expression: IrExpression, file: IrFile? = null): IrExpression {
commandCount = 0
callStack.newFrame(expression, listOf(CompoundInstruction(expression)), file)
callStack.newFrame(expression, file)
callStack.addInstruction(CompoundInstruction(expression))
while (!callStack.hasNoInstructions()) {
callStack.popInstruction().handle()
incrementAndCheckCommands()
}
return callStack.popState().toIrExpression(expression).apply { callStack.dropFrame() }
@@ -81,8 +81,8 @@ class IrInterpreter private constructor(
internal fun withNewCallStack(call: IrCall, init: IrInterpreter.() -> Any?): State {
return with(IrInterpreter(environment.copyWithNewCallStack(), bodyMap)) {
callStack.newFrame(call.symbol.owner, listOf())
callStack.newSubFrame(call, listOf())
callStack.newFrame(call.symbol.owner)
callStack.newSubFrame(call)
init()
while (!callStack.hasNoInstructions()) {
@@ -115,7 +115,7 @@ class IrInterpreter private constructor(
is IrEnumEntry -> interpretEnumEntry(element)
is IrConst<*> -> interpretConst(element)
is IrVariable -> callStack.addVariable(Variable(element.symbol, callStack.popState()))
is IrSetValue -> callStack.getVariable(element.symbol).state = callStack.popState()
is IrSetValue -> callStack.setState(element.symbol, callStack.popState())
is IrTypeOperatorCall -> interpretTypeOperatorCall(element)
is IrBranch -> interpretBranch(element)
is IrWhileLoop -> interpretWhile(element)
@@ -169,9 +169,9 @@ class IrInterpreter private constructor(
private fun interpretCall(call: IrCall) {
val owner = call.symbol.owner
// 1. load evaluated arguments from stack
var dispatchReceiver = owner.getDispatchReceiver()?.let { callStack.getVariable(it) }?.state
val extensionReceiver = owner.getExtensionReceiver()?.let { callStack.getVariable(it) }?.state
val valueArguments = owner.valueParameters.map { callStack.getVariable(it.symbol).state }
var dispatchReceiver = owner.getDispatchReceiver()?.let { callStack.getState(it) }
val extensionReceiver = owner.getExtensionReceiver()?.let { callStack.getState(it) }
val valueArguments = owner.valueParameters.map { callStack.getState(it.symbol) }
// 2. get correct function for interpretation
val irFunction = dispatchReceiver?.getIrFunctionByIrCall(call) ?: call.symbol.owner
@@ -181,7 +181,8 @@ class IrInterpreter private constructor(
}
callStack.dropSubFrame() // drop intermediate frame that contains variables for default arg evaluation
callStack.newFrame(irFunction, listOf(SimpleInstruction(irFunction)))
callStack.newFrame(irFunction)
callStack.addInstruction(SimpleInstruction(irFunction))
// TODO: if using KTypeState then it's class must be corresponding
// `call.type` is used in check cast and emptyArray
callStack.addVariable(Variable(irFunction.symbol, KTypeState(call.type, irBuiltIns.anyClass.owner)))
@@ -222,7 +223,7 @@ class IrInterpreter private constructor(
property.backingField?.initializer?.expression ?: return@forEach
val receiver = irClass.thisReceiver!!.symbol
if (property.backingField?.initializer != null) {
val receiverState = callStack.getVariable(receiver).state
val receiverState = callStack.getState(receiver)
val propertyVar = Variable(property.symbol, callStack.popState())
receiverState.setField(propertyVar)
}
@@ -232,7 +233,7 @@ class IrInterpreter private constructor(
private fun interpretField(field: IrField) {
val irClass = field.parentAsClass
val receiver = irClass.thisReceiver!!.symbol
val receiverState = callStack.getVariable(receiver).state
val receiverState = callStack.getState(receiver)
receiverState.setField(Variable(field.correspondingPropertySymbol!!, callStack.popState()))
}
@@ -243,21 +244,22 @@ class IrInterpreter private constructor(
private fun interpretConstructorCall(constructorCall: IrFunctionAccessExpression) {
val constructor = constructorCall.symbol.owner
val valueArguments = constructor.valueParameters.map { callStack.getVariable(it.symbol).state }
val valueArguments = constructor.valueParameters.map { callStack.getState(it.symbol) }
val irClass = constructor.parentAsClass
val objectVar = callStack.getVariable(constructorCall.getThisReceiver())
if (irClass.isLocal) callStack.storeUpValues(objectVar.state as StateWithClosure)
val outerClass = if (irClass.isInner) callStack.getVariable(constructor.dispatchReceiverParameter!!.symbol).state else null
val objectState = callStack.getState(constructorCall.getThisReceiver())
if (irClass.isLocal) callStack.storeUpValues(objectState as StateWithClosure)
val outerClass = if (irClass.isInner) callStack.getState(constructor.dispatchReceiverParameter!!.symbol) else null
callStack.dropSubFrame()
callStack.newFrame(constructor, listOf(SimpleInstruction(constructor)))
callStack.addVariable(objectVar)
callStack.newFrame(constructor)
callStack.addInstruction(SimpleInstruction(constructor))
callStack.addVariable(Variable(constructorCall.getThisReceiver(), objectState))
constructor.valueParameters.forEachIndexed { i, param -> callStack.addVariable(Variable(param.symbol, valueArguments[i])) }
if (irClass.isLocal) callStack.loadUpValues(objectVar.state as StateWithClosure)
if (irClass.isLocal) callStack.loadUpValues(objectState as StateWithClosure)
if (outerClass != null) {
val outerClassVar = Variable(irClass.parentAsClass.thisReceiver!!.symbol, outerClass)
(objectVar.state as Complex).outerClass = outerClassVar
(objectState as Complex).outerClass = outerClassVar
// used in case when inner class has inner super class
callStack.addVariable(Variable(constructor.dispatchReceiverParameter!!.symbol, outerClass))
// used to get information from outer class
@@ -271,10 +273,10 @@ class IrInterpreter private constructor(
is IrBlock -> (irStatement.statements.last() as IrFunctionAccessExpression).getThisReceiver()
else -> TODO("${irStatement::class.java} is not supported as first statement in constructor call")
}
superReceiver?.let { callStack.addVariable(Variable(it, objectVar.state)) }
superReceiver?.let { callStack.addVariable(Variable(it, objectState)) }
callInterceptor.interceptConstructor(constructorCall, objectVar.state, valueArguments) {
callStack.pushState(objectVar.state)
callInterceptor.interceptConstructor(constructorCall, objectState, valueArguments) {
callStack.pushState(objectState)
callStack.addInstruction(CompoundInstruction(constructor))
}
}
@@ -321,10 +323,10 @@ class IrInterpreter private constructor(
val result = callStack.popState().asBoolean()
callStack.dropSubFrame()
if (result) {
callStack.newSubFrame(
loop,
listOf(CompoundInstruction(loop.body), CompoundInstruction(loop.condition), SimpleInstruction(loop))
)
callStack.newSubFrame(loop)
callStack.addInstruction(SimpleInstruction(loop))
callStack.addInstruction(CompoundInstruction(loop.condition))
callStack.addInstruction(CompoundInstruction(loop.body))
}
}
@@ -332,10 +334,10 @@ class IrInterpreter private constructor(
val result = callStack.popState().asBoolean()
callStack.dropSubFrame()
if (result) {
callStack.newSubFrame(
loop,
listOf(CompoundInstruction(loop.body), CompoundInstruction(loop.condition), SimpleInstruction(loop))
)
callStack.newSubFrame(loop)
callStack.addInstruction(SimpleInstruction(loop))
callStack.addInstruction(CompoundInstruction(loop.condition))
callStack.addInstruction(CompoundInstruction(loop.body))
}
}
@@ -350,7 +352,7 @@ class IrInterpreter private constructor(
private fun interpretSetField(expression: IrSetField) {
val receiver = (expression.receiver as IrDeclarationReference).symbol
val propertySymbol = expression.symbol.owner.correspondingPropertySymbol!!
callStack.getVariable(receiver).apply { this.state.setField(Variable(propertySymbol, callStack.popState())) }
callStack.getState(receiver).apply { this.setField(Variable(propertySymbol, callStack.popState())) }
}
private fun interpretGetField(expression: IrGetField) {
@@ -375,7 +377,7 @@ class IrInterpreter private constructor(
callStack.addInstruction(CompoundInstruction(expression.symbol.owner.initializer?.expression))
}
else -> {
val result = callStack.getVariable(receiver!!).state.getState(field.correspondingPropertySymbol!!)
val result = callStack.getState(receiver!!).getField(field.correspondingPropertySymbol!!)
callStack.pushState(result!!)
}
}
@@ -390,7 +392,8 @@ class IrInterpreter private constructor(
val constructor = objectClass.constructors.first()
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
callStack.newSubFrame(constructorCall, listOf(SimpleInstruction(constructorCall)))
callStack.newSubFrame(constructorCall)
callStack.addInstruction(SimpleInstruction(constructorCall))
}
}
@@ -422,7 +425,9 @@ class IrInterpreter private constructor(
val enumClassObject = Variable(enumConstructorCall.getThisReceiver(), Common(enumEntry.correspondingClass ?: enumClass))
environment.mapOfEnums[enumEntry.symbol] = enumClassObject.state as Complex
callStack.newSubFrame(enumEntry, listOf(CompoundInstruction(enumConstructorCall), CustomInstruction(cleanEnumSuperCall)))
callStack.newSubFrame(enumEntry)
callStack.addInstruction(CustomInstruction(cleanEnumSuperCall))
callStack.addInstruction(CompoundInstruction(enumConstructorCall))
callStack.addVariable(enumClassObject)
}
}
@@ -431,7 +436,7 @@ class IrInterpreter private constructor(
val typeClassifier = expression.typeOperand.classifierOrFail
val isReified = (typeClassifier.owner as? IrTypeParameter)?.isReified == true
val isErased = typeClassifier.owner is IrTypeParameter && !isReified
val typeOperand = if (isReified) (callStack.getVariable(typeClassifier).state as KTypeState).irType else expression.typeOperand
val typeOperand = if (isReified) (callStack.getState(typeClassifier) as KTypeState).irType else expression.typeOperand
when (expression.operator) {
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
@@ -531,7 +536,7 @@ class IrInterpreter private constructor(
private fun interpretThrow(expression: IrThrow) {
val exception = callStack.popState()
callStack.newSubFrame(expression, listOf()) // temporary frame to get correct stack trace
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()))
@@ -565,12 +570,12 @@ class IrInterpreter private constructor(
val function = KFunctionState(reference)
val irFunction = function.irFunction
val dispatchReceiver = reference.dispatchReceiver?.let { callStack.getVariable(irFunction.getDispatchReceiver()!!) }?.state
val extensionReceiver = reference.extensionReceiver?.let { callStack.getVariable(irFunction.getExtensionReceiver()!!) }?.state
val dispatchReceiver = reference.dispatchReceiver?.let { callStack.getState(irFunction.getDispatchReceiver()!!) }
val extensionReceiver = reference.extensionReceiver?.let { callStack.getState(irFunction.getExtensionReceiver()!!) }
callStack.dropSubFrame()
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> function.fields.add(Variable(it, receiver)) } }
irFunction.getExtensionReceiver()?.let { extensionReceiver?.let { receiver -> function.fields.add(Variable(it, receiver)) } }
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> function.fields += Variable(it, receiver) } }
irFunction.getExtensionReceiver()?.let { extensionReceiver?.let { receiver -> function.fields += Variable(it, receiver) } }
if (irFunction.isLocal) callStack.storeUpValues(function)
callStack.pushState(function)
@@ -580,7 +585,7 @@ class IrInterpreter private constructor(
val receiverSymbol = propertyReference.getter?.owner?.let { it.getDispatchReceiver() ?: it.getExtensionReceiver() }
// it is impossible to get KProperty2 through ::, so only one receiver can be not null (or both null)
val receiver = (propertyReference.dispatchReceiver ?: propertyReference.extensionReceiver)
?.let { callStack.getVariable(receiverSymbol!!) }?.state
?.let { callStack.getState(receiverSymbol!!) }
callStack.dropSubFrame()
val propertyState = KPropertyState(propertyReference, receiver)
@@ -590,7 +595,7 @@ class IrInterpreter private constructor(
private fun interpretClassReference(classReference: IrClassReference) {
when (classReference.symbol) {
is IrTypeParameterSymbol -> { // reified
val kTypeState = callStack.getVariable(classReference.symbol).state as KTypeState
val kTypeState = callStack.getState(classReference.symbol) as KTypeState
callStack.pushState(KClassState(kTypeState.irType.classOrNull!!.owner, classReference.type.classOrNull!!.owner))
}
else -> callStack.pushState(KClassState(classReference))
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
import org.jetbrains.kotlin.ir.interpreter.proxy.wrap
@@ -260,7 +259,7 @@ internal fun IrFunction?.checkCast(environment: IrInterpreterEnvironment): Boole
if (actualType.classifierOrNull !is IrTypeParameterSymbol) return true
// TODO expectedType can be missing for functions called as proxy
val expectedType = (environment.callStack.getVariable(this.symbol).state as? KTypeState)?.irType ?: return true
val expectedType = (environment.callStack.getState(this.symbol) as? KTypeState)?.irType ?: return true
if (expectedType.classifierOrFail is IrTypeParameterSymbol) return true
val actualState = environment.callStack.peekState() ?: return true
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.interpreter.intrinsics
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -48,7 +49,7 @@ internal object EmptyArray : IntrinsicBase() {
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val returnType = environment.callStack.getVariable(irFunction.symbol).state as KTypeState
val returnType = environment.callStack.getState(irFunction.symbol) as KTypeState
environment.callStack.pushState(emptyArray<Any?>().toState(returnType.irType))
}
}
@@ -65,7 +66,7 @@ internal object ArrayOf : IntrinsicBase() {
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val elementsSymbol = irFunction.valueParameters.single().symbol
val varargVariable = environment.callStack.getVariable(elementsSymbol).state
val varargVariable = environment.callStack.getState(elementsSymbol)
environment.callStack.pushState(varargVariable)
}
}
@@ -81,9 +82,9 @@ internal object ArrayOfNulls : IntrinsicBase() {
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val size = environment.callStack.getVariable(irFunction.valueParameters.first().symbol).state.asInt()
val size = environment.callStack.getState(irFunction.valueParameters.first().symbol).asInt()
val array = arrayOfNulls<Any?>(size)
val typeArgument = irFunction.typeParameters.map { environment.callStack.getVariable(it.symbol) }.single().state as KTypeState
val typeArgument = irFunction.typeParameters.map { environment.callStack.getState(it.symbol) }.single() as KTypeState
val returnType = (irFunction.returnType as IrSimpleType).buildSimpleType {
arguments = listOf(makeTypeProjection(typeArgument.irType, Variance.INVARIANT))
}
@@ -101,7 +102,7 @@ internal object EnumValues : IntrinsicBase() {
private fun getEnumClass(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrClass {
return when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.enumValues" -> {
val kType = environment.callStack.getVariable(irFunction.typeParameters.first().symbol).state as KTypeState
val kType = environment.callStack.getState(irFunction.typeParameters.first().symbol) as KTypeState
kType.irType.classOrNull!!.owner
}
else -> irFunction.parent as IrClass
@@ -132,7 +133,7 @@ internal object EnumValueOf : IntrinsicBase() {
private fun getEnumClass(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrClass {
return when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.enumValueOf" -> {
val kType = environment.callStack.getVariable(irFunction.typeParameters.first().symbol).state as KTypeState
val kType = environment.callStack.getState(irFunction.typeParameters.first().symbol) as KTypeState
kType.irType.classOrNull!!.owner
}
else -> irFunction.parent as IrClass
@@ -141,7 +142,7 @@ internal object EnumValueOf : IntrinsicBase() {
private fun getEnumEntryByName(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrEnumEntry? {
val enumClass = getEnumClass(irFunction, environment)
val enumEntryName = environment.callStack.getVariable(irFunction.valueParameters.first().symbol).state.asString()
val enumEntryName = environment.callStack.getState(irFunction.valueParameters.first().symbol).asString()
val enumEntry = enumClass.declarations.filterIsInstance<IrEnumEntry>().singleOrNull { it.name.asString() == enumEntryName }
if (enumEntry == null) {
IllegalArgumentException("No enum constant ${enumClass.fqNameWhenAvailable}.$enumEntryName").handleUserException(environment)
@@ -171,7 +172,7 @@ internal object EnumHashCode : IntrinsicBase() {
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val hashCode = environment.callStack.getVariable(irFunction.dispatchReceiverParameter!!.symbol).state.hashCode()
val hashCode = environment.callStack.getState(irFunction.dispatchReceiverParameter!!.symbol).hashCode()
environment.callStack.pushState(hashCode.toState(irFunction.returnType))
}
}
@@ -189,12 +190,12 @@ internal object JsPrimitives : IntrinsicBase() {
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
when (irFunction.fqNameWhenAvailable.toString()) {
"kotlin.Long.<init>" -> {
val low = environment.callStack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
val high = environment.callStack.getVariable(irFunction.valueParameters[1].symbol).state.asInt()
val low = environment.callStack.getState(irFunction.valueParameters[0].symbol).asInt()
val high = environment.callStack.getState(irFunction.valueParameters[1].symbol).asInt()
environment.callStack.pushState((high.toLong().shl(32) + low).toState(irFunction.returnType))
}
"kotlin.Char.<init>" -> {
val value = environment.callStack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
val value = environment.callStack.getState(irFunction.valueParameters[0].symbol).asInt()
environment.callStack.pushState(value.toChar().toState(irFunction.returnType))
}
}
@@ -212,16 +213,16 @@ internal object ArrayConstructor : IntrinsicBase() {
val instructions = mutableListOf<Instruction>(customEvaluateInstruction(irFunction, environment))
val sizeDescriptor = irFunction.valueParameters[0].symbol
val size = environment.callStack.getVariable(sizeDescriptor).state.asInt()
val size = environment.callStack.getState(sizeDescriptor).asInt()
val initDescriptor = irFunction.valueParameters[1].symbol
val initLambda = environment.callStack.getVariable(initDescriptor).state as KFunctionState
val initLambda = environment.callStack.getState(initDescriptor) as KFunctionState
environment.callStack.loadUpValues(initLambda)
val function = initLambda.irFunction as IrSimpleFunction
val index = initLambda.irFunction.valueParameters.single()
for (i in 0 until size) {
val call = IrCallImpl.fromSymbolOwner(0, 0, function.returnType, function.symbol)
call.putValueArgument(0, IrConstImpl.int(0, 0, index.type, i))
val call = IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.returnType, function.symbol)
call.putValueArgument(0, IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, index.type, i))
instructions += CompoundInstruction(call)
}
@@ -230,7 +231,7 @@ internal object ArrayConstructor : IntrinsicBase() {
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val sizeDescriptor = irFunction.valueParameters[0].symbol
val size = environment.callStack.getVariable(sizeDescriptor).state.asInt()
val size = environment.callStack.getState(sizeDescriptor).asInt()
val arrayValue = MutableList<Any?>(size) { if (irFunction.returnType.isCharArray()) 0.toChar() else 0 }
if (irFunction.valueParameters.size == 2) {
@@ -246,7 +247,7 @@ internal object ArrayConstructor : IntrinsicBase() {
}
}
val type = environment.callStack.getVariable(irFunction.symbol).state as KTypeState
val type = environment.callStack.getState(irFunction.symbol) as KTypeState
environment.callStack.pushState(arrayValue.toPrimitiveStateArray(type.irType))
}
}
@@ -275,15 +276,15 @@ internal object AssertIntrinsic : IntrinsicBase() {
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
if (irFunction.valueParameters.size == 1) return listOf(customEvaluateInstruction(irFunction, environment))
val messageLambda = environment.callStack.getVariable(irFunction.valueParameters.last().symbol).state as KFunctionState
val messageLambda = environment.callStack.getState(irFunction.valueParameters.last().symbol) as KFunctionState
val function = messageLambda.irFunction as IrSimpleFunction
val call = IrCallImpl.fromSymbolOwner(0, 0, function.returnType, function.symbol)
val call = IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.returnType, function.symbol)
return listOf(customEvaluateInstruction(irFunction, environment), CompoundInstruction(call))
}
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
val value = environment.callStack.getVariable(irFunction.valueParameters.first().symbol).state.asBoolean()
val value = environment.callStack.getState(irFunction.valueParameters.first().symbol).asBoolean()
if (value) return
when (irFunction.valueParameters.size) {
1 -> AssertionError("Assertion failed").handleUserException(environment)
@@ -26,7 +26,7 @@ internal class CommonProxy private constructor(override val state: Common, overr
private fun IrFunction.wasAlreadyCalled(): Boolean {
val anyParameter = this.getLastOverridden().dispatchReceiverParameter!!.symbol
if (callInterceptor.environment.callStack.containsVariable(anyParameter)) return true
return this == callInterceptor.environment.callStack.getCurrentFrameOwner()
return this == callInterceptor.environment.callStack.currentFrameOwner
}
override fun equals(other: Any?): Boolean {
@@ -28,7 +28,7 @@ internal open class KProperty0Proxy(
val receiver = state.receiver // null receiver <=> property is on top level
?: return callInterceptor.interceptProxy(getter, emptyList())
val value = receiver.getState(actualPropertySymbol)
val value = receiver.getField(actualPropertySymbol)
return when {
// null value <=> property is extension or Primitive; receiver.isNull() <=> nullable extension
value == null || receiver.isNull() -> {
@@ -19,22 +19,22 @@ import org.jetbrains.kotlin.ir.util.fileOrNull
internal class CallStack {
private val frames = mutableListOf<Frame>()
private fun getCurrentFrame() = frames.last()
internal fun getCurrentFrameOwner() = frames.last().currentSubFrameOwner
private val currentFrame get() = frames.last()
internal val currentFrameOwner get() = currentFrame.currentSubFrameOwner
fun newFrame(frameOwner: IrElement, instructions: List<Instruction>, irFile: IrFile? = null) {
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
fun newFrame(frameOwner: IrElement, irFile: IrFile? = null) {
val newFrame = SubFrame(frameOwner)
frames.add(Frame(newFrame, irFile))
}
fun newFrame(frameOwner: IrFunction, instructions: List<Instruction>) {
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
fun newFrame(frameOwner: IrFunction) {
val newFrame = SubFrame(frameOwner)
frames.add(Frame(newFrame, frameOwner.fileOrNull))
}
fun newSubFrame(frameOwner: IrElement, instructions: List<Instruction>) {
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
getCurrentFrame().addSubFrame(newFrame)
fun newSubFrame(frameOwner: IrElement) {
val newFrame = SubFrame(frameOwner)
currentFrame.addSubFrame(newFrame)
}
fun dropFrame() {
@@ -49,12 +49,12 @@ internal class CallStack {
}
fun dropSubFrame() {
getCurrentFrame().removeSubFrame()
currentFrame.removeSubFrame()
}
fun returnFromFrameWithResult(irReturn: IrReturn) {
val result = popState()
var frameOwner = getCurrentFrameOwner()
var frameOwner = currentFrameOwner
while (frameOwner != irReturn.returnTargetSymbol.owner) {
when (frameOwner) {
is IrTry -> {
@@ -65,7 +65,7 @@ internal class CallStack {
return
}
is IrCatch -> {
val tryBlock = getCurrentFrame().dropInstructions()!!.element as IrTry// last instruction in `catch` block is `try`
val tryBlock = currentFrame.dropInstructions()!!.element as IrTry// last instruction in `catch` block is `try`
dropSubFrame()
pushState(result)
addInstruction(SimpleInstruction(irReturn))
@@ -74,44 +74,46 @@ internal class CallStack {
}
else -> {
dropSubFrame()
if (getCurrentFrame().hasNoSubFrames() && frameOwner != irReturn.returnTargetSymbol.owner) dropFrame()
frameOwner = getCurrentFrameOwner()
if (currentFrame.hasNoSubFrames() && frameOwner != irReturn.returnTargetSymbol.owner) dropFrame()
frameOwner = currentFrameOwner
}
}
}
dropFrame()
// check that last frame is not a function itself; use case for proxyInterpret
if (frames.size == 0) newFrame(irReturn, emptyList()) // just stub frame
if (frames.size == 0) newFrame(irReturn) // just stub frame
pushState(result)
}
fun unrollInstructionsForBreakContinue(breakOrContinue: IrBreakContinue) {
var frameOwner = getCurrentFrameOwner()
var frameOwner = currentFrameOwner
while (frameOwner != breakOrContinue.loop) {
when (frameOwner) {
is IrTry -> {
getCurrentFrame().removeSubFrameWithoutDataPropagation()
currentFrame.removeSubFrameWithoutDataPropagation()
addInstruction(CompoundInstruction(breakOrContinue))
newSubFrame(frameOwner, listOf(SimpleInstruction(frameOwner))) // will be deleted when interpret 'try'
newSubFrame(frameOwner) // will be deleted when interpret 'try'
addInstruction(SimpleInstruction(frameOwner))
return
}
is IrCatch -> {
val tryInstruction = getCurrentFrame().dropInstructions()!! // last instruction in `catch` block is `try`
getCurrentFrame().removeSubFrameWithoutDataPropagation()
val tryInstruction = currentFrame.dropInstructions()!! // last instruction in `catch` block is `try`
currentFrame.removeSubFrameWithoutDataPropagation()
addInstruction(CompoundInstruction(breakOrContinue))
newSubFrame(tryInstruction.element!!, listOf(tryInstruction)) // will be deleted when interpret 'try'
newSubFrame(tryInstruction.element!!) // will be deleted when interpret 'try'
addInstruction(tryInstruction)
return
}
else -> {
getCurrentFrame().removeSubFrameWithoutDataPropagation()
frameOwner = getCurrentFrameOwner()
currentFrame.removeSubFrameWithoutDataPropagation()
frameOwner = currentFrameOwner
}
}
}
when (breakOrContinue) {
is IrBreak -> getCurrentFrame().removeSubFrameWithoutDataPropagation() // drop loop
is IrBreak -> currentFrame.removeSubFrameWithoutDataPropagation() // drop loop
else -> if (breakOrContinue.loop is IrDoWhileLoop) {
addInstruction(SimpleInstruction(breakOrContinue.loop))
addInstruction(CompoundInstruction(breakOrContinue.loop.condition))
@@ -123,15 +125,15 @@ internal class CallStack {
fun dropFramesUntilTryCatch() {
val exception = popState()
var frameOwner = getCurrentFrameOwner()
var frameOwner = currentFrameOwner
while (frames.isNotEmpty()) {
val frame = getCurrentFrame()
val frame = currentFrame
while (!frame.hasNoSubFrames()) {
frameOwner = frame.currentSubFrameOwner
when (frameOwner) {
is IrTry -> {
dropSubFrame() // drop all instructions that left
newSubFrame(frameOwner, listOf())
newSubFrame(frameOwner)
addInstruction(SimpleInstruction(frameOwner)) // to evaluate finally at the end
frameOwner.catches.reversed().forEach { addInstruction(CompoundInstruction(it)) }
pushState(exception)
@@ -149,37 +151,38 @@ internal class CallStack {
dropFrame()
}
if (frames.size == 0) newFrame(frameOwner, emptyList()) // just stub frame
if (frames.size == 0) newFrame(frameOwner) // just stub frame
pushState(exception)
}
fun hasNoInstructions() = frames.isEmpty() || (frames.size == 1 && frames.first().hasNoInstructions())
fun addInstruction(instruction: Instruction) {
getCurrentFrame().addInstruction(instruction)
currentFrame.addInstruction(instruction)
}
fun popInstruction(): Instruction {
return getCurrentFrame().popInstruction()
return currentFrame.popInstruction()
}
fun pushState(state: State) {
getCurrentFrame().pushState(state)
currentFrame.pushState(state)
}
fun popState(): State = getCurrentFrame().popState()
fun peekState(): State? = getCurrentFrame().peekState()
fun popState(): State = currentFrame.popState()
fun peekState(): State? = currentFrame.peekState()
fun addVariable(variable: Variable) {
getCurrentFrame().addVariable(variable)
currentFrame.addVariable(variable)
}
fun getVariable(symbol: IrSymbol): Variable = getCurrentFrame().getVariable(symbol)
fun containsVariable(symbol: IrSymbol): Boolean = getCurrentFrame().containsVariable(symbol)
fun getState(symbol: IrSymbol): State = currentFrame.getState(symbol)
fun setState(symbol: IrSymbol, newState: State) = currentFrame.setState(symbol, newState)
fun containsVariable(symbol: IrSymbol): Boolean = currentFrame.containsVariable(symbol)
fun storeUpValues(state: StateWithClosure) {
// TODO save only necessary declarations
state.upValues.addAll(getCurrentFrame().getAll().toMutableList())
state.upValues.addAll(currentFrame.getAll().toMutableList())
}
fun loadUpValues(state: StateWithClosure) {
@@ -14,26 +14,24 @@ import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal class Frame(subFrame: SubFrame, val irFile: IrFile? = null) {
private val innerStack = mutableListOf(subFrame)
private var currentInstruction: Instruction? = null
val currentSubFrameOwner: IrElement
get() = getCurrentFrame().owner
private val currentFrame get() = innerStack.last()
val currentSubFrameOwner: IrElement get() = currentFrame.owner
companion object {
const val NOT_DEFINED = "Not defined"
}
private fun getCurrentFrame() = innerStack.last()
fun addSubFrame(frame: SubFrame) {
innerStack.add(frame)
}
fun removeSubFrame() {
getCurrentFrame().peekState()?.let { if (innerStack.size > 1) innerStack[innerStack.size - 2].pushState(it) }
currentFrame.peekState()?.let { if (innerStack.size > 1) innerStack[innerStack.size - 2].pushState(it) }
removeSubFrameWithoutDataPropagation()
}
@@ -45,34 +43,39 @@ internal class Frame(subFrame: SubFrame, val irFile: IrFile? = null) {
fun hasNoInstructions() = hasNoSubFrames() || (innerStack.size == 1 && innerStack.first().isEmpty())
fun addInstruction(instruction: Instruction) {
getCurrentFrame().pushInstruction(instruction)
currentFrame.pushInstruction(instruction)
}
fun popInstruction(): Instruction {
return getCurrentFrame().popInstruction().apply { currentInstruction = this }
return currentFrame.popInstruction().apply { currentInstruction = this }
}
fun dropInstructions() = getCurrentFrame().dropInstructions()
fun dropInstructions() = currentFrame.dropInstructions()
fun pushState(state: State) {
getCurrentFrame().pushState(state)
currentFrame.pushState(state)
}
fun popState(): State = getCurrentFrame().popState()
fun peekState(): State? = getCurrentFrame().peekState()
fun popState(): State = currentFrame.popState()
fun peekState(): State? = currentFrame.peekState()
fun addVariable(variable: Variable) {
getCurrentFrame().addVariable(variable)
currentFrame.addVariable(variable)
}
fun getVariable(symbol: IrSymbol): Variable {
return tryToGetVariable(symbol)
fun getState(symbol: IrSymbol): State {
return (innerStack.lastIndex downTo 0).firstNotNullOfOrNull { innerStack[it].getState(symbol) }
?: throw InterpreterError("$symbol not found") // TODO better message
}
fun containsVariable(symbol: IrSymbol): Boolean = tryToGetVariable(symbol) != null
fun setState(symbol: IrSymbol, newState: State) {
(innerStack.lastIndex downTo 0).forEach {
if (innerStack[it].containsVariable(symbol))
return innerStack[it].setState(symbol, newState)
}
}
private fun tryToGetVariable(symbol: IrSymbol): Variable? = innerStack.reversed().firstNotNullResult { it.getVariable(symbol) }
fun containsVariable(symbol: IrSymbol): Boolean = (innerStack.lastIndex downTo 0).any { innerStack[it].containsVariable(symbol) }
fun getAll(): List<Variable> = innerStack.flatMap { it.getAll() }
@@ -102,46 +105,44 @@ internal class Frame(subFrame: SubFrame, val irFile: IrFile? = null) {
}
}
internal class SubFrame(private val instructions: MutableList<Instruction>, val owner: IrElement) {
private val memory = mutableListOf<Variable>()
internal class SubFrame(val owner: IrElement) {
private val instructions = mutableListOf<Instruction>()
private val dataStack = DataStack()
private val memory = mutableListOf<Variable>()
// Methods to work with instruction
fun isEmpty() = instructions.isEmpty()
fun pushInstruction(instruction: Instruction) {
instructions.add(0, instruction)
}
fun popInstruction(): Instruction {
return instructions.removeFirst()
}
fun pushInstruction(instruction: Instruction) = instructions.add(0, instruction)
fun popInstruction(): Instruction = instructions.removeFirst()
fun dropInstructions() = instructions.lastOrNull()?.apply { instructions.clear() }
fun pushState(state: State) {
dataStack.push(state)
}
// Methods to work with data
fun pushState(state: State) = dataStack.push(state)
fun popState(): State = dataStack.pop()
fun peekState(): State? = if (!dataStack.isEmpty()) dataStack.peek() else null
fun peekState(): State? = dataStack.peek()
// Methods to work with memory
fun addVariable(variable: Variable) {
memory += variable
}
fun getVariable(symbol: IrSymbol): Variable? = memory.firstOrNull { it.symbol == symbol }
private fun getVariable(symbol: IrSymbol): Variable? = memory.firstOrNull { it.symbol == symbol }
fun containsVariable(symbol: IrSymbol): Boolean = getVariable(symbol) != null
fun getState(symbol: IrSymbol): State? = getVariable(symbol)?.state
fun setState(symbol: IrSymbol, newState: State) {
getVariable(symbol)?.state = newState
}
fun getAll(): List<Variable> = memory
}
private class DataStack {
private val stack = mutableListOf<State>()
fun isEmpty() = stack.isEmpty()
fun push(state: State) {
stack.add(state)
}
fun pop(): State = stack.removeLast()
fun peek(): State = stack.last()
fun peek(): State? = stack.lastOrNull()
}
@@ -23,9 +23,9 @@ internal class ExceptionState private constructor(
override var outerClass: Variable? = null
override val message: String?
get() = getState(messageProperty.symbol)?.asStringOrNull()
get() = getField(messageProperty.symbol)?.asStringOrNull()
override val cause: Throwable?
get() = getState(causeProperty.symbol)?.let { if (it is ExceptionState) it else null }
get() = getField(causeProperty.symbol)?.let { if (it is ExceptionState) it else null }
private lateinit var exceptionFqName: String
private val exceptionHierarchy = mutableListOf<String>()
@@ -115,7 +115,7 @@ internal class ExceptionState private constructor(
val causeVar = exception.cause?.let {
Variable(causeProperty.symbol, ExceptionState(it, irClass, stackTrace + it.stackTrace.reversed().map { "at $it" }))
}
return listOfNotNull(messageVar, causeVar).toMutableList()
return causeVar?.let { mutableListOf(messageVar, it) } ?: mutableListOf(messageVar)
}
private fun evaluateAdditionalStackTrace(e: Throwable): List<String> {
@@ -19,7 +19,7 @@ internal class Primitive<T>(val value: T, val type: IrType) : State {
override val fields: MutableList<Variable> = mutableListOf()
override val irClass: IrClass = type.classOrNull!!.owner
override fun getState(symbol: IrSymbol): State? = null
override fun getField(symbol: IrSymbol): State? = null
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction {
val owner = expression.symbol.owner
@@ -19,7 +19,7 @@ internal interface State {
val fields: MutableList<Variable>
val irClass: IrClass
fun getState(symbol: IrSymbol): State? {
fun getField(symbol: IrSymbol): State? {
return fields.firstOrNull { it.symbol == symbol }?.state
}