Support sam conversion in ir interpreter
This commit is contained in:
committed by
TeamCityServer
parent
b85a796492
commit
23b315446f
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+26
-4
@@ -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 }
|
||||
}
|
||||
+2
-4
@@ -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
-1
@@ -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 {
|
||||
|
||||
+5
-1
@@ -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
|
||||
}
|
||||
|
||||
+28
-3
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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" })<!>
|
||||
Reference in New Issue
Block a user