Add while loop support in interpreter

This commit is contained in:
Ivan Kylchik
2019-11-21 19:49:48 +03:00
parent c2a70b4e57
commit dd93deddd7
4 changed files with 138 additions and 69 deletions
@@ -10,8 +10,8 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
@@ -21,26 +21,20 @@ class IrInterpreter : IrElementVisitor<State, Frame> {
private val empty = EmptyState()
fun interpret(expression: IrExpression): IrExpression {
return visitExpression(expression, InterpreterFrame()).convertToIrExpression(expression)
}
private fun State.convertToIrExpression(expression: IrExpression): IrExpression {
return when (this) {
is Primitive<*> -> this.getIrConst().value.toIrConst(expression) // it is necessary to replace ir offsets
else -> TODO("not supported")
}
return visitExpression(expression, InterpreterFrame()).toIrExpression(expression)
}
override fun visitElement(element: IrElement, data: Frame): State {
return when (element) {
is IrCall -> visitCall(element, data)
is IrConstructor -> visitConstructor(element, data)
is IrConstructorCall -> visitConstructor(element, data)
is IrDelegatingConstructorCall -> visitDelegatingConstructorCall(element, data)
is IrBody -> visitStatements(element.statements, data)
is IrBlock -> visitStatements(element.statements, data)
is IrSetField -> visitSetField(element, data)
is IrGetField -> visitGetField(element, data)
is IrGetValue -> visitGetValue(element, data)
is IrGetObjectValue -> visitGetObjectValue(element, data)
is IrConst<*> -> visitConst(element, data)
is IrWhen -> visitWhen(element, data)
else -> TODO("${element.javaClass} not supported")
@@ -54,22 +48,18 @@ class IrInterpreter : IrElementVisitor<State, Frame> {
val dispatchReceiver = expression.dispatchReceiver?.accept(this, data) // can be either Primitive or Complex
val extensionReceiver = expression.extensionReceiver?.accept(this, data) // similarly
val irFunction = dispatchReceiver.getIrFunction(expression)
// it is important firstly to add receiver, then arguments
val receiverParameter = (expression.symbol.descriptor.dispatchReceiverParameter ?: expression.symbol.descriptor.extensionReceiverParameter)
when (val receiver = (dispatchReceiver ?: extensionReceiver)) {
// if receiver is complex then frame will contain receiver and its supers as raw list (not tree)
// this is necessary because it is hard to instantly say that receiver will be used; for example, in abstract methods
is Complex -> newFrame.addAll(receiver.getAllStates())
else -> receiver?.let { newFrame.addVar(Variable(receiverParameter!!, it)) }
}
val receiverParameter = irFunction.symbol.getReceiverDescriptor()
val receiver = (dispatchReceiver ?: extensionReceiver)
receiver?.let { newFrame.addVar(Variable(receiverParameter!!, it)) }
newFrame.addAll(valueParameters)
val irFunction = (dispatchReceiver as? Complex)?.getIrFunctionByName(expression.symbol.descriptor.name)
return when {
expression.isAbstract() -> calculateAbstract(irFunction, newFrame) //abstract check must be first
expression.isFakeOverridden() -> calculateOverridden(irFunction!!.symbol, newFrame)
expression.getBody() == null -> calculateBuiltIns(expression.symbol.descriptor, newFrame).toIrConst(expression).toPrimitive()
else -> expression.getBody()!!.accept(this, newFrame)
expression.isFakeOverridden() -> calculateOverridden(irFunction as IrFunctionImpl, newFrame)
expression.getBody() == null -> calculateBuiltIns(expression, newFrame).toState(expression)
else -> (irFunction.body ?: expression.getBody())!!.accept(this, newFrame)
}
}
@@ -85,7 +75,7 @@ class IrInterpreter : IrElementVisitor<State, Frame> {
is IrDelegatingConstructorCall -> {
val delegatingConstructorCall = visitDelegatingConstructorCall(it, newFrame)
if (delegatingConstructorCall != empty) {
val superObj = Variable(it.symbol.descriptor.containingDeclaration.thisAsReceiverParameter, delegatingConstructorCall)
val superObj = Variable(it.getThisAsReceiver(), delegatingConstructorCall)
obj.addSuperQualifier(superObj)
}
newFrame.addVar(Variable(constructor.getThisAsReceiver(), obj))
@@ -96,10 +86,6 @@ class IrInterpreter : IrElementVisitor<State, Frame> {
return obj
}
override fun visitConstructorCall(expression: IrConstructorCall, data: Frame): State {
return visitConstructor(expression, data)
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: Frame): State {
if (expression.symbol.descriptor.containingDeclaration.defaultType == builtIns.anyType) {
return empty
@@ -133,8 +119,8 @@ class IrInterpreter : IrElementVisitor<State, Frame> {
override fun visitGetField(expression: IrGetField, data: Frame): State {
val receiver = (expression.receiver as? IrDeclarationReference)?.symbol?.descriptor // receiver is null, for example, for top level fields
?: return expression.symbol.owner.initializer!!.expression.accept(this, data)
return data.getVariableState(receiver).getState(expression.symbol.descriptor).copy()
return receiver?.let { data.getVariableState(receiver).getState(expression.symbol.descriptor)?.copy() }
?: expression.symbol.owner.initializer!!.expression.accept(this, data)
}
override fun visitGetValue(expression: IrGetValue, data: Frame): State {
@@ -166,4 +152,25 @@ class IrInterpreter : IrElementVisitor<State, Frame> {
}
return empty
}
override fun visitGetObjectValue(expression: IrGetObjectValue, data: Frame): State {
return Complex(expression.symbol.owner, mutableListOf())
}
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: Frame): State {
when (expression.operator) {
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> {
expression.argument.accept(this, data)
return empty
}
else -> TODO("${expression.operator} not implemented")
}
}
override fun visitWhileLoop(loop: IrWhileLoop, data: Frame): State {
while ((loop.condition.accept(this, data) as? Primitive<*>)?.getIrConst()?.value == true) {
loop.body?.accept(this, InterpreterFrame(data.getAll().toMutableList()))
}
return empty
}
}
@@ -9,35 +9,53 @@ import org.jetbrains.kotlin.backend.common.interpreter.builtins.CompileTimeFunct
import org.jetbrains.kotlin.backend.common.interpreter.builtins.binaryFunctions
import org.jetbrains.kotlin.backend.common.interpreter.builtins.unaryFunctions
import org.jetbrains.kotlin.backend.common.interpreter.stack.*
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
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.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
fun IrInterpreter.calculateOverridden(symbol: IrFunctionSymbol, data: Frame): State {
val owner = symbol.owner as IrFunctionImpl
val overridden = owner.overriddenSymbols.first()
val overriddenReceiver = data.getVariableState(symbol.getThisAsReceiver()).getState(overridden.getThisAsReceiver())
val valueParameters = symbol.owner.valueParameters.zip(overridden.owner.valueParameters)
.map { Variable(it.second.descriptor, data.getVariableState(it.first.descriptor)) }
val newStates = InterpreterFrame((valueParameters + Variable(overridden.getThisAsReceiver(), overriddenReceiver)).toMutableList())
return if (overridden.owner.body != null) {
overridden.owner.body!!.accept(this, newStates)
} else {
calculateOverridden(overridden.owner.symbol, newStates)
}
fun IrInterpreter.calculateAbstract(irFunction: IrFunction?, data: Frame): State {
return irFunction?.body?.accept(this, data)
?: throw NoSuchMethodException("Method \"$irFunction\" wasn't implemented")
}
fun calculateBuiltIns(descriptor: FunctionDescriptor, frame: Frame): Any {
fun IrInterpreter.calculateOverridden(owner: IrFunctionImpl, data: Frame): State {
val overridden = owner.overriddenSymbols.first()
val variableDescriptor = owner.symbol.getReceiverDescriptor()!!
val overriddenReceiver = overridden.getReceiverDescriptor()!!
val overriddenReceiverState = data.getVariableState(variableDescriptor).getState(overriddenReceiver)
?: throw NoSuchElementException("Variable \"$variableDescriptor\" doesn't contains state \"$overriddenReceiver\"")
val valueParameters = owner.valueParameters.zip(overridden.owner.valueParameters)
.map { Variable(it.second.descriptor, data.getVariableState(it.first.descriptor)) }
val newStates = InterpreterFrame((valueParameters + Variable(overriddenReceiver, overriddenReceiverState)).toMutableList())
var overriddenOwner: IrSimpleFunction? = overridden.owner
do {
val body = overriddenOwner?.body
when {
body != null -> return body.accept(this, newStates)
else -> overriddenOwner = overriddenOwner?.overriddenSymbols?.firstOrNull()?.owner
}
} while (overriddenOwner != null)
throw NoSuchMethodException("$owner has no body")
}
fun IrInterpreter.calculateBuiltIns(expression: IrCall, frame: Frame): Any {
val descriptor = expression.symbol.descriptor
val methodName = descriptor.name.asString()
val receiverType = descriptor.dispatchReceiverParameter?.type ?: descriptor.extensionReceiverParameter?.type
val argsType = listOfNotNull(receiverType) + descriptor.valueParameters.map { TypeUtils.makeNotNullable(it.original.type) }
@@ -55,14 +73,26 @@ fun calculateBuiltIns(descriptor: FunctionDescriptor, frame: Frame): Any {
2 -> {
val function = binaryFunctions[signature]
?: throw NoSuchMethodException("For given function $signature there is no entry in binary map")
function.invoke(argsValues[0], argsValues[1])
when (methodName) {
"rangeTo" -> calculateRangeTo(expression, frame)
else -> function.invoke(argsValues[0], argsValues[1])
}
}
else -> throw UnsupportedOperationException("Unsupported number of arguments")
}
}
fun IrInterpreter.calculateAbstract(irFunction: IrFunction?, data: Frame): State {
return irFunction?.body?.accept(this, data)!!
private fun IrInterpreter.calculateRangeTo(expression: IrExpression, data: Frame): Any {
val constructor = expression.type.classOrNull!!.owner.constructors.first()
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
val primitiveValueParameters = data.getAll().map { it.state as Primitive<*> }
primitiveValueParameters.forEachIndexed { index, primitive -> constructorCall.putValueArgument(index, primitive.getIrConst()) }
val constructorValueParameters = constructor.valueParameters.map { it.descriptor }.zip(primitiveValueParameters)
val newFrame = InterpreterFrame(constructorValueParameters.map { Variable(it.first, it.second) }.toMutableList())
return constructorCall.accept(this, newFrame)
}
fun IrInterpreter.convertValueParameters(memberAccess: IrMemberAccessExpression, data: Frame): MutableList<Variable> {
@@ -74,18 +104,32 @@ fun IrInterpreter.convertValueParameters(memberAccess: IrMemberAccessExpression,
}
}
fun IrFunctionSymbol.getThisAsReceiver(): ReceiverParameterDescriptor {
return (this.descriptor.containingDeclaration as ClassDescriptor).thisAsReceiverParameter
// main purpose is to get receiver from constructor call
fun IrFunctionAccessExpression.getThisAsReceiver(): DeclarationDescriptor {
return (this.symbol.descriptor.containingDeclaration as ClassDescriptor).thisAsReceiverParameter
}
fun IrFunctionAccessExpression.getThisAsReceiver(): ReceiverParameterDescriptor {
return (this.symbol.descriptor.containingDeclaration as ClassDescriptor).thisAsReceiverParameter
fun IrFunctionSymbol.getReceiverDescriptor(): DeclarationDescriptor? {
return this.owner.dispatchReceiverParameter?.descriptor ?: this.owner.extensionReceiverParameter?.descriptor
}
fun IrFunctionAccessExpression.getBody(): IrBody? {
return this.symbol.owner.body
}
fun DeclarationDescriptor.isSubtypeOf(other: DeclarationDescriptor): Boolean {
if (this !is ReceiverParameterDescriptor || other !is ReceiverParameterDescriptor) return false
return when {
this.value is ImplicitClassReceiver && other.value is ImplicitClassReceiver -> this.value.type.isSubtypeOf(other.value.type)
this.value is ExtensionReceiver && other.value is ExtensionReceiver -> this.value == other.value
else -> false
}
}
fun DeclarationDescriptor.hasSameNameAs(other: DeclarationDescriptor): Boolean {
return this is ValueParameterDescriptor && other is ValueParameterDescriptor && this.name == other.name
}
fun IrCall.isAbstract(): Boolean {
return (this.symbol.owner as? IrSimpleFunction)?.modality == Modality.ABSTRACT
}
@@ -94,7 +138,25 @@ fun IrCall.isFakeOverridden(): Boolean {
return this.symbol.owner.isFakeOverride
}
fun Any?.toIrConst(expression: IrExpression): IrConst<*> {
fun State?.getIrFunction(expression: IrCall): IrFunction {
return this.let { (it as? Complex)?.getIrFunctionByName(expression.symbol.descriptor.name) } ?: expression.symbol.owner
}
fun State.toIrExpression(expression: IrExpression): IrExpression {
return when (this) {
is Primitive<*> -> this.getIrConst().value.toIrConst(expression) // it is necessary to replace ir offsets
else -> TODO("not supported")
}
}
fun Any?.toState(expression: IrExpression): State {
return when (this) {
is Complex -> this
else -> this.toIrConst(expression).toPrimitive()
}
}
private fun Any?.toIrConst(expression: IrExpression): IrConst<*> {
return when (this) {
is Boolean -> expression.copyParametersTo(IrConstKind.Boolean, this)
is Char -> expression.copyParametersTo(IrConstKind.Char, this)
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.backend.common.interpreter.stack
import org.jetbrains.kotlin.backend.common.interpreter.hasSameNameAs
import org.jetbrains.kotlin.backend.common.interpreter.isSubtypeOf
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import java.util.*
@@ -37,8 +39,9 @@ class InterpreterFrame(val pool: MutableList<Variable> = mutableListOf()) : Fram
}
override fun getVariableState(variableDescriptor: DeclarationDescriptor): State {
return pool.firstOrNull { it.descriptor == variableDescriptor }?.state
?: throw NoSuchElementException("Frame pool doesn't contains variable with descriptor $variableDescriptor")
return pool.firstOrNull {
it.descriptor.isSubtypeOf(variableDescriptor) || it.descriptor.hasSameNameAs(variableDescriptor) || it.descriptor == variableDescriptor
}?.state ?: throw NoSuchElementException("Frame pool doesn't contains variable with descriptor $variableDescriptor")
}
override fun getAll(): List<Variable> {
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.backend.common.interpreter.stack
import org.jetbrains.kotlin.backend.common.interpreter.hasSameNameAs
import org.jetbrains.kotlin.backend.common.interpreter.isSubtypeOf
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -13,10 +15,9 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.name.Name
import java.util.*
interface State {
fun getState(descriptor: DeclarationDescriptor): State
fun getState(descriptor: DeclarationDescriptor): State?
fun setState(newVar: Variable)
fun copy(): State
}
@@ -43,6 +44,7 @@ class Primitive<T>(private var value: IrConst<T>) : State {
return "Primitive(value=${value.value})"
}
}
class Complex(private var classOfObject: IrClass, private val values: MutableList<Variable>) : State {
fun addSuperQualifier(superObj: Variable) {
val superTypesList = getSuperTypes(classOfObject).map { it.descriptor.thisAsReceiverParameter }
@@ -55,19 +57,14 @@ class Complex(private var classOfObject: IrClass, private val values: MutableLis
return superTypesList + superTypesList.flatMap { getSuperTypes(it.owner) }
}
fun getIrFunctionByName(name: Name): IrFunction {
return classOfObject.declarations.firstOrNull { it.descriptor.name == name } as? IrFunction
?: throw NoSuchMethodException("Abstract method $name has not been implemented")
fun getIrFunctionByName(name: Name): IrFunction? {
return classOfObject.declarations.filterIsInstance<IrFunction>().firstOrNull { it.descriptor.name == name }
}
fun getAllStates(): List<Variable> {
val superTypesList = getSuperTypes(classOfObject).map { it.descriptor.thisAsReceiverParameter }
return values.filter { superTypesList.contains(it.descriptor) } + Variable(classOfObject.descriptor.thisAsReceiverParameter, this)
}
override fun getState(descriptor: DeclarationDescriptor): State {
return values.firstOrNull { it.descriptor == descriptor }?.state
?: throw NoSuchElementException("Complex object doesn't contains state with descriptor $descriptor")
override fun getState(descriptor: DeclarationDescriptor): State? {
return values.firstOrNull {
it.descriptor.isSubtypeOf(descriptor) || it.descriptor.hasSameNameAs(descriptor) || it.descriptor == descriptor
}?.state
}
override fun setState(newVar: Variable) {