Add while loop support in interpreter
This commit is contained in:
+35
-28
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+88
-26
@@ -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
-2
@@ -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> {
|
||||
|
||||
+10
-13
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user