From 23392b73a9ff9b69b0e640a3f09daa951a50adda Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Mon, 31 May 2021 20:08:24 +0300 Subject: [PATCH] Simplify the interpretation logic of methods defined in super Wrapper --- .../kotlin/ir/interpreter/CallInterceptor.kt | 14 ++++----- .../kotlin/ir/interpreter/IrInterpreter.kt | 30 +++++++++---------- .../kotlin/ir/interpreter/state/Complex.kt | 10 +------ .../kotlin/ir/interpreter/state/Wrapper.kt | 16 ++++++++-- .../ir/interpreter/proxy/superWrapper.kt | 18 ++++++++--- 5 files changed, 50 insertions(+), 38 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 a81d9a75853..1f06fd558fe 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 @@ -42,8 +42,8 @@ internal interface CallInterceptor { val interpreter: IrInterpreter fun interceptProxy(irFunction: IrFunction, valueArguments: List, expectedResultClass: Class<*> = Any::class.java): Any? - fun interceptCall(call: IrCall, irFunction: IrFunction, receiver: State?, args: List, defaultAction: () -> Unit) - fun interceptConstructor(constructorCall: IrFunctionAccessExpression, receiver: State, args: List, defaultAction: () -> Unit) + fun interceptCall(call: IrCall, irFunction: IrFunction, args: List, defaultAction: () -> Unit) + fun interceptConstructor(constructorCall: IrFunctionAccessExpression, args: List, defaultAction: () -> Unit) fun interceptGetObjectValue(expression: IrGetObjectValue, defaultAction: () -> Unit) fun interceptEnumEntry(enumEntry: IrEnumEntry, defaultAction: () -> Unit) fun interceptJavaStaticField(expression: IrGetField) @@ -65,8 +65,9 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : }.wrap(this@DefaultCallInterceptor, remainArraysAsIs = true, extendFrom = expectedResultClass) } - override fun interceptCall(call: IrCall, irFunction: IrFunction, receiver: State?, args: List, defaultAction: () -> Unit) { + override fun interceptCall(call: IrCall, irFunction: IrFunction, args: List, defaultAction: () -> Unit) { val isInlineOnly = irFunction.hasAnnotation(FqName("kotlin.internal.InlineOnly")) + val receiver = if (irFunction.dispatchReceiverParameter != null) args[0] else null when { receiver is Wrapper && !isInlineOnly -> receiver.getMethod(irFunction).invokeMethod(irFunction, args) Wrapper.mustBeHandledWithWrapper(irFunction) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, args) @@ -123,14 +124,13 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : } if (invokedFunction.symbol is IrConstructorSymbol) return - this.interceptCall(call, function, dispatchReceiver, listOfNotNull(dispatchReceiver, extensionReceiver) + valueArguments) { + this.interceptCall(call, function, listOfNotNull(dispatchReceiver, extensionReceiver) + valueArguments) { callStack.addInstruction(CompoundInstruction(function)) } } - override fun interceptConstructor( - constructorCall: IrFunctionAccessExpression, receiver: State, args: List, defaultAction: () -> Unit - ) { + override fun interceptConstructor(constructorCall: IrFunctionAccessExpression, args: List, defaultAction: () -> Unit) { + val receiver = callStack.getState(constructorCall.getThisReceiver()) val irConstructor = constructorCall.symbol.owner val irClass = irConstructor.parentAsClass when { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index 9e26d60c37a..ed6791b5876 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -170,18 +170,20 @@ class IrInterpreter private constructor( } private fun interpretCall(call: IrCall) { + fun IrFunction.mustBeHandledWithSuperOf(state: State?): State? { + if (state is Common && this.parent == state.superWrapperClass?.irClass) return state.superWrapperClass + return state + } + val owner = call.symbol.owner // 1. load evaluated arguments from stack - var dispatchReceiver = owner.getDispatchReceiver()?.let { callStack.getState(it) } + val dispatchReceiver = owner.getDispatchReceiver()?.let { callStack.getState(it) } val extensionReceiver = owner.getExtensionReceiver()?.let { callStack.getState(it) } - val valueArguments = owner.valueParameters.map { callStack.getState(it.symbol) } + val valueArguments = owner.valueParameters.map { callStack.getState(it.symbol) }.toMutableList() // 2. get correct function for interpretation val irFunction = dispatchReceiver?.getIrFunctionByIrCall(call) ?: call.symbol.owner - dispatchReceiver = when (irFunction.parent) { - (dispatchReceiver as? Complex)?.superWrapperClass?.irClass -> dispatchReceiver.superWrapperClass - else -> dispatchReceiver - } + val args = listOfNotNull(irFunction.mustBeHandledWithSuperOf(dispatchReceiver), extensionReceiver) + valueArguments callStack.dropSubFrame() // drop intermediate frame that contains variables for default arg evaluation callStack.newFrame(irFunction) @@ -191,13 +193,11 @@ class IrInterpreter private constructor( callStack.addVariable(Variable(irFunction.symbol, KTypeState(call.type, irBuiltIns.anyClass.owner))) // 3. store arguments in memory (remap args on actual names) - val args = mutableListOf() - irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> args.add(Variable(it, receiver)) } } - irFunction.getExtensionReceiver()?.let { args.add(Variable(it, extensionReceiver ?: valueArguments.first())) } - // `shift` is used when extension receiver is actually a parameter in lambda - val shift = if (irFunction.extensionReceiverParameter != null && extensionReceiver == null) 1 else 0 - irFunction.valueParameters.forEachIndexed { i, param -> args.add(Variable(param.symbol, valueArguments[i + shift])) } - args.forEach { callStack.addVariable(it) } + val variables = mutableListOf() + irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> variables.add(Variable(it, receiver)) } } + irFunction.getExtensionReceiver()?.let { variables.add(Variable(it, extensionReceiver ?: valueArguments.removeFirst())) } + irFunction.valueParameters.forEach { variables.add(Variable(it.symbol, valueArguments.removeFirst())) } + variables.forEach { callStack.addVariable(it) } // 4. store reified type parameters irFunction.typeParameters.filter { it.isReified } @@ -213,7 +213,7 @@ class IrInterpreter private constructor( generateSequence(dispatchReceiver.outerClass) { (it.state as? Complex)?.outerClass }.forEach { callStack.addVariable(it) } } - callInterceptor.interceptCall(call, irFunction, dispatchReceiver, listOfNotNull(dispatchReceiver, extensionReceiver) + valueArguments) { + callInterceptor.interceptCall(call, irFunction, args) { callStack.addInstruction(CompoundInstruction(irFunction)) } } @@ -289,7 +289,7 @@ class IrInterpreter private constructor( } superReceiver?.let { callStack.addVariable(Variable(it, objectState)) } - callInterceptor.interceptConstructor(constructorCall, objectState, valueArguments) { + callInterceptor.interceptConstructor(constructorCall, valueArguments) { callStack.pushState(objectState) callStack.addInstruction(CompoundInstruction(constructor)) } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt index 7a85c433c97..5fcd21d85b2 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt @@ -38,14 +38,6 @@ internal interface Complex: State { } } - private fun getThisOrSuperReceiver(superIrClass: IrClass?): IrClass? { - return when { - superIrClass == null -> this.irClass - superIrClass.isInterface -> superIrClass - else -> irClass.superTypes.map { it.classOrNull?.owner }.singleOrNull { it?.isInterface == false } - } - } - fun getOverridden(owner: IrSimpleFunction): IrSimpleFunction { if (owner.parent == superWrapperClass?.irClass) return owner if (!owner.isFakeOverride || owner.body != null || owner.parentAsClass.defaultType.isAny()) return owner @@ -55,7 +47,7 @@ internal interface Complex: State { } override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? { - val receiver = getThisOrSuperReceiver(expression.superQualifierSymbol?.owner) ?: return null + val receiver = expression.superQualifierSymbol?.owner ?: irClass val irFunction = getIrFunctionFromGivenClass(receiver, expression.symbol) ?: return null return getOverridden(irFunction as IrSimpleFunction) } 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 e47514e89fe..be43c3da390 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 @@ -34,12 +34,12 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex when { javaClass == HashMap::class.java -> { val nodeClass = javaClass.declaredClasses.single { it.name.contains("\$Node") } - val mutableMap = irClass.superTypes.mapNotNull { it.classOrNull?.owner }.single { it.isInterface } + val mutableMap = irClass.superTypes.mapNotNull { it.classOrNull?.owner }.single { it.name.asString() == "MutableMap" } javaClassToIrClass += nodeClass to mutableMap.declarations.filterIsInstance().single() } javaClass == LinkedHashMap::class.java -> { val entryClass = javaClass.declaredClasses.single { it.name.contains("\$Entry") } - val mutableMap = irClass.superTypes.mapNotNull { it.classOrNull?.owner }.single { it.isInterface } + val mutableMap = irClass.superTypes.mapNotNull { it.classOrNull?.owner }.single { it.name.asString() == "MutableMap" } javaClassToIrClass += entryClass to mutableMap.declarations.filterIsInstance().single() } javaClass.canonicalName == "java.util.Collections.SingletonMap" -> { @@ -92,7 +92,9 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex // TODO remove later; used for tests only private val intrinsicClasses = setOf( - "kotlin.text.StringBuilder", "kotlin.Pair", "kotlin.collections.HashMap", + "kotlin.text.StringBuilder", "kotlin.Pair", "kotlin.collections.ArrayList", + "kotlin.collections.HashMap", "kotlin.collections.LinkedHashMap", + "kotlin.collections.HashSet", "kotlin.collections.LinkedHashSet", "kotlin.text.RegexOption", "kotlin.text.Regex", "kotlin.text.Regex.Companion", "kotlin.text.MatchGroup", ) @@ -102,6 +104,7 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex "kotlin.collections.arrayListOf(Array)" to "kotlin.collections.CollectionsKt", "Char.kotlin.text.isWhitespace()" to "kotlin.text.CharsKt", "Array.kotlin.collections.toMutableList()" to "kotlin.collections.ArraysKt", + "Array.kotlin.collections.copyToArrayOfAny(Boolean)" to "kotlin.collections.CollectionsKt", ) fun associateJavaClassWithIrClass(javaClass: Class<*>, irClass: IrClass) { @@ -240,6 +243,13 @@ 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.ArrayList" -> ArrayList::class.java + fqName == "kotlin.collections.HashMap" -> HashMap::class.java + fqName == "kotlin.collections.HashSet" -> HashSet::class.java + fqName == "kotlin.collections.LinkedHashMap" -> LinkedHashMap::class.java + fqName == "kotlin.collections.LinkedHashSet" -> LinkedHashSet::class.java + fqName == "kotlin.text.StringBuilder" -> StringBuilder::class.java + fqName == "kotlin.text.Appendable" -> Appendable::class.java fqName == null -> Any::class.java // null if this.isTypeParameter() else -> Class.forName(owner.internalName()) } diff --git a/compiler/testData/ir/interpreter/proxy/superWrapper.kt b/compiler/testData/ir/interpreter/proxy/superWrapper.kt index 68da6b20e48..24444143c35 100644 --- a/compiler/testData/ir/interpreter/proxy/superWrapper.kt +++ b/compiler/testData/ir/interpreter/proxy/superWrapper.kt @@ -1,7 +1,7 @@ import kotlin.collections.* @CompileTimeCalculation -class MyArrayList: ArrayList() { +open class MyArrayList: ArrayList() { var addCounter = 0 override fun add(element: E): Boolean { addCounter++ @@ -10,12 +10,22 @@ class MyArrayList: ArrayList() { } @CompileTimeCalculation -fun test(): String { - val list = MyArrayList() +class MyOtherArrayList: MyArrayList() { + override fun addAll(elements: Collection): Boolean { + return true // do nothing + } +} + +@CompileTimeCalculation +fun test(list: MyArrayList): String { list.add(1) list.add(2) list.add(3) + + val otherList = arrayListOf(4, 5, 6) + list.addAll(otherList) return "Counter " + list.addCounter + "; size " + list.size } -const val testResult = test() +const val testResult1 = test(MyArrayList()) +const val testResult2 = test(MyOtherArrayList())