JVM IR: Fixes in SAM lowering
This commit is contained in:
committed by
max-kammerer
parent
829102b3e3
commit
c77fa1ecd6
+93
-67
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
@@ -20,6 +19,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.irArray
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.codegen.PropertyReferenceCodegen
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
@@ -32,10 +32,7 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeProjection
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
@@ -51,9 +48,7 @@ internal val callableReferencePhase = makeIrFilePhase(
|
||||
|
||||
// Originally copied from K/Native
|
||||
internal class CallableReferenceLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
|
||||
// This pass ignores function references used in inline arguments to inline functions references or in SAM conversions.
|
||||
// We also implicitly ignore all suspend function references by only dealing with subclasses of (K)Function and not
|
||||
// (K)SuspendFunction.
|
||||
// This pass ignores suspend function references and function references used in inline arguments to inline functions.
|
||||
private val ignoredFunctionReferences = mutableSetOf<IrFunctionReference>()
|
||||
|
||||
private val IrFunctionReference.isIgnored: Boolean
|
||||
@@ -83,19 +78,6 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
return super.visitFunctionAccess(expression)
|
||||
}
|
||||
|
||||
// Ignore function references handled in SAM conversion
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||
if (expression.operator == IrTypeOperator.SAM_CONVERSION) {
|
||||
val invokable = expression.argument
|
||||
if (invokable is IrFunctionReference) {
|
||||
ignoredFunctionReferences += invokable
|
||||
} else if (invokable is IrBlock && invokable.statements.last() is IrFunctionReference) {
|
||||
ignoredFunctionReferences += invokable.statements.last() as IrFunctionReference
|
||||
}
|
||||
}
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
|
||||
override fun visitBlock(expression: IrBlock): IrExpression {
|
||||
if (!expression.origin.isLambda)
|
||||
return super.visitBlock(expression)
|
||||
@@ -114,10 +96,31 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
return if (expression.isIgnored) expression else FunctionReferenceBuilder(expression).build()
|
||||
}
|
||||
|
||||
private inner class FunctionReferenceBuilder(val irFunctionReference: IrFunctionReference) {
|
||||
private val isLambda = irFunctionReference.origin.isLambda
|
||||
// Handle SAM conversions which wrap a function reference:
|
||||
// class sam$n(private val receiver: R) : Interface { override fun method(...) = receiver.target(...) }
|
||||
//
|
||||
// This avoids materializing an invokable KFunction representing, thus producing one less class.
|
||||
// This is actually very common, as `Interface { something }` is a local function + a SAM-conversion
|
||||
// of a reference to it into an implementation.
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||
if (expression.operator == IrTypeOperator.SAM_CONVERSION) {
|
||||
val invokable = expression.argument
|
||||
val reference = if (invokable is IrFunctionReference) {
|
||||
invokable
|
||||
} else if (invokable is IrBlock && invokable.origin.isLambda && invokable.statements.last() is IrFunctionReference) {
|
||||
invokable.statements.dropLast(1).forEach { it.transform(this, null) }
|
||||
invokable.statements.last() as IrFunctionReference
|
||||
} else {
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
reference.transformChildrenVoid()
|
||||
return FunctionReferenceBuilder(reference, expression.typeOperand).build()
|
||||
}
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
|
||||
private val functionReferenceOrLambda = if (isLambda) context.ir.symbols.lambdaClass else context.ir.symbols.functionReference
|
||||
private inner class FunctionReferenceBuilder(val irFunctionReference: IrFunctionReference, val samSuperType: IrType? = null) {
|
||||
private val isLambda = irFunctionReference.origin.isLambda
|
||||
|
||||
private val callee = irFunctionReference.symbol.owner
|
||||
|
||||
@@ -133,38 +136,46 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
|
||||
private val typeArgumentsMap = irFunctionReference.typeSubstitutionMap
|
||||
|
||||
private val functionSuperClass =
|
||||
samSuperType?.classOrNull ?: context.ir.symbols.getJvmFunctionClass(argumentTypes.size)
|
||||
private val superMethod =
|
||||
functionSuperClass.functions.single { it.owner.modality == Modality.ABSTRACT }
|
||||
private val superType =
|
||||
samSuperType ?: (if (isLambda) context.ir.symbols.lambdaClass else context.ir.symbols.functionReference).owner.typeWith()
|
||||
|
||||
private val functionReferenceClass = buildClass {
|
||||
setSourceRange(irFunctionReference)
|
||||
visibility = Visibilities.LOCAL
|
||||
// A callable reference results in a synthetic class, while a lambda is not synthetic.
|
||||
// We don't produce GENERATED_SAM_IMPLEMENTATION, which is always synthetic.
|
||||
origin = if (isLambda) JvmLoweredDeclarationOrigin.LAMBDA_IMPL else JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
|
||||
name = Name.special("<function reference to ${callee.fqNameWhenAvailable}>")
|
||||
}.apply {
|
||||
parent = currentDeclarationParent
|
||||
superTypes += functionReferenceOrLambda.owner.defaultType
|
||||
superTypes += superType
|
||||
if (samSuperType == null)
|
||||
superTypes += functionSuperClass.typeWith(parameterTypes)
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
copyAttributes(irFunctionReference)
|
||||
}
|
||||
|
||||
fun build(): IrExpression {
|
||||
fun build(): IrExpression = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).irBlock {
|
||||
val constructor = createConstructor()
|
||||
val invokeMethod = createInvokeMethod()
|
||||
createInvokeMethod(
|
||||
if (samSuperType != null && boundReceiver != null) {
|
||||
irTemporary(boundReceiver.second)
|
||||
} else null
|
||||
)
|
||||
|
||||
val functionClass = context.ir.symbols.getJvmFunctionClass(argumentTypes.size)
|
||||
functionReferenceClass.superTypes += functionClass.typeWith(parameterTypes)
|
||||
invokeMethod.overriddenSymbols += functionClass.functions.single { it.owner.name.asString() == "invoke" }
|
||||
|
||||
if (!isLambda) {
|
||||
if (!isLambda && samSuperType == null) {
|
||||
createGetSignatureMethod(functionGetSignature)
|
||||
createGetNameMethod(functionGetName)
|
||||
createGetOwnerMethod(functionGetOwner)
|
||||
}
|
||||
|
||||
return context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol).irBlock(irFunctionReference) {
|
||||
+functionReferenceClass
|
||||
+irCall(constructor.symbol).apply {
|
||||
boundReceiver?.second?.let { putValueArgument(0, it) }
|
||||
}
|
||||
+functionReferenceClass
|
||||
+irCall(constructor.symbol).apply {
|
||||
if (valueArgumentsCount > 0) putValueArgument(0, boundReceiver!!.second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,39 +187,49 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
isPrimary = true
|
||||
}.apply {
|
||||
// Add receiver parameter for bound function references
|
||||
boundReceiver?.first?.let { param ->
|
||||
valueParameters += param.copyTo(
|
||||
irFunction = this,
|
||||
index = valueParameters.size,
|
||||
type = param.type.substitute(typeArgumentsMap)
|
||||
)
|
||||
if (samSuperType == null) {
|
||||
boundReceiver?.first?.let { param ->
|
||||
valueParameters += param.copyTo(
|
||||
irFunction = this,
|
||||
index = 0,
|
||||
type = param.type.substitute(typeArgumentsMap)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Super constructor:
|
||||
// - For SAM references, the super class is Any
|
||||
// - For function references with bound receivers, accepts arity and receiver
|
||||
// - For lambdas and function references without bound receivers, accepts arity
|
||||
val kFunctionRefConstructor = functionReferenceOrLambda.owner.constructors.single {
|
||||
it.valueParameters.size == if (boundReceiver != null) 2 else 1
|
||||
val constructor = if (samSuperType != null) {
|
||||
context.irBuiltIns.anyClass.owner.constructors.single()
|
||||
} else {
|
||||
superType.getClass()!!.constructors.single {
|
||||
it.valueParameters.size == if (boundReceiver != null) 2 else 1
|
||||
}
|
||||
}
|
||||
|
||||
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
|
||||
+irDelegatingConstructorCall(kFunctionRefConstructor).apply {
|
||||
putValueArgument(0, irInt(argumentTypes.size))
|
||||
if (boundReceiver != null)
|
||||
putValueArgument(1, irGet(valueParameters.first()))
|
||||
+irDelegatingConstructorCall(constructor).apply {
|
||||
if (samSuperType == null) {
|
||||
putValueArgument(0, irInt(argumentTypes.size))
|
||||
if (boundReceiver != null)
|
||||
putValueArgument(1, irGet(valueParameters.first()))
|
||||
}
|
||||
}
|
||||
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, context.irBuiltIns.unitType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createInvokeMethod(): IrSimpleFunction =
|
||||
private fun createInvokeMethod(receiverVar: IrValueDeclaration?): IrSimpleFunction =
|
||||
functionReferenceClass.addFunction {
|
||||
name = Name.identifier("invoke")
|
||||
name = superMethod.owner.name
|
||||
returnType = callee.returnType
|
||||
isSuspend = callee.isSuspend
|
||||
}.apply {
|
||||
overriddenSymbols += superMethod
|
||||
dispatchReceiverParameter = parentAsClass.thisReceiver!!.copyTo(this)
|
||||
if (isLambda) createLambdaInvokeMethod() else createFunctionReferenceInvokeMethod()
|
||||
if (isLambda) createLambdaInvokeMethod() else createFunctionReferenceInvokeMethod(receiverVar)
|
||||
}
|
||||
|
||||
// Inline the body of an anonymous function into the generated lambda subclass.
|
||||
@@ -248,7 +269,7 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.createFunctionReferenceInvokeMethod() {
|
||||
private fun IrSimpleFunction.createFunctionReferenceInvokeMethod(receiver: IrValueDeclaration?) {
|
||||
for ((index, argumentType) in argumentTypes.withIndex()) {
|
||||
addValueParameter {
|
||||
name = Name.identifier("p$index")
|
||||
@@ -266,22 +287,31 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
for (parameter in callee.explicitParameters) {
|
||||
when {
|
||||
boundReceiver?.first == parameter ->
|
||||
// Bound receiver parameter
|
||||
irImplicitCast(
|
||||
irGetField(irGet(dispatchReceiverParameter!!), functionReferenceReceiverField),
|
||||
boundReceiver.second.type
|
||||
)
|
||||
// Bound receiver parameter. For function references, this is stored in a field of the superclass.
|
||||
// For sam references, we just capture the value in a local variable and LocalDeclarationsLowering
|
||||
// will put it into a field.
|
||||
if (samSuperType == null)
|
||||
irImplicitCast(
|
||||
irGetField(irGet(dispatchReceiverParameter!!), functionReferenceReceiverField),
|
||||
boundReceiver.second.type
|
||||
)
|
||||
else
|
||||
irGet(receiver!!)
|
||||
|
||||
// If a vararg parameter corresponds to exactly one KFunction argument, which is an array, that array
|
||||
// is forwarded as is. In all other cases, excess arguments are packed into a new array.
|
||||
// is forwarded as is.
|
||||
//
|
||||
// fun f(x: (Int, Array<String>) -> String) = x(0, arrayOf("OK", "FAIL"))
|
||||
// fun g(x: (Int, String, String) -> String) = x(0, "OK", "FAIL")
|
||||
// fun h(i: Int, vararg xs: String) = xs[i]
|
||||
// f(::h) == g(::h)
|
||||
// f(::h)
|
||||
//
|
||||
parameter.isVararg && unboundIndex < argumentTypes.size && parameter.type == valueParameters[unboundIndex].type ->
|
||||
irArray(parameter.type) { addSpread(irGet(valueParameters[unboundIndex++])) }
|
||||
irGet(valueParameters[unboundIndex++])
|
||||
// In all other cases, excess arguments are packed into a new array.
|
||||
//
|
||||
// fun g(x: (Int, String, String) -> String) = x(0, "OK", "FAIL")
|
||||
// f(::h) == g(::h)
|
||||
//
|
||||
parameter.isVararg && (unboundIndex < argumentTypes.size || !parameter.hasDefaultValue()) ->
|
||||
irArray(parameter.type) {
|
||||
(unboundIndex until argumentTypes.size).forEach { +irGet(valueParameters[unboundIndex++]) }
|
||||
@@ -341,11 +371,7 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
companion object {
|
||||
private fun IrBuilderWithScope.kClassReference(classType: IrType) =
|
||||
IrClassReferenceImpl(
|
||||
startOffset,
|
||||
endOffset,
|
||||
context.irBuiltIns.kClassClass.typeWith(),
|
||||
context.irBuiltIns.kClassClass,
|
||||
classType
|
||||
startOffset, endOffset, context.irBuiltIns.kClassClass.typeWith(), context.irBuiltIns.kClassClass, classType
|
||||
)
|
||||
|
||||
private fun IrBuilderWithScope.kClassToJavaClass(kClassReference: IrExpression, context: JvmBackendContext) =
|
||||
|
||||
+44
-126
@@ -20,8 +20,12 @@ import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.copyAttributes
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
@@ -44,7 +48,6 @@ internal val singleAbstractMethodPhase = makeIrFilePhase(
|
||||
class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLoweringPass {
|
||||
override fun lower(irClass: IrClass) {
|
||||
val cachedImplementations = mutableMapOf<Pair<IrType, IrType>, IrClass>()
|
||||
val localImplementations = mutableListOf<IrClass>()
|
||||
irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||
if (expression.operator != IrTypeOperator.SAM_CONVERSION)
|
||||
@@ -53,32 +56,32 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
val invokable = expression.argument.transform(this, null)
|
||||
// Running in the class context, so empty scope means this is a field/anonymous initializer.
|
||||
val scopeOwnerSymbol = currentScope?.scope?.scopeOwnerSymbol ?: irClass.symbol
|
||||
return context.createIrBuilder(scopeOwnerSymbol).irBlock(invokable, null, superType) {
|
||||
// Not an exhaustive check, but enough to cover the most common cases.
|
||||
if (invokable is IrFunctionReference) {
|
||||
// Runnable(::function)
|
||||
+createFunctionProxyInstance(superType, invokable)
|
||||
} else if (invokable is IrBlock && invokable.statements.last() is IrFunctionReference) {
|
||||
// Runnable { lambda }
|
||||
for (statement in invokable.statements.dropLast(1))
|
||||
+statement
|
||||
+createFunctionProxyInstance(superType, invokable.statements.last() as IrFunctionReference)
|
||||
context.createIrBuilder(scopeOwnerSymbol).apply {
|
||||
// Do not generate a wrapper class for null, it has no invoke() anyway.
|
||||
if (invokable.isNullConst())
|
||||
return invokable
|
||||
|
||||
val implementation = cachedImplementations.getOrPut(superType to invokable.type) {
|
||||
createObjectProxy(superType, invokable.type, expression)
|
||||
}
|
||||
|
||||
return if (superType.isNullable() && invokable.type.isNullable()) {
|
||||
irBlock(invokable, null, superType) {
|
||||
val invokableVariable = irTemporary(invokable)
|
||||
val instance = irCall(implementation.constructors.single()).apply {
|
||||
putValueArgument(0, irGet(invokableVariable))
|
||||
}
|
||||
irIfNull(superType, irGet(invokableVariable), irNull(), instance)
|
||||
}
|
||||
} else {
|
||||
// Fall back to materializing an invokable object.
|
||||
+createObjectProxyInstance(superType, invokable, expression)
|
||||
irCall(implementation.constructors.single()).apply { putValueArgument(0, invokable) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct a class that implements the specified SAM interface and contains a bunch of fields
|
||||
// in which the arguments to the constructor are stored:
|
||||
// class sam$n(private val arg0: T0, ...) { override fun method(...) = <whatever buildOverride returns> }
|
||||
private fun implement(
|
||||
superType: IrType,
|
||||
parameters: List<IrType>,
|
||||
attributeOwner: IrAttributeContainer,
|
||||
buildOverride: IrSimpleFunction.(List<IrField>) -> IrBody
|
||||
): IrClass {
|
||||
// Construct a class that wraps an invokable object into an implementation of an interface:
|
||||
// class sam$n(private val invokable: F) : Interface { override fun method(...) = invokable(...) }
|
||||
private fun createObjectProxy(superType: IrType, invokableType: IrType, attributeOwner: IrAttributeContainer): IrClass {
|
||||
val superClass = superType.classifierOrFail.owner as IrClass
|
||||
// The language documentation prohibits casting lambdas to classes, but if it was allowed,
|
||||
// the `irDelegatingConstructorCall` in the constructor below would need to be modified.
|
||||
@@ -94,30 +97,26 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
superTypes += superType
|
||||
}.copyAttributes(attributeOwner)
|
||||
|
||||
val fields = parameters.mapIndexed { i, fieldType ->
|
||||
subclass.addField {
|
||||
name = Name.identifier("arg$i")
|
||||
type = fieldType
|
||||
origin = subclass.origin
|
||||
visibility = Visibilities.PRIVATE
|
||||
}
|
||||
val field = subclass.addField {
|
||||
name = Name.identifier("arg0")
|
||||
type = invokableType
|
||||
origin = subclass.origin
|
||||
visibility = Visibilities.PRIVATE
|
||||
}
|
||||
|
||||
subclass.addConstructor {
|
||||
origin = subclass.origin
|
||||
isPrimary = true
|
||||
}.apply {
|
||||
for (field in fields) {
|
||||
addValueParameter {
|
||||
name = field.name
|
||||
type = field.type
|
||||
origin = subclass.origin
|
||||
}
|
||||
val parameter = addValueParameter {
|
||||
name = field.name
|
||||
type = field.type
|
||||
origin = subclass.origin
|
||||
}
|
||||
|
||||
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
|
||||
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single())
|
||||
for ((field, parameter) in fields.zip(valueParameters))
|
||||
+irSetField(irGet(subclass.thisReceiver!!), field, irGet(parameter))
|
||||
+irSetField(irGet(subclass.thisReceiver!!), field, irGet(parameter))
|
||||
+IrInstanceInitializerCallImpl(startOffset, endOffset, subclass.symbol, context.irBuiltIns.unitType)
|
||||
}
|
||||
}
|
||||
@@ -129,102 +128,21 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
visibility = superMethod.visibility
|
||||
origin = subclass.origin
|
||||
}.apply {
|
||||
overriddenSymbols.add(superMethod.symbol)
|
||||
overriddenSymbols += superMethod.symbol
|
||||
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
|
||||
superMethod.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
|
||||
body = buildOverride(fields)
|
||||
}
|
||||
|
||||
return subclass
|
||||
}
|
||||
|
||||
// Construct a class that wraps an invokable object into an implementation of an interface:
|
||||
// class sam$n(private val invokable: F) : Interface { override fun method(...) = invokable(...) }
|
||||
private fun createObjectProxy(superType: IrType, invokableType: IrType, attributeOwner: IrAttributeContainer): IrClass =
|
||||
implement(superType, listOf(invokableType), attributeOwner) { fields ->
|
||||
context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
|
||||
val invokableClass = invokableType.classifierOrFail.owner as IrClass
|
||||
+irReturn(irCall(invokableClass.functions.single { it.name == OperatorNameConventions.INVOKE }).apply {
|
||||
dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), fields[0])
|
||||
val invokableClass = invokableType.classifierOrFail.owner as IrClass
|
||||
body = context.createIrBuilder(symbol).run {
|
||||
irExprBody(irCall(invokableClass.functions.single { it.name == OperatorNameConventions.INVOKE }).apply {
|
||||
dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field)
|
||||
valueParameters.forEachIndexed { i, parameter -> putValueArgument(i, irGet(parameter)) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBuilder.createObjectProxyInstance(
|
||||
superType: IrType, invokable: IrExpression, attributeOwner: IrAttributeContainer
|
||||
): IrExpression {
|
||||
// Do not generate a wrapper class for null, it has no invoke() anyway.
|
||||
if (invokable.isNullConst())
|
||||
return invokable
|
||||
val implementation = cachedImplementations.getOrPut(superType to invokable.type) {
|
||||
createObjectProxy(superType, invokable.type, attributeOwner)
|
||||
}
|
||||
return if (superType.isNullable() && invokable.type.isNullable()) {
|
||||
val invokableVariable = irTemporary(invokable)
|
||||
val instance = irCall(implementation.constructors.single()).apply { putValueArgument(0, irGet(invokableVariable)) }
|
||||
irIfNull(superType, irGet(invokableVariable), irNull(), instance)
|
||||
} else {
|
||||
irCall(implementation.constructors.single()).apply { putValueArgument(0, invokable) }
|
||||
}
|
||||
}
|
||||
|
||||
// This lowering is located after LocalDeclarationsLowering. On the one hand, this allows us to cache
|
||||
// object-wrapping classes without checking if the types are local declarations or not. On the other hand,
|
||||
// this means we need to manually create fields for all bound arguments when wrapping callable references.
|
||||
private val IrFunctionReference.arguments: List<IrExpression?>
|
||||
get() = listOf(dispatchReceiver).filter { symbol.owner.dispatchReceiverParameter != null } +
|
||||
listOf(extensionReceiver).filter { symbol.owner.extensionReceiverParameter != null } +
|
||||
(0 until valueArgumentsCount).map(::getValueArgument)
|
||||
|
||||
// Construct a class that forwards method calls to an existing function/method:
|
||||
// class sam$n(private val receiver: R) : Interface { override fun method(...) = receiver.target(...) }
|
||||
//
|
||||
// Unlike the above variant, this avoids materializing an invokable KFunction representing
|
||||
// the target in CallableReferenceLowering, thus producing one less class. This is actually very
|
||||
// common, as `Interface { something }` is a local function + a SAM-conversion of a reference
|
||||
// to it into an implementation.
|
||||
private fun createFunctionProxy(superType: IrType, reference: IrFunctionReference): IrClass =
|
||||
implement(superType, reference.arguments.mapNotNull { it?.type }, reference) { fields ->
|
||||
context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
|
||||
+irReturn(irCall(reference.symbol).apply {
|
||||
var boundOffset = 0
|
||||
var offset = 0
|
||||
val arguments = reference.arguments.map {
|
||||
if (it != null)
|
||||
irGetField(irGet(dispatchReceiverParameter!!), fields[boundOffset++])
|
||||
else
|
||||
irGet(valueParameters[offset++])
|
||||
}.toMutableList()
|
||||
if (reference.symbol.owner.dispatchReceiverParameter != null) {
|
||||
dispatchReceiver = arguments.removeAt(0)
|
||||
}
|
||||
if (reference.symbol.owner.extensionReceiverParameter != null) {
|
||||
extensionReceiver = arguments.removeAt(0)
|
||||
}
|
||||
arguments.forEachIndexed(::putValueArgument)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBuilder.createFunctionProxyInstance(superType: IrType, reference: IrFunctionReference): IrExpression {
|
||||
val implementation = createFunctionProxy(superType, reference)
|
||||
if (reference.origin == IrStatementOrigin.ANONYMOUS_FUNCTION || reference.origin == IrStatementOrigin.LAMBDA) {
|
||||
val target = reference.symbol.owner
|
||||
implementation.functions.single().apply {
|
||||
annotations.addAll(target.annotations)
|
||||
valueParameters.forEachIndexed { i, p ->
|
||||
p.annotations.addAll(target.valueParameters[i].annotations)
|
||||
}
|
||||
}
|
||||
}
|
||||
localImplementations += implementation
|
||||
return irCall(implementation.constructors.single()).apply {
|
||||
reference.arguments.filterNotNull().forEachIndexed(::putValueArgument)
|
||||
}
|
||||
return subclass
|
||||
}
|
||||
})
|
||||
irClass.declarations += cachedImplementations.values
|
||||
irClass.declarations += localImplementations
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
Reference in New Issue
Block a user