JVM_IR: have SAM wrapper constructors accept FunctionN
Otherwise, the cached instances cannot be reused for different wrapped types. Also, if the wrapped type is regenerated during inlining, the inliner would produce a call to a nonexistent constructor that takes the regenerated type as an argument.
This commit is contained in:
+14
-11
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.lower
|
package org.jetbrains.kotlin.backend.jvm.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||||
@@ -13,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclaration
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||||
import org.jetbrains.kotlin.codegen.SamWrapperCodegen.FUNCTION_FIELD_NAME
|
import org.jetbrains.kotlin.codegen.SamWrapperCodegen.FUNCTION_FIELD_NAME
|
||||||
@@ -47,7 +47,7 @@ internal val singleAbstractMethodPhase = makeIrFilePhase(
|
|||||||
description = "Replace SAM conversions with instances of interface-implementing classes"
|
description = "Replace SAM conversions with instances of interface-implementing classes"
|
||||||
)
|
)
|
||||||
|
|
||||||
class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
|
class SingleAbstractMethodLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
|
||||||
// SAM wrappers are cached, either in the file class (if it exists), or in a top-level enclosing class.
|
// SAM wrappers are cached, either in the file class (if it exists), or in a top-level enclosing class.
|
||||||
// In the latter case, the names of SAM wrappers depend on the order of classes in the file. For example:
|
// In the latter case, the names of SAM wrappers depend on the order of classes in the file. For example:
|
||||||
//
|
//
|
||||||
@@ -96,9 +96,9 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
|
|||||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||||
if (expression.operator != IrTypeOperator.SAM_CONVERSION)
|
if (expression.operator != IrTypeOperator.SAM_CONVERSION)
|
||||||
return super.visitTypeOperator(expression)
|
return super.visitTypeOperator(expression)
|
||||||
// TODO: We have to erase type parameters here, since we cache SAM wrappers based on the erased
|
// TODO: there must be exactly one wrapper per Java interface; ideally, if the interface has generic
|
||||||
// underlying representation. We should do the same for the underlying function type, otherwise
|
// parameters, so should the wrapper. Currently, we just erase them and generate something that
|
||||||
// we end up with wrong generic information.
|
// erases to the same result at codegen time.
|
||||||
val erasedSuperType = expression.typeOperand.erasedUpperBound.defaultType
|
val erasedSuperType = expression.typeOperand.erasedUpperBound.defaultType
|
||||||
val superType = if (expression.typeOperand.isNullable()) erasedSuperType.makeNullable() else erasedSuperType
|
val superType = if (expression.typeOperand.isNullable()) erasedSuperType.makeNullable() else erasedSuperType
|
||||||
val invokable = expression.argument.transform(this, null)
|
val invokable = expression.argument.transform(this, null)
|
||||||
@@ -110,7 +110,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
|
|||||||
val inInlineFunctionScope = allScopes.any { scope -> (scope.irElement as? IrFunction)?.isInline ?: false }
|
val inInlineFunctionScope = allScopes.any { scope -> (scope.irElement as? IrFunction)?.isInline ?: false }
|
||||||
val cache = if (inInlineFunctionScope) inlineCachedImplementations else cachedImplementations
|
val cache = if (inInlineFunctionScope) inlineCachedImplementations else cachedImplementations
|
||||||
val implementation = cache.getOrPut(superType) {
|
val implementation = cache.getOrPut(superType) {
|
||||||
createObjectProxy(superType, invokable.type, inInlineFunctionScope)
|
createObjectProxy(superType, inInlineFunctionScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (superType.isNullable() && invokable.type.isNullable()) {
|
return if (superType.isNullable() && invokable.type.isNullable()) {
|
||||||
@@ -138,7 +138,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
|
|||||||
|
|
||||||
// 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, generatePublicWrapper: Boolean): IrClass {
|
private fun createObjectProxy(superType: IrType, generatePublicWrapper: Boolean): IrClass {
|
||||||
val superClass = superType.classifierOrFail.owner as IrClass
|
val superClass = superType.classifierOrFail.owner as IrClass
|
||||||
// The language documentation prohibits casting lambdas to classes, but if it was allowed,
|
// The language documentation prohibits casting lambdas to classes, but if it was allowed,
|
||||||
// the `irDelegatingConstructorCall` in the constructor below would need to be modified.
|
// the `irDelegatingConstructorCall` in the constructor below would need to be modified.
|
||||||
@@ -147,6 +147,11 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
|
|||||||
val superFqName = superClass.fqNameWhenAvailable!!.asString().replace('.', '_')
|
val superFqName = superClass.fqNameWhenAvailable!!.asString().replace('.', '_')
|
||||||
val inlinePrefix = if (generatePublicWrapper) "\$i" else ""
|
val inlinePrefix = if (generatePublicWrapper) "\$i" else ""
|
||||||
val wrapperName = Name.identifier("sam$inlinePrefix\$$superFqName$SAM_WRAPPER_SUFFIX")
|
val wrapperName = Name.identifier("sam$inlinePrefix\$$superFqName$SAM_WRAPPER_SUFFIX")
|
||||||
|
val superMethod = superClass.functions.single { it.modality == Modality.ABSTRACT }
|
||||||
|
// TODO: have psi2ir cast the argument to the correct function type. Also see the TODO
|
||||||
|
// about type parameters in `visitTypeOperator`.
|
||||||
|
val wrappedFunctionClass = context.ir.symbols.functionN(superMethod.valueParameters.size).owner
|
||||||
|
val wrappedFunctionType = wrappedFunctionClass.defaultType
|
||||||
|
|
||||||
val wrapperVisibility = if (generatePublicWrapper) Visibilities.PUBLIC else JavaVisibilities.PACKAGE_VISIBILITY
|
val wrapperVisibility = if (generatePublicWrapper) Visibilities.PUBLIC else JavaVisibilities.PACKAGE_VISIBILITY
|
||||||
val subclass = buildClass {
|
val subclass = buildClass {
|
||||||
@@ -161,7 +166,7 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
|
|||||||
|
|
||||||
val field = subclass.addField {
|
val field = subclass.addField {
|
||||||
name = Name.identifier(FUNCTION_FIELD_NAME)
|
name = Name.identifier(FUNCTION_FIELD_NAME)
|
||||||
type = invokableType
|
type = wrappedFunctionType
|
||||||
origin = subclass.origin
|
origin = subclass.origin
|
||||||
visibility = Visibilities.PRIVATE
|
visibility = Visibilities.PRIVATE
|
||||||
}
|
}
|
||||||
@@ -184,7 +189,6 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val superMethod = superClass.functions.single { it.modality == Modality.ABSTRACT }
|
|
||||||
subclass.addFunction {
|
subclass.addFunction {
|
||||||
name = superMethod.name
|
name = superMethod.name
|
||||||
returnType = superMethod.returnType
|
returnType = superMethod.returnType
|
||||||
@@ -194,9 +198,8 @@ class SingleAbstractMethodLowering(val context: CommonBackendContext) : FileLowe
|
|||||||
overriddenSymbols += superMethod.symbol
|
overriddenSymbols += superMethod.symbol
|
||||||
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
|
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
|
||||||
superMethod.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
|
superMethod.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
|
||||||
val invokableClass = invokableType.classifierOrFail.owner as IrClass
|
|
||||||
body = context.createIrBuilder(symbol).run {
|
body = context.createIrBuilder(symbol).run {
|
||||||
irExprBody(irCall(invokableClass.functions.single { it.name == OperatorNameConventions.INVOKE }).apply {
|
irExprBody(irCall(wrappedFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }).apply {
|
||||||
dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field)
|
dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field)
|
||||||
valueParameters.forEachIndexed { i, parameter -> putValueArgument(i, irGet(parameter)) }
|
valueParameters.forEachIndexed { i, parameter -> putValueArgument(i, irGet(parameter)) }
|
||||||
})
|
})
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user