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:
Alexander Udalov
2019-06-28 15:36:29 +02:00
parent a15575d546
commit 9595c815f1
2 changed files with 35 additions and 15 deletions
@@ -11,9 +11,9 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol 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.util.isAnonymousObject
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -160,6 +160,27 @@ class InventNamesForLocalClasses(private val context: JvmBackendContext) : FileL
declaration.acceptChildren(this, data.makeLocal()) 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) { override fun visitElement(element: IrElement, data: Data) {
element.acceptChildren(this, data) element.acceptChildren(this, data)
} }
@@ -19,9 +19,7 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
@@ -65,7 +63,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLow
+createFunctionProxyInstance(superType, invokable.statements.last() as IrFunctionReference) +createFunctionProxyInstance(superType, invokable.statements.last() as IrFunctionReference)
} else { } else {
// Fall back to materializing an invokable object. // 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( private fun implement(
superType: IrType, superType: IrType,
parameters: List<IrType>, parameters: List<IrType>,
attributeOwner: IrAttributeContainer,
buildOverride: IrSimpleFunction.(List<IrField>) -> IrBody buildOverride: IrSimpleFunction.(List<IrField>) -> IrBody
): IrClass { ): IrClass {
val superClass = superType.classifierOrFail.owner as 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. // the `irDelegatingConstructorCall` in the constructor below would need to be modified.
assert(superClass.kind == ClassKind.INTERFACE) { "SAM conversion to an abstract class not allowed" } assert(superClass.kind == ClassKind.INTERFACE) { "SAM conversion to an abstract class not allowed" }
val superClassName = superClass.fqNameWhenAvailable!!.pathSegments().joinToString("_") { it.toString() }
val subclass = buildClass { val subclass = buildClass {
// TODO this is not the name some tests (e.g. kt11519) expect name = Name.special("<sam adapter for ${superClass.fqNameWhenAvailable}>")
name = Name.identifier("sam\$$superClassName\$${cachedImplementations.size + localImplementations.size}")
origin = JvmLoweredDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION origin = JvmLoweredDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION
}.apply { }.apply {
createImplicitParameterDeclarationWithWrappedDescriptor() createImplicitParameterDeclarationWithWrappedDescriptor()
parent = irClass parent = irClass
// TODO convert all type parameters to upper bounds? See the kt11696 test. // TODO convert all type parameters to upper bounds? See the kt11696 test.
superTypes += superType superTypes += superType
} }.copyAttributes(attributeOwner)
val fields = parameters.mapIndexed { i, fieldType -> val fields = parameters.mapIndexed { i, fieldType ->
subclass.addField { 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: // 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(...) } // class sam$n(private val invokable: F) : Interface { override fun method(...) = invokable(...) }
private fun createObjectProxy(superType: IrType, invokableType: IrType): IrClass = private fun createObjectProxy(superType: IrType, invokableType: IrType, attributeOwner: IrAttributeContainer): IrClass =
implement(superType, listOf(invokableType)) { fields -> implement(superType, listOf(invokableType), attributeOwner) { fields ->
context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) { context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
val invokableClass = invokableType.classifierOrFail.owner as IrClass val invokableClass = invokableType.classifierOrFail.owner as IrClass
+irReturn(irCall(invokableClass.functions.single { it.name == OperatorNameConventions.INVOKE }).apply { +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. // Do not generate a wrapper class for null, it has no invoke() anyway.
if (invokable.isNullConst()) if (invokable.isNullConst())
return invokable return invokable
val implementation = cachedImplementations.getOrPut(superType to invokable.type) { val implementation = cachedImplementations.getOrPut(superType to invokable.type) {
createObjectProxy(superType, invokable.type) createObjectProxy(superType, invokable.type, attributeOwner)
} }
return if (superType.isNullable() && invokable.type.isNullable()) { return if (superType.isNullable() && invokable.type.isNullable()) {
val invokableVariable = irTemporary(invokable) 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 // common, as `Interface { something }` is a local function + a SAM-conversion of a reference
// to it into an implementation. // to it into an implementation.
private fun createFunctionProxy(superType: IrType, reference: IrFunctionReference): IrClass = 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) { context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
+irReturn(irCall(reference.symbol).apply { +irReturn(irCall(reference.symbol).apply {
var boundOffset = 0 var boundOffset = 0