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
This commit is contained in:
Ivan Kylchik
2021-06-03 02:54:17 +03:00
committed by TeamCityServer
parent 06a8156376
commit b44fd1a6fe
7 changed files with 120 additions and 68 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ plugins {
} }
dependencies { dependencies {
compileOnly(project(":compiler:ir.tree")) compileOnly(project(":compiler:ir.tree.impl"))
compileOnly(project(":kotlin-reflect-api")) compileOnly(project(":kotlin-reflect-api"))
} }
@@ -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.CallStack
import org.jetbrains.kotlin.ir.interpreter.stack.Variable import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.* 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.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.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull 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.ir.util.hasAnnotation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
import java.lang.invoke.MethodHandle import java.lang.invoke.MethodHandle
internal interface CallInterceptor { internal interface CallInterceptor {
@@ -75,8 +71,7 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
receiver is Wrapper && !isInlineOnly -> receiver.getMethod(irFunction).invokeMethod(irFunction, args) receiver is Wrapper && !isInlineOnly -> receiver.getMethod(irFunction).invokeMethod(irFunction, args)
Wrapper.mustBeHandledWithWrapper(irFunction) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, args) Wrapper.mustBeHandledWithWrapper(irFunction) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, args)
handleIntrinsicMethods(irFunction) -> return handleIntrinsicMethods(irFunction) -> return
receiver is KFunctionState && call.symbol.owner.name == OperatorNameConventions.INVOKE -> handleInvoke(call, receiver, args) receiver.mustBeHandledAsReflection(call) -> Wrapper.getReflectionMethod(irFunction).invokeMethod(irFunction, 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 receiver is Primitive<*> -> calculateBuiltIns(irFunction, args) // check for js char, js long and get field for primitives
irFunction.body == null -> irFunction.body == null ->
irFunction.trySubstituteFunctionBody() ?: irFunction.tryCalculateLazyConst() ?: calculateBuiltIns(irFunction, args) 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<State>) {
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<State>, defaultAction: () -> Unit) { override fun interceptConstructor(constructorCall: IrFunctionAccessExpression, args: List<State>, defaultAction: () -> Unit) {
val receiver = callStack.getState(constructorCall.getThisReceiver()) val receiver = callStack.getState(constructorCall.getThisReceiver())
val irConstructor = constructorCall.symbol.owner val irConstructor = constructorCall.symbol.owner
@@ -585,18 +585,18 @@ class IrInterpreter private constructor(
} }
private fun interpretFunctionReference(reference: IrFunctionReference) { private fun interpretFunctionReference(reference: IrFunctionReference) {
val function = KFunctionState(reference) val irFunction = reference.symbol.owner
val irFunction = function.irFunction
val dispatchReceiver = reference.dispatchReceiver?.let { callStack.getState(irFunction.getDispatchReceiver()!!) } val dispatchReceiver = reference.dispatchReceiver?.let { callStack.getState(irFunction.getDispatchReceiver()!!) }
val extensionReceiver = reference.extensionReceiver?.let { callStack.getState(irFunction.getExtensionReceiver()!!) } val extensionReceiver = reference.extensionReceiver?.let { callStack.getState(irFunction.getExtensionReceiver()!!) }
callStack.dropSubFrame() 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) if (irFunction.isLocal) callStack.storeUpValues(function)
callStack.pushState(function) callStack.pushState(function)
} }
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import java.lang.invoke.MethodType import java.lang.invoke.MethodType
@@ -5,15 +5,18 @@
package org.jetbrains.kotlin.ir.interpreter.state 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.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
import org.jetbrains.kotlin.ir.interpreter.exceptions.handleUserException 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.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.util.OperatorNameConventions
internal interface State { internal interface State {
val fields: MutableList<Variable> val fields: MutableList<Variable>
@@ -75,3 +78,7 @@ internal fun State.checkNullability(
} }
return this return this
} }
internal fun State?.mustBeHandledAsReflection(call: IrCall): Boolean {
return this is ReflectionState && !(this is KFunctionState && call.symbol.owner.name == OperatorNameConventions.INVOKE)
}
@@ -5,32 +5,125 @@
package org.jetbrains.kotlin.ir.interpreter.state.reflection 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.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrFunction 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.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.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KParameterProxy 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.KTypeParameterProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KTypeProxy
import org.jetbrains.kotlin.ir.interpreter.stack.Variable import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.StateWithClosure 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.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.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
import kotlin.reflect.KParameter import kotlin.reflect.KParameter
import kotlin.reflect.KType import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter 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<Variable>,
) : ReflectionState(), StateWithClosure {
override val upValues: MutableList<Variable> = mutableListOf() override val upValues: MutableList<Variable> = mutableListOf()
override val fields: MutableList<Variable> = mutableListOf()
private var _parameters: List<KParameter>? = null private var _parameters: List<KParameter>? = null
private var _returnType: KType? = null private var _returnType: KType? = null
private var _typeParameters: List<KTypeParameter>? = null private var _typeParameters: List<KTypeParameter>? = 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<IrSimpleFunction>().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) : 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<KParameter> { fun getParameters(callInterceptor: CallInterceptor): List<KParameter> {
if (_parameters != null) return _parameters!! if (_parameters != null) return _parameters!!
@@ -59,6 +59,7 @@ Exception java.lang.ClassCastException: kotlin.Int cannot be cast to kotlin.Stri
const val stringList = getIntList<List<String>>().<!WAS_NOT_EVALUATED: ` const val stringList = getIntList<List<String>>().<!WAS_NOT_EVALUATED: `
Exception java.lang.ClassCastException: kotlin.Int cannot be cast to kotlin.String Exception java.lang.ClassCastException: kotlin.Int cannot be cast to kotlin.String
at ClassCastExceptionKt.stringList.<anonymous>(classCastException.kt:54) at ClassCastExceptionKt.stringList.<anonymous>(classCastException.kt:54)
at ClassCastExceptionKt.stringList.Function$0.invoke(classCastException.kt:0)
at StandardKt.kotlin.let(Standard.kt:32) at StandardKt.kotlin.let(Standard.kt:32)
at ClassCastExceptionKt.<clinit>(classCastException.kt:53)`!>let { at ClassCastExceptionKt.<clinit>(classCastException.kt:53)`!>let {
it[0].length it[0].length
@@ -66,6 +67,7 @@ Exception java.lang.ClassCastException: kotlin.Int cannot be cast to kotlin.Stri
const val nullableStringList = getStringNullableList<List<String>>().<!WAS_NOT_EVALUATED: ` const val nullableStringList = getStringNullableList<List<String>>().<!WAS_NOT_EVALUATED: `
Exception java.lang.NullPointerException Exception java.lang.NullPointerException
at ClassCastExceptionKt.nullableStringList.<anonymous>(classCastException.kt) at ClassCastExceptionKt.nullableStringList.<anonymous>(classCastException.kt)
at ClassCastExceptionKt.nullableStringList.Function$0.invoke(classCastException.kt:0)
at StandardKt.kotlin.let(Standard.kt:32) at StandardKt.kotlin.let(Standard.kt:32)
at ClassCastExceptionKt.<clinit>(classCastException.kt:56)`!>let { it[0].length }<!> at ClassCastExceptionKt.<clinit>(classCastException.kt:56)`!>let { it[0].length }<!>
const val nullableStringLength = <!WAS_NOT_EVALUATED: ` const val nullableStringLength = <!WAS_NOT_EVALUATED: `