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
@@ -289,6 +289,12 @@ public class IrInterpreterAfterFir2IrTestGenerated extends AbstractIrInterpreter
runTest("compiler/testData/ir/interpreter/safeClassCast.kt");
}
@Test
@TestMetadata("samConversion.kt")
public void testSamConversion() throws Exception {
runTest("compiler/testData/ir/interpreter/samConversion.kt");
}
@Test
@TestMetadata("scopeFunctions.kt")
public void testScopeFunctions() throws Exception {
@@ -289,6 +289,12 @@ public class IrInterpreterAfterPsi2IrTestGenerated extends AbstractIrInterpreter
runTest("compiler/testData/ir/interpreter/safeClassCast.kt");
}
@Test
@TestMetadata("samConversion.kt")
public void testSamConversion() throws Exception {
runTest("compiler/testData/ir/interpreter/samConversion.kt");
}
@Test
@TestMetadata("scopeFunctions.kt")
public void testScopeFunctions() throws Exception {
@@ -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)
}
+10
View File
@@ -136,6 +136,16 @@ public fun <T, C : MutableCollection<in T>> Iterable<T>.toCollection(destination
return destination
}
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(if (this is Collection<*>) this.size else 10), transform)
}
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
for (item in this)
destination.add(transform(item))
return destination
}
internal fun <T> List<T>.optimizeReadOnlyList() = when (size) {
0 -> emptyList()
1 -> listOf(this[0])
+32
View File
@@ -0,0 +1,32 @@
import kotlin.*
import kotlin.collections.*
@CompileTimeCalculation
fun interface IntPredicate {
fun accept(i: Int): Boolean
fun defaultMethod() = 1
}
const val isEven = IntPredicate { it % 2 == 0 }
.<!EVALUATED: `false, true, false, true, false`!>let { predicate -> listOf(1, 2, 3, 4, 5).map { predicate.accept(it) }.joinToString() }<!>
const val isOdd = IntPredicate { it % 2 != 0 }
.<!EVALUATED: `true, false, true, false, true`!>let { predicate -> listOf(1, 2, 3, 4, 5).map { predicate.accept(it) }.joinToString() }<!>
const val callToDefault = IntPredicate { false }.<!EVALUATED: `1`!>defaultMethod()<!>
const val callToString = IntPredicate { false }.<!EVALUATED: `(kotlin.Int) -> kotlin.Boolean`!>toString()<!>
@CompileTimeCalculation
fun interface KRunnable {
fun invoke(): String
}
@CompileTimeCalculation
object OK : () -> String {
override fun invoke(): String = "OK"
}
@CompileTimeCalculation
fun foo(k: KRunnable) = k.invoke()
const val invokeFromObject = <!EVALUATED: `OK`!>foo(OK)<!>
const val invokeFromFunInterface = <!EVALUATED: `OK`!>foo(KRunnable { "OK" })<!>