Support sam conversion in ir interpreter

This commit is contained in:
Ivan Kylchik
2021-08-05 13:12:52 +03:00
committed by TeamCityServer
parent b85a796492
commit 23b315446f
12 changed files with 156 additions and 22 deletions
@@ -11,8 +11,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterTimeOutError
import org.jetbrains.kotlin.ir.interpreter.exceptions.*
import org.jetbrains.kotlin.ir.interpreter.exceptions.handleUserException
import org.jetbrains.kotlin.ir.interpreter.exceptions.verify
import org.jetbrains.kotlin.ir.interpreter.proxy.CommonProxy.Companion.asProxy
@@ -24,6 +23,7 @@ import org.jetbrains.kotlin.ir.interpreter.state.reflection.*
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.util.OperatorNameConventions
internal interface Instruction {
val element: IrElement?
@@ -188,7 +188,7 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
if (irFunction.isLocal) callStack.copyUpValuesFromPreviousFrame()
// 5. store arguments in memory (remap args on actual names)
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> callStack.addVariable(Variable(it, receiver)) } }
irFunction.getDispatchReceiver()?.let { callStack.addVariable(Variable(it, dispatchReceiver!!)) }
irFunction.getExtensionReceiver()?.let { callStack.addVariable(Variable(it, extensionReceiver ?: callStack.getState(it))) }
irFunction.valueParameters.forEachIndexed { i, param -> callStack.addVariable(Variable(param.symbol, valueArguments[i])) }
// `call.type` is used in check cast and emptyArray
@@ -481,7 +481,29 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
else -> callStack.pushState(state)
}
}
else -> TODO("${expression.operator} not implemented")
IrTypeOperator.SAM_CONVERSION -> {
val newState = when {
state.isNull() -> state
state is KFunctionState -> state.apply { this.funInterface = typeOperand }
else -> {
// this case supports implicit conversion of `invoke` method from FunctionN to similar in fun interface
val samClass = typeOperand.classOrNull!!.owner
val samFunction = samClass.getSingleAbstractMethod()
val invokeFunction = state.irClass.declarations.filterIsInstance<IrFunction>()
.first { it.name == OperatorNameConventions.INVOKE && it.valueParameters.size == samFunction.valueParameters.size }
val functionClass = invokeFunction.getLastOverridden().parentAsClass
// receiver will be stored as up value
val dispatchReceiver = Variable(invokeFunction.dispatchReceiverParameter!!.symbol, state)
val newInvoke = invokeFunction.deepCopyWithSymbols(samClass).apply { dispatchReceiverParameter = null }
KFunctionState(newInvoke, functionClass, environment, mutableListOf(dispatchReceiver))
.apply { this.funInterface = typeOperand }
}
}
callStack.pushState(newState)
}
else -> stop { "Type operator ${expression.operator} is not supported for interpretation" }
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.interpreter
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
@@ -272,4 +273,12 @@ internal fun IrFunctionAccessExpression.getSuperEnumCall(): IrEnumConstructorCal
is IrTypeOperatorCall -> (delegatingCall.argument as IrFunctionAccessExpression).getSuperEnumCall()
else -> TODO("$delegatingCall is unexpected")
}
}
internal fun IrFunction.hasFunInterfaceParent(): Boolean {
return this.parentClassOrNull?.isFun == true
}
internal fun IrClass.getSingleAbstractMethod(): IrFunction {
return declarations.filterIsInstance<IrSimpleFunction>().single { it.modality == Modality.ABSTRACT }
}
@@ -215,14 +215,12 @@ class IrCompileTimeChecker(
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: Nothing?): Boolean {
return when (expression.operator) {
IrTypeOperator.INSTANCEOF, IrTypeOperator.NOT_INSTANCEOF,
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT,
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.SAFE_CAST,
IrTypeOperator.IMPLICIT_NOTNULL -> {
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT, IrTypeOperator.IMPLICIT_NOTNULL, IrTypeOperator.SAM_CONVERSION,
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.SAFE_CAST -> {
val operand = expression.typeOperand.classifierOrNull?.owner
if (operand is IrTypeParameter && !visitedStack.contains(operand.parent)) return false
expression.argument.accept(this, data)
}
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> false
else -> false
}
}
@@ -7,9 +7,13 @@ package org.jetbrains.kotlin.ir.interpreter.proxy.reflection
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
import org.jetbrains.kotlin.ir.interpreter.exceptions.verify
import org.jetbrains.kotlin.ir.interpreter.internalName
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.interpreter.state.isSubtypeOf
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KClassState
import org.jetbrains.kotlin.ir.util.defaultType
import kotlin.reflect.*
internal class KClassProxy(
@@ -60,7 +64,9 @@ internal class KClassProxy(
get() = state.classReference.isInline
override fun isInstance(value: Any?): Boolean {
TODO("Not yet implemented")
verify(value is State) { "Cannot interpret `isInstance` method for $value" }
// TODO fix problems with typealias and java classes subtype check
return (value as State).isSubtypeOf(state.classReference.defaultType)
}
override fun equals(other: Any?): Boolean {
@@ -15,8 +15,10 @@ 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.hasTheSameFieldsWith
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KFunctionState
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.statements
import kotlin.reflect.*
@@ -80,7 +82,9 @@ internal class KFunctionProxy(
if (this === other) return true
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
// SAM wrappers for Java do not implement equals
if (this.state.funInterface?.classOrNull?.owner?.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) return this.state === other.state
if (!state.hasTheSameFieldsWith(other.state)) return false
return when {
state.irFunction.isAdapter() && other.state.irFunction.isAdapter() -> state.irFunction.eqaulsByAdapteeCall(other.state.irFunction)
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.fqName
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.util.overrides
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
@@ -21,15 +20,14 @@ internal interface Complex : State {
fun irClassFqName() = irClass.fqName
private fun getIrFunctionFromGivenClass(irClass: IrClass, symbol: IrFunctionSymbol): IrFunction? {
private fun getIrFunctionFromGivenClass(irClass: IrClass, owner: IrFunction): IrFunction? {
val propertyGetters = irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.getter }
val propertySetters = irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.setter }
val functions = irClass.declarations.filterIsInstance<IrFunction>()
return (propertyGetters + propertySetters + functions).firstOrNull {
val owner = symbol.owner
when {
it is IrSimpleFunction && owner is IrSimpleFunction -> it.overrides(owner) || owner.overrides(it)
else -> it == symbol.owner
else -> it == owner
}
}
}
@@ -40,7 +38,7 @@ internal interface Complex : State {
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
val receiver = expression.superQualifierSymbol?.owner ?: irClass
val irFunction = getIrFunctionFromGivenClass(receiver, expression.symbol) ?: return null
val irFunction = getIrFunctionFromGivenClass(receiver, expression.symbol.owner) ?: return null
return (irFunction as IrSimpleFunction).resolveFakeOverride()
}
@@ -63,4 +61,4 @@ internal interface Complex : State {
(variable.state as? StateWithClosure)?.let { callStack.loadUpValues(it) }
}
}
}
}
@@ -66,6 +66,10 @@ internal fun State.isSubtypeOf(other: IrType): Boolean {
return this.type.arraySubtypeCheck(other)
}
if (other.classOrNull?.owner?.isFun == true) {
return this is KFunctionState && this.funInterface?.isSubtypeOfClass(other.classOrNull!!) == true
}
val thisType = this.irClass.defaultType
if (other.isFunction() && thisType.isKFunction()/* TODO || (other.isSuspendFunction && thisType.isKSuspendFunction())*/) {
// KFunction{n} has no super type of Function{n},
@@ -92,6 +96,20 @@ internal fun State.checkNullability(
}
internal fun State?.mustBeHandledAsReflection(call: IrCall): Boolean {
if (call.symbol.owner.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) return false
return this is ReflectionState && !(this is KFunctionState && call.symbol.owner.name == OperatorNameConventions.INVOKE)
}
val owner = call.symbol.owner
if (owner.body != null || owner.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) return false
return this is ReflectionState && !(this is KFunctionState && KFunctionState.isCallToInvokeOrMethodFromFunInterface(call))
}
internal fun State.hasTheSameFieldsWith(other: State): Boolean {
if (this.fields.size != other.fields.size) return false
for (i in 0 until this.fields.size) {
val firstState = this.fields[i].state
val secondState = other.fields[i].state
when {
firstState is Primitive<*> && secondState is Primitive<*> -> if (firstState.value != secondState.value) return false
else -> if (firstState !== secondState) return false
}
}
return true
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.interpreter.state.reflection
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
@@ -16,9 +17,11 @@ 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.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
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.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.name.SpecialNames
@@ -33,6 +36,17 @@ internal class KFunctionState(
override val fields: MutableList<Variable> = mutableListOf()
) : ReflectionState(), StateWithClosure {
override val upValues: MutableList<Variable> = mutableListOf()
var funInterface: IrType? = null
set(value) {
field = value ?: return
val samFunction = value.classOrNull!!.owner.getSingleAbstractMethod()
if (samFunction.extensionReceiverParameter != null) {
// this change of parameter is needed because of difference in `invoke` and sam calls
invokeSymbol.owner.extensionReceiverParameter = invokeSymbol.owner.valueParameters[0]
invokeSymbol.owner.valueParameters = invokeSymbol.owner.valueParameters.drop(1)
}
}
private var _parameters: List<KParameter>? = null
private var _returnType: KType? = null
private var _typeParameters: List<KTypeParameter>? = null
@@ -47,7 +61,7 @@ internal class KFunctionState(
}
companion object {
fun createInvokeFunction(
private fun createInvokeFunction(
irFunction: IrFunction, irClass: IrClass, hasDispatchReceiver: Boolean, hasExtensionReceiver: Boolean
): IrSimpleFunction {
val invokeFunction = irClass.declarations
@@ -86,14 +100,24 @@ internal class KFunctionState(
putArgument(it, it.createGetValue())
newValueParameters += it
}
this@impl.valueParameters = newValueParameters
}
valueParameters = newValueParameters
body = listOf(this.createReturn(call)).wrapWithBlockBody()
}
functionClass.declarations += newFunctionToInvoke
return newFunctionToInvoke
}
private fun isCallToNonAbstractMethodOfFunInterface(expression: IrCall): Boolean {
val owner = expression.symbol.owner
return owner.hasFunInterfaceParent() && owner.modality != Modality.ABSTRACT
}
fun isCallToInvokeOrMethodFromFunInterface(expression: IrCall): Boolean {
val owner = expression.symbol.owner
return owner.name == OperatorNameConventions.INVOKE || owner.hasFunInterfaceParent()
}
}
constructor(
@@ -112,7 +136,8 @@ internal class KFunctionState(
}
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
if (expression.symbol.owner.name == OperatorNameConventions.INVOKE) return invokeSymbol.owner
if (isCallToNonAbstractMethodOfFunInterface(expression)) return expression.symbol.owner.resolveFakeOverride()
if (isCallToInvokeOrMethodFromFunInterface(expression)) return invokeSymbol.owner
return super.getIrFunctionByIrCall(expression)
}