From b44fd1a6fe8295aed421463ca6e9bfbc1d0c1a7e Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Thu, 3 Jun 2021 02:54:17 +0300 Subject: [PATCH] Simplify logic of interpreting invoke method for lambdas For now will be created separate invoke method that will contains a call to lambda. This way it is clearer and more intuitive --- compiler/ir/ir.interpreter/build.gradle.kts | 2 +- .../kotlin/ir/interpreter/CallInterceptor.kt | 55 +-------- .../kotlin/ir/interpreter/IrInterpreter.kt | 12 +- .../jetbrains/kotlin/ir/interpreter/Utils.kt | 3 +- .../kotlin/ir/interpreter/state/State.kt | 9 +- .../state/reflection/KFunctionState.kt | 105 +++++++++++++++++- .../exceptions/classCastException.kt | 2 + 7 files changed, 120 insertions(+), 68 deletions(-) diff --git a/compiler/ir/ir.interpreter/build.gradle.kts b/compiler/ir/ir.interpreter/build.gradle.kts index 21ebc6991d2..d32113b962b 100644 --- a/compiler/ir/ir.interpreter/build.gradle.kts +++ b/compiler/ir/ir.interpreter/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } dependencies { - compileOnly(project(":compiler:ir.tree")) + compileOnly(project(":compiler:ir.tree.impl")) compileOnly(project(":kotlin-reflect-api")) } 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 4842782f57a..3cedacd04f5 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 @@ -23,10 +23,7 @@ import org.jetbrains.kotlin.ir.interpreter.proxy.wrap import org.jetbrains.kotlin.ir.interpreter.stack.CallStack import org.jetbrains.kotlin.ir.interpreter.stack.Variable import org.jetbrains.kotlin.ir.interpreter.state.* -import org.jetbrains.kotlin.ir.interpreter.state.reflection.KFunctionState import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState -import org.jetbrains.kotlin.ir.interpreter.state.reflection.ReflectionState -import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull @@ -36,7 +33,6 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.util.OperatorNameConventions import java.lang.invoke.MethodHandle internal interface CallInterceptor { @@ -75,8 +71,7 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : receiver is Wrapper && !isInlineOnly -> receiver.getMethod(irFunction).invokeMethod(irFunction, args) Wrapper.mustBeHandledWithWrapper(irFunction) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, args) handleIntrinsicMethods(irFunction) -> return - receiver is KFunctionState && call.symbol.owner.name == OperatorNameConventions.INVOKE -> handleInvoke(call, receiver, args) - receiver is ReflectionState -> Wrapper.getReflectionMethod(irFunction).invokeMethod(irFunction, args) + receiver.mustBeHandledAsReflection(call) -> 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 == null -> irFunction.trySubstituteFunctionBody() ?: irFunction.tryCalculateLazyConst() ?: calculateBuiltIns(irFunction, args) @@ -84,52 +79,6 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : } } - // TODO unite this logic with interpretCall - private fun handleInvoke(call: IrCall, functionState: KFunctionState, args: List) { - val invokedFunction = functionState.irFunction - - var index = 1 // drop first arg that is reference to function - val dispatchReceiver = invokedFunction.dispatchReceiverParameter?.let { functionState.getField(it.symbol) ?: args[index++] } - val extensionReceiver = invokedFunction.extensionReceiverParameter?.let { functionState.getField(it.symbol) ?: args[index++] } - val valueArguments = invokedFunction.valueParameters.map { args[index++] } - - val function = when (val symbol = invokedFunction.symbol) { - is IrSimpleFunctionSymbol -> { - val irCall = IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, invokedFunction.returnType, symbol) - val actualFunction = dispatchReceiver?.getIrFunctionByIrCall(irCall) ?: invokedFunction - callStack.newFrame(actualFunction) - callStack.addInstruction(SimpleInstruction(actualFunction)) - callStack.addVariable(Variable(actualFunction.symbol, KTypeState(call.type, irBuiltIns.anyClass.owner))) - - actualFunction - } - is IrConstructorSymbol -> { - val irConstructorCall = IrConstructorCallImpl.fromSymbolOwner(invokedFunction.returnType, symbol) - callStack.newSubFrame(irConstructorCall) - callStack.addInstruction(SimpleInstruction(irConstructorCall)) - callStack.addVariable(Variable(invokedFunction.parentAsClass.thisReceiver!!.symbol, Common(invokedFunction.parentAsClass))) - - invokedFunction - } - else -> TODO("unsupported symbol $symbol for invoke") - } - - function.dispatchReceiverParameter?.let { callStack.addVariable(Variable(it.symbol, dispatchReceiver!!)) } - function.extensionReceiverParameter?.let { callStack.addVariable(Variable(it.symbol, extensionReceiver!!)) } - function.valueParameters.forEachIndexed { i, param -> callStack.addVariable(Variable(param.symbol, valueArguments[i])) } - if (invokedFunction.symbol is IrConstructorSymbol) return - - callStack.loadUpValues(functionState) - if (extensionReceiver is StateWithClosure) callStack.loadUpValues(extensionReceiver) - if (dispatchReceiver is Complex && function.parentClassOrNull?.isInner == true) { - generateSequence(dispatchReceiver.outerClass) { (it.state as? Complex)?.outerClass }.forEach { callStack.addVariable(it) } - } - - this.interceptCall(call, function, listOfNotNull(dispatchReceiver, extensionReceiver) + valueArguments) { - callStack.addInstruction(CompoundInstruction(function)) - } - } - override fun interceptConstructor(constructorCall: IrFunctionAccessExpression, args: List, defaultAction: () -> Unit) { val receiver = callStack.getState(constructorCall.getThisReceiver()) val irConstructor = constructorCall.symbol.owner @@ -275,4 +224,4 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) : val expression = this.correspondingPropertySymbol?.owner?.backingField?.initializer?.expression return expression?.apply { callStack.addInstruction(CompoundInstruction(this)) } } -} \ No newline at end of file +} 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 d4a0373d19e..02dd0f0d94d 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 @@ -585,18 +585,18 @@ class IrInterpreter private constructor( } private fun interpretFunctionReference(reference: IrFunctionReference) { - val function = KFunctionState(reference) - val irFunction = function.irFunction + val irFunction = reference.symbol.owner val dispatchReceiver = reference.dispatchReceiver?.let { callStack.getState(irFunction.getDispatchReceiver()!!) } val extensionReceiver = reference.extensionReceiver?.let { callStack.getState(irFunction.getExtensionReceiver()!!) } callStack.dropSubFrame() - // receivers in fields are used in comparison of two functions in KFunctionProxy - dispatchReceiver?.let { function.fields += Variable(irFunction.getDispatchReceiver()!!, it) } - extensionReceiver?.let { function.fields += Variable(irFunction.getExtensionReceiver()!!, it) } - function.upValues += function.fields + val function = KFunctionState( + reference, + dispatchReceiver?.let { Variable(irFunction.getDispatchReceiver()!!, it) }, + extensionReceiver?.let { Variable(irFunction.getExtensionReceiver()!!, it) } + ) if (irFunction.isLocal) callStack.storeUpValues(function) callStack.pushState(function) } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt index 48f2a27d179..d44214e3f98 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import java.lang.invoke.MethodType @@ -269,4 +270,4 @@ internal fun IrType.getOnlyName(): String { internal fun IrFieldAccessExpression.accessesTopLevelOrObjectField(): Boolean { return this.receiver == null || (this.receiver?.type?.classifierOrNull?.owner as? IrClass)?.isObject == true -} \ No newline at end of file +} diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/State.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/State.kt index 0190a6542e4..361e140d441 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/State.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/State.kt @@ -5,15 +5,18 @@ package org.jetbrains.kotlin.ir.interpreter.state -import org.jetbrains.kotlin.ir.interpreter.stack.Variable import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment import org.jetbrains.kotlin.ir.interpreter.exceptions.handleUserException +import org.jetbrains.kotlin.ir.interpreter.stack.Variable +import org.jetbrains.kotlin.ir.interpreter.state.reflection.KFunctionState +import org.jetbrains.kotlin.ir.interpreter.state.reflection.ReflectionState import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.util.OperatorNameConventions internal interface State { val fields: MutableList @@ -74,4 +77,8 @@ internal fun State.checkNullability( return null } return this +} + +internal fun State?.mustBeHandledAsReflection(call: IrCall): Boolean { + return this is ReflectionState && !(this is KFunctionState && call.symbol.owner.name == OperatorNameConventions.INVOKE) } \ No newline at end of file diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/reflection/KFunctionState.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/reflection/KFunctionState.kt index 1e4ef5f869c..994e91c9dca 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/reflection/KFunctionState.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/reflection/KFunctionState.kt @@ -5,32 +5,125 @@ package org.jetbrains.kotlin.ir.interpreter.state.reflection +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrBuiltIns -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrFunctionReference +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.expressions.putArgument import org.jetbrains.kotlin.ir.interpreter.CallInterceptor import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KParameterProxy import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeParameterProxy import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy import org.jetbrains.kotlin.ir.interpreter.stack.Variable import org.jetbrains.kotlin.ir.interpreter.state.StateWithClosure +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl +import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols +import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions import kotlin.reflect.KParameter import kotlin.reflect.KType import kotlin.reflect.KTypeParameter -internal class KFunctionState(val irFunction: IrFunction, override val irClass: IrClass) : ReflectionState(), StateWithClosure { +internal class KFunctionState( + val irFunction: IrFunction, override val irClass: IrClass, override val fields: MutableList, +) : ReflectionState(), StateWithClosure { override val upValues: MutableList = mutableListOf() - override val fields: MutableList = mutableListOf() private var _parameters: List? = null private var _returnType: KType? = null private var _typeParameters: List? = null - constructor(functionReference: IrFunctionReference) : this(functionReference.symbol.owner, functionReference.type.classOrNull!!.owner) + private val functionClass: IrClass + val invokeSymbol: IrFunctionSymbol + + init { + val invokeFunction = irClass.declarations.filterIsInstance().single { it.name == OperatorNameConventions.INVOKE } + // TODO do we need new class here? if yes, do we need different names for temp classes? + functionClass = IrFactoryImpl.createClass( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + object : IrDeclarationOriginImpl("TEMP_CLASS_FOR_INTERPRETER") {}, IrClassSymbolImpl(), + Name.identifier("Function\$0"), ClassKind.CLASS, DescriptorVisibilities.PRIVATE, Modality.FINAL + ).apply { + parent = irFunction.parent + } + + functionClass.superTypes += irClass.defaultType + functionClass.declarations += IrFunctionImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + object : IrDeclarationOriginImpl("TEMP_FUNCTION_FOR_INTERPRETER") {}, IrSimpleFunctionSymbolImpl(), + OperatorNameConventions.INVOKE, DescriptorVisibilities.PUBLIC, Modality.FINAL, irFunction.returnType, + isInline = false, isExternal = false, isTailrec = false, isSuspend = false, isOperator = true, isInfix = false, isExpect = false + ).apply impl@{ + parent = functionClass + overriddenSymbols = listOf(invokeFunction.symbol) + + dispatchReceiverParameter = invokeFunction.dispatchReceiverParameter?.deepCopyWithSymbols(initialParent = this) + valueParameters = mutableListOf() + + val call = when (val symbol = irFunction.symbol) { + is IrSimpleFunctionSymbol -> IrCallImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + irFunction.returnType, symbol, irFunction.typeParameters.size, irFunction.valueParameters.size + ) + is IrConstructorSymbol -> IrConstructorCallImpl.fromSymbolOwner(irFunction.returnType, symbol) + else -> TODO("Unsupported symbol $symbol for invoke") + }.apply { + val dispatchParameter = irFunction.dispatchReceiverParameter + val extensionParameter = irFunction.extensionReceiverParameter + + if (dispatchParameter != null && getField(dispatchParameter.symbol) == null) { + dispatchReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchParameter.type, dispatchParameter.symbol) + (this@impl.valueParameters as MutableList).add(dispatchParameter) + } + if (extensionParameter != null && getField(extensionParameter.symbol) == null) { + extensionReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, extensionParameter.type, extensionParameter.symbol) + (this@impl.valueParameters as MutableList).add(extensionParameter) + } + irFunction.valueParameters.forEach { + putArgument(it, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.type, it.symbol)) + (this@impl.valueParameters as MutableList).add(it) + } + } + + body = IrBlockBodyImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, this.returnType, this.symbol, call)) + ) + + invokeSymbol = this.symbol + } + } + + constructor(irFunction: IrFunction, irClass: IrClass) : this(irFunction, irClass, mutableListOf()) + + constructor(functionReference: IrFunctionReference, dispatchReceiver: Variable?, extensionReceiver: Variable?) : this( + functionReference.symbol.owner, + functionReference.type.classOrNull!!.owner, + listOfNotNull(dispatchReceiver, extensionReceiver).toMutableList() + ) { + // receivers are used in comparison of two functions in KFunctionProxy + upValues += fields + } + constructor(irFunction: IrFunction, irBuiltIns: IrBuiltIns) : - this(irFunction, irBuiltIns.kFunctionN(irFunction.valueParameters.size)) + this(irFunction, irBuiltIns.kFunctionN(irFunction.valueParameters.size), mutableListOf()) + + override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? { + if (expression.symbol.owner.name == OperatorNameConventions.INVOKE) return invokeSymbol.owner + return super.getIrFunctionByIrCall(expression) + } fun getParameters(callInterceptor: CallInterceptor): List { if (_parameters != null) return _parameters!! diff --git a/compiler/testData/ir/interpreter/exceptions/classCastException.kt b/compiler/testData/ir/interpreter/exceptions/classCastException.kt index e820402b767..0a25ba5c7d8 100644 --- a/compiler/testData/ir/interpreter/exceptions/classCastException.kt +++ b/compiler/testData/ir/interpreter/exceptions/classCastException.kt @@ -59,6 +59,7 @@ Exception java.lang.ClassCastException: kotlin.Int cannot be cast to kotlin.Stri const val stringList = getIntList>().(classCastException.kt:54) + at ClassCastExceptionKt.stringList.Function$0.invoke(classCastException.kt:0) at StandardKt.kotlin.let(Standard.kt:32) at ClassCastExceptionKt.(classCastException.kt:53)`!>let { it[0].length @@ -66,6 +67,7 @@ Exception java.lang.ClassCastException: kotlin.Int cannot be cast to kotlin.Stri const val nullableStringList = getStringNullableList>().(classCastException.kt) + at ClassCastExceptionKt.nullableStringList.Function$0.invoke(classCastException.kt:0) at StandardKt.kotlin.let(Standard.kt:32) at ClassCastExceptionKt.(classCastException.kt:56)`!>let { it[0].length } const val nullableStringLength =