Implement better workflow of interpretation with callable reference

This commit is contained in:
Ivan Kylchik
2021-04-14 16:38:35 +03:00
committed by TeamCityServer
parent 9a8e0f3fb3
commit e0438d1123
8 changed files with 168 additions and 50 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.interpreter.builtins.interpretBinaryFunction
@@ -23,6 +24,8 @@ 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.*
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.*
@@ -51,19 +54,19 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
private val bodyMap: Map<IdSignature, IrBody> = interpreter.bodyMap
override fun interceptProxy(irFunction: IrFunction, valueArguments: List<Variable>, expectedResultClass: Class<*>): Any? {
return interpreter.withNewCallStack(irFunction) {
this@withNewCallStack.environment.callStack.addInstruction(CompoundInstruction(irFunction))
val irCall = IrCallImpl.fromSymbolOwner(0, 0, irFunction.returnType, irFunction.symbol as IrSimpleFunctionSymbol)
return interpreter.withNewCallStack(irCall) {
this@withNewCallStack.environment.callStack.addInstruction(SimpleInstruction(irCall))
valueArguments.forEach { this@withNewCallStack.environment.callStack.addVariable(it) }
}.wrap(this@DefaultCallInterceptor, remainArraysAsIs = true, extendFrom = expectedResultClass)
}
override fun interceptCall(call: IrCall, irFunction: IrFunction, receiver: State?, args: List<State>, defaultAction: () -> Unit) {
val isInlineOnly = irFunction.hasAnnotation(FqName("kotlin.internal.InlineOnly"))
val originalCallName = call.symbol.owner.name.asString()
when {
receiver is Wrapper && !isInlineOnly -> receiver.getMethod(irFunction).invokeMethod(irFunction, args)
irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, args)
receiver is KFunctionState && originalCallName == "invoke" -> callStack.addInstruction(CompoundInstruction(irFunction))
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)
@@ -73,6 +76,50 @@ 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.getState(it.symbol) ?: args[index++] }
val extensionReceiver = invokedFunction.extensionReceiverParameter?.let { functionState.getState(it.symbol) ?: args[index++] }
val valueArguments = invokedFunction.valueParameters.map { args[index++] }
val function = when (val symbol = invokedFunction.symbol) {
is IrSimpleFunctionSymbol -> {
val irCall = IrCallImpl.fromSymbolOwner(0, 0, invokedFunction.returnType, invokedFunction.symbol as IrSimpleFunctionSymbol)
val actualFunction = dispatchReceiver?.getIrFunctionByIrCall(irCall) ?: invokedFunction
callStack.newFrame(actualFunction, listOf(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, listOf(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])) }
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) }
}
if (invokedFunction.symbol is IrConstructorSymbol) return
this.interceptCall(call, function, dispatchReceiver, listOfNotNull(dispatchReceiver, extensionReceiver) + valueArguments) {
callStack.addInstruction(CompoundInstruction(function))
}
}
override fun interceptConstructor(
constructorCall: IrFunctionAccessExpression, receiver: State, args: List<State>, defaultAction: () -> Unit
) {
@@ -133,7 +180,7 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
this ?: return handleIntrinsicMethods(irFunction)
val argsForMethodInvocation = irFunction.getArgsForMethodInvocation(this@DefaultCallInterceptor, this.type(), args)
withExceptionHandler(environment) {
val result = this.invokeWithArguments(argsForMethodInvocation)
val result = this.invokeWithArguments(argsForMethodInvocation) // TODO if null return Unit
callStack.pushState(result.toState(result.getType(irFunction.returnType)))
}
}
@@ -192,8 +192,8 @@ private fun unfoldStatements(statements: List<IrStatement>, callStack: CallStack
}
private fun unfoldReturn(expression: IrReturn, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(expression)) //2
callStack.addInstruction(CompoundInstruction(expression.value)) //1
callStack.addInstruction(SimpleInstruction(expression))
callStack.addInstruction(CompoundInstruction(expression.value))
}
private fun unfoldSetField(expression: IrSetField, callStack: CallStack) {
@@ -375,11 +375,14 @@ private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: C
}
private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(propertyReference))
propertyReference.dispatchReceiver?.let {
callStack.addInstruction(SimpleInstruction(propertyReference.getter!!.owner.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
val getter = propertyReference.getter!!.owner
callStack.newSubFrame(propertyReference, listOf(SimpleInstruction(propertyReference)))
propertyReference.dispatchReceiver?.let { callStack.addInstruction(SimpleInstruction(getter.dispatchReceiverParameter!!)) }
propertyReference.extensionReceiver?.let { callStack.addInstruction(SimpleInstruction(getter.extensionReceiverParameter!!)) }
propertyReference.extensionReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) }
propertyReference.dispatchReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) }
}
private fun unfoldClassReference(classReference: IrClassReference, callStack: CallStack) {
@@ -79,9 +79,10 @@ class IrInterpreter private constructor(
return callStack.popState().toIrExpression(expression).apply { callStack.dropFrame() }
}
internal fun withNewCallStack(frameOwner: IrFunction, init: IrInterpreter.() -> Any?): State {
internal fun withNewCallStack(call: IrCall, init: IrInterpreter.() -> Any?): State {
return with(IrInterpreter(environment.copyWithNewCallStack(), bodyMap)) {
callStack.newFrame(frameOwner, listOf())
callStack.newFrame(call.symbol.owner, listOf())
callStack.newSubFrame(call, listOf())
init()
while (!callStack.hasNoInstructions()) {
@@ -168,8 +169,8 @@ class IrInterpreter private constructor(
private fun interpretCall(call: IrCall) {
val owner = call.symbol.owner
// 1. load evaluated arguments from stack
var dispatchReceiver = call.dispatchReceiver?.let { callStack.getVariable(owner.getDispatchReceiver()!!) }?.state
val extensionReceiver = call.extensionReceiver?.let { callStack.getVariable(owner.getExtensionReceiver()!!) }?.state
var dispatchReceiver = owner.getDispatchReceiver()?.let { callStack.getVariable(it) }?.state
val extensionReceiver = owner.getExtensionReceiver()?.let { callStack.getVariable(it) }?.state
val valueArguments = owner.valueParameters.map { callStack.getVariable(it.symbol).state }
// 2. get correct function for interpretation
@@ -208,7 +209,7 @@ class IrInterpreter private constructor(
generateSequence(dispatchReceiver.outerClass) { (it.state as? Complex)?.outerClass }.forEach { callStack.addVariable(it) }
}
callInterceptor.interceptCall(call, irFunction, dispatchReceiver, args.map { it.state }) {
callInterceptor.interceptCall(call, irFunction, dispatchReceiver, listOfNotNull(dispatchReceiver, extensionReceiver) + valueArguments) {
callStack.addInstruction(CompoundInstruction(irFunction))
}
}
@@ -241,8 +242,8 @@ class IrInterpreter private constructor(
}
private fun interpretConstructorCall(constructorCall: IrFunctionAccessExpression) {
val valueArguments = constructorCall.symbol.owner.valueParameters.map { callStack.getVariable(it.symbol).state }
val constructor = constructorCall.symbol.owner
val valueArguments = constructor.valueParameters.map { callStack.getVariable(it.symbol).state }
val irClass = constructor.parentAsClass
val objectVar = callStack.getVariable(constructorCall.getThisReceiver())
if (irClass.isLocal) callStack.storeUpValues(objectVar.state as StateWithClosure)
@@ -367,7 +368,7 @@ class IrInterpreter private constructor(
callStack.addInstruction(CompoundInstruction(field.initializer?.expression))
}
// receiver is null, for example, for top level fields
expression.receiver.let { it == null || (it.type.classifierOrNull?.owner as? IrClass)?.isObject == true } -> {
expression.receiver == null -> {
val propertyOwner = field.correspondingPropertySymbol?.owner
val isConst = propertyOwner?.isConst == true || propertyOwner?.backingField?.initializer?.expression is IrConst<*>
assert(isConst) { "Cannot interpret get method on top level non const properties" }
@@ -576,10 +577,13 @@ class IrInterpreter private constructor(
}
private fun interpretPropertyReference(propertyReference: IrPropertyReference) {
val dispatchReceiverSymbol = propertyReference.getter?.owner?.getDispatchReceiver()
val dispatchReceiver = propertyReference.dispatchReceiver?.let { callStack.getVariable(dispatchReceiverSymbol!!) }?.state
// it is impossible to get KProperty2 through ::, so extension receiver is always null
val propertyState = KPropertyState(propertyReference, dispatchReceiver)
val receiverSymbol = propertyReference.getter?.owner?.let { it.getDispatchReceiver() ?: it.getExtensionReceiver() }
// it is impossible to get KProperty2 through ::, so only one receiver can be not null (or both null)
val receiver = (propertyReference.dispatchReceiver ?: propertyReference.extensionReceiver)
?.let { callStack.getVariable(receiverSymbol!!) }?.state
callStack.dropSubFrame()
val propertyState = KPropertyState(propertyReference, receiver)
callStack.pushState(propertyState)
}
@@ -7,12 +7,19 @@ package org.jetbrains.kotlin.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KFunctionState
import org.jetbrains.kotlin.ir.interpreter.toState
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.statements
import kotlin.reflect.*
internal class KFunctionProxy(
@@ -68,17 +75,62 @@ internal class KFunctionProxy(
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ReflectionProxy) return false
if (other !is KFunctionProxy) return false
if (arity != other.arity || isSuspend != other.isSuspend) return false
if (state.fields.zip(other.state.fields).any { (first, second) -> first.state !== second.state }) return false
return state == other.state
return when {
state.irFunction.isAdapter() && other.state.irFunction.isAdapter() -> state.irFunction.eqaulsByAdapteeCall(other.state.irFunction)
else -> state.irFunction == other.state.irFunction
}
}
override fun hashCode(): Int {
return state.hashCode()
return when {
state.irFunction.isAdapter() -> state.irFunction.getAdapteeCallSymbol()!!.hashCode()
else -> state.irFunction.hashCode()
}
}
override fun toString(): String {
return state.toString()
}
private fun IrFunction.isAdapter() = this.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE
private fun IrFunction.getAdapteeCallSymbol(): IrFunctionSymbol? {
if (!this.isAdapter()) return null
val call = when (val statement = this.body!!.statements.single()) {
is IrTypeOperatorCall -> statement.argument
is IrReturn -> statement.value
else -> statement
}
return (call as? IrFunctionAccessExpression)?.symbol
}
private fun IrFunction.eqaulsByAdapteeCall(other: IrFunction): Boolean {
if (!this.isAdapter() || !other.isAdapter()) return false
val statement = this.body!!.statements.single()
val otherStatement = other.body!!.statements.single()
val (thisArg, otherArg) = when (statement) {
is IrTypeOperatorCall -> {
if (otherStatement !is IrTypeOperatorCall) return false
Pair(statement.argument, otherStatement.argument)
}
is IrReturn -> {
if (otherStatement !is IrReturn) return false
Pair(statement.value, otherStatement.value)
}
else -> Pair(statement, otherStatement)
}
if (thisArg !is IrFunctionAccessExpression || otherArg !is IrFunctionAccessExpression) return false
if (thisArg.symbol != otherArg.symbol) return false
return true
}
}
@@ -6,7 +6,10 @@
package org.jetbrains.kotlin.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.getDispatchReceiver
import org.jetbrains.kotlin.ir.interpreter.getExtensionReceiver
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.isNull
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KPropertyState
import org.jetbrains.kotlin.ir.interpreter.toState
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
@@ -22,7 +25,19 @@ internal open class KProperty0Proxy(
override fun call(vararg args: Any?): Any? {
checkArguments(0, args.size)
val actualPropertySymbol = state.property.resolveFakeOverride()?.symbol ?: state.property.symbol
return state.dispatchReceiver!!.getState(actualPropertySymbol)!!
val receiver = state.receiver // null receiver <=> property is on top level
?: return callInterceptor.interceptProxy(getter, emptyList())
val value = receiver.getState(actualPropertySymbol)
return when {
// null value <=> property is extension or Primitive; receiver.isNull() <=> nullable extension
value == null || receiver.isNull() -> {
val receiverSymbol = getter.getDispatchReceiver() ?: getter.getExtensionReceiver()
val receiverVariable = receiverSymbol?.let { Variable(it, receiver) }
callInterceptor.interceptProxy(getter, listOfNotNull(receiverVariable))
}
else -> value
}
}
override fun callBy(args: Map<KParameter, Any?>): Any? {
@@ -48,7 +63,16 @@ internal class KMutableProperty0Proxy(
override fun call(vararg args: Any?) {
checkArguments(1, args.size)
state.dispatchReceiver!!.setField(Variable(state.property.symbol, args.single().toState(propertyType)))
// null receiver <=> property is on top level
assert(state.receiver != null) { "Cannot interpret set method on top level non const properties" }
val receiver = state.receiver!!
val newValue = args.single().toState(propertyType)
setter.getExtensionReceiver()
?.let {
val fieldSymbol = setter.valueParameters.single().symbol
callInterceptor.interceptProxy(setter, listOf(Variable(it, receiver), Variable(fieldSymbol, newValue)))
}
?: receiver.setField(Variable(state.property.symbol, newValue))
}
override fun callBy(args: Map<KParameter, Any?>) {
@@ -7,8 +7,6 @@ package org.jetbrains.kotlin.ir.interpreter.state
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.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.getLastOverridden
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
@@ -16,16 +14,12 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.overrides
internal class Primitive<T>(val value: T, val type: IrType) : State {
override val fields: MutableList<Variable> = mutableListOf()
override val irClass: IrClass = type.classOrNull!!.owner
override fun getState(symbol: IrSymbol): State {
return super.getState(symbol) ?: this
}
override fun getState(symbol: IrSymbol): State? = null
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction {
val owner = expression.symbol.owner
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.ir.interpreter.state.reflection
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.descriptors.IrAbstractFunctionFactory
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.proxy.reflection.KParameterProxy
@@ -61,11 +60,9 @@ internal class KFunctionState(val irFunction: IrFunction, override val irClass:
}
fun getArity(): Int? {
return irClass.name.asString().removePrefix("Function").removePrefix("KFunction").toIntOrNull()
}
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
return if (expression.symbol.owner.name.asString() == "invoke") irFunction else null
return irClass.name.asString()
.removePrefix("Suspend").removePrefix("Function").removePrefix("KFunction")
.toIntOrNull()
}
private fun isLambda(): Boolean = irFunction.name.let { it == Name.special("<anonymous>") || it == Name.special("<no name provided>") }
@@ -16,12 +16,9 @@ import org.jetbrains.kotlin.ir.types.classOrNull
import kotlin.reflect.KParameter
import kotlin.reflect.KType
internal class KPropertyState(
val property: IrProperty, override val irClass: IrClass, val dispatchReceiver: State? = null
) : ReflectionState() {
constructor(propertyReference: IrPropertyReference, dispatchReceiver: State?)
: this(propertyReference.symbol.owner, propertyReference.type.classOrNull!!.owner, dispatchReceiver)
internal class KPropertyState(val property: IrProperty, override val irClass: IrClass, val receiver: State? = null) : ReflectionState() {
constructor(propertyReference: IrPropertyReference, receiver: State?)
: this(propertyReference.symbol.owner, propertyReference.type.classOrNull!!.owner, receiver)
private var _parameters: List<KParameter>? = null
private var _returnType: KType? = null
@@ -30,7 +27,7 @@ internal class KPropertyState(
if (_parameters != null) return _parameters!!
val kParameterIrClass = irClass.getIrClassOfReflectionFromList("parameters")
var index = 0
val instanceParameter = property.getter?.dispatchReceiverParameter?.takeIf { dispatchReceiver == null }
val instanceParameter = property.getter?.dispatchReceiverParameter?.takeIf { receiver == null }
?.let { KParameterProxy(KParameterState(kParameterIrClass, it, index++, KParameter.Kind.INSTANCE), callInterceptor) }
val extensionParameter = property.getter?.extensionReceiverParameter
?.let { KParameterProxy(KParameterState(kParameterIrClass, it, index++, KParameter.Kind.EXTENSION_RECEIVER), callInterceptor) }
@@ -64,14 +61,14 @@ internal class KPropertyState(
other as KPropertyState
if (property != other.property) return false
if (dispatchReceiver != other.dispatchReceiver) return false
if (receiver != other.receiver) return false
return true
}
override fun hashCode(): Int {
var result = property.hashCode()
result = 31 * result + (dispatchReceiver?.hashCode() ?: 0)
result = 31 * result + (receiver?.hashCode() ?: 0)
return result
}