From f99829ce5ea61413c1eae3ec83f692ebe66c0961 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Wed, 10 Jun 2020 19:10:21 +0300 Subject: [PATCH] Implement trim function interpretation in compile time --- .../common/interpreter/IrInterpreter.kt | 139 +++++++++++------- .../backend/common/interpreter/Utils.kt | 99 +++++++++---- .../interpreter/builtins/CompileTimeUtils.kt | 10 ++ .../builtins/IrBuiltInsMapGenerated.kt | 4 + .../backend/common/interpreter/stack/Frame.kt | 2 - .../backend/common/interpreter/stack/State.kt | 106 +++++++------ generators/evaluate/GenerateBuiltInsMap.kt | 15 ++ 7 files changed, 248 insertions(+), 127 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 0aec814cc37..6405daeb545 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 @@ -5,32 +5,36 @@ package org.jetbrains.kotlin.backend.common.interpreter -import org.jetbrains.kotlin.backend.common.CommonBackendContext -import org.jetbrains.kotlin.backend.common.interpreter.builtins.CompileTimeFunction -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.builtins.* import org.jetbrains.kotlin.backend.common.interpreter.stack.* -import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.statements +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.types.TypeUtils +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType enum class Code(var info: String = "") { NEXT, RETURN, BREAK_LOOP, BREAK_WHEN, CONTINUE, EXCEPTION } -class IrInterpreter(private val irSymbols: Symbols) { +class IrInterpreter(private val irBuiltIns: IrBuiltIns) { fun interpret(expression: IrExpression): IrExpression { - return InterpreterFrame().apply { expression.interpret(this) }.popReturnValue().toIrExpression(expression) + return InterpreterFrame().apply { expression.interpret(this) }.popReturnValue().toIrExpression(irBuiltIns, expression) } private fun IrElement.interpret(data: Frame): Code { @@ -83,18 +87,30 @@ class IrInterpreter(private val irSymbols: Symbols) { } private fun calculateAbstract(irFunction: IrFunction, data: Frame): Code { - return irFunction.body?.interpret(data) - ?: throw NoSuchMethodException("Method \"${irFunction.name}\" wasn't implemented") + if (irFunction.body == null) { + val receiver = data.getVariableState(irFunction.symbol.getReceiver()!!) as Complex + val instance = receiver.instance!! + + val functionImplementation = instance.getIrFunction(irFunction.descriptor) + if (functionImplementation?.body == null) throw NoSuchMethodException("Method \"${irFunction.name}\" wasn't implemented") + val arguments = functionImplementation.valueParameters.map { Variable(it.descriptor, data.getVariableState(it.descriptor)) } + val newFrame = InterpreterFrame() + newFrame.addVar(Variable(functionImplementation.symbol.getReceiver()!!, instance)) + newFrame.addAll(arguments) + return functionImplementation.body!!.interpret(newFrame).apply { data.pushReturnValue(newFrame) } + } + return irFunction.body!!.interpret(data) } private fun calculateOverridden(owner: IrFunctionImpl, data: Frame): Code { - val variableDescriptor = owner.symbol.getThisAsReceiver()!! - val superQualifier = (data.getVariableState(variableDescriptor) as Complex).getSuperQualifier()!! - val overridden = owner.overriddenSymbols.first { it.getThisAsReceiver()?.equalTo(superQualifier.getThisReceiver()) == true } + val variableDescriptor = owner.symbol.getReceiver()!! + val superQualifier = (data.getVariableState(variableDescriptor) as Complex).superType!! + val overridden = owner.overriddenSymbols.first { it.getReceiver()?.equalTo(superQualifier.getReceiver()) == true } val valueParameters = owner.valueParameters.zip(overridden.owner.valueParameters) .map { Variable(it.second.descriptor, data.getVariableState(it.first.descriptor)) } - val newStates = InterpreterFrame((valueParameters + Variable(superQualifier.getThisReceiver(), superQualifier)).toMutableList()) + val newStates = InterpreterFrame(valueParameters.toMutableList()) + newStates.addVar(Variable(overridden.getReceiver()!!, superQualifier)) val overriddenOwner = overridden.owner as IrFunctionImpl val body = overriddenOwner.body @@ -104,26 +120,15 @@ class IrInterpreter(private val irSymbols: Symbols) { }.apply { data.pushReturnValue(newStates) } } - private fun isBuiltIn(irFunction: IrFunction): Boolean { + private fun calculateBuiltIns(irFunction: IrFunction, data: Frame): Code { val descriptor = irFunction.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) } - val signature = CompileTimeFunction( - methodName, - argsType.map { it.toString() }) - return (unaryFunctions[signature] ?: binaryFunctions[signature]) != null - } - - private fun calculateBuiltIns(expression: IrCall, data: Frame): Code { - 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) } val argsValues = data.getAll() .map { it.state } .map { it as? Primitive<*> ?: throw IllegalArgumentException("Builtin functions accept only const args") } - .map { it.getIrConst().value } + .map { it.getValue() } val signature = CompileTimeFunction(methodName, argsType.map { it.toString() }) //todo try catch val result = when (argsType.size) { @@ -136,22 +141,27 @@ class IrInterpreter(private val irSymbols: Symbols) { val function = binaryFunctions[signature] ?: throw NoSuchMethodException("For given function $signature there is no entry in binary map") when (methodName) { - "rangeTo" -> return calculateRangeTo(expression, data) + "rangeTo" -> return calculateRangeTo(irFunction.returnType, data) else -> function.invoke(argsValues[0], argsValues[1]) } } + 3 -> { + val function = ternaryFunctions[signature] + ?: throw NoSuchMethodException("For given function $signature there is no entry in ternary map") + function.invoke(argsValues[0], argsValues[1], argsValues[2]) + } else -> throw UnsupportedOperationException("Unsupported number of arguments") } - data.pushReturnValue(result.toState(expression)) + data.pushReturnValue(result.toState(irBuiltIns)) return Code.NEXT } - private fun calculateRangeTo(expression: IrExpression, data: Frame): Code { - val constructor = expression.type.classOrNull!!.owner.constructors.first() + private fun calculateRangeTo(type: IrType, data: Frame): Code { + val constructor = 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()) } + primitiveValueParameters.forEachIndexed { index, primitive -> constructorCall.putValueArgument(index, primitive.getValue().toIrConst(irBuiltIns)) } val constructorValueParameters = constructor.valueParameters.map { it.descriptor }.zip(primitiveValueParameters) val newFrame = InterpreterFrame(constructorValueParameters.map { Variable(it.first, it.second) }.toMutableList()) @@ -161,6 +171,27 @@ class IrInterpreter(private val irSymbols: Symbols) { return code } + private fun calculateIntrinsic(irFunction: IrFunction, data: Frame): Code { + val annotation = irFunction.getAnnotation(evaluateIntrinsicAnnotation) + val argsValues = data.getAll() + .map { it.state } + .map { it as? Primitive<*> ?: throw IllegalArgumentException("Builtin functions accept only const args") } + .map { it.getValue() } + + val textClass = Class.forName((annotation.getValueArgument(0) as IrConst<*>).value.toString()) + val returnClass = irFunction.returnType.getFqName()!!.let { getPrimitiveClass(it) ?: Class.forName(it) } + val extensionClass = irFunction.extensionReceiverParameter?.type?.getFqName()?.let { getPrimitiveClass(it) ?: Class.forName(it) } + val argsClasses = irFunction.valueParameters.map { + it.descriptor.fqNameSafe.asString().let { getPrimitiveClass(it) ?: Class.forName(it) } + } + + val methodSignature = MethodType.methodType(returnClass, listOfNotNull(extensionClass) + argsClasses) + val method = MethodHandles.lookup().findStatic(textClass, irFunction.name.asString(), methodSignature) + val result = method.invokeWithArguments(argsValues) + data.pushReturnValue(result.toState(irBuiltIns)) + return Code.NEXT + } + private fun interpretValueParameters(parametersContainer: IrMemberAccessExpression, data: Frame): Code { for (i in (parametersContainer.valueArgumentsCount - 1) downTo 0) { val code = parametersContainer.getValueArgument(i)?.interpret(data) ?: Code.NEXT @@ -175,25 +206,28 @@ class IrInterpreter(private val irSymbols: Symbols) { interpretValueParameters(expression, data).also { if (it != Code.NEXT) return it } val valueParameters = expression.symbol.descriptor.valueParameters.map { Variable(it, data.popReturnValue()) } - val rawReceiver = expression.dispatchReceiver ?: expression.extensionReceiver - rawReceiver?.interpret(data)?.also { if (it != Code.NEXT) return it } + // dispatch receiver processing + val rawDispatchReceiver = expression.dispatchReceiver + rawDispatchReceiver?.interpret(data)?.also { if (it != Code.NEXT) return it } + val irCallReceiver = rawDispatchReceiver?.let { data.popReturnValue() } + val irFunctionReceiver = if (expression.superQualifierSymbol == null) irCallReceiver else (irCallReceiver as Complex).superType + // it is important firstly to add receiver, then arguments; this order is used in builtin method call + val irFunction = irFunctionReceiver?.getIrFunction(expression.symbol.descriptor) ?: expression.symbol.owner + irFunctionReceiver?.let { newFrame.addVar(Variable(irFunction.symbol.getDispatchReceiver()!!, it)) } + + // extension receiver processing + val rawExtensionReceiver = expression.extensionReceiver + rawExtensionReceiver?.interpret(data)?.also { if (it != Code.NEXT) return it } + rawExtensionReceiver?.let { newFrame.addVar(Variable(irFunction.symbol.getExtensionReceiver()!!, data.popReturnValue())) } - val receiver = rawReceiver?.let { data.popReturnValue() } - val irFunction = receiver.getIrFunction(expression) - val receiverParameter = irFunction.symbol.getThisAsReceiver() - // it is important firstly to add receiver, then arguments - receiver?.let { newFrame.addVar(Variable(receiverParameter!!, it)) } newFrame.addAll(valueParameters) val code = when { - //irFunction.annotations.any { it.descriptor.containingDeclaration.fqNameSafe == evaluateIntrinsicAnnotation } -> empty - isBuiltIn(irFunction) -> calculateBuiltIns(expression, newFrame) - expression.isAbstract() -> calculateAbstract( - irFunction, - newFrame - ) //abstract check must be before fake overridden check - expression.isFakeOverridden() -> calculateOverridden(irFunction as IrFunctionImpl, newFrame) - else -> (irFunction.body ?: expression.getBody())!!.interpret(newFrame) + irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> calculateIntrinsic(irFunction, newFrame) + irFunction.isAbstract() -> calculateAbstract(irFunction, newFrame) //abstract check must be before fake overridden check + irFunction.isFakeOverridden() -> calculateOverridden(irFunction as IrFunctionImpl, newFrame) + irFunction.body == null -> calculateBuiltIns(irFunction, newFrame) + else -> irFunction.body!!.interpret(newFrame) } data.pushReturnValue(newFrame) return code @@ -208,14 +242,17 @@ class IrInterpreter(private val irSymbols: Symbols) { newFrame.addVar(Variable(constructorCall.getThisAsReceiver(), state)) //used to set up fields in body val code = constructorCall.getBody()?.interpret(newFrame) ?: Code.NEXT if (newFrame.hasReturnValue()) { - state.setSuperQualifier(newFrame.popReturnValue() as Complex) + state.superType = newFrame.popReturnValue() as Complex } data.pushReturnValue(state) return code } private fun interpretConstructorCall(constructorCall: IrConstructorCall, data: Frame): Code { - return interpretConstructor(constructorCall, data) + return interpretConstructor(constructorCall, data).apply { + val instance = data.peekReturnValue() as Complex + instance.setInstanceRecursive(instance) + } } private fun interpretDelegatedConstructorCall(delegatingConstructorCall: IrDelegatingConstructorCall, data: Frame): Code { @@ -261,7 +298,7 @@ class IrInterpreter(private val irSymbols: Symbols) { var code = Code.NEXT while (code == Code.NEXT) { code = expression.condition.interpret(data) - if (code == Code.NEXT && (data.popReturnValue() as? Primitive<*>)?.getIrConst()?.value as? Boolean == true) { + if (code == Code.NEXT && (data.popReturnValue() as? Primitive<*>)?.getValue() as? Boolean == true) { code = expression.body?.interpret(data) ?: Code.NEXT } else { break @@ -281,7 +318,7 @@ class IrInterpreter(private val irSymbols: Symbols) { private fun interpretBranch(expression: IrBranch, data: Frame): Code { var code = expression.condition.interpret(data) - if (code == Code.NEXT && (data.popReturnValue() as? Primitive<*>)?.getIrConst()?.value as? Boolean == true) { + if (code == Code.NEXT && (data.popReturnValue() as? Primitive<*>)?.getValue() as? Boolean == true) { code = expression.result.interpret(data) if (code == Code.NEXT) return Code.BREAK_WHEN } 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 0405c5b71e9..c10f5306879 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,15 +9,22 @@ import org.jetbrains.kotlin.backend.common.interpreter.stack.Complex import org.jetbrains.kotlin.backend.common.interpreter.stack.Primitive import org.jetbrains.kotlin.backend.common.interpreter.stack.State import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.isFakeOverride +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe 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 // main purpose is to get receiver from constructor call @@ -25,14 +32,17 @@ fun IrMemberAccessExpression.getThisAsReceiver(): DeclarationDescriptor { return (this.symbol.descriptor.containingDeclaration as ClassDescriptor).thisAsReceiverParameter } -fun IrFunctionSymbol.getThisAsReceiver(): DeclarationDescriptor? { +fun IrFunctionSymbol.getDispatchReceiver(): DeclarationDescriptor? { return (this.descriptor.containingDeclaration as? ClassDescriptor)?.thisAsReceiverParameter - ?: this.owner.extensionReceiverParameter?.descriptor } -/*fun IrFunctionSymbol.getReceiverDescriptor(): DeclarationDescriptor? { - return this.owner.dispatchReceiverParameter?.descriptor ?: this.owner.extensionReceiverParameter?.descriptor -}*/ +fun IrFunctionSymbol.getExtensionReceiver(): DeclarationDescriptor? { + return this.owner.extensionReceiverParameter?.descriptor +} + +fun IrFunctionSymbol.getReceiver(): DeclarationDescriptor? { + return this.getDispatchReceiver() ?: this.getExtensionReceiver() +} fun IrFunctionAccessExpression.getBody(): IrBody? { return this.symbol.owner.body @@ -52,7 +62,7 @@ private fun DeclarationDescriptor.isSubtypeOf(other: DeclarationDescriptor): Boo } private fun DeclarationDescriptor.hasSameNameAs(other: DeclarationDescriptor): Boolean { - return this is ValueParameterDescriptor && other is ValueParameterDescriptor && this.name == other.name + return this is VariableDescriptor && other is VariableDescriptor && this.name == other.name } fun IrCall.isAbstract(): Boolean { @@ -63,45 +73,76 @@ fun IrCall.isFakeOverridden(): Boolean { return this.symbol.owner.isFakeOverride } -fun State?.getIrFunction(expression: IrCall): IrFunction { - return this.let { it?.getIrFunctionByName(expression.symbol.descriptor.name) } ?: expression.symbol.owner +fun IrFunction.isAbstract(): Boolean { + return (this.symbol.owner as? IrSimpleFunction)?.modality == Modality.ABSTRACT } -fun State.toIrExpression(expression: IrExpression): IrExpression { +fun IrFunction.isFakeOverridden(): Boolean { + return this.symbol.owner.isFakeOverride +} + +fun State.toIrExpression(irBuiltIns: IrBuiltIns, expression: IrExpression): IrExpression { return when (this) { - is Primitive<*> -> this.getIrConst().value.toIrConst(expression) // it is necessary to replace ir offsets + is Primitive<*> -> this.getValue().toIrConst( // this is necessary to replace ir offsets + irBuiltIns, expression.startOffset, expression.endOffset + ) else -> TODO("not supported") } } -fun Any?.toState(expression: IrExpression): State { +fun Any?.toState(irBuiltIns: IrBuiltIns): State { return when (this) { is Complex -> this - else -> this.toIrConst(expression).toPrimitive() + else -> this.toIrConst(irBuiltIns).toPrimitive() } } -private fun Any?.toIrConst(expression: IrExpression): IrConst<*> { +fun Any?.toIrConst(irBuiltIns: IrBuiltIns, startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET): IrConst<*> { return when (this) { - is Boolean -> expression.copyParametersTo(IrConstKind.Boolean, this) - is Char -> expression.copyParametersTo(IrConstKind.Char, this) - is Byte -> expression.copyParametersTo(IrConstKind.Byte, this) - is Short -> expression.copyParametersTo(IrConstKind.Short, this) - is Int -> expression.copyParametersTo(IrConstKind.Int, this) - is Long -> expression.copyParametersTo(IrConstKind.Long, this) - is String -> expression.copyParametersTo(IrConstKind.String, this) - is Float -> expression.copyParametersTo(IrConstKind.Float, this) - is Double -> expression.copyParametersTo(IrConstKind.Double, this) - null -> expression.copyParametersTo(IrConstKind.Null, this) + is Boolean -> IrConstImpl(startOffset, endOffset, irBuiltIns.booleanType, IrConstKind.Boolean, this) + is Char -> IrConstImpl(startOffset, endOffset, irBuiltIns.charType, IrConstKind.Char, this) + is Byte -> IrConstImpl(startOffset, endOffset, irBuiltIns.byteType, IrConstKind.Byte, this) + is Short -> IrConstImpl(startOffset, endOffset, irBuiltIns.shortType, IrConstKind.Short, this) + is Int -> IrConstImpl(startOffset, endOffset, irBuiltIns.intType, IrConstKind.Int, this) + is Long -> IrConstImpl(startOffset, endOffset, irBuiltIns.longType, IrConstKind.Long, this) + is String -> IrConstImpl(startOffset, endOffset, irBuiltIns.stringType, IrConstKind.String, this) + is Float -> IrConstImpl(startOffset, endOffset, irBuiltIns.floatType, IrConstKind.Float, this) + is Double -> IrConstImpl(startOffset, endOffset, irBuiltIns.doubleType, IrConstKind.Double, this) + null -> IrConstImpl(startOffset, endOffset, irBuiltIns.nothingType, IrConstKind.Null, this) else -> throw UnsupportedOperationException("Unsupported const element type $this") } } -@Suppress("UNCHECKED_CAST") -private fun IrExpression.copyParametersTo(kind: IrConstKind, value: Any?): IrConst { - return IrConstImpl(startOffset, endOffset, type, kind, value as T) +fun IrConst.toPrimitive(): Primitive { + return Primitive(this.value, this.type) } -fun IrConst.toPrimitive(): Primitive { - return Primitive(this) +fun IrAnnotationContainer.hasAnnotation(annotation: FqName): Boolean { + if (this.annotations.isNotEmpty()) { + return this.annotations.any { it.symbol.descriptor.containingDeclaration.fqNameSafe == annotation } + } + return false +} + +fun IrAnnotationContainer.getAnnotation(annotation: FqName): IrConstructorCall { + return this.annotations.first { it.symbol.descriptor.containingDeclaration.fqNameSafe == annotation } +} + +fun getPrimitiveClass(fqName: String): Class<*>? { + return when (fqName) { + "kotlin.Boolean" -> Boolean::class.java + "kotlin.Char" -> Char::class.java + "kotlin.Byte" -> Byte::class.java + "kotlin.Short" -> Short::class.java + "kotlin.Int" -> Int::class.java + "kotlin.Long" -> Long::class.java + "kotlin.String" -> String::class.java + "kotlin.Float" -> Float::class.java + "kotlin.Double" -> Double::class.java + else -> null + } +} + +fun IrType.getFqName(): String? { + return this.classOrNull?.owner?.fqNameForIrSerialization?.asString() } \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/CompileTimeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/CompileTimeUtils.kt index 69da24f64bb..466b29c46e8 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/CompileTimeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/CompileTimeUtils.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.common.interpreter.builtins import org.jetbrains.kotlin.name.FqName val compileTimeAnnotation = FqName("kotlin.CompileTimeCalculation") +val evaluateIntrinsicAnnotation = FqName("kotlin.EvaluateIntrinsic") data class CompileTimeFunction(val methodName: String, val args: List) @@ -24,3 +25,12 @@ fun binaryOperation( ): Pair> { return CompileTimeFunction(methodName, listOfNotNull(receiverType, parameterType)) to function as Function2 } + +@Suppress("UNCHECKED_CAST") +fun ternaryOperation( + methodName: String, receiverType: String, firstParameterType: String, secondParameterType: String, function: (T, E, R) -> Any +): Pair> { + return CompileTimeFunction( + methodName, listOfNotNull(receiverType, firstParameterType, secondParameterType) + ) to function as Function3 +} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt index 2587bffb336..75094e086e9 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt @@ -397,3 +397,7 @@ val binaryFunctions = mapOf>( binaryOperation("ANDAND", "Boolean", "Boolean") { a, b -> a && b }, binaryOperation("OROR", "Boolean", "Boolean") { a, b -> a || b } ) +val ternaryFunctions = mapOf>( + ternaryOperation("subSequence", "String", "Int", "Int") { a, b, c -> a.subSequence(b, c) } +) + 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 92675aec81f..9fff20d67c1 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 @@ -29,9 +29,7 @@ interface Frame { fun pushReturnValue(state: State) fun pushReturnValue(frame: Frame) fun peekReturnValue(): State - //fun peekReturnValueOrNull(): State? fun popReturnValue(): State - //fun popReturnValueOrNull(): State? fun hasReturnValue(): Boolean fun copy(): Frame } 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 578b8ef046d..d5164986764 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,30 +5,49 @@ package org.jetbrains.kotlin.backend.common.interpreter.stack +import org.jetbrains.kotlin.backend.common.interpreter.builtins.CompileTimeFunction +import org.jetbrains.kotlin.backend.common.interpreter.builtins.unaryFunctions import org.jetbrains.kotlin.backend.common.interpreter.equalTo import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.expressions.IrConst +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization -import org.jetbrains.kotlin.name.Name interface State { - fun getState(descriptor: DeclarationDescriptor): State? - fun setState(newVar: Variable) - fun copy(): State - fun getIrFunctionByName(name: Name): IrFunction? -} + val fields: MutableList -class Primitive(private var value: IrConst) : State { - fun getIrConst(): IrConst { - return value + fun getState(descriptor: DeclarationDescriptor): State? { + return fields.firstOrNull { it.descriptor.equalTo(descriptor) }?.state } - override fun getState(descriptor: DeclarationDescriptor): State { - throw UnsupportedOperationException("Only complex are allowed") + fun setState(newVar: Variable) + fun copy(): State + fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? +} + +class Primitive(private var value: T, private val type: IrType?) : State { + override val fields: MutableList = mutableListOf() + + init { + if (type != null) { + val properties = type.classOrNull!!.owner.declarations.filterIsInstance() + for (property in properties) { + val propertySignature = CompileTimeFunction(property.name.asString(), listOf(type.getName())) + val propertyValue = unaryFunctions[propertySignature]?.invoke(value) + ?: throw NoSuchMethodException("For given property $propertySignature there is no entry in unary map") + fields.add(Variable(property.descriptor, Primitive(propertyValue, null))) + } + } + } + + private fun IrType?.getName() = this?.classOrNull!!.owner.name.asString() + + fun getValue(): T { + return value } override fun setState(newVar: Variable) { @@ -37,60 +56,57 @@ class Primitive(private var value: IrConst) : State { } override fun copy(): State { - return Primitive(value) + return Primitive(value, type) } - override fun getIrFunctionByName(name: Name): IrFunction? { - //if (this.value.kind != IrConstKind.String) return null - val declarations = value.type.classOrNull!!.owner.declarations.flatMap { - when { - it is IrProperty -> listOf(it, it.getter) - else -> listOf(it) - } - } - return declarations.firstOrNull { it?.descriptor?.name == name } as? IrFunction + override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? { + if (type == null) return null + // must add property's getter to declaration's list because they are not present in ir class for primitives + val declarations = type.classOrNull!!.owner.declarations.map { if (it is IrProperty) it.getter else it } + return declarations.filterIsInstance() + .filter { it.descriptor.name == descriptor.name } + .firstOrNull { it.descriptor.valueParameters.map { it.type } == descriptor.valueParameters.map { it.type } } } override fun toString(): String { - return "Primitive(value=${value.value})" + return "Primitive(value=$value, type=${type?.getName()})" } } -class Complex(private var classOfObject: IrClass, private val values: MutableList) : State { +class Complex(private var type: IrClass, override val fields: MutableList) : State { var superType: Complex? = null + var instance: Complex? = null - fun setSuperQualifier(superType: Complex) { - this.superType = superType + fun setInstanceRecursive(instance: Complex) { + this.instance = instance + superType?.setInstanceRecursive(instance) } - fun getSuperQualifier(): Complex? { - return superType - } - - fun getThisReceiver(): DeclarationDescriptor { - return classOfObject.thisReceiver!!.descriptor - } - - override fun getIrFunctionByName(name: Name): IrFunction? { - return classOfObject.declarations.filterIsInstance().firstOrNull { it.descriptor.name == name } - } - - override fun getState(descriptor: DeclarationDescriptor): State? { - return values.firstOrNull { it.descriptor.equalTo(descriptor) }?.state + fun getReceiver(): DeclarationDescriptor { + return type.thisReceiver!!.descriptor } override fun setState(newVar: Variable) { - when (val oldState = values.firstOrNull { it.descriptor == newVar.descriptor }) { - null -> values.add(newVar) // newVar isn't present in value list - else -> values[values.indexOf(oldState)] = newVar // newVar already present + when (val oldState = fields.firstOrNull { it.descriptor == newVar.descriptor }) { + null -> fields.add(newVar) // newVar isn't present in value list + else -> fields[fields.indexOf(oldState)] = newVar // newVar already present } } + override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? { + return type.declarations.filterIsInstance() + .filter { it.descriptor.name == descriptor.name } + .firstOrNull { it.descriptor.valueParameters.map { it.type } == descriptor.valueParameters.map { it.type } } + } + override fun copy(): State { - return Complex(classOfObject, values).apply { this@apply.superType = this@Complex.superType } + return Complex(type, fields).apply { + this@apply.superType = this@Complex.superType + this@apply.instance = this@Complex.instance + } } override fun toString(): String { - return "Complex(obj='${classOfObject.fqNameForIrSerialization}', super=$superType, values=$values)" + return "Complex(obj='${type.fqNameForIrSerialization}', super=$superType, values=$fields)" } } diff --git a/generators/evaluate/GenerateBuiltInsMap.kt b/generators/evaluate/GenerateBuiltInsMap.kt index 191e4eefac1..7f76f88046e 100644 --- a/generators/evaluate/GenerateBuiltInsMap.kt +++ b/generators/evaluate/GenerateBuiltInsMap.kt @@ -48,6 +48,7 @@ fun generateMap(): String { val unaryOperationsMap = mutableMapOf>() val binaryOperationsMap = mutableMapOf>() + val ternaryOperationsMap = mutableMapOf>() val builtIns = DefaultBuiltIns.Instance //save common built ins @@ -67,6 +68,7 @@ fun generateMap(): String { when (parametersTypes.size) { 1 -> unaryOperationsMap[function] = parametersTypes 2 -> binaryOperationsMap[function] = parametersTypes + 3 -> ternaryOperationsMap[function] = parametersTypes else -> throw IllegalStateException("Couldn't add following method from builtins to operations map: ${function.name} in class ${descriptor.name}") } } @@ -134,6 +136,19 @@ fun generateMap(): String { p.println(binaryBody) p.println(")") + p.println("val ternaryFunctions = mapOf>(") + val ternaryBody = ternaryOperationsMap.entries.joinToString(",\n") { (function, parameters) -> + val receiverType = parameters.last().toString() + val firstParameter = parameters[0].toString() + val secondParameter = parameters[1].toString() + val methodName = "${function.name}" + " ternaryOperation<$receiverType, $firstParameter, $secondParameter>" + + "(\"$methodName\", \"$receiverType\", \"$firstParameter\", \"$secondParameter\") { a, b, c -> a.$methodName(b, c) }" + } + p.println(ternaryBody) + p.println(")") + p.println() + return sb.toString() }