From dd93deddd7d5c0854972d4dca6a6fa2a1dd836e7 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Thu, 21 Nov 2019 19:49:48 +0300 Subject: [PATCH] Add while loop support in interpreter --- .../common/interpreter/IrInterpreter.kt | 63 +++++----- .../backend/common/interpreter/Utils.kt | 114 ++++++++++++++---- .../backend/common/interpreter/stack/Frame.kt | 7 +- .../backend/common/interpreter/stack/State.kt | 23 ++-- 4 files changed, 138 insertions(+), 69 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt index 3fdeab26dac..36da3be3479 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -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 { 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 { 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 { 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 { 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 { 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 { } 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 + } } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt index 374a25ead53..67b84dc07ed 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt @@ -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 { @@ -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) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Frame.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Frame.kt index 47d35c5dda5..f01bbc6cee5 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Frame.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/Frame.kt @@ -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 = 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 { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt index 31fa5d1283e..75312362c07 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt @@ -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(private var value: IrConst) : State { return "Primitive(value=${value.value})" } } + class Complex(private var classOfObject: IrClass, private val values: MutableList) : 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().firstOrNull { it.descriptor.name == name } } - fun getAllStates(): List { - 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) {