Refactor entire IrInterpreter to achieve better code quality
This commit is contained in:
committed by
TeamCityServer
parent
3294af5e49
commit
c8b268a789
+436
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
|
||||
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.*
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
|
||||
internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEnvironment) {
|
||||
val callStack = environment.callStack
|
||||
when (element) {
|
||||
null -> return
|
||||
is IrSimpleFunction -> unfoldFunction(element, environment)
|
||||
is IrConstructor -> unfoldConstructor(element, callStack)
|
||||
is IrCall -> unfoldCall(element, callStack)
|
||||
is IrConstructorCall -> unfoldConstructorCall(element, callStack)
|
||||
is IrEnumConstructorCall -> unfoldEnumConstructorCall(element, callStack)
|
||||
is IrDelegatingConstructorCall -> unfoldDelegatingConstructorCall(element, callStack)
|
||||
is IrInstanceInitializerCall -> unfoldInstanceInitializerCall(element, callStack)
|
||||
is IrField -> unfoldField(element, callStack)
|
||||
is IrBody -> unfoldBody(element, callStack)
|
||||
is IrBlock -> unfoldBlock(element, callStack)
|
||||
is IrReturn -> unfoldReturn(element, callStack)
|
||||
is IrSetField -> unfoldSetField(element, callStack)
|
||||
is IrGetField -> callStack.addInstruction(SimpleInstruction(element))
|
||||
is IrGetValue -> unfoldGetValue(element, environment)
|
||||
is IrGetObjectValue -> unfoldGetObjectValue(element, environment)
|
||||
is IrGetEnumValue -> unfoldGetEnumValue(element, environment)
|
||||
is IrEnumEntry -> unfoldEnumEntry(element, environment)
|
||||
is IrConst<*> -> callStack.addInstruction(SimpleInstruction(element))
|
||||
is IrVariable -> unfoldVariable(element, callStack)
|
||||
is IrSetValue -> unfoldSetValue(element, callStack)
|
||||
is IrTypeOperatorCall -> unfoldTypeOperatorCall(element, callStack)
|
||||
is IrBranch -> unfoldBranch(element, callStack)
|
||||
is IrWhileLoop -> unfoldWhileLoop(element, callStack)
|
||||
is IrDoWhileLoop -> unfoldDoWhileLoop(element, callStack)
|
||||
is IrWhen -> unfoldWhen(element, callStack)
|
||||
is IrBreak -> unfoldBreak(element, callStack)
|
||||
is IrContinue -> unfoldContinue(element, callStack)
|
||||
is IrVararg -> unfoldVararg(element, callStack)
|
||||
is IrSpreadElement -> callStack.addInstruction(CompoundInstruction(element.expression))
|
||||
is IrTry -> unfoldTry(element, callStack)
|
||||
is IrCatch -> unfoldCatch(element, callStack)
|
||||
is IrThrow -> unfoldThrow(element, callStack)
|
||||
is IrStringConcatenation -> unfoldStringConcatenation(element, environment)
|
||||
is IrFunctionExpression -> callStack.addInstruction(SimpleInstruction(element))
|
||||
is IrFunctionReference -> unfoldFunctionReference(element, callStack)
|
||||
is IrPropertyReference -> unfoldPropertyReference(element, callStack)
|
||||
is IrClassReference -> unfoldClassReference(element, callStack)
|
||||
is IrComposite -> unfoldComposite(element, callStack)
|
||||
|
||||
else -> TODO("${element.javaClass} not supported")
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldFunction(function: IrSimpleFunction, environment: IrInterpreterEnvironment) {
|
||||
if (environment.callStack.getStackCount() >= IrInterpreterEnvironment.MAX_STACK)
|
||||
return StackOverflowError().handleUserException(environment)
|
||||
// SimpleInstruction with function is added in IrCall
|
||||
// It will serve as endpoint for all possible calls, there we drop frame and copy result to new one
|
||||
function.body?.let { environment.callStack.addInstruction(CompoundInstruction(it)) }
|
||||
?: throw InterpreterError("Ir function must be with body")
|
||||
}
|
||||
|
||||
private fun unfoldConstructor(constructor: IrConstructor, callStack: CallStack) {
|
||||
// SimpleInstruction with function is added in constructor call
|
||||
// It will serve as endpoint for all possible constructor calls, there we drop frame and return object
|
||||
callStack.addInstruction(CompoundInstruction(constructor.body!!))
|
||||
}
|
||||
|
||||
private fun unfoldCall(call: IrCall, callStack: CallStack) {
|
||||
val function = call.symbol.owner
|
||||
// new sub frame is used to store value arguments, in case then they are used in default args evaluation
|
||||
callStack.newSubFrame(call, listOf())
|
||||
callStack.addInstruction(SimpleInstruction(call))
|
||||
unfoldValueParameters(call, callStack)
|
||||
|
||||
// must save receivers in memory in case then they are used in default args evaluation
|
||||
call.extensionReceiver?.let {
|
||||
callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!))
|
||||
callStack.addInstruction(CompoundInstruction(it))
|
||||
}
|
||||
call.dispatchReceiver?.let {
|
||||
callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!))
|
||||
callStack.addInstruction(CompoundInstruction(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldConstructorCall(constructorCall: IrFunctionAccessExpression, callStack: CallStack) {
|
||||
val constructor = constructorCall.symbol.owner
|
||||
callStack.newSubFrame(constructorCall, listOf()) // used to store value arguments, in case then they are use as default args
|
||||
// this variable is used to create object once
|
||||
callStack.addVariable(Variable(constructorCall.getThisReceiver(), Common(constructor.parentAsClass)))
|
||||
callStack.addInstruction(SimpleInstruction(constructorCall))
|
||||
unfoldValueParameters(constructorCall, callStack)
|
||||
|
||||
constructorCall.dispatchReceiver?.let {
|
||||
callStack.addInstruction(SimpleInstruction(constructor.dispatchReceiverParameter!!))
|
||||
callStack.addInstruction(CompoundInstruction(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldEnumConstructorCall(enumConstructorCall: IrEnumConstructorCall, callStack: CallStack) {
|
||||
callStack.newSubFrame(enumConstructorCall, listOf()) // used to store value arguments, in case then they are use as default args
|
||||
callStack.addInstruction(SimpleInstruction(enumConstructorCall))
|
||||
unfoldValueParameters(enumConstructorCall, callStack)
|
||||
}
|
||||
|
||||
private fun unfoldDelegatingConstructorCall(delegatingConstructorCall: IrFunctionAccessExpression, callStack: CallStack) {
|
||||
callStack.newSubFrame(delegatingConstructorCall, listOf()) // used to store value arguments, in case then they are use as default args
|
||||
callStack.addInstruction(SimpleInstruction(delegatingConstructorCall))
|
||||
unfoldValueParameters(delegatingConstructorCall, callStack)
|
||||
}
|
||||
|
||||
private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callStack: CallStack) {
|
||||
fun IrValueParameter.getDefault(): IrExpressionBody? {
|
||||
return defaultValue
|
||||
?: (this.parent as? IrSimpleFunction)?.overriddenSymbols
|
||||
?.map { it.owner.valueParameters[this.index].getDefault() }
|
||||
?.firstNotNullOfOrNull { it }
|
||||
}
|
||||
|
||||
fun getDefaultForParameterAt(index: Int): IrExpression? {
|
||||
return expression.symbol.owner.valueParameters[index].getDefault()?.expression
|
||||
}
|
||||
|
||||
val irFunction = expression.symbol.owner
|
||||
val valueParametersSymbols = irFunction.valueParameters.map { it.symbol }
|
||||
val valueArguments = (0 until expression.valueArgumentsCount).map { expression.getValueArgument(it) }
|
||||
|
||||
for (i in valueParametersSymbols.indices.reversed()) {
|
||||
callStack.addInstruction(SimpleInstruction(valueParametersSymbols[i].owner))
|
||||
val arg = valueArguments[i] ?: getDefaultForParameterAt(i)
|
||||
when {
|
||||
arg != null -> callStack.addInstruction(CompoundInstruction(arg))
|
||||
else ->
|
||||
// case when value parameter is vararg and it is missing
|
||||
expression.getVarargType(i)?.let {
|
||||
callStack.addInstruction(SimpleInstruction(IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hack for extension receiver in lambda
|
||||
if (expression.valueArgumentsCount != irFunction.valueParameters.size) {
|
||||
val extensionReceiver = irFunction.getExtensionReceiver() ?: return
|
||||
callStack.addInstruction(SimpleInstruction(extensionReceiver.owner))
|
||||
valueArguments[0]?.let { callStack.addInstruction(CompoundInstruction(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall, callStack: CallStack) {
|
||||
val irClass = instanceInitializerCall.classSymbol.owner
|
||||
|
||||
// init blocks processing
|
||||
val anonymousInitializer = irClass.declarations.filterIsInstance<IrAnonymousInitializer>().filter { !it.isStatic }
|
||||
anonymousInitializer.reversed().forEach { callStack.addInstruction(CompoundInstruction(it.body)) }
|
||||
|
||||
// properties processing
|
||||
val classProperties = irClass.declarations.filterIsInstance<IrProperty>()
|
||||
classProperties.filter { it.backingField?.initializer?.expression != null }.reversed()
|
||||
.forEach { callStack.addInstruction(CompoundInstruction(it.backingField)) }
|
||||
}
|
||||
|
||||
private fun unfoldField(field: IrField, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(field))
|
||||
callStack.addInstruction(CompoundInstruction(field.initializer?.expression))
|
||||
}
|
||||
|
||||
private fun unfoldBody(body: IrBody, callStack: CallStack) {
|
||||
unfoldStatements(body.statements, callStack)
|
||||
}
|
||||
|
||||
private fun unfoldBlock(block: IrBlock, callStack: CallStack) {
|
||||
callStack.newSubFrame(block, listOf())
|
||||
callStack.addInstruction(SimpleInstruction(block))
|
||||
unfoldStatements(block.statements, callStack)
|
||||
}
|
||||
|
||||
private fun unfoldStatements(statements: List<IrStatement>, callStack: CallStack) {
|
||||
for (i in statements.indices.reversed()) {
|
||||
when (val statement = statements[i]) {
|
||||
is IrClass -> if (!statement.isLocal) TODO("Only local classes are supported")
|
||||
is IrFunction -> if (!statement.isLocal) TODO("Only local functions are supported")
|
||||
else -> callStack.addInstruction(CompoundInstruction(statement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldReturn(expression: IrReturn, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(expression)) //2
|
||||
callStack.addInstruction(CompoundInstruction(expression.value)) //1
|
||||
}
|
||||
|
||||
private fun unfoldSetField(expression: IrSetField, callStack: CallStack) {
|
||||
// receiver is null, for example, for top level fields; cannot interpret set on top level var
|
||||
if (expression.receiver.let { it == null || (it.type.classifierOrNull?.owner as? IrClass)?.isObject == true }) {
|
||||
error("Cannot interpret set method on top level properties")
|
||||
}
|
||||
|
||||
callStack.addInstruction(SimpleInstruction(expression))
|
||||
callStack.addInstruction(CompoundInstruction(expression.value))
|
||||
}
|
||||
|
||||
private fun unfoldGetValue(expression: IrGetValue, environment: IrInterpreterEnvironment) {
|
||||
val expectedClass = expression.type.classOrNull?.owner
|
||||
// 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)
|
||||
}
|
||||
environment.callStack.pushState(environment.callStack.getVariable(expression.symbol).state)
|
||||
}
|
||||
|
||||
private fun unfoldGetObjectValue(expression: IrGetObjectValue, environment: IrInterpreterEnvironment) {
|
||||
val callStack = environment.callStack
|
||||
val objectClass = expression.symbol.owner
|
||||
environment.mapOfObjects[objectClass.symbol]?.let { return callStack.pushState(it) }
|
||||
|
||||
callStack.newSubFrame(expression, listOf(SimpleInstruction(expression)))
|
||||
when {
|
||||
!objectClass.hasAnnotation(evaluateIntrinsicAnnotation) -> {
|
||||
val state = Common(objectClass)
|
||||
environment.mapOfObjects[objectClass.symbol] = state // must set object's state here to avoid cyclic evaluation
|
||||
callStack.addVariable(Variable(objectClass.thisReceiver!!.symbol, state))
|
||||
|
||||
val constructor = objectClass.constructors.first()
|
||||
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
|
||||
callStack.addInstruction(CompoundInstruction(constructorCall))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldGetEnumValue(expression: IrGetEnumValue, environment: IrInterpreterEnvironment) {
|
||||
val callStack = environment.callStack
|
||||
environment.mapOfEnums[expression.symbol]?.let { return callStack.pushState(it) }
|
||||
|
||||
callStack.addInstruction(SimpleInstruction(expression))
|
||||
val enumEntry = expression.symbol.owner
|
||||
val enumClass = enumEntry.symbol.owner.parentAsClass
|
||||
enumClass.declarations.filterIsInstance<IrEnumEntry>().forEach {
|
||||
if (enumClass.hasAnnotation(evaluateIntrinsicAnnotation)) return@forEach callStack.addInstruction(SimpleInstruction(it))
|
||||
callStack.addInstruction(CompoundInstruction(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldEnumEntry(enumEntry: IrEnumEntry, environment: IrInterpreterEnvironment) {
|
||||
val enumClass = enumEntry.symbol.owner.parentAsClass
|
||||
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
|
||||
|
||||
val enumSuperCall = (enumClass.primaryConstructor?.body?.statements?.firstOrNull() as? IrEnumConstructorCall)
|
||||
if (enumEntries.isNotEmpty() && enumSuperCall != null) {
|
||||
val valueArguments = listOf(
|
||||
enumEntry.name.asString().toIrConst(environment.irBuiltIns.stringType),
|
||||
enumEntries.indexOf(enumEntry).toIrConst(environment.irBuiltIns.intType)
|
||||
)
|
||||
valueArguments.forEachIndexed { index, irConst -> enumSuperCall.putValueArgument(index, irConst) }
|
||||
}
|
||||
|
||||
val enumConstructorCall = enumEntry.initializerExpression?.expression as? IrEnumConstructorCall
|
||||
?: throw InterpreterError("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null")
|
||||
val enumClassObject = Variable(enumConstructorCall.getThisReceiver(), Common(enumEntry.correspondingClass ?: enumClass))
|
||||
environment.mapOfEnums[enumEntry.symbol] = enumClassObject.state as Complex
|
||||
|
||||
environment.callStack.newSubFrame(enumEntry, listOf(CompoundInstruction(enumConstructorCall), SimpleInstruction(enumEntry)))
|
||||
environment.callStack.addVariable(enumClassObject)
|
||||
}
|
||||
|
||||
private fun unfoldVariable(variable: IrVariable, callStack: CallStack) {
|
||||
when (variable.initializer) {
|
||||
null -> callStack.addVariable(Variable(variable.symbol))
|
||||
else -> {
|
||||
callStack.addInstruction(SimpleInstruction(variable))
|
||||
callStack.addInstruction(CompoundInstruction(variable.initializer!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldSetValue(expression: IrSetValue, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(expression))
|
||||
callStack.addInstruction(CompoundInstruction(expression.value))
|
||||
}
|
||||
|
||||
private fun unfoldTypeOperatorCall(element: IrTypeOperatorCall, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(element))
|
||||
callStack.addInstruction(CompoundInstruction(element.argument))
|
||||
}
|
||||
|
||||
private fun unfoldBranch(branch: IrBranch, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(branch)) //2
|
||||
callStack.addInstruction(CompoundInstruction(branch.condition)) //1
|
||||
}
|
||||
|
||||
private fun unfoldWhileLoop(loop: IrWhileLoop, callStack: CallStack) {
|
||||
callStack.newSubFrame(loop, listOf())
|
||||
callStack.addInstruction(SimpleInstruction(loop))
|
||||
callStack.addInstruction(CompoundInstruction(loop.condition))
|
||||
}
|
||||
|
||||
private fun unfoldDoWhileLoop(loop: IrDoWhileLoop, callStack: CallStack) {
|
||||
callStack.newSubFrame(loop, listOf())
|
||||
callStack.addInstruction(SimpleInstruction(loop))
|
||||
callStack.addInstruction(CompoundInstruction(loop.condition))
|
||||
callStack.addInstruction(CompoundInstruction(loop.body))
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
private fun unfoldContinue(element: IrContinue, callStack: CallStack) {
|
||||
callStack.unrollInstructionsForBreakContinue(element)
|
||||
}
|
||||
|
||||
private fun unfoldBreak(element: IrBreak, callStack: CallStack) {
|
||||
callStack.unrollInstructionsForBreakContinue(element)
|
||||
}
|
||||
|
||||
private fun unfoldVararg(element: IrVararg, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(element))
|
||||
element.elements.reversed().forEach { callStack.addInstruction(CompoundInstruction(it)) }
|
||||
}
|
||||
|
||||
private fun unfoldTry(element: IrTry, callStack: CallStack) {
|
||||
callStack.newSubFrame(element, listOf())
|
||||
callStack.addInstruction(SimpleInstruction(element))
|
||||
callStack.addInstruction(CompoundInstruction(element.tryResult))
|
||||
}
|
||||
|
||||
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
|
||||
callStack.dropSubFrame() // drop other catch blocks
|
||||
callStack.newSubFrame(element, listOf()) // 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))
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldThrow(expression: IrThrow, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(expression))
|
||||
callStack.addInstruction(CompoundInstruction(expression.value))
|
||||
}
|
||||
|
||||
private fun unfoldStringConcatenation(expression: IrStringConcatenation, environment: IrInterpreterEnvironment) {
|
||||
val callStack = environment.callStack
|
||||
callStack.newSubFrame(expression, listOf())
|
||||
callStack.addInstruction(SimpleInstruction(expression))
|
||||
|
||||
// this callback is used to check the need for an explicit toString call
|
||||
val explicitToStringCheck = fun() {
|
||||
when (val state = callStack.peekState()) {
|
||||
is Common -> {
|
||||
callStack.popState()
|
||||
// TODO this check can be dropped after serialization introduction
|
||||
// for now declarations in unsigned class don't have bodies and must be treated separately
|
||||
if (state.irClass.defaultType.isUnsigned()) {
|
||||
val result = when (val value = (state.fields.single().state as Primitive<*>).value) {
|
||||
is Byte -> value.toUByte().toString()
|
||||
is Short -> value.toUShort().toString()
|
||||
is Int -> value.toUInt().toString()
|
||||
else -> (value as Number).toLong().toULong().toString()
|
||||
}
|
||||
return callStack.pushState(result.toState(environment.irBuiltIns.stringType))
|
||||
}
|
||||
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
|
||||
|
||||
callStack.newSubFrame(toStringCall, listOf(SimpleInstruction(toStringCall)))
|
||||
callStack.pushState(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
expression.arguments.reversed().forEach {
|
||||
callStack.addInstruction(CustomInstruction(explicitToStringCheck))
|
||||
callStack.addInstruction(CompoundInstruction(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldComposite(element: IrComposite, callStack: CallStack) {
|
||||
when (element.origin) {
|
||||
IrStatementOrigin.DESTRUCTURING_DECLARATION -> element.statements.reversed()
|
||||
.forEach { callStack.addInstruction(CompoundInstruction(it)) }
|
||||
null -> element.statements.reversed()
|
||||
.forEach { callStack.addInstruction(CompoundInstruction(it)) } // is null for body of do while loop
|
||||
else -> TODO("${element.origin} not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: CallStack) {
|
||||
val function = reference.symbol.owner
|
||||
callStack.newSubFrame(reference, listOf())
|
||||
callStack.addInstruction(SimpleInstruction(reference))
|
||||
|
||||
reference.extensionReceiver?.let {
|
||||
callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!))
|
||||
callStack.addInstruction(CompoundInstruction(it))
|
||||
}
|
||||
reference.dispatchReceiver?.let {
|
||||
callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!))
|
||||
callStack.addInstruction(CompoundInstruction(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(propertyReference))
|
||||
callStack.addInstruction(CompoundInstruction(propertyReference.dispatchReceiver))
|
||||
}
|
||||
|
||||
private fun unfoldClassReference(classReference: IrClassReference, callStack: CallStack) {
|
||||
callStack.addInstruction(SimpleInstruction(classReference))
|
||||
}
|
||||
+421
-606
File diff suppressed because it is too large
Load Diff
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.Complex
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.util.isSubclassOf
|
||||
|
||||
internal class IrInterpreterEnvironment(val irBuiltIns: IrBuiltIns, val callStack: CallStack) {
|
||||
val irExceptions = mutableListOf<IrClass>()
|
||||
var mapOfEnums = mutableMapOf<IrSymbol, Complex>()
|
||||
var mapOfObjects = mutableMapOf<IrSymbol, Complex>()
|
||||
|
||||
private constructor(environment: IrInterpreterEnvironment) : this(environment.irBuiltIns, CallStack()) {
|
||||
irExceptions.addAll(environment.irExceptions)
|
||||
mapOfEnums = environment.mapOfEnums
|
||||
mapOfObjects = environment.mapOfObjects
|
||||
}
|
||||
|
||||
constructor(irModule: IrModuleFragment) : this(irModule.irBuiltins, CallStack()) {
|
||||
irExceptions.addAll(
|
||||
irModule.files
|
||||
.flatMap { it.declarations }
|
||||
.filterIsInstance<IrClass>()
|
||||
.filter { it.isSubclassOf(irBuiltIns.throwableClass.owner) }
|
||||
)
|
||||
}
|
||||
|
||||
fun copyWithNewCallStack(): IrInterpreterEnvironment {
|
||||
return IrInterpreterEnvironment(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MAX_STACK = 10_000
|
||||
const val MAX_COMMANDS = 1_000_000
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Stack
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.Primitive
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.isSubtypeOf
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
|
||||
enum class ReturnLabel {
|
||||
REGULAR, RETURN, BREAK_LOOP, BREAK_WHEN, CONTINUE, EXCEPTION
|
||||
}
|
||||
|
||||
open class ExecutionResult(val returnLabel: ReturnLabel, private val owner: IrElement? = null) {
|
||||
fun getNextLabel(irElement: IrElement, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
return when (returnLabel) {
|
||||
ReturnLabel.RETURN -> when (irElement) {
|
||||
is IrCall, is IrReturnableBlock, is IrSimpleFunction -> if (owner == irElement) Next else this
|
||||
else -> this
|
||||
}
|
||||
ReturnLabel.BREAK_WHEN -> when (irElement) {
|
||||
is IrWhen -> Next
|
||||
else -> this
|
||||
}
|
||||
ReturnLabel.BREAK_LOOP -> when (irElement) {
|
||||
is IrWhileLoop, is IrDoWhileLoop -> if (owner == irElement) Next else this
|
||||
else -> this
|
||||
}
|
||||
ReturnLabel.CONTINUE -> when (irElement) {
|
||||
is IrWhileLoop -> if (owner == irElement) irElement.interpret() else this
|
||||
is IrDoWhileLoop -> {
|
||||
if (owner != irElement) return this
|
||||
error("Continue must be handled inside DoWhile interpreter function")
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
ReturnLabel.EXCEPTION -> Exception
|
||||
ReturnLabel.REGULAR -> Next
|
||||
}
|
||||
}
|
||||
|
||||
fun addOwnerInfo(owner: IrElement): ExecutionResult {
|
||||
return ExecutionResult(returnLabel, owner)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun ExecutionResult.check(
|
||||
vararg toCheckLabel: ReturnLabel = arrayOf(ReturnLabel.REGULAR), returnBlock: (ExecutionResult) -> Unit
|
||||
): ExecutionResult {
|
||||
if (this.returnLabel !in toCheckLabel) returnBlock(this)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is analog of `checkcast` jvm bytecode operation. Throw exception whenever actual type is not a subtype of expected.
|
||||
*/
|
||||
internal fun ExecutionResult.implicitCastIfNeeded(expectedType: IrType, actualType: IrType, stack: Stack): ExecutionResult {
|
||||
if (actualType.classifierOrNull !is IrTypeParameterSymbol) return this
|
||||
|
||||
if (expectedType.classifierOrFail is IrTypeParameterSymbol) return this
|
||||
|
||||
val actualState = stack.peekReturnValue()
|
||||
if (actualState is Primitive<*> && actualState.value == null) return this // this is handled as NullPointerException
|
||||
|
||||
if (!actualState.isSubtypeOf(expectedType)) {
|
||||
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
|
||||
ClassCastException("$convertibleClassName cannot be cast to ${expectedType.render()}").throwAsUserException()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
object Next : ExecutionResult(ReturnLabel.REGULAR)
|
||||
object Return : ExecutionResult(ReturnLabel.RETURN)
|
||||
object BreakLoop : ExecutionResult(ReturnLabel.BREAK_LOOP)
|
||||
object BreakWhen : ExecutionResult(ReturnLabel.BREAK_WHEN)
|
||||
object Continue : ExecutionResult(ReturnLabel.CONTINUE)
|
||||
object Exception : ExecutionResult(ReturnLabel.EXCEPTION)
|
||||
@@ -13,12 +13,13 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
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.exceptions.throwAsUserException
|
||||
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
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
|
||||
@@ -28,7 +29,6 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.lang.invoke.MethodType
|
||||
import kotlin.math.min
|
||||
|
||||
@@ -53,6 +53,9 @@ internal fun State.toIrExpression(expression: IrExpression): IrExpression {
|
||||
type.isPrimitiveType() || type.isString() -> this.value.toIrConst(type, start, end)
|
||||
else -> expression // TODO support for arrays
|
||||
}
|
||||
is ExceptionState -> {
|
||||
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + this.getFullDescription())
|
||||
}
|
||||
is Complex -> {
|
||||
val stateType = this.irClass.defaultType
|
||||
when {
|
||||
@@ -234,17 +237,51 @@ fun IrClass.internalName(): String {
|
||||
return internalName.toString()
|
||||
}
|
||||
|
||||
inline fun withExceptionHandler(block: () -> Any?): Any? {
|
||||
internal inline fun withExceptionHandler(environment: IrInterpreterEnvironment, block: () -> Unit) {
|
||||
try {
|
||||
return block()
|
||||
block()
|
||||
// check if during proxy interpretation was an exception
|
||||
val possibleException = environment.callStack.peekState() as? ExceptionState
|
||||
if (possibleException != null) environment.callStack.dropFramesUntilTryCatch()
|
||||
} catch (e: Throwable) {
|
||||
e.throwAsUserException()
|
||||
e.handleUserException(environment)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IrFunction.getArgsForMethodInvocation(interpreter: IrInterpreter, methodType: MethodType, args: List<Variable>): List<Any?> {
|
||||
internal fun Throwable.handleUserException(environment: IrInterpreterEnvironment) {
|
||||
val exceptionName = this::class.java.simpleName
|
||||
val irExceptionClass = environment.irExceptions.firstOrNull { it.name.asString() == exceptionName }
|
||||
?: environment.irBuiltIns.throwableClass.owner
|
||||
environment.callStack.pushState(ExceptionState(this, irExceptionClass, environment.callStack.getStackTrace()))
|
||||
environment.callStack.dropFramesUntilTryCatch()
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is analog of `checkcast` jvm bytecode operation. Throw exception whenever actual type is not a subtype of expected.
|
||||
*/
|
||||
internal fun IrFunction?.checkCast(environment: IrInterpreterEnvironment): Boolean {
|
||||
this ?: return true
|
||||
val actualType = this.returnType
|
||||
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
|
||||
if (expectedType.classifierOrFail is IrTypeParameterSymbol) return true
|
||||
|
||||
val actualState = environment.callStack.peekState() ?: return true
|
||||
if (actualState is Primitive<*> && actualState.value == null) return true // this is handled in checkNullability
|
||||
|
||||
if (!actualState.isSubtypeOf(expectedType)) {
|
||||
val convertibleClassName = environment.callStack.popState().irClass.fqNameWhenAvailable
|
||||
ClassCastException("$convertibleClassName cannot be cast to ${expectedType.render()}").handleUserException(environment)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun IrFunction.getArgsForMethodInvocation(interpreter: IrInterpreter, methodType: MethodType, args: List<State>): List<Any?> {
|
||||
val argsValues = args
|
||||
.mapIndexed { index, variable -> variable.state.wrap(interpreter, methodType.parameterType(index)) }
|
||||
.mapIndexed { index, state -> state.wrap(interpreter, methodType.parameterType(index)) }
|
||||
.toMutableList()
|
||||
|
||||
// TODO if vararg isn't last parameter
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.exceptions
|
||||
|
||||
/**
|
||||
* This class used to wrap 2 types of exceptions:
|
||||
* 1. exceptions from JVM such as: ArithmeticException, StackOverflowError and others
|
||||
* 2. exceptions defined by user
|
||||
*/
|
||||
class UserException(val exception: Throwable) : InterpreterException() {
|
||||
override fun fillInStackTrace(): Throwable = this
|
||||
}
|
||||
|
||||
fun Throwable.throwAsUserException(): Nothing {
|
||||
throw UserException(this)
|
||||
}
|
||||
+15
-16
@@ -5,25 +5,24 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.ExecutionResult
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterMethodNotFoundError
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Stack
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.interpreter.Instruction
|
||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterMethodNotFoundError
|
||||
|
||||
internal class IntrinsicEvaluator {
|
||||
fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
internal object IntrinsicEvaluator {
|
||||
fun unwindInstructions(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
return when {
|
||||
EmptyArray.equalTo(irFunction) -> EmptyArray.evaluate(irFunction, stack, interpret)
|
||||
ArrayOf.equalTo(irFunction) -> ArrayOf.evaluate(irFunction, stack, interpret)
|
||||
ArrayOfNulls.equalTo(irFunction) -> ArrayOfNulls.evaluate(irFunction, stack, interpret)
|
||||
EnumValues.equalTo(irFunction) -> EnumValues.evaluate(irFunction, stack, interpret)
|
||||
EnumValueOf.equalTo(irFunction) -> EnumValueOf.evaluate(irFunction, stack, interpret)
|
||||
EnumHashCode.equalTo(irFunction) -> EnumHashCode.evaluate(irFunction, stack, interpret)
|
||||
JsPrimitives.equalTo(irFunction) -> JsPrimitives.evaluate(irFunction, stack, interpret)
|
||||
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.evaluate(irFunction, stack, interpret)
|
||||
SourceLocation.equalTo(irFunction) -> SourceLocation.evaluate(irFunction, stack, interpret)
|
||||
AssertIntrinsic.equalTo(irFunction) -> AssertIntrinsic.evaluate(irFunction, stack, interpret)
|
||||
EmptyArray.equalTo(irFunction) -> EmptyArray.unwind(irFunction, environment)
|
||||
ArrayOf.equalTo(irFunction) -> ArrayOf.unwind(irFunction, environment)
|
||||
ArrayOfNulls.equalTo(irFunction) -> ArrayOfNulls.unwind(irFunction, environment)
|
||||
EnumValues.equalTo(irFunction) -> EnumValues.unwind(irFunction, environment)
|
||||
EnumValueOf.equalTo(irFunction) -> EnumValueOf.unwind(irFunction, environment)
|
||||
EnumHashCode.equalTo(irFunction) -> EnumHashCode.unwind(irFunction, environment)
|
||||
JsPrimitives.equalTo(irFunction) -> JsPrimitives.unwind(irFunction, environment)
|
||||
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.unwind(irFunction, environment)
|
||||
SourceLocation.equalTo(irFunction) -> SourceLocation.unwind(irFunction, environment)
|
||||
AssertIntrinsic.equalTo(irFunction) -> AssertIntrinsic.unwind(irFunction, environment)
|
||||
else -> throw InterpreterMethodNotFoundError("Method ${irFunction.name} hasn't implemented")
|
||||
}
|
||||
}
|
||||
|
||||
+141
-77
@@ -5,15 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Stack
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.interpreter.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KFunctionState
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
@@ -22,12 +21,20 @@ import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||
import org.jetbrains.kotlin.ir.types.isArray
|
||||
import org.jetbrains.kotlin.ir.types.isCharArray
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
internal sealed class IntrinsicBase {
|
||||
abstract fun equalTo(irFunction: IrFunction): Boolean
|
||||
abstract fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult
|
||||
abstract fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment)
|
||||
abstract fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction>
|
||||
|
||||
fun customEvaluateInstruction(irFunction: IrFunction, environment: IrInterpreterEnvironment): CustomInstruction {
|
||||
return CustomInstruction {
|
||||
evaluate(irFunction, environment)
|
||||
environment.callStack.dropFrameAndCopyResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object EmptyArray : IntrinsicBase() {
|
||||
@@ -36,13 +43,13 @@ internal object EmptyArray : IntrinsicBase() {
|
||||
return fqName in setOf("kotlin.emptyArray", "kotlin.ArrayIntrinsicsKt.emptyArray")
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
val typeArgument = irFunction.typeParameters.map { stack.getVariable(it.symbol) }.single().state as KTypeState
|
||||
val returnType = (irFunction.returnType as IrSimpleType).buildSimpleType {
|
||||
arguments = listOf(makeTypeProjection(typeArgument.irType, Variance.INVARIANT))
|
||||
}
|
||||
stack.pushReturnValue(emptyArray<Any?>().toState(returnType))
|
||||
return Next
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
val returnType = environment.callStack.getVariable(irFunction.symbol).state as KTypeState
|
||||
environment.callStack.pushState(emptyArray<Any?>().toState(returnType.irType))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,10 +59,14 @@ internal object ArrayOf : IntrinsicBase() {
|
||||
return fqName == "kotlin.arrayOf"
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
val elementsVariable = irFunction.valueParameters.single().symbol
|
||||
stack.pushReturnValue(stack.getVariable(elementsVariable).state)
|
||||
return Next
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
val elementsSymbol = irFunction.valueParameters.single().symbol
|
||||
val varargVariable = environment.callStack.getVariable(elementsSymbol).state
|
||||
environment.callStack.pushState(varargVariable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,15 +76,19 @@ internal object ArrayOfNulls : IntrinsicBase() {
|
||||
return fqName == "kotlin.arrayOfNulls"
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
val size = stack.getVariable(irFunction.valueParameters.first().symbol).state.asInt()
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
val size = environment.callStack.getVariable(irFunction.valueParameters.first().symbol).state.asInt()
|
||||
val array = arrayOfNulls<Any?>(size)
|
||||
val typeArgument = irFunction.typeParameters.map { stack.getVariable(it.symbol) }.single().state as KTypeState
|
||||
val typeArgument = irFunction.typeParameters.map { environment.callStack.getVariable(it.symbol) }.single().state as KTypeState
|
||||
val returnType = (irFunction.returnType as IrSimpleType).buildSimpleType {
|
||||
arguments = listOf(makeTypeProjection(typeArgument.irType, Variance.INVARIANT))
|
||||
}
|
||||
stack.pushReturnValue(array.toState(returnType))
|
||||
return Next
|
||||
|
||||
environment.callStack.pushState(array.toState(returnType))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,19 +98,28 @@ internal object EnumValues : IntrinsicBase() {
|
||||
return (fqName == "kotlin.enumValues" || fqName.endsWith(".values")) && irFunction.valueParameters.isEmpty()
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
val enumClass = when (irFunction.fqNameWhenAvailable.toString()) {
|
||||
private fun getEnumClass(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrClass {
|
||||
return when (irFunction.fqNameWhenAvailable.toString()) {
|
||||
"kotlin.enumValues" -> {
|
||||
val kType = stack.getVariable(irFunction.typeParameters.first().symbol).state as KTypeState
|
||||
val kType = environment.callStack.getVariable(irFunction.typeParameters.first().symbol).state as KTypeState
|
||||
kType.irType.classOrNull!!.owner
|
||||
}
|
||||
else -> irFunction.parent as IrClass
|
||||
}
|
||||
}
|
||||
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
val enumClass = getEnumClass(irFunction, environment)
|
||||
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
|
||||
.map { entry -> entry.interpret().check { return it }.let { stack.popReturnValue() as Common } }
|
||||
stack.pushReturnValue(enumEntries.toTypedArray().toState(irFunction.returnType))
|
||||
return Next
|
||||
|
||||
return listOf(customEvaluateInstruction(irFunction, environment)) + enumEntries.reversed().map { CompoundInstruction(it) }
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
val enumClass = getEnumClass(irFunction, environment)
|
||||
|
||||
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>().map { environment.mapOfEnums[it.symbol] }
|
||||
environment.callStack.pushState(enumEntries.toTypedArray().toState(irFunction.returnType))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,20 +129,34 @@ internal object EnumValueOf : IntrinsicBase() {
|
||||
return (fqName == "kotlin.enumValueOf" || fqName.endsWith(".valueOf")) && irFunction.valueParameters.size == 1
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
val enumClass = when (irFunction.fqNameWhenAvailable.toString()) {
|
||||
private fun getEnumClass(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrClass {
|
||||
return when (irFunction.fqNameWhenAvailable.toString()) {
|
||||
"kotlin.enumValueOf" -> {
|
||||
val kType = stack.getVariable(irFunction.typeParameters.first().symbol).state as KTypeState
|
||||
val kType = environment.callStack.getVariable(irFunction.typeParameters.first().symbol).state as KTypeState
|
||||
kType.irType.classOrNull!!.owner
|
||||
}
|
||||
else -> irFunction.parent as IrClass
|
||||
}
|
||||
val enumEntryName = stack.getVariable(irFunction.valueParameters.first().symbol).state.asString()
|
||||
val enumEntry = enumClass.declarations.filterIsInstance<IrEnumEntry>().singleOrNull { it.name.asString() == enumEntryName }
|
||||
enumEntry?.interpret()?.check { return it }
|
||||
?: IllegalArgumentException("No enum constant ${enumClass.fqNameWhenAvailable}.$enumEntryName").throwAsUserException()
|
||||
}
|
||||
|
||||
return Next
|
||||
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 enumEntry = enumClass.declarations.filterIsInstance<IrEnumEntry>().singleOrNull { it.name.asString() == enumEntryName }
|
||||
if (enumEntry == null) {
|
||||
IllegalArgumentException("No enum constant ${enumClass.fqNameWhenAvailable}.$enumEntryName").handleUserException(environment)
|
||||
}
|
||||
return enumEntry
|
||||
}
|
||||
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
val enumEntry = getEnumEntryByName(irFunction, environment) ?: return emptyList()
|
||||
return listOf(customEvaluateInstruction(irFunction, environment), CompoundInstruction(enumEntry))
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
val enumEntry = getEnumEntryByName(irFunction, environment)!!
|
||||
environment.callStack.pushState(environment.mapOfEnums[enumEntry.symbol]!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,10 +166,13 @@ internal object EnumHashCode : IntrinsicBase() {
|
||||
return fqName == "kotlin.Enum.hashCode"
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
val hashCode = stack.getAll().single().state.hashCode()
|
||||
stack.pushReturnValue(hashCode.toState(irFunction.returnType))
|
||||
return Next
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
val hashCode = environment.callStack.getVariable(irFunction.dispatchReceiverParameter!!.symbol).state.hashCode()
|
||||
environment.callStack.pushState(hashCode.toState(irFunction.returnType))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,19 +182,22 @@ internal object JsPrimitives : IntrinsicBase() {
|
||||
return fqName == "kotlin.Long.<init>" || fqName == "kotlin.Char.<init>"
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
when (irFunction.fqNameWhenAvailable.toString()) {
|
||||
"kotlin.Long.<init>" -> {
|
||||
val low = stack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
|
||||
val high = stack.getVariable(irFunction.valueParameters[1].symbol).state.asInt()
|
||||
stack.pushReturnValue((high.toLong().shl(32) + low).toState(irFunction.returnType))
|
||||
val low = environment.callStack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
|
||||
val high = environment.callStack.getVariable(irFunction.valueParameters[1].symbol).state.asInt()
|
||||
environment.callStack.pushState((high.toLong().shl(32) + low).toState(irFunction.returnType))
|
||||
}
|
||||
"kotlin.Char.<init>" -> {
|
||||
val value = stack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
|
||||
stack.pushReturnValue(value.toChar().toState(irFunction.returnType))
|
||||
val value = environment.callStack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
|
||||
environment.callStack.pushState(value.toChar().toState(irFunction.returnType))
|
||||
}
|
||||
}
|
||||
return Next
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,24 +207,36 @@ internal object ArrayConstructor : IntrinsicBase() {
|
||||
return fqName.matches("kotlin\\.(Byte|Char|Short|Int|Long|Float|Double|Boolean|)Array\\.<init>".toRegex())
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
if (irFunction.valueParameters.size == 1) return listOf(customEvaluateInstruction(irFunction, environment))
|
||||
val instructions = mutableListOf<Instruction>(customEvaluateInstruction(irFunction, environment))
|
||||
|
||||
val sizeDescriptor = irFunction.valueParameters[0].symbol
|
||||
val size = stack.getVariable(sizeDescriptor).state.asInt()
|
||||
val size = environment.callStack.getVariable(sizeDescriptor).state.asInt()
|
||||
|
||||
val initDescriptor = irFunction.valueParameters[1].symbol
|
||||
val initLambda = environment.callStack.getVariable(initDescriptor).state 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))
|
||||
instructions += CompoundInstruction(call)
|
||||
}
|
||||
|
||||
return instructions
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
val sizeDescriptor = irFunction.valueParameters[0].symbol
|
||||
val size = environment.callStack.getVariable(sizeDescriptor).state.asInt()
|
||||
val arrayValue = MutableList<Any?>(size) { if (irFunction.returnType.isCharArray()) 0.toChar() else 0 }
|
||||
|
||||
if (irFunction.valueParameters.size == 2) {
|
||||
val initDescriptor = irFunction.valueParameters[1].symbol
|
||||
val initLambda = stack.getVariable(initDescriptor).state as KFunctionState
|
||||
val index = initLambda.irFunction.valueParameters.single()
|
||||
val nonLocalDeclarations = initLambda.extractNonLocalDeclarations()
|
||||
for (i in 0 until size) {
|
||||
val indexVar = listOf(Variable(index.symbol, i.toState(index.type)))
|
||||
// TODO throw exception if label != RETURN
|
||||
stack.newFrame(
|
||||
asSubFrame = initLambda.irFunction.isLocal || initLambda.irFunction.isInline,
|
||||
initPool = nonLocalDeclarations + indexVar
|
||||
) { initLambda.irFunction.body!!.interpret() }.check(ReturnLabel.RETURN) { return it }
|
||||
arrayValue[i] = stack.popReturnValue().let {
|
||||
arrayValue[i] = environment.callStack.popState().let {
|
||||
// TODO wrap
|
||||
when (it) {
|
||||
is Wrapper -> it.value
|
||||
is Primitive<*> -> if (it.type.isArray() || it.type.isPrimitiveArray()) it else it.value
|
||||
@@ -190,8 +246,8 @@ internal object ArrayConstructor : IntrinsicBase() {
|
||||
}
|
||||
}
|
||||
|
||||
stack.pushReturnValue(arrayValue.toPrimitiveStateArray(irFunction.parentAsClass.defaultType))
|
||||
return Next
|
||||
val type = environment.callStack.getVariable(irFunction.symbol).state as KTypeState
|
||||
environment.callStack.pushState(arrayValue.toPrimitiveStateArray(type.irType))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,9 +257,12 @@ internal object SourceLocation : IntrinsicBase() {
|
||||
return fqName == "kotlin.experimental.sourceLocation" || fqName == "kotlin.experimental.SourceLocationKt.sourceLocation"
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
stack.pushReturnValue(stack.getCurrentStackInfo().toState(irFunction.returnType))
|
||||
return Next
|
||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||
environment.callStack.pushState(environment.callStack.getFileAndPositionInfo().toState(irFunction.returnType))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,17 +272,22 @@ internal object AssertIntrinsic : IntrinsicBase() {
|
||||
return fqName == "kotlin.PreconditionsKt.assert"
|
||||
}
|
||||
|
||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
||||
val value = stack.getVariable(irFunction.valueParameters.first().symbol).state.asBoolean()
|
||||
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 function = messageLambda.irFunction as IrSimpleFunction
|
||||
val call = IrCallImpl.fromSymbolOwner(0, 0, 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()
|
||||
if (value) return
|
||||
when (irFunction.valueParameters.size) {
|
||||
1 -> if (!value) AssertionError("Assertion failed").throwAsUserException()
|
||||
2 -> if (!value) {
|
||||
val messageLambda = stack.getVariable(irFunction.valueParameters.last().symbol).state as KFunctionState
|
||||
stack.newFrame(asSubFrame = true) { messageLambda.irFunction.body!!.interpret() }
|
||||
.check(ReturnLabel.RETURN) { return it }
|
||||
AssertionError(stack.popReturnValue().asString()).throwAsUserException()
|
||||
}
|
||||
1 -> AssertionError("Assertion failed").handleUserException(environment)
|
||||
2 -> AssertionError(environment.callStack.popState().asString()).handleUserException(environment)
|
||||
}
|
||||
return Next
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -33,7 +33,7 @@ internal class CommonProxy private constructor(
|
||||
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
|
||||
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, other.state))
|
||||
|
||||
return with(interpreter) { equalsFun.interpret(valueArguments) } as Boolean
|
||||
return with(interpreter) { equalsFun.proxyInterpret(valueArguments) } as Boolean
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@@ -42,7 +42,7 @@ internal class CommonProxy private constructor(
|
||||
if (hashCodeFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return System.identityHashCode(state)
|
||||
|
||||
hashCodeFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
|
||||
return with(interpreter) { hashCodeFun.interpret(valueArguments) } as Int
|
||||
return with(interpreter) { hashCodeFun.proxyInterpret(valueArguments) } as Int
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -53,7 +53,7 @@ internal class CommonProxy private constructor(
|
||||
}
|
||||
|
||||
toStringFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
|
||||
return with(interpreter) { toStringFun.interpret(valueArguments) } as String
|
||||
return with(interpreter) { toStringFun.proxyInterpret(valueArguments) } as String
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -79,7 +79,7 @@ internal class CommonProxy private constructor(
|
||||
valueArguments += Variable(irFunction.getDispatchReceiver()!!, commonProxy.state)
|
||||
valueArguments += irFunction.valueParameters
|
||||
.mapIndexed { index, parameter -> Variable(parameter.symbol, args[index].toState(parameter.type)) }
|
||||
with(interpreter) { irFunction.interpret(valueArguments, method.returnType) }
|
||||
with(interpreter) { irFunction.proxyInterpret(valueArguments, method.returnType) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ internal class KFunctionProxy(
|
||||
val extensionReceiver = state.irFunction.extensionReceiverParameter?.let { Variable(it.symbol, args[index++].toState(it.type)) }
|
||||
val valueArguments = listOfNotNull(dispatchReceiver, extensionReceiver) +
|
||||
state.irFunction.valueParameters.map { parameter -> Variable(parameter.symbol, args[index++].toState(parameter.type)) }
|
||||
return with(interpreter) { state.irFunction.interpret(valueArguments) }
|
||||
return with(interpreter) { state.irFunction.proxyInterpret(valueArguments) }
|
||||
}
|
||||
|
||||
override fun callBy(args: Map<KParameter, Any?>): Any? {
|
||||
|
||||
+2
-4
@@ -6,8 +6,6 @@
|
||||
package org.jetbrains.kotlin.ir.interpreter.proxy.reflection
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
|
||||
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
|
||||
import org.jetbrains.kotlin.ir.interpreter.proxy.wrap
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KPropertyState
|
||||
import org.jetbrains.kotlin.ir.interpreter.toState
|
||||
@@ -24,7 +22,7 @@ internal open class KProperty1Proxy(
|
||||
checkArguments(1, args.size)
|
||||
val receiverParameter = getter.dispatchReceiverParameter ?: getter.extensionReceiverParameter
|
||||
val receiver = Variable(receiverParameter!!.symbol, args[0].toState(receiverParameter.type))
|
||||
return with(this@KProperty1Proxy.interpreter) { getter.interpret(listOf(receiver)) }
|
||||
return with(this@KProperty1Proxy.interpreter) { getter.proxyInterpret(listOf(receiver)) }
|
||||
}
|
||||
|
||||
override fun callBy(args: Map<KParameter, Any?>): Any? {
|
||||
@@ -54,7 +52,7 @@ internal class KMutableProperty1Proxy(
|
||||
val receiver = Variable(receiverParameter!!.symbol, args[0].toState(receiverParameter.type))
|
||||
val valueParameter = setter.valueParameters.single()
|
||||
val value = Variable(valueParameter.symbol, args[1].toState(valueParameter.type))
|
||||
with(this@KMutableProperty1Proxy.interpreter) { setter.interpret(listOf(receiver, value)) }
|
||||
with(this@KMutableProperty1Proxy.interpreter) { setter.proxyInterpret(listOf(receiver, value)) }
|
||||
}
|
||||
|
||||
override fun callBy(args: Map<KParameter, Any?>) {
|
||||
|
||||
+3
-3
@@ -14,15 +14,15 @@ import kotlin.reflect.*
|
||||
internal open class KProperty2Proxy(
|
||||
override val state: KPropertyState, override val interpreter: IrInterpreter
|
||||
) : AbstractKPropertyProxy(state, interpreter), KProperty2<Any?, Any?, Any?> {
|
||||
override val getter: KProperty2.Getter<Any?, Any?, Any?> =
|
||||
object : Getter(state.property.getter!!), KProperty2.Getter<Any?, Any?, Any?> {
|
||||
override val getter: KProperty2.Getter<Any?, Any?, Any?>
|
||||
get() = object : Getter(state.property.getter!!), KProperty2.Getter<Any?, Any?, Any?> {
|
||||
override fun invoke(p1: Any?, p2: Any?): Any? = call(p1, p2)
|
||||
|
||||
override fun call(vararg args: Any?): Any? {
|
||||
checkArguments(2, args.size)
|
||||
val dispatch = Variable(getter.dispatchReceiverParameter!!.symbol, args[0].toState(getter.dispatchReceiverParameter!!.type))
|
||||
val extension = Variable(getter.extensionReceiverParameter!!.symbol, args[1].toState(getter.extensionReceiverParameter!!.type))
|
||||
return with(this@KProperty2Proxy.interpreter) { getter.interpret(listOf(dispatch, extension)) }
|
||||
return with(this@KProperty2Proxy.interpreter) { getter.proxyInterpret(listOf(dispatch, extension)) }
|
||||
}
|
||||
|
||||
override fun callBy(args: Map<KParameter, Any?>): Any? {
|
||||
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.stack
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.CompoundInstruction
|
||||
import org.jetbrains.kotlin.ir.interpreter.Instruction
|
||||
import org.jetbrains.kotlin.ir.interpreter.SimpleInstruction
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.State
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.StateWithClosure
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
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
|
||||
|
||||
fun newFrame(frameOwner: IrElement, instructions: List<Instruction>, irFile: IrFile? = null) {
|
||||
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
|
||||
frames.add(Frame(newFrame, irFile))
|
||||
}
|
||||
|
||||
fun newFrame(frameOwner: IrFunction, instructions: List<Instruction>) {
|
||||
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
|
||||
frames.add(Frame(newFrame, frameOwner.fileOrNull))
|
||||
}
|
||||
|
||||
fun newSubFrame(frameOwner: IrElement, instructions: List<Instruction>) {
|
||||
val newFrame = SubFrame(instructions.toMutableList(), frameOwner)
|
||||
getCurrentFrame().addSubFrame(newFrame)
|
||||
}
|
||||
|
||||
fun dropFrame() {
|
||||
frames.removeLast()
|
||||
}
|
||||
|
||||
fun dropFrameAndCopyResult() {
|
||||
val result = peekState() ?: return dropFrame()
|
||||
popState()
|
||||
dropFrame()
|
||||
pushState(result)
|
||||
}
|
||||
|
||||
fun dropSubFrame() {
|
||||
getCurrentFrame().removeSubFrame()
|
||||
}
|
||||
|
||||
fun returnFromFrameWithResult(irReturn: IrReturn) {
|
||||
val result = popState()
|
||||
var frameOwner = getCurrentFrameOwner()
|
||||
while (frameOwner != irReturn.returnTargetSymbol.owner) {
|
||||
when (frameOwner) {
|
||||
is IrTry -> {
|
||||
dropSubFrame()
|
||||
pushState(result)
|
||||
addInstruction(SimpleInstruction(irReturn))
|
||||
addInstruction(CompoundInstruction(frameOwner.finallyExpression))
|
||||
return
|
||||
}
|
||||
is IrCatch -> {
|
||||
val tryBlock = getCurrentFrame().dropInstructions()!!.element as IrTry// last instruction in `catch` block is `try`
|
||||
dropSubFrame()
|
||||
pushState(result)
|
||||
addInstruction(SimpleInstruction(irReturn))
|
||||
addInstruction(CompoundInstruction(tryBlock.finallyExpression))
|
||||
return
|
||||
}
|
||||
else -> {
|
||||
dropSubFrame()
|
||||
if (getCurrentFrame().hasNoSubFrames() && frameOwner != irReturn.returnTargetSymbol.owner) dropFrame()
|
||||
frameOwner = getCurrentFrameOwner()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropFrame()
|
||||
// check that last frame is not a function itself; use case for proxyInterpret
|
||||
if (frames.size == 0) newFrame(irReturn, emptyList()) // just stub frame
|
||||
pushState(result)
|
||||
}
|
||||
|
||||
fun unrollInstructionsForBreakContinue(breakOrContinue: IrBreakContinue) {
|
||||
var frameOwner = getCurrentFrameOwner()
|
||||
while (frameOwner != breakOrContinue.loop) {
|
||||
when (frameOwner) {
|
||||
is IrTry -> {
|
||||
getCurrentFrame().removeSubFrameWithoutDataPropagation()
|
||||
addInstruction(CompoundInstruction(breakOrContinue))
|
||||
newSubFrame(frameOwner, listOf(SimpleInstruction(frameOwner))) // will be deleted when interpret 'try'
|
||||
return
|
||||
}
|
||||
is IrCatch -> {
|
||||
val tryInstruction = getCurrentFrame().dropInstructions()!! // last instruction in `catch` block is `try`
|
||||
getCurrentFrame().removeSubFrameWithoutDataPropagation()
|
||||
addInstruction(CompoundInstruction(breakOrContinue))
|
||||
newSubFrame(tryInstruction.element!!, listOf(tryInstruction)) // will be deleted when interpret 'try'
|
||||
return
|
||||
}
|
||||
else -> {
|
||||
getCurrentFrame().removeSubFrameWithoutDataPropagation()
|
||||
frameOwner = getCurrentFrameOwner()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (breakOrContinue) {
|
||||
is IrBreak -> getCurrentFrame().removeSubFrameWithoutDataPropagation() // drop loop
|
||||
else -> if (breakOrContinue.loop is IrDoWhileLoop) {
|
||||
addInstruction(SimpleInstruction(breakOrContinue.loop))
|
||||
addInstruction(CompoundInstruction(breakOrContinue.loop.condition))
|
||||
} else {
|
||||
addInstruction(CompoundInstruction(breakOrContinue.loop))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dropFramesUntilTryCatch() {
|
||||
val exception = popState()
|
||||
var frameOwner = getCurrentFrameOwner()
|
||||
while (frames.isNotEmpty()) {
|
||||
val frame = getCurrentFrame()
|
||||
while (!frame.hasNoSubFrames()) {
|
||||
frameOwner = frame.currentSubFrameOwner
|
||||
when (frameOwner) {
|
||||
is IrTry -> {
|
||||
dropSubFrame() // drop all instructions that left
|
||||
newSubFrame(frameOwner, listOf())
|
||||
addInstruction(SimpleInstruction(frameOwner)) // to evaluate finally at the end
|
||||
frameOwner.catches.reversed().forEach { addInstruction(CompoundInstruction(it)) }
|
||||
pushState(exception)
|
||||
return
|
||||
}
|
||||
is IrCatch -> {
|
||||
// in case of exception in catch, drop everything except of last `try` instruction
|
||||
addInstruction(frame.dropInstructions()!!)
|
||||
pushState(exception)
|
||||
return
|
||||
}
|
||||
else -> frame.removeSubFrameWithoutDataPropagation()
|
||||
}
|
||||
}
|
||||
dropFrame()
|
||||
}
|
||||
|
||||
if (frames.size == 0) newFrame(frameOwner, emptyList()) // just stub frame
|
||||
pushState(exception)
|
||||
}
|
||||
|
||||
fun hasNoInstructions() = frames.isEmpty() || (frames.size == 1 && frames.first().hasNoInstructions())
|
||||
|
||||
fun addInstruction(instruction: Instruction) {
|
||||
getCurrentFrame().addInstruction(instruction)
|
||||
}
|
||||
|
||||
fun popInstruction(): Instruction {
|
||||
return getCurrentFrame().popInstruction()
|
||||
}
|
||||
|
||||
fun pushState(state: State) {
|
||||
getCurrentFrame().pushState(state)
|
||||
}
|
||||
|
||||
fun popState(): State = getCurrentFrame().popState()
|
||||
fun peekState(): State? = getCurrentFrame().peekState()
|
||||
|
||||
fun addVariable(variable: Variable) {
|
||||
getCurrentFrame().addVariable(variable)
|
||||
}
|
||||
|
||||
fun getVariable(symbol: IrSymbol): Variable = getCurrentFrame().getVariable(symbol)
|
||||
|
||||
fun storeUpValues(state: StateWithClosure) {
|
||||
// TODO save only necessary declarations
|
||||
state.upValues.addAll(getCurrentFrame().getAll().toMutableList())
|
||||
}
|
||||
|
||||
fun loadUpValues(state: StateWithClosure) {
|
||||
state.upValues.forEach { addVariable(it) }
|
||||
}
|
||||
|
||||
fun copyUpValuesFromPreviousFrame() {
|
||||
frames[frames.size - 2].getAll().forEach { addVariable(it) }
|
||||
}
|
||||
|
||||
fun getStackTrace(): List<String> {
|
||||
return frames.map { it.toString() }.filter { it != Frame.NOT_DEFINED }
|
||||
}
|
||||
|
||||
fun getFileAndPositionInfo(): String {
|
||||
return frames[frames.size - 2].getFileAndPositionInfo()
|
||||
}
|
||||
|
||||
fun getStackCount(): Int = frames.size
|
||||
}
|
||||
+122
-59
@@ -1,80 +1,143 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.stack
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterEmptyReturnStackError
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.interpreter.Instruction
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.State
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
internal interface Frame {
|
||||
fun addVar(variable: Variable)
|
||||
fun addAll(variables: List<Variable>)
|
||||
fun getVariable(symbol: IrSymbol): Variable?
|
||||
fun getAll(): List<Variable>
|
||||
fun contains(symbol: IrSymbol): Boolean
|
||||
fun pushReturnValue(state: State)
|
||||
fun pushReturnValue(frame: Frame) // TODO rename to getReturnValueFrom
|
||||
fun peekReturnValue(): State
|
||||
fun popReturnValue(): State
|
||||
fun hasReturnValue(): Boolean
|
||||
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
|
||||
|
||||
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) }
|
||||
removeSubFrameWithoutDataPropagation()
|
||||
}
|
||||
|
||||
fun removeSubFrameWithoutDataPropagation() {
|
||||
innerStack.removeLast()
|
||||
}
|
||||
|
||||
fun hasNoSubFrames() = innerStack.isEmpty()
|
||||
fun hasNoInstructions() = hasNoSubFrames() || (innerStack.size == 1 && innerStack.first().isEmpty())
|
||||
|
||||
fun addInstruction(instruction: Instruction) {
|
||||
getCurrentFrame().pushInstruction(instruction)
|
||||
}
|
||||
|
||||
fun popInstruction(): Instruction {
|
||||
return getCurrentFrame().popInstruction().apply { currentInstruction = this }
|
||||
}
|
||||
|
||||
fun dropInstructions() = getCurrentFrame().dropInstructions()
|
||||
|
||||
fun pushState(state: State) {
|
||||
getCurrentFrame().pushState(state)
|
||||
}
|
||||
|
||||
fun popState(): State = getCurrentFrame().popState()
|
||||
fun peekState(): State? = getCurrentFrame().peekState()
|
||||
|
||||
fun addVariable(variable: Variable) {
|
||||
getCurrentFrame().addVariable(variable)
|
||||
}
|
||||
|
||||
fun getVariable(symbol: IrSymbol): Variable {
|
||||
return innerStack.firstNotNullResult { it.getVariable(symbol) }
|
||||
?: throw InterpreterError("$symbol not found") // TODO better message
|
||||
}
|
||||
|
||||
fun getAll(): List<Variable> = innerStack.flatMap { it.getAll() }
|
||||
|
||||
private fun getLineNumberForCurrentInstruction(): String {
|
||||
irFile ?: return ""
|
||||
val frameOwner = currentInstruction?.element
|
||||
return when {
|
||||
frameOwner is IrExpression || (frameOwner is IrDeclaration && frameOwner.origin == IrDeclarationOrigin.DEFINED) ->
|
||||
":${irFile.fileEntry.getLineNumber(frameOwner.startOffset) + 1}"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fun getFileAndPositionInfo(): String {
|
||||
irFile ?: return NOT_DEFINED
|
||||
val lineNum = getLineNumberForCurrentInstruction()
|
||||
return "${irFile.name}$lineNum"
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
irFile ?: return NOT_DEFINED
|
||||
val fileNameCapitalized = irFile.name.replace(".kt", "Kt").capitalizeAsciiOnly()
|
||||
val entryPoint = innerStack.firstOrNull { it.owner is IrFunction }?.owner as? IrFunction
|
||||
val lineNum = getLineNumberForCurrentInstruction()
|
||||
|
||||
return "at $fileNameCapitalized.${entryPoint?.fqNameWhenAvailable ?: "<clinit>"}(${irFile.name}$lineNum)"
|
||||
}
|
||||
}
|
||||
|
||||
// TODO replace exceptions with InterpreterException
|
||||
internal class InterpreterFrame(
|
||||
private val pool: MutableList<Variable> = mutableListOf(),
|
||||
private val typeArguments: List<Variable> = listOf()
|
||||
) : Frame {
|
||||
private val returnStack: MutableList<State> = mutableListOf()
|
||||
internal class SubFrame(private val instructions: MutableList<Instruction>, val owner: IrElement) {
|
||||
private val memory = mutableListOf<Variable>()
|
||||
private val dataStack = DataStack()
|
||||
|
||||
override fun addVar(variable: Variable) {
|
||||
pool.add(variable)
|
||||
fun isEmpty() = instructions.isEmpty()
|
||||
|
||||
fun pushInstruction(instruction: Instruction) {
|
||||
instructions.add(0, instruction)
|
||||
}
|
||||
|
||||
override fun addAll(variables: List<Variable>) {
|
||||
pool.addAll(variables)
|
||||
fun popInstruction(): Instruction {
|
||||
return instructions.removeFirst()
|
||||
}
|
||||
|
||||
override fun getVariable(symbol: IrSymbol): Variable? {
|
||||
return (if (symbol is IrTypeParameterSymbol) typeArguments else pool).firstOrNull { it.symbol == symbol }
|
||||
fun dropInstructions() = instructions.lastOrNull()?.apply { instructions.clear() }
|
||||
|
||||
fun pushState(state: State) {
|
||||
dataStack.push(state)
|
||||
}
|
||||
|
||||
override fun getAll(): List<Variable> {
|
||||
return pool
|
||||
fun popState(): State = dataStack.pop()
|
||||
fun peekState(): State? = if (!dataStack.isEmpty()) dataStack.peek() else null
|
||||
|
||||
fun addVariable(variable: Variable) {
|
||||
memory += variable
|
||||
}
|
||||
|
||||
override fun contains(symbol: IrSymbol): Boolean {
|
||||
return (typeArguments + pool).any { it.symbol == symbol }
|
||||
}
|
||||
|
||||
override fun pushReturnValue(state: State) {
|
||||
returnStack += state
|
||||
}
|
||||
|
||||
override fun pushReturnValue(frame: Frame) {
|
||||
if (frame.hasReturnValue()) this.pushReturnValue(frame.popReturnValue())
|
||||
}
|
||||
|
||||
override fun hasReturnValue(): Boolean {
|
||||
return returnStack.isNotEmpty()
|
||||
}
|
||||
|
||||
override fun peekReturnValue(): State {
|
||||
if (returnStack.isNotEmpty()) {
|
||||
return returnStack.last()
|
||||
}
|
||||
throw InterpreterEmptyReturnStackError()
|
||||
}
|
||||
|
||||
override fun popReturnValue(): State {
|
||||
if (returnStack.isNotEmpty()) {
|
||||
val item = returnStack.last()
|
||||
returnStack.removeAt(returnStack.size - 1)
|
||||
return item
|
||||
}
|
||||
throw InterpreterEmptyReturnStackError()
|
||||
}
|
||||
fun getVariable(symbol: IrSymbol): Variable? = memory.firstOrNull { it.symbol == symbol }
|
||||
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()
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.stack
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.name
|
||||
import org.jetbrains.kotlin.ir.interpreter.ExecutionResult
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.State
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.util.fileOrNull
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isLocal
|
||||
|
||||
internal interface Stack {
|
||||
fun newFrame(asSubFrame: Boolean = false, initPool: List<Variable> = listOf(), block: () -> ExecutionResult): ExecutionResult
|
||||
fun newFrame(irFunction: IrFunction, initPool: List<Variable> = listOf(), block: () -> ExecutionResult): ExecutionResult
|
||||
fun newFrame(irFile: IrFile?, initPool: List<Variable> = listOf(), block: () -> ExecutionResult): ExecutionResult
|
||||
|
||||
fun fixCallEntryPoint(irExpression: IrExpression)
|
||||
fun getStackTrace(): List<String>
|
||||
fun getCurrentStackInfo(): String
|
||||
fun getStackCount(): Int
|
||||
|
||||
fun clean(rootFile: IrFile?)
|
||||
fun addVar(variable: Variable)
|
||||
fun addAll(variables: List<Variable>)
|
||||
fun getVariable(symbol: IrSymbol): Variable
|
||||
fun getAll(): List<Variable>
|
||||
|
||||
fun contains(symbol: IrSymbol): Boolean
|
||||
fun hasReturnValue(): Boolean
|
||||
fun pushReturnValue(state: State)
|
||||
fun popReturnValue(): State
|
||||
fun peekReturnValue(): State
|
||||
}
|
||||
|
||||
internal class StackImpl : Stack {
|
||||
private val frameList = mutableListOf<FrameContainer>()
|
||||
private fun getCurrentFrame() = frameList.last()
|
||||
private var stackCount = 0
|
||||
|
||||
private fun addNewFrame(asSubFrame: Boolean, initPool: List<Variable>, irFunction: IrFunction? = null, irFile: IrFile? = null) {
|
||||
val typeArgumentsPool = initPool.filter { it.symbol is IrTypeParameterSymbol }
|
||||
val valueArguments = initPool.filter { it.symbol !is IrTypeParameterSymbol }
|
||||
val newFrame = InterpreterFrame(valueArguments.toMutableList(), typeArgumentsPool)
|
||||
when {
|
||||
asSubFrame -> getCurrentFrame().addSubFrame(newFrame)
|
||||
else -> frameList.add(FrameContainer(irFile, irFunction, newFrame))
|
||||
}
|
||||
}
|
||||
|
||||
private fun withStackIncrement(asSubFrame: Boolean, block: () -> ExecutionResult): ExecutionResult {
|
||||
return try {
|
||||
stackCount++
|
||||
block()
|
||||
} finally {
|
||||
stackCount--
|
||||
if (asSubFrame) getCurrentFrame().removeSubFrame() else removeLastFrame()
|
||||
}
|
||||
}
|
||||
|
||||
override fun newFrame(asSubFrame: Boolean, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult {
|
||||
addNewFrame(asSubFrame, initPool, null, null)
|
||||
return withStackIncrement(asSubFrame, block)
|
||||
}
|
||||
|
||||
override fun newFrame(irFunction: IrFunction, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult {
|
||||
val asSubFrame = irFunction.isInline || irFunction.isLocal
|
||||
addNewFrame(asSubFrame, initPool, irFunction, irFunction.fileOrNull)
|
||||
return withStackIncrement(asSubFrame, block)
|
||||
}
|
||||
|
||||
override fun newFrame(irFile: IrFile?, initPool: List<Variable>, block: () -> ExecutionResult): ExecutionResult {
|
||||
addNewFrame(false, initPool, null, irFile)
|
||||
return withStackIncrement(false, block)
|
||||
}
|
||||
|
||||
private fun removeLastFrame() {
|
||||
if (frameList.size > 1 && getCurrentFrame().hasReturnValue()) frameList[frameList.lastIndex - 1].pushReturnValue(getCurrentFrame())
|
||||
frameList.removeAt(frameList.lastIndex)
|
||||
}
|
||||
|
||||
override fun fixCallEntryPoint(irExpression: IrExpression) {
|
||||
val fileEntry = getCurrentFrame().irFile?.fileEntry ?: return
|
||||
val lineNum = fileEntry.getLineNumber(irExpression.startOffset) + 1
|
||||
getCurrentFrame().lineNumber = lineNum
|
||||
}
|
||||
|
||||
override fun getStackTrace(): List<String> {
|
||||
// TODO implement some sort of cache
|
||||
return frameList.map { it.toString() }
|
||||
}
|
||||
|
||||
override fun getCurrentStackInfo(): String {
|
||||
val frame = getCurrentFrame()
|
||||
frame.irFile ?: return "Not defined"
|
||||
val lineNum = if (frame.lineNumber != -1) ":${frame.lineNumber}" else ""
|
||||
return "${frame.irFile.name}$lineNum"
|
||||
}
|
||||
|
||||
override fun getStackCount(): Int = stackCount
|
||||
|
||||
override fun clean(rootFile: IrFile?) {
|
||||
stackCount = 0
|
||||
frameList.clear()
|
||||
frameList.add(FrameContainer(rootFile))
|
||||
}
|
||||
|
||||
override fun addVar(variable: Variable) {
|
||||
getCurrentFrame().addVar(variable)
|
||||
}
|
||||
|
||||
override fun addAll(variables: List<Variable>) {
|
||||
getCurrentFrame().addAll(variables)
|
||||
}
|
||||
|
||||
override fun getVariable(symbol: IrSymbol): Variable {
|
||||
return getCurrentFrame().getVariable(symbol)
|
||||
}
|
||||
|
||||
override fun getAll(): List<Variable> {
|
||||
return getCurrentFrame().getAll()
|
||||
}
|
||||
|
||||
override fun contains(symbol: IrSymbol): Boolean {
|
||||
return getCurrentFrame().contains(symbol)
|
||||
}
|
||||
|
||||
override fun hasReturnValue(): Boolean {
|
||||
return getCurrentFrame().hasReturnValue()
|
||||
}
|
||||
|
||||
override fun pushReturnValue(state: State) {
|
||||
getCurrentFrame().pushReturnValue(state)
|
||||
}
|
||||
|
||||
override fun popReturnValue(): State {
|
||||
return getCurrentFrame().popReturnValue()
|
||||
}
|
||||
|
||||
override fun peekReturnValue(): State {
|
||||
return getCurrentFrame().peekReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
private class FrameContainer(val irFile: IrFile? = null, private val entryPoint: IrFunction? = null, current: Frame = InterpreterFrame()) {
|
||||
var lineNumber: Int = -1
|
||||
private val innerStack = mutableListOf(current)
|
||||
private fun getTopFrame() = innerStack.first()
|
||||
|
||||
fun addSubFrame(frame: Frame) {
|
||||
innerStack.add(0, frame)
|
||||
}
|
||||
|
||||
fun removeSubFrame() {
|
||||
if (getTopFrame().hasReturnValue() && innerStack.size > 1) innerStack[1].pushReturnValue(getTopFrame())
|
||||
innerStack.removeAt(0)
|
||||
}
|
||||
|
||||
fun addVar(variable: Variable) = getTopFrame().addVar(variable)
|
||||
fun addAll(variables: List<Variable>) = getTopFrame().addAll(variables)
|
||||
fun getAll() = innerStack.flatMap { it.getAll() }
|
||||
fun getVariable(symbol: IrSymbol): Variable {
|
||||
return innerStack.firstNotNullOfOrNull { it.getVariable(symbol) }
|
||||
?: throw InterpreterError("$symbol not found") // TODO better message
|
||||
}
|
||||
|
||||
fun contains(symbol: IrSymbol) = innerStack.any { it.contains(symbol) }
|
||||
fun hasReturnValue() = getTopFrame().hasReturnValue()
|
||||
fun pushReturnValue(container: FrameContainer) = getTopFrame().pushReturnValue(container.getTopFrame())
|
||||
fun pushReturnValue(state: State) = getTopFrame().pushReturnValue(state)
|
||||
fun popReturnValue() = getTopFrame().popReturnValue()
|
||||
fun peekReturnValue() = getTopFrame().peekReturnValue()
|
||||
|
||||
override fun toString(): String {
|
||||
irFile ?: return "Not defined"
|
||||
val fileNameCapitalized = irFile.name.replace(".kt", "Kt").capitalize()
|
||||
val lineNum = if (lineNumber != -1) ":$lineNumber" else ""
|
||||
return "at $fileNameCapitalized.${entryPoint?.fqNameWhenAvailable ?: "<clinit>"}(${irFile.name}$lineNum)"
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,8 @@ import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
||||
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
|
||||
|
||||
internal class Common private constructor(override val irClass: IrClass, override val fields: MutableList<Variable>) : Complex {
|
||||
internal class Common private constructor(override val irClass: IrClass, override val fields: MutableList<Variable>) : Complex, StateWithClosure {
|
||||
override val upValues: MutableList<Variable> = mutableListOf()
|
||||
override var superWrapperClass: Wrapper? = null
|
||||
override var outerClass: Variable? = null
|
||||
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@ import kotlin.math.min
|
||||
|
||||
internal class ExceptionState private constructor(
|
||||
override val irClass: IrClass, override val fields: MutableList<Variable>, stackTrace: List<String>
|
||||
) : Complex, Throwable() {
|
||||
|
||||
) : Complex, StateWithClosure, Throwable() {
|
||||
override val upValues: MutableList<Variable> = mutableListOf()
|
||||
override var superWrapperClass: Wrapper? = null
|
||||
override var outerClass: Variable? = null
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
|
||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
|
||||
import org.jetbrains.kotlin.ir.interpreter.handleUserException
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
@@ -64,11 +65,12 @@ internal fun State.isSubtypeOf(other: IrType): Boolean {
|
||||
* This method used to check if for not null parameter there was passed null argument.
|
||||
*/
|
||||
internal fun State.checkNullability(
|
||||
irType: IrType?, throwException: () -> Nothing = { NullPointerException().throwAsUserException() }
|
||||
): State {
|
||||
irType: IrType?, environment: IrInterpreterEnvironment, exceptionToThrow: () -> Throwable = { NullPointerException() }
|
||||
): State? {
|
||||
if (irType !is IrSimpleType) return this
|
||||
if (this.isNull() && !irType.isNullable()) {
|
||||
throwException()
|
||||
exceptionToThrow().handleUserException(environment)
|
||||
return null
|
||||
}
|
||||
return this
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.state
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
|
||||
internal interface StateWithClosure {
|
||||
val upValues: MutableList<Variable>
|
||||
}
|
||||
+3
-5
@@ -7,25 +7,23 @@ package org.jetbrains.kotlin.ir.interpreter.state.reflection
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrAbstractFunctionFactory
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
|
||||
import org.jetbrains.kotlin.ir.interpreter.getLastOverridden
|
||||
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KParameterProxy
|
||||
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeParameterProxy
|
||||
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.StateWithClosure
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.KTypeParameter
|
||||
|
||||
internal class KFunctionState(val irFunction: IrFunction, override val irClass: IrClass) : ReflectionState() {
|
||||
internal class KFunctionState(val irFunction: IrFunction, override val irClass: IrClass) : ReflectionState(), StateWithClosure {
|
||||
override val upValues: MutableList<Variable> = mutableListOf()
|
||||
override val fields: MutableList<Variable> = mutableListOf()
|
||||
private var _parameters: List<KParameter>? = null
|
||||
private var _returnType: KType? = null
|
||||
|
||||
Reference in New Issue
Block a user