Implement interpretation for return statements with labels

This commit is contained in:
Ivan Kylchik
2020-03-04 01:30:06 +03:00
parent 5c845da18a
commit c45993b2b1
2 changed files with 52 additions and 34 deletions
@@ -55,6 +55,21 @@ class IrInterpreter(irModule: IrModuleFragment) {
}
}
private inline fun Code.check(toCheck: Code = Code.NEXT, returnBlock: (Code) -> Unit): Code {
if (this != toCheck) returnBlock(this)
return this
}
private inline fun Code.checkForReturn(newFrame: Frame, oldFrame: Frame, returnBlock: (Code) -> Unit): Code {
if (this != Code.NEXT) {
if ((this == Code.RETURN || this == Code.EXCEPTION) && newFrame.hasReturnValue()) {
oldFrame.pushReturnValue(newFrame)
}
returnBlock(this)
}
return this
}
fun interpret(expression: IrExpression): IrExpression {
val data = InterpreterFrame()
return runBlocking {
@@ -109,9 +124,11 @@ class IrInterpreter(irModule: IrModuleFragment) {
}
return when (code) {
Code.RETURN -> when (this) { // TODO check label
is IrCall, is IrReturnableBlock, is IrFunctionImpl -> Code.NEXT
else -> Code.RETURN
Code.RETURN -> when (this) {
is IrCall -> if (code.info == this.symbol.descriptor.toString()) Code.NEXT else code
is IrReturnableBlock -> if (code.info == this.symbol.descriptor.toString()) Code.NEXT else code
is IrFunctionImpl -> if (code.info == this.descriptor.toString()) Code.NEXT else code
else -> code
}
Code.BREAK_WHEN -> when (this) {
is IrWhen -> Code.NEXT
@@ -225,7 +242,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
val enumEntries = enumClass.declarations
.filterIsInstance<IrEnumEntry>()
.map { entry ->
entry.interpret(data).also { if (it != Code.NEXT) return it }
entry.interpret(data).check { return it }
data.popReturnValue() as Common
}
data.pushReturnValue(enumEntries.toTypedArray().toState(irBuiltIns.arrayClass.defaultType))
@@ -242,7 +259,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
data.pushReturnValue(ExceptionState(IllegalArgumentException(message), illegalArgumentException, stackTrace))
return Code.EXCEPTION
} else {
enumEntry.interpret(data).also { if (it != Code.NEXT) return it }
enumEntry.interpret(data).check { return it }
}
}
"replace" -> {
@@ -261,11 +278,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
}
"hashCode" -> {
if (irFunction.parentAsClass.isEnumClass) {
var anyHashCode = irFunction as IrFunctionImpl
while (anyHashCode.overriddenSymbols.isNotEmpty()) {
anyHashCode = anyHashCode.overriddenSymbols.first().owner as IrFunctionImpl
}
calculateBuiltIns(anyHashCode as IrFunctionImpl, data)
calculateBuiltIns(irFunction.getLastOverridden() as IrFunctionImpl, data)
} else {
throw AssertionError("Hash code function intrinsic is supported only for enum class")
}
@@ -293,17 +306,12 @@ class IrInterpreter(irModule: IrModuleFragment) {
}
private suspend fun calculateOverridden(owner: IrFunctionImpl, data: Frame): Code {
fun IrFunctionImpl.getLastPossibleOverridden(): IrFunctionImpl {
// main usage: get throwable properties getter signature from exception classes
// then use that signatures to get right method from ir builtins map
var overridden = this
while (overridden.overriddenSymbols.isNotEmpty()) overridden = overridden.overriddenSymbols.first().owner as IrFunctionImpl
return overridden
}
val variableDescriptor = owner.symbol.getReceiver()!!
val superQualifier = (data.getVariableState(variableDescriptor) as? Complex)?.superType
?: return calculateBuiltIns(owner.getLastPossibleOverridden(), data)
if (superQualifier == null) {
// superQualifier is null for exception state => find method in builtins
return calculateBuiltIns(owner.getLastOverridden() as IrFunctionImpl, data)
}
val overridden = owner.overriddenSymbols.first { it.getReceiver()?.equalTo(superQualifier.getReceiver()) == true }
val newStates = InterpreterFrame(mutableListOf(Variable(overridden.getReceiver()!!, superQualifier)))
@@ -378,8 +386,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
private suspend fun interpretValueParameters(parametersContainer: IrMemberAccessExpression, data: Frame): Code {
val defaultValues = (parametersContainer.symbol.owner as IrFunction).valueParameters.map { it.defaultValue }
for (i in (parametersContainer.valueArgumentsCount - 1) downTo 0) {
val code = parametersContainer.getValueArgument(i)?.interpret(data) ?: defaultValues[i]!!.expression.interpret(data)
if (code != Code.NEXT) return code
(parametersContainer.getValueArgument(i)?.interpret(data) ?: defaultValues[i]!!.expression.interpret(data)).check { return it }
}
return Code.NEXT
}
@@ -389,7 +396,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
// dispatch receiver processing
val rawDispatchReceiver = expression.dispatchReceiver
rawDispatchReceiver?.interpret(data)?.also { if (it != Code.NEXT) return it }
rawDispatchReceiver?.interpret(data)?.check { return it }
val dispatchReceiver = rawDispatchReceiver?.let { data.popReturnValue() }
val irFunctionReceiver = if (expression.superQualifierSymbol == null) dispatchReceiver else (dispatchReceiver as Complex).superType
// it is important firstly to add receiver, then arguments; this order is used in builtin method call
@@ -401,7 +408,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
// extension receiver processing
val rawExtensionReceiver = expression.extensionReceiver
rawExtensionReceiver?.interpret(data)?.also { if (it != Code.NEXT) return it }
rawExtensionReceiver?.interpret(data)?.check { return it }
val extensionReceiver = rawExtensionReceiver?.let { data.popReturnValue() }
extensionReceiver?.let { newFrame.addVar(Variable(irFunction.symbol.getExtensionReceiver()!!, it)) }
@@ -409,7 +416,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
val receiverAsFirstArgument = if (dispatchReceiver is Lambda) listOfNotNull(irFunction.symbol.getExtensionReceiver()) else listOf()
val valueParametersDescriptors = receiverAsFirstArgument + irFunction.descriptor.valueParameters
val frameWithReceiverAndData = data.copy().apply { addAll(newFrame.getAll()) } // primary use case: copy method in data class
interpretValueParameters(expression, frameWithReceiverAndData).also { if (it != Code.NEXT) return it }
interpretValueParameters(expression, frameWithReceiverAndData).checkForReturn(frameWithReceiverAndData, data) { return it }
newFrame.addAll(valueParametersDescriptors.map { Variable(it, frameWithReceiverAndData.popReturnValue()) })
irFunction.takeIf { it.isInline }?.typeParameters?.forEachIndexed { index, typeParameter ->
@@ -438,21 +445,21 @@ class IrInterpreter(irModule: IrModuleFragment) {
val classProperties = irClass.declarations.filterIsInstance<IrProperty>()
classProperties.forEach { property ->
property.backingField?.initializer?.expression?.interpret(data)?.also { if (it != Code.NEXT) return it }
property.backingField?.initializer?.expression?.interpret(data)?.check { return it }
val receiver = irClass.thisReceiver!!.descriptor
if (property.backingField?.initializer != null)
property.backingField?.let { data.getVariableState(receiver).setState(Variable(it.descriptor, data.popReturnValue())) }
}
val anonymousInitializer = irClass.declarations.filterIsInstance<IrAnonymousInitializer>().filter { !it.isStatic }
anonymousInitializer.forEach { init -> init.body.interpret(data).also { if (it != Code.NEXT) return it } }
anonymousInitializer.forEach { init -> init.body.interpret(data).check { return it } }
return Code.NEXT
}
private suspend fun interpretConstructor(constructorCall: IrFunctionAccessExpression, data: Frame): Code {
val isPrimary = (constructorCall.symbol.owner as IrConstructor).isPrimary
interpretValueParameters(constructorCall, data).also { if (it != Code.NEXT) return it }
interpretValueParameters(constructorCall, data).check { return it }
val valueParameters = constructorCall.symbol.descriptor.valueParameters.map { Variable(it, data.popReturnValue()) }.toMutableList()
val newFrame = InterpreterFrame(valueParameters)
@@ -488,7 +495,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
val indexDescriptor = initLambda.irFunction.valueParameters.single().descriptor
for (i in 0 until size) {
val lambdaFrame = InterpreterFrame(mutableListOf(Variable(indexDescriptor, i.toState(irBuiltIns.intType))))
initLambda.irFunction.body!!.interpret(lambdaFrame).also { if (it != Code.RETURN) return it }
initLambda.irFunction.body!!.interpret(lambdaFrame).check(Code.RETURN) { return it }
arrayValue[i] = lambdaFrame.popReturnValue().let { (it as? Wrapper)?.value ?: (it as? Primitive<*>)?.value ?: it }
}
}
@@ -511,7 +518,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
val state = Common(parent, mutableListOf())
newFrame.addVar(Variable(constructorCall.getThisAsReceiver(), state)) //used to set up fields in body
constructorCall.getBody()?.interpret(newFrame)?.also { if (it != Code.NEXT) return it.apply { data.pushReturnValue(newFrame) } }
constructorCall.getBody()?.interpret(newFrame)?.checkForReturn(newFrame, data) { return it }
val returnedState = newFrame.popReturnValue() as Complex
data.pushReturnValue(if (isPrimary) state.apply { superType = returnedState } else returnedState.apply { setStatesFrom(state) })
return Code.NEXT
@@ -585,7 +592,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
private suspend fun interpretReturn(expression: IrReturn, data: Frame): Code {
val code = expression.value.interpret(data)
return if (code == Code.NEXT) Code.RETURN else code
return if (code == Code.NEXT) Code.RETURN.apply { this.info = expression.returnTargetSymbol.descriptor.toString() } else code
}
private suspend fun interpretWhile(expression: IrWhileLoop, data: Frame): Code {
@@ -715,14 +722,14 @@ class IrInterpreter(irModule: IrModuleFragment) {
enumSuperCall.mapValueParameters { valueArguments[it.index] }
}
val code = enumEntry.initializerExpression?.interpret(data)?.also { if (it != Code.NEXT) return it }
val code = enumEntry.initializerExpression?.interpret(data)?.check { return it }
enumSuperCall?.mapValueParameters { null }
data.pushReturnValue((data.popReturnValue() as Complex))
return code ?: throw AssertionError("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null")
}
private suspend fun interpretTypeOperatorCall(expression: IrTypeOperatorCall, data: Frame): Code {
val code = expression.argument.interpret(data).also { if (it != Code.NEXT) return it }
val code = expression.argument.interpret(data).check { return it }
return when (expression.operator) {
// coercion to unit means that return value isn't used
@@ -804,7 +811,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
}
private suspend fun interpretThrow(expression: IrThrow, data: Frame): Code {
expression.value.interpret(data).also { if (it != Code.NEXT) return it }
expression.value.interpret(data).check { return it }
when (val exception = data.popReturnValue()) {
is Common -> data.pushReturnValue(ExceptionState(exception, stackTrace))
is Wrapper -> data.pushReturnValue(ExceptionState(exception, stackTrace))
@@ -817,7 +824,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
private suspend fun interpretStringConcatenation(expression: IrStringConcatenation, data: Frame): Code {
val result = StringBuilder()
expression.arguments.forEach {
it.interpret(data).also { code -> if (code != Code.NEXT) return code }
it.interpret(data).check { code -> return code }
result.append(
when (val returnValue = data.popReturnValue()) {
is Primitive<*> -> returnValue.value.toString()
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
@@ -193,4 +194,14 @@ fun IrFunction.getArgsForMethodInvocation(data: Frame): List<Any?> {
}
return argsValues
}
fun IrFunction.getLastOverridden(): IrFunction {
if (this !is IrFunctionImpl) return this
var function = this as IrFunctionImpl
while (function.overriddenSymbols.isNotEmpty()) {
function = function.overriddenSymbols.first().owner as IrFunctionImpl
}
return function
}