JVM IR: invent names for SAM wrapper classes
Similarly to how it's done for callable references. This is needed for IrTypeMapper to be able to correctly compute the name of the SAM wrapper IrClass without resorting to the name of the underlying descriptor (computed in CodegenAnnotatingVisitor)
This commit is contained in:
+23
-2
@@ -11,9 +11,9 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isAnonymousObject
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -160,6 +160,27 @@ class InventNamesForLocalClasses(private val context: JvmBackendContext) : FileL
|
||||
declaration.acceptChildren(this, data.makeLocal())
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: Data) {
|
||||
if (expression.operator == IrTypeOperator.SAM_CONVERSION) {
|
||||
val invokable = expression.argument
|
||||
// Function references (even those that are transformed into SAM wrappers) are handled in visitFunctionReference.
|
||||
if (invokable !is IrFunctionReference && !(invokable is IrBlock && invokable.statements.last() is IrFunctionReference)) {
|
||||
val superClass = expression.typeOperandClassifier.owner as IrClass
|
||||
val superClassName = superClass.fqNameWhenAvailable!!.pathSegments().joinToString("_") { it.toString() }
|
||||
|
||||
// TODO: consider dropping "sam\$$superClassName$0" from the name here.
|
||||
// It's only here to give resemblance of the name generated for SAM wrappers by the old backend. The exact logic
|
||||
// of the old backend is somewhat difficult to emulate consistently, see `SamWrapperCodegen.getWrapperName`.
|
||||
val internalName = inventName(Name.identifier("sam\$$superClassName$0"), data)
|
||||
context.putLocalClassInfo(expression, JvmBackendContext.LocalClassInfo(internalName))
|
||||
invokable.accept(this, data.withName(internalName))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
expression.acceptChildren(this, data)
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement, data: Data) {
|
||||
element.acceptChildren(this, data)
|
||||
}
|
||||
|
||||
+12
-13
@@ -19,9 +19,7 @@ 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.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
@@ -65,7 +63,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
+createFunctionProxyInstance(superType, invokable.statements.last() as IrFunctionReference)
|
||||
} else {
|
||||
// Fall back to materializing an invokable object.
|
||||
+createObjectProxyInstance(superType, invokable)
|
||||
+createObjectProxyInstance(superType, invokable, expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,6 +74,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
private fun implement(
|
||||
superType: IrType,
|
||||
parameters: List<IrType>,
|
||||
attributeOwner: IrAttributeContainer,
|
||||
buildOverride: IrSimpleFunction.(List<IrField>) -> IrBody
|
||||
): IrClass {
|
||||
val superClass = superType.classifierOrFail.owner as IrClass
|
||||
@@ -83,17 +82,15 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
// the `irDelegatingConstructorCall` in the constructor below would need to be modified.
|
||||
assert(superClass.kind == ClassKind.INTERFACE) { "SAM conversion to an abstract class not allowed" }
|
||||
|
||||
val superClassName = superClass.fqNameWhenAvailable!!.pathSegments().joinToString("_") { it.toString() }
|
||||
val subclass = buildClass {
|
||||
// TODO this is not the name some tests (e.g. kt11519) expect
|
||||
name = Name.identifier("sam\$$superClassName\$${cachedImplementations.size + localImplementations.size}")
|
||||
name = Name.special("<sam adapter for ${superClass.fqNameWhenAvailable}>")
|
||||
origin = JvmLoweredDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION
|
||||
}.apply {
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
parent = irClass
|
||||
// TODO convert all type parameters to upper bounds? See the kt11696 test.
|
||||
superTypes += superType
|
||||
}
|
||||
}.copyAttributes(attributeOwner)
|
||||
|
||||
val fields = parameters.mapIndexed { i, fieldType ->
|
||||
subclass.addField {
|
||||
@@ -141,8 +138,8 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
|
||||
// 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): IrClass =
|
||||
implement(superType, listOf(invokableType)) { fields ->
|
||||
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 {
|
||||
@@ -152,12 +149,14 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBuilder.createObjectProxyInstance(superType: IrType, invokable: IrExpression): IrExpression {
|
||||
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)
|
||||
createObjectProxy(superType, invokable.type, attributeOwner)
|
||||
}
|
||||
return if (superType.isNullable() && invokable.type.isNullable()) {
|
||||
val invokableVariable = irTemporary(invokable)
|
||||
@@ -184,7 +183,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
|
||||
// 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 }) { fields ->
|
||||
implement(superType, reference.arguments.mapNotNull { it?.type }, reference) { fields ->
|
||||
context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
|
||||
+irReturn(irCall(reference.symbol).apply {
|
||||
var boundOffset = 0
|
||||
|
||||
Reference in New Issue
Block a user