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