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.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
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.interpreter.builtins.evaluateIntrinsicAnnotation
|
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.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
|
||||||
|
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
|
||||||
import org.jetbrains.kotlin.ir.symbols.*
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
|
||||||
import java.lang.invoke.MethodType
|
import java.lang.invoke.MethodType
|
||||||
import kotlin.math.min
|
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)
|
type.isPrimitiveType() || type.isString() -> this.value.toIrConst(type, start, end)
|
||||||
else -> expression // TODO support for arrays
|
else -> expression // TODO support for arrays
|
||||||
}
|
}
|
||||||
|
is ExceptionState -> {
|
||||||
|
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + this.getFullDescription())
|
||||||
|
}
|
||||||
is Complex -> {
|
is Complex -> {
|
||||||
val stateType = this.irClass.defaultType
|
val stateType = this.irClass.defaultType
|
||||||
when {
|
when {
|
||||||
@@ -234,17 +237,51 @@ fun IrClass.internalName(): String {
|
|||||||
return internalName.toString()
|
return internalName.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun withExceptionHandler(block: () -> Any?): Any? {
|
internal inline fun withExceptionHandler(environment: IrInterpreterEnvironment, block: () -> Unit) {
|
||||||
try {
|
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) {
|
} 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
|
val argsValues = args
|
||||||
.mapIndexed { index, variable -> variable.state.wrap(interpreter, methodType.parameterType(index)) }
|
.mapIndexed { index, state -> state.wrap(interpreter, methodType.parameterType(index)) }
|
||||||
.toMutableList()
|
.toMutableList()
|
||||||
|
|
||||||
// TODO if vararg isn't last parameter
|
// 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
|
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.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 {
|
internal object IntrinsicEvaluator {
|
||||||
fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
fun unwindInstructions(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
return when {
|
return when {
|
||||||
EmptyArray.equalTo(irFunction) -> EmptyArray.evaluate(irFunction, stack, interpret)
|
EmptyArray.equalTo(irFunction) -> EmptyArray.unwind(irFunction, environment)
|
||||||
ArrayOf.equalTo(irFunction) -> ArrayOf.evaluate(irFunction, stack, interpret)
|
ArrayOf.equalTo(irFunction) -> ArrayOf.unwind(irFunction, environment)
|
||||||
ArrayOfNulls.equalTo(irFunction) -> ArrayOfNulls.evaluate(irFunction, stack, interpret)
|
ArrayOfNulls.equalTo(irFunction) -> ArrayOfNulls.unwind(irFunction, environment)
|
||||||
EnumValues.equalTo(irFunction) -> EnumValues.evaluate(irFunction, stack, interpret)
|
EnumValues.equalTo(irFunction) -> EnumValues.unwind(irFunction, environment)
|
||||||
EnumValueOf.equalTo(irFunction) -> EnumValueOf.evaluate(irFunction, stack, interpret)
|
EnumValueOf.equalTo(irFunction) -> EnumValueOf.unwind(irFunction, environment)
|
||||||
EnumHashCode.equalTo(irFunction) -> EnumHashCode.evaluate(irFunction, stack, interpret)
|
EnumHashCode.equalTo(irFunction) -> EnumHashCode.unwind(irFunction, environment)
|
||||||
JsPrimitives.equalTo(irFunction) -> JsPrimitives.evaluate(irFunction, stack, interpret)
|
JsPrimitives.equalTo(irFunction) -> JsPrimitives.unwind(irFunction, environment)
|
||||||
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.evaluate(irFunction, stack, interpret)
|
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.unwind(irFunction, environment)
|
||||||
SourceLocation.equalTo(irFunction) -> SourceLocation.evaluate(irFunction, stack, interpret)
|
SourceLocation.equalTo(irFunction) -> SourceLocation.unwind(irFunction, environment)
|
||||||
AssertIntrinsic.equalTo(irFunction) -> AssertIntrinsic.evaluate(irFunction, stack, interpret)
|
AssertIntrinsic.equalTo(irFunction) -> AssertIntrinsic.unwind(irFunction, environment)
|
||||||
else -> throw InterpreterMethodNotFoundError("Method ${irFunction.name} hasn't implemented")
|
else -> throw InterpreterMethodNotFoundError("Method ${irFunction.name} hasn't implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+141
-77
@@ -5,15 +5,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.interpreter.intrinsics
|
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.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
|
||||||
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.KFunctionState
|
||||||
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
|
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
|
||||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
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.impl.makeTypeProjection
|
||||||
import org.jetbrains.kotlin.ir.types.isArray
|
import org.jetbrains.kotlin.ir.types.isArray
|
||||||
import org.jetbrains.kotlin.ir.types.isCharArray
|
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
|
import org.jetbrains.kotlin.types.Variance
|
||||||
|
|
||||||
internal sealed class IntrinsicBase {
|
internal sealed class IntrinsicBase {
|
||||||
abstract fun equalTo(irFunction: IrFunction): Boolean
|
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() {
|
internal object EmptyArray : IntrinsicBase() {
|
||||||
@@ -36,13 +43,13 @@ internal object EmptyArray : IntrinsicBase() {
|
|||||||
return fqName in setOf("kotlin.emptyArray", "kotlin.ArrayIntrinsicsKt.emptyArray")
|
return fqName in setOf("kotlin.emptyArray", "kotlin.ArrayIntrinsicsKt.emptyArray")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
val typeArgument = irFunction.typeParameters.map { stack.getVariable(it.symbol) }.single().state as KTypeState
|
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||||
val returnType = (irFunction.returnType as IrSimpleType).buildSimpleType {
|
}
|
||||||
arguments = listOf(makeTypeProjection(typeArgument.irType, Variance.INVARIANT))
|
|
||||||
}
|
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||||
stack.pushReturnValue(emptyArray<Any?>().toState(returnType))
|
val returnType = environment.callStack.getVariable(irFunction.symbol).state as KTypeState
|
||||||
return Next
|
environment.callStack.pushState(emptyArray<Any?>().toState(returnType.irType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,10 +59,14 @@ internal object ArrayOf : IntrinsicBase() {
|
|||||||
return fqName == "kotlin.arrayOf"
|
return fqName == "kotlin.arrayOf"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
val elementsVariable = irFunction.valueParameters.single().symbol
|
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||||
stack.pushReturnValue(stack.getVariable(elementsVariable).state)
|
}
|
||||||
return Next
|
|
||||||
|
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"
|
return fqName == "kotlin.arrayOfNulls"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
val size = stack.getVariable(irFunction.valueParameters.first().symbol).state.asInt()
|
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 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 {
|
val returnType = (irFunction.returnType as IrSimpleType).buildSimpleType {
|
||||||
arguments = listOf(makeTypeProjection(typeArgument.irType, Variance.INVARIANT))
|
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()
|
return (fqName == "kotlin.enumValues" || fqName.endsWith(".values")) && irFunction.valueParameters.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
private fun getEnumClass(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrClass {
|
||||||
val enumClass = when (irFunction.fqNameWhenAvailable.toString()) {
|
return when (irFunction.fqNameWhenAvailable.toString()) {
|
||||||
"kotlin.enumValues" -> {
|
"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
|
kType.irType.classOrNull!!.owner
|
||||||
}
|
}
|
||||||
else -> irFunction.parent as IrClass
|
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>()
|
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 listOf(customEvaluateInstruction(irFunction, environment)) + enumEntries.reversed().map { CompoundInstruction(it) }
|
||||||
return Next
|
}
|
||||||
|
|
||||||
|
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
|
return (fqName == "kotlin.enumValueOf" || fqName.endsWith(".valueOf")) && irFunction.valueParameters.size == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
private fun getEnumClass(irFunction: IrFunction, environment: IrInterpreterEnvironment): IrClass {
|
||||||
val enumClass = when (irFunction.fqNameWhenAvailable.toString()) {
|
return when (irFunction.fqNameWhenAvailable.toString()) {
|
||||||
"kotlin.enumValueOf" -> {
|
"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
|
kType.irType.classOrNull!!.owner
|
||||||
}
|
}
|
||||||
else -> irFunction.parent as IrClass
|
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"
|
return fqName == "kotlin.Enum.hashCode"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
val hashCode = stack.getAll().single().state.hashCode()
|
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||||
stack.pushReturnValue(hashCode.toState(irFunction.returnType))
|
}
|
||||||
return Next
|
|
||||||
|
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>"
|
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()) {
|
when (irFunction.fqNameWhenAvailable.toString()) {
|
||||||
"kotlin.Long.<init>" -> {
|
"kotlin.Long.<init>" -> {
|
||||||
val low = stack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
|
val low = environment.callStack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
|
||||||
val high = stack.getVariable(irFunction.valueParameters[1].symbol).state.asInt()
|
val high = environment.callStack.getVariable(irFunction.valueParameters[1].symbol).state.asInt()
|
||||||
stack.pushReturnValue((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 = stack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
|
val value = environment.callStack.getVariable(irFunction.valueParameters[0].symbol).state.asInt()
|
||||||
stack.pushReturnValue(value.toChar().toState(irFunction.returnType))
|
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())
|
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 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 }
|
val arrayValue = MutableList<Any?>(size) { if (irFunction.returnType.isCharArray()) 0.toChar() else 0 }
|
||||||
|
|
||||||
if (irFunction.valueParameters.size == 2) {
|
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) {
|
for (i in 0 until size) {
|
||||||
val indexVar = listOf(Variable(index.symbol, i.toState(index.type)))
|
arrayValue[i] = environment.callStack.popState().let {
|
||||||
// TODO throw exception if label != RETURN
|
// TODO wrap
|
||||||
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 {
|
|
||||||
when (it) {
|
when (it) {
|
||||||
is Wrapper -> it.value
|
is Wrapper -> it.value
|
||||||
is Primitive<*> -> if (it.type.isArray() || it.type.isPrimitiveArray()) it else 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))
|
val type = environment.callStack.getVariable(irFunction.symbol).state as KTypeState
|
||||||
return Next
|
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"
|
return fqName == "kotlin.experimental.sourceLocation" || fqName == "kotlin.experimental.SourceLocationKt.sourceLocation"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
stack.pushReturnValue(stack.getCurrentStackInfo().toState(irFunction.returnType))
|
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||||
return Next
|
}
|
||||||
|
|
||||||
|
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"
|
return fqName == "kotlin.PreconditionsKt.assert"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
val value = stack.getVariable(irFunction.valueParameters.first().symbol).state.asBoolean()
|
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) {
|
when (irFunction.valueParameters.size) {
|
||||||
1 -> if (!value) AssertionError("Assertion failed").throwAsUserException()
|
1 -> AssertionError("Assertion failed").handleUserException(environment)
|
||||||
2 -> if (!value) {
|
2 -> AssertionError(environment.callStack.popState().asString()).handleUserException(environment)
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Next
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-4
@@ -33,7 +33,7 @@ internal class CommonProxy private constructor(
|
|||||||
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
|
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
|
||||||
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, other.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 {
|
override fun hashCode(): Int {
|
||||||
@@ -42,7 +42,7 @@ internal class CommonProxy private constructor(
|
|||||||
if (hashCodeFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return System.identityHashCode(state)
|
if (hashCodeFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return System.identityHashCode(state)
|
||||||
|
|
||||||
hashCodeFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, 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 {
|
override fun toString(): String {
|
||||||
@@ -53,7 +53,7 @@ internal class CommonProxy private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
toStringFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
|
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 {
|
companion object {
|
||||||
@@ -79,7 +79,7 @@ internal class CommonProxy private constructor(
|
|||||||
valueArguments += Variable(irFunction.getDispatchReceiver()!!, commonProxy.state)
|
valueArguments += Variable(irFunction.getDispatchReceiver()!!, commonProxy.state)
|
||||||
valueArguments += irFunction.valueParameters
|
valueArguments += irFunction.valueParameters
|
||||||
.mapIndexed { index, parameter -> Variable(parameter.symbol, args[index].toState(parameter.type)) }
|
.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 extensionReceiver = state.irFunction.extensionReceiverParameter?.let { Variable(it.symbol, args[index++].toState(it.type)) }
|
||||||
val valueArguments = listOfNotNull(dispatchReceiver, extensionReceiver) +
|
val valueArguments = listOfNotNull(dispatchReceiver, extensionReceiver) +
|
||||||
state.irFunction.valueParameters.map { parameter -> Variable(parameter.symbol, args[index++].toState(parameter.type)) }
|
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? {
|
override fun callBy(args: Map<KParameter, Any?>): Any? {
|
||||||
|
|||||||
+2
-4
@@ -6,8 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.ir.interpreter.proxy.reflection
|
package org.jetbrains.kotlin.ir.interpreter.proxy.reflection
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
|
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.stack.Variable
|
||||||
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KPropertyState
|
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KPropertyState
|
||||||
import org.jetbrains.kotlin.ir.interpreter.toState
|
import org.jetbrains.kotlin.ir.interpreter.toState
|
||||||
@@ -24,7 +22,7 @@ internal open class KProperty1Proxy(
|
|||||||
checkArguments(1, args.size)
|
checkArguments(1, args.size)
|
||||||
val receiverParameter = getter.dispatchReceiverParameter ?: getter.extensionReceiverParameter
|
val receiverParameter = getter.dispatchReceiverParameter ?: getter.extensionReceiverParameter
|
||||||
val receiver = Variable(receiverParameter!!.symbol, args[0].toState(receiverParameter.type))
|
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? {
|
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 receiver = Variable(receiverParameter!!.symbol, args[0].toState(receiverParameter.type))
|
||||||
val valueParameter = setter.valueParameters.single()
|
val valueParameter = setter.valueParameters.single()
|
||||||
val value = Variable(valueParameter.symbol, args[1].toState(valueParameter.type))
|
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?>) {
|
override fun callBy(args: Map<KParameter, Any?>) {
|
||||||
|
|||||||
+3
-3
@@ -14,15 +14,15 @@ import kotlin.reflect.*
|
|||||||
internal open class KProperty2Proxy(
|
internal open class KProperty2Proxy(
|
||||||
override val state: KPropertyState, override val interpreter: IrInterpreter
|
override val state: KPropertyState, override val interpreter: IrInterpreter
|
||||||
) : AbstractKPropertyProxy(state, interpreter), KProperty2<Any?, Any?, Any?> {
|
) : AbstractKPropertyProxy(state, interpreter), KProperty2<Any?, Any?, Any?> {
|
||||||
override val getter: KProperty2.Getter<Any?, Any?, Any?> =
|
override val getter: KProperty2.Getter<Any?, Any?, Any?>
|
||||||
object : Getter(state.property.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 invoke(p1: Any?, p2: Any?): Any? = call(p1, p2)
|
||||||
|
|
||||||
override fun call(vararg args: Any?): Any? {
|
override fun call(vararg args: Any?): Any? {
|
||||||
checkArguments(2, args.size)
|
checkArguments(2, args.size)
|
||||||
val dispatch = Variable(getter.dispatchReceiverParameter!!.symbol, args[0].toState(getter.dispatchReceiverParameter!!.type))
|
val dispatch = Variable(getter.dispatchReceiverParameter!!.symbol, args[0].toState(getter.dispatchReceiverParameter!!.type))
|
||||||
val extension = Variable(getter.extensionReceiverParameter!!.symbol, args[1].toState(getter.extensionReceiverParameter!!.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? {
|
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.
|
* 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
|
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.interpreter.state.State
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
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 {
|
internal class Frame(subFrame: SubFrame, val irFile: IrFile? = null) {
|
||||||
fun addVar(variable: Variable)
|
private val innerStack = mutableListOf(subFrame)
|
||||||
fun addAll(variables: List<Variable>)
|
private var currentInstruction: Instruction? = null
|
||||||
fun getVariable(symbol: IrSymbol): Variable?
|
val currentSubFrameOwner: IrElement
|
||||||
fun getAll(): List<Variable>
|
get() = getCurrentFrame().owner
|
||||||
fun contains(symbol: IrSymbol): Boolean
|
|
||||||
fun pushReturnValue(state: State)
|
companion object {
|
||||||
fun pushReturnValue(frame: Frame) // TODO rename to getReturnValueFrom
|
const val NOT_DEFINED = "Not defined"
|
||||||
fun peekReturnValue(): State
|
}
|
||||||
fun popReturnValue(): State
|
|
||||||
fun hasReturnValue(): Boolean
|
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 SubFrame(private val instructions: MutableList<Instruction>, val owner: IrElement) {
|
||||||
internal class InterpreterFrame(
|
private val memory = mutableListOf<Variable>()
|
||||||
private val pool: MutableList<Variable> = mutableListOf(),
|
private val dataStack = DataStack()
|
||||||
private val typeArguments: List<Variable> = listOf()
|
|
||||||
) : Frame {
|
|
||||||
private val returnStack: MutableList<State> = mutableListOf()
|
|
||||||
|
|
||||||
override fun addVar(variable: Variable) {
|
fun isEmpty() = instructions.isEmpty()
|
||||||
pool.add(variable)
|
|
||||||
|
fun pushInstruction(instruction: Instruction) {
|
||||||
|
instructions.add(0, instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addAll(variables: List<Variable>) {
|
fun popInstruction(): Instruction {
|
||||||
pool.addAll(variables)
|
return instructions.removeFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getVariable(symbol: IrSymbol): Variable? {
|
fun dropInstructions() = instructions.lastOrNull()?.apply { instructions.clear() }
|
||||||
return (if (symbol is IrTypeParameterSymbol) typeArguments else pool).firstOrNull { it.symbol == symbol }
|
|
||||||
|
fun pushState(state: State) {
|
||||||
|
dataStack.push(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAll(): List<Variable> {
|
fun popState(): State = dataStack.pop()
|
||||||
return pool
|
fun peekState(): State? = if (!dataStack.isEmpty()) dataStack.peek() else null
|
||||||
|
|
||||||
|
fun addVariable(variable: Variable) {
|
||||||
|
memory += variable
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun contains(symbol: IrSymbol): Boolean {
|
fun getVariable(symbol: IrSymbol): Variable? = memory.firstOrNull { it.symbol == symbol }
|
||||||
return (typeArguments + pool).any { it.symbol == symbol }
|
fun getAll(): List<Variable> = memory
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.fqNameForIrSerialization
|
||||||
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
|
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 superWrapperClass: Wrapper? = null
|
||||||
override var outerClass: Variable? = null
|
override var outerClass: Variable? = null
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -17,8 +17,8 @@ import kotlin.math.min
|
|||||||
|
|
||||||
internal class ExceptionState private constructor(
|
internal class ExceptionState private constructor(
|
||||||
override val irClass: IrClass, override val fields: MutableList<Variable>, stackTrace: List<String>
|
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 superWrapperClass: Wrapper? = null
|
||||||
override var outerClass: Variable? = 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.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
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.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
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.
|
* This method used to check if for not null parameter there was passed null argument.
|
||||||
*/
|
*/
|
||||||
internal fun State.checkNullability(
|
internal fun State.checkNullability(
|
||||||
irType: IrType?, throwException: () -> Nothing = { NullPointerException().throwAsUserException() }
|
irType: IrType?, environment: IrInterpreterEnvironment, exceptionToThrow: () -> Throwable = { NullPointerException() }
|
||||||
): State {
|
): State? {
|
||||||
if (irType !is IrSimpleType) return this
|
if (irType !is IrSimpleType) return this
|
||||||
if (this.isNull() && !irType.isNullable()) {
|
if (this.isNull() && !irType.isNullable()) {
|
||||||
throwException()
|
exceptionToThrow().handleUserException(environment)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
return this
|
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.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
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.descriptors.IrAbstractFunctionFactory
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
|
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.KParameterProxy
|
||||||
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeParameterProxy
|
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeParameterProxy
|
||||||
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
|
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
|
||||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
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.types.classOrNull
|
||||||
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
|
||||||
import kotlin.reflect.KParameter
|
import kotlin.reflect.KParameter
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.KTypeParameter
|
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()
|
override val fields: MutableList<Variable> = mutableListOf()
|
||||||
private var _parameters: List<KParameter>? = null
|
private var _parameters: List<KParameter>? = null
|
||||||
private var _returnType: KType? = null
|
private var _returnType: KType? = null
|
||||||
|
|||||||
Reference in New Issue
Block a user