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.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)
}
@@ -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