From 06ee4ac21fce075c4e4a570e93836acdac4fc583 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Fri, 27 Dec 2019 12:43:30 +0300 Subject: [PATCH] Implement List and MutableList interpretation --- .../common/interpreter/IrInterpreter.kt | 25 +-- .../backend/common/interpreter/Utils.kt | 7 + .../backend/common/interpreter/stack/State.kt | 167 +++++++++++++----- 3 files changed, 128 insertions(+), 71 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 fd3502a1b7b..7cafcc89e16 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 @@ -198,28 +198,6 @@ class IrInterpreter(irModule: IrModuleFragment) { return code } - // TODO extract in Wrapper - 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.value } - - 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(result.getType(irFunction.returnType))) - 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 @@ -255,7 +233,7 @@ class IrInterpreter(irModule: IrModuleFragment) { (extensionReceiver is Wrapper && rawDispatchReceiver == null) val code = when { isWrapper -> ((dispatchReceiver ?: extensionReceiver) as Wrapper).invoke(irFunction as IrFunctionImpl, newFrame) - irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> calculateIntrinsic(irFunction, newFrame) + irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.invokeStatic(irFunction as IrFunctionImpl, 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) @@ -268,7 +246,6 @@ class IrInterpreter(irModule: IrModuleFragment) { private fun interpretConstructor(constructorCall: IrFunctionAccessExpression, data: Frame): Code { interpretValueParameters(constructorCall, data).also { if (it != Code.NEXT) return it } val valueParameters = constructorCall.symbol.descriptor.valueParameters.map { Variable(it, data.popReturnValue()) }.toMutableList() - val newFrame = InterpreterFrame(valueParameters) val state = Complex(constructorCall.symbol.owner.parent as IrClass, mutableListOf()) newFrame.addVar(Variable(constructorCall.getThisAsReceiver(), state)) //used to set up fields in body 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 08af8e5d6ba..fe534b6c800 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 @@ -18,8 +18,11 @@ 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.types.isArray +import org.jetbrains.kotlin.ir.types.isPrimitiveType import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.isFakeOverride +import org.jetbrains.kotlin.ir.util.isPrimitiveArray import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver @@ -151,4 +154,8 @@ fun getPrimitiveClass(fqName: String, asObject: Boolean = false): Class<*>? { fun IrType.getFqName(): String? { return this.classOrNull?.owner?.fqNameForIrSerialization?.asString() +} + +fun IrType?.isPrimitiveState(): Boolean { + return this?.isPrimitiveType() == true || this?.isArray() == true || this?.isPrimitiveArray() == true } \ No newline at end of file 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 7fef2eefe30..b5224fbefd0 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 @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.common.interpreter.stack import org.jetbrains.kotlin.backend.common.interpreter.* import org.jetbrains.kotlin.backend.common.interpreter.builtins.CompileTimeFunction +import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation import org.jetbrains.kotlin.backend.common.interpreter.builtins.unaryFunctions import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -15,15 +16,12 @@ 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.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.isNullable -import org.jetbrains.kotlin.ir.types.isPrimitiveType +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.isTypeParameter import org.jetbrains.kotlin.name.FqNameUnsafe -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import java.lang.invoke.MethodHandles import java.lang.invoke.MethodType @@ -71,6 +69,26 @@ open class Primitive(var value: T, val type: IrType) : State { .firstOrNull { it.descriptor.valueParameters.map { it.type } == descriptor.valueParameters.map { it.type } } } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Primitive<*> + + if (value != other.value) return false + if (type != other.type) return false + if (fields != other.fields) return false + + return true + } + + override fun hashCode(): Int { + var result = value?.hashCode() ?: 0 + result = 31 * result + type.hashCode() + result = 31 * result + fields.hashCode() + return result + } + override fun toString(): String { return "Primitive(value=$value, type=${type.getName()})" } @@ -121,63 +139,118 @@ class Wrapper(private val type: IrClass, private val wrapperValue: Any) : Comple ?: Class.forName(typeFqName.toString()) fun invoke(irFunction: IrFunctionImpl, data: Frame): Code { - val methodName = irFunction.name.toString() + val methodName = (irFunction.correspondingPropertySymbol?.owner?.name ?: irFunction.name).toString() - val returnClass = irFunction.returnType.getCorrespondingClass(irFunction.isReturnTypePrimitiveAsObject()) - val argsClasses = irFunction.valueParameters.map { it.type.getCorrespondingClass(irFunction.isValueParameterPrimitiveAsObject(it.index)) } - val argsValues = data.getAll().map { (it.state as? Wrapper)?.wrapperValue ?: (it.state as? Primitive<*>)?.value } - - val methodType = MethodType.methodType(returnClass, argsClasses) + val methodType = irFunction.getMethodType() val method = MethodHandles.lookup().findVirtual(receiverClass, methodName, methodType) - val result = method.invokeWithArguments(argsValues) + val result = method.invokeWithArguments(irFunction.getArgs(data)) data.pushReturnValue(result.toState(irFunction.returnType)) return Code.NEXT } - private fun IrType.getCorrespondingClass(asObject: Boolean): Class { - val fqName = this.getFqName() - return when { - this.isPrimitiveType() -> getPrimitiveClass(fqName!!, asObject) - this.isTypeParameter() -> Any::class.java - else -> JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(fqName!!))?.let { Class.forName(it.asSingleFqName().toString()) } - } ?: Class.forName(fqName) - } + companion object { + fun invokeStatic(irFunction: IrFunctionImpl, data: Frame): Code { + val annotation = irFunction.getAnnotation(evaluateIntrinsicAnnotation) + val jvmFileName = Class.forName((annotation.getValueArgument(0) as IrConst<*>).value.toString()) - private fun IrFunctionImpl.getOriginalOverriddenSymbols(): MutableList { - val overriddenSymbols = mutableListOf() - val pool = this.overriddenSymbols.toMutableList() - val iterator = pool.listIterator() - for (symbol in iterator) { - if (symbol.owner.overriddenSymbols.isEmpty()) { - overriddenSymbols += symbol - iterator.remove() - } else { - symbol.owner.overriddenSymbols.forEach { iterator.add(it) } - } + val methodType = irFunction.getMethodType(withExtensionReceiver = true) + val method = MethodHandles.lookup().findStatic(jvmFileName, irFunction.name.asString(), methodType) + + val result = method.invokeWithArguments(irFunction.getArgs(data)) + data.pushReturnValue(result.toState(irFunction.returnType)) + + return Code.NEXT } - if (overriddenSymbols.isEmpty()) overriddenSymbols.add(this.symbol) - return overriddenSymbols - } + private fun IrFunctionImpl.getMethodType(withExtensionReceiver: Boolean = false): MethodType { + val returnClass = this.returnType.getClass(this.isReturnTypePrimitiveAsObject()) + val argsClasses = this.valueParameters.map { it.type.getClass(this.isValueParameterPrimitiveAsObject(it.index)) } - private fun IrFunctionImpl.isReturnTypePrimitiveAsObject(): Boolean { - for (symbol in getOriginalOverriddenSymbols()) { - if (!symbol.owner.returnType.isTypeParameter() && !symbol.owner.returnType.isNullable()) { - return false + val extensionClass = when { + withExtensionReceiver -> this.extensionReceiverParameter?.type?.getClass(this.isExtensionReceiverPrimitiveAsObject()) + else -> null } - } - return true - } - private fun IrFunctionImpl.isValueParameterPrimitiveAsObject(index: Int): Boolean { - for (symbol in getOriginalOverriddenSymbols()) { - if (!symbol.owner.valueParameters[index].type.isTypeParameter() && !symbol.owner.valueParameters[index].type.isNullable()) { - return false - } + return MethodType.methodType(returnClass, listOfNotNull(extensionClass) + argsClasses) + } + + private fun IrFunctionImpl.getArgs(data: Frame): List { + val argsValues = data.getAll().map { (it.state as? Wrapper)?.wrapperValue ?: (it.state as? Any) }.toMutableList() + + if (this.extensionReceiverParameter?.type.isPrimitiveState()) { + val varargState = data.getVariableState(this.symbol.getExtensionReceiver()!!) + argsValues[0] = (varargState as Primitive<*>).value + } + + for ((index, valueParameter) in this.valueParameters.withIndex().reversed()) { + if (valueParameter.type.isPrimitiveState()) { + argsValues[argsValues.size - 1 - index] = (argsValues[argsValues.size - 1 - index] as Primitive<*>).value + } + } + + // TODO if vararg isn't last parameter + // must convert vararg array into separated elements for correct invoke + if (this.valueParameters.lastOrNull()?.varargElementType != null) { + argsValues.removeAt(argsValues.size - 1) + val varargState = data.getVariableState(this.valueParameters.last().descriptor) + val varargValue = (varargState as? Wrapper)?.wrapperValue ?: (varargState as Primitive<*>).value + argsValues.addAll(varargValue as Array) + } + + return argsValues + } + + private fun IrType.getClass(asObject: Boolean): Class { + val fqName = this.getFqName() + return when { + this.isPrimitiveType() -> getPrimitiveClass(fqName!!, asObject) + this.isArray() -> if (asObject) Array::class.javaObjectType else Array::class.java + //TODO primitive array + this.isTypeParameter() -> Any::class.java + else -> JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(fqName!!))?.let { Class.forName(it.asSingleFqName().toString()) } + } ?: Class.forName(fqName) + } + + private fun IrFunctionImpl.getOriginalOverriddenSymbols(): MutableList { + val overriddenSymbols = mutableListOf() + val pool = this.overriddenSymbols.toMutableList() + val iterator = pool.listIterator() + for (symbol in iterator) { + if (symbol.owner.overriddenSymbols.isEmpty()) { + overriddenSymbols += symbol + iterator.remove() + } else { + symbol.owner.overriddenSymbols.forEach { iterator.add(it) } + } + } + + if (overriddenSymbols.isEmpty()) overriddenSymbols.add(this.symbol) + return overriddenSymbols + } + + private fun IrFunctionImpl.isExtensionReceiverPrimitiveAsObject(): Boolean { + return this.extensionReceiverParameter?.type?.isPrimitiveType() == false + } + + private fun IrFunctionImpl.isReturnTypePrimitiveAsObject(): Boolean { + for (symbol in getOriginalOverriddenSymbols()) { + if (!symbol.owner.returnType.isTypeParameter() && !symbol.owner.returnType.isNullable()) { + return false + } + } + return true + } + + private fun IrFunctionImpl.isValueParameterPrimitiveAsObject(index: Int): Boolean { + for (symbol in getOriginalOverriddenSymbols()) { + if (!symbol.owner.valueParameters[index].type.isTypeParameter() && !symbol.owner.valueParameters[index].type.isNullable()) { + return false + } + } + return true } - return true } override fun copy(): State {