From 9595c815f12b7ea0942da0341b9c23ea4031b357 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 28 Jun 2019 15:36:29 +0200 Subject: [PATCH] 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) --- .../jvm/lower/InventNamesForLocalClasses.kt | 25 +++++++++++++++++-- .../jvm/lower/SingleAbstractMethodLowering.kt | 25 +++++++++---------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InventNamesForLocalClasses.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InventNamesForLocalClasses.kt index 28ef3cca083..04f816fab6c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InventNamesForLocalClasses.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InventNamesForLocalClasses.kt @@ -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) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SingleAbstractMethodLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SingleAbstractMethodLowering.kt index b74a07fcd9a..10302b5ad69 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SingleAbstractMethodLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SingleAbstractMethodLowering.kt @@ -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, + attributeOwner: IrAttributeContainer, buildOverride: IrSimpleFunction.(List) -> 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("") 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