From 3b250132d0627dcddb83decb963e8e120ae2c406 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Wed, 19 May 2021 16:15:34 +0300 Subject: [PATCH] Put all logic of working with expect/actual declarations inside Wrapper For now decision of how to work with class or function is made by their signatures. --- .../kotlin/ir/interpreter/CallInterceptor.kt | 25 +++--- .../kotlin/ir/interpreter/state/Wrapper.kt | 87 ++++++++++++------- 2 files changed, 70 insertions(+), 42 deletions(-) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/CallInterceptor.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/CallInterceptor.kt index afc2c08fdd3..98a8c78d960 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/CallInterceptor.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/CallInterceptor.kt @@ -66,11 +66,11 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : val isInlineOnly = irFunction.hasAnnotation(FqName("kotlin.internal.InlineOnly")) when { receiver is Wrapper && !isInlineOnly -> receiver.getMethod(irFunction).invokeMethod(irFunction, args) - irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, args) + Wrapper.mustBeHandledWithWrapper(irFunction) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, args) + handleIntrinsicMethods(irFunction) -> return receiver is KFunctionState && call.symbol.owner.name.asString() == "invoke" -> handleInvoke(call, receiver, args) receiver is ReflectionState -> Wrapper.getReflectionMethod(irFunction).invokeMethod(irFunction, args) receiver is Primitive<*> -> calculateBuiltIns(irFunction, args) // check for js char, js long and get field for primitives - irFunction.body is IrSyntheticBody -> handleIntrinsicMethods(irFunction) irFunction.body == null -> irFunction.trySubstituteFunctionBody() ?: irFunction.tryCalculateLazyConst() ?: calculateBuiltIns(irFunction, args) else -> defaultAction() @@ -128,17 +128,17 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : override fun interceptConstructor( constructorCall: IrFunctionAccessExpression, receiver: State, args: List, defaultAction: () -> Unit ) { - val constructor = constructorCall.symbol.owner - val irClass = constructor.parentAsClass + val irConstructor = constructorCall.symbol.owner + val irClass = irConstructor.parentAsClass when { - irClass.hasAnnotation(evaluateIntrinsicAnnotation) || irClass.fqNameWhenAvailable!!.startsWith(Name.identifier("java")) -> { - Wrapper.getConstructorMethod(constructor).invokeMethod(constructor, args) + Wrapper.mustBeHandledWithWrapper(irClass) || irClass.fqNameWhenAvailable!!.startsWith(Name.identifier("java")) -> { + Wrapper.getConstructorMethod(irConstructor).invokeMethod(irConstructor, args) if (constructorCall !is IrConstructorCall) (receiver as Common).superWrapperClass = callStack.popState() as Wrapper } irClass.defaultType.isArray() || irClass.defaultType.isPrimitiveArray() -> { // array constructor doesn't have body so must be treated separately - callStack.addVariable(Variable(constructor.symbol, KTypeState(constructorCall.type, irBuiltIns.anyClass.owner))) - handleIntrinsicMethods(constructor) + callStack.addVariable(Variable(irConstructor.symbol, KTypeState(constructorCall.type, irBuiltIns.anyClass.owner))) + assert(handleIntrinsicMethods(irConstructor)) { "Unsupported intrinsic constructor: ${irConstructor.render()}" } } irClass.defaultType.isUnsignedType() -> { val propertySymbol = irClass.declarations.single { it is IrProperty }.symbol @@ -151,7 +151,7 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : override fun interceptGetObjectValue(expression: IrGetObjectValue, defaultAction: () -> Unit) { val objectClass = expression.symbol.owner when { - objectClass.hasAnnotation(evaluateIntrinsicAnnotation) -> { + Wrapper.mustBeHandledWithWrapper(objectClass) -> { val result = Wrapper.getCompanionObject(objectClass) environment.mapOfObjects[expression.symbol] = result callStack.pushState(result) @@ -162,12 +162,11 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : override fun interceptEnumEntry(enumEntry: IrEnumEntry, defaultAction: () -> Unit) { val enumClass = enumEntry.symbol.owner.parentAsClass - when { - enumClass.hasAnnotation(evaluateIntrinsicAnnotation) -> { + Wrapper.mustBeHandledWithWrapper(enumClass) -> { val enumEntryName = enumEntry.name.asString().toState(environment.irBuiltIns.stringType) val valueOfFun = enumClass.declarations.single { it.nameForIrSerialization.asString() == "valueOf" } as IrFunction - Wrapper.getEnumEntry(enumClass)!!.invokeMethod(valueOfFun, listOf(enumEntryName)) + Wrapper.getEnumEntry(enumClass).invokeMethod(valueOfFun, listOf(enumEntryName)) environment.mapOfEnums[enumEntry.symbol] = callStack.popState() as Complex } else -> defaultAction() @@ -178,7 +177,7 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : val field = expression.symbol.owner assert(field.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && field.isStatic) assert(field.initializer?.expression !is IrConst<*>) - callStack.pushState(Wrapper.getStaticGetter(field)!!.invokeWithArguments().toState(field.type)) + callStack.pushState(Wrapper.getStaticGetter(field).invokeWithArguments().toState(field.type)) } private fun MethodHandle?.invokeMethod(irFunction: IrFunction, args: List) { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Wrapper.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Wrapper.kt index dd0ebe93a9e..e47514e89fe 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Wrapper.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Wrapper.kt @@ -6,20 +6,13 @@ package org.jetbrains.kotlin.ir.interpreter.state import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrField -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.interpreter.* -import org.jetbrains.kotlin.ir.interpreter.evaluateIntrinsicAnnotation import org.jetbrains.kotlin.ir.interpreter.stack.Variable import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable -import org.jetbrains.kotlin.ir.util.isInterface -import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import java.lang.invoke.MethodHandle import java.lang.invoke.MethodHandles @@ -54,7 +47,12 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex javaClassToIrClass += AbstractMap.SimpleImmutableEntry::class.java to irClass.declarations.filterIsInstance().single() } } - javaClassToIrClass += value::class.java to irClass + if (javaClassToIrClass[value::class.java].let { it == null || irClass.isSubclassOf(it) }) { + // second condition guarantees that implementation class will not be replaced with its interface + // for example: map will store ArrayList instead of just List + // this is needed for parallel calculations + javaClassToIrClass[value::class.java] = irClass + } } constructor(value: Any) : this(value, javaClassToIrClass[value::class.java]!!) @@ -62,7 +60,6 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? = null fun getMethod(irFunction: IrFunction): MethodHandle? { - if (irFunction.getEvaluateIntrinsicValue()?.isEmpty() == true) return null // this method will handle IntrinsicEvaluator // if function is actually a getter, then use "get${property.name.capitalize()}" as method name val propertyName = (irFunction as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.name?.asString() val propertyCall = listOfNotNull(propertyName, "get${propertyName?.capitalizeAsciiOnly()}") @@ -93,10 +90,47 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex private val companionObjectValue = mapOf("kotlin.text.Regex\$Companion" to Regex.Companion) private val javaClassToIrClass = mutableMapOf, IrClass>() + // TODO remove later; used for tests only + private val intrinsicClasses = setOf( + "kotlin.text.StringBuilder", "kotlin.Pair", "kotlin.collections.HashMap", + "kotlin.text.RegexOption", "kotlin.text.Regex", "kotlin.text.Regex.Companion", "kotlin.text.MatchGroup", + ) + + private val intrinsicFunctionToHandler = mapOf( + "Array.kotlin.collections.asList()" to "kotlin.collections.ArraysKt", + "kotlin.collections.mutableListOf(Array)" to "kotlin.collections.CollectionsKt", + "kotlin.collections.arrayListOf(Array)" to "kotlin.collections.CollectionsKt", + "Char.kotlin.text.isWhitespace()" to "kotlin.text.CharsKt", + "Array.kotlin.collections.toMutableList()" to "kotlin.collections.ArraysKt", + ) + fun associateJavaClassWithIrClass(javaClass: Class<*>, irClass: IrClass) { javaClassToIrClass += javaClass to irClass } + private fun IrDeclarationWithName.getSignature(): String? { + val fqName = this.fqNameWhenAvailable?.asString() + return when (this) { + is IrFunction -> { + val receiver = (dispatchReceiverParameter ?: extensionReceiverParameter)?.type?.getOnlyName()?.let { "$it." } ?: "" + this.valueParameters.joinToString(prefix = "$receiver$fqName(", postfix = ")") { it.type.getOnlyName() } + } + else -> fqName + } + } + + private fun IrFunction.getJvmClassName(): String? { + return intrinsicFunctionToHandler[this.getSignature()] + } + + fun mustBeHandledWithWrapper(declaration: IrDeclarationWithName): Boolean { + val fqName = declaration.fqNameWhenAvailable?.asString() + return when (declaration) { + is IrFunction -> declaration.getSignature() in intrinsicFunctionToHandler + else -> fqName in intrinsicClasses || fqName?.startsWith("java") == true + } + } + fun getReflectionMethod(irFunction: IrFunction): MethodHandle { val receiverClass = irFunction.dispatchReceiverParameter!!.type.getClass(asObject = true) val methodType = irFunction.getMethodType() @@ -115,41 +149,40 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex } fun getCompanionObject(irClass: IrClass): Wrapper { - val objectName = irClass.getEvaluateIntrinsicValue()!! + val objectName = irClass.internalName() val objectValue = companionObjectValue[objectName] ?: throw InternalError("Companion object $objectName cannot be interpreted") return Wrapper(objectValue, irClass) } fun getConstructorMethod(irConstructor: IrFunction): MethodHandle? { - val intrinsicValue = irConstructor.parentAsClass.getEvaluateIntrinsicValue() - if (intrinsicValue == "kotlin.Char" || intrinsicValue == "kotlin.Long") return null + val intrinsicValue = irConstructor.parentAsClass.internalName() + if (intrinsicValue == "kotlin.Char" || intrinsicValue == "kotlin.Long") return null // used in JS, must be handled as intrinsics val methodType = irConstructor.getMethodType() return MethodHandles.lookup().findConstructor(irConstructor.returnType.getClass(true), methodType) } fun getStaticMethod(irFunction: IrFunction): MethodHandle? { - val intrinsicName = irFunction.getEvaluateIntrinsicValue() - if (intrinsicName?.isEmpty() == true) return null - val jvmClassName = Class.forName(intrinsicName!!) + val intrinsicName = irFunction.getJvmClassName() + if (intrinsicName?.isEmpty() != false) return null + val jvmClass = Class.forName(intrinsicName) val methodType = irFunction.getMethodType() - return MethodHandles.lookup().findStatic(jvmClassName, irFunction.name.asString(), methodType) + return MethodHandles.lookup().findStatic(jvmClass, irFunction.name.asString(), methodType) } - fun getStaticGetter(field: IrField): MethodHandle? { + fun getStaticGetter(field: IrField): MethodHandle { val jvmClass = field.parentAsClass.defaultType.getClass(true) val returnType = field.type.let { it.getClass((it as IrSimpleType).hasQuestionMark) } return MethodHandles.lookup().findStaticGetter(jvmClass, field.name.asString(), returnType) } - fun getEnumEntry(enumClass: IrClass): MethodHandle? { - val intrinsicName = enumClass.getEvaluateIntrinsicValue() - if (intrinsicName?.isEmpty() == true) return null - val enumClassName = Class.forName(intrinsicName!!) + fun getEnumEntry(irEnumClass: IrClass): MethodHandle { + val intrinsicName = irEnumClass.internalName() + val jvmEnumClass = Class.forName(intrinsicName) - val methodType = MethodType.methodType(enumClassName, String::class.java) - return MethodHandles.lookup().findStatic(enumClassName, "valueOf", methodType) + val methodType = MethodType.methodType(jvmEnumClass, String::class.java) + return MethodHandles.lookup().findStatic(jvmEnumClass, "valueOf", methodType) } private fun IrFunction.getMethodType(): MethodType { @@ -207,10 +240,6 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex fqName == "kotlin.collections.ListIterator" || fqName == "kotlin.collections.MutableListIterator" -> ListIterator::class.java fqName == "kotlin.collections.Iterator" || fqName == "kotlin.collections.MutableIterator" -> Iterator::class.java fqName == "kotlin.collections.Map.Entry" || fqName == "kotlin.collections.MutableMap.MutableEntry" -> Map.Entry::class.java - fqName == "kotlin.collections.ListIterator" || fqName == "kotlin.collections.MutableListIterator" -> ListIterator::class.java - fqName == "kotlin.collections.HashMap" -> HashMap::class.java - - owner.hasAnnotation(evaluateIntrinsicAnnotation) -> Class.forName(owner!!.getEvaluateIntrinsicValue()) fqName == null -> Any::class.java // null if this.isTypeParameter() else -> Class.forName(owner.internalName()) }