JVM_IR wrap function into a proxy if indy SAM-conversion becomes valid
This commit is contained in:
committed by
teamcityserver
parent
9ad62eb1fd
commit
40fe67880b
@@ -341,11 +341,13 @@ private val jvmFilePhases = listOf(
|
||||
propertyReferencePhase,
|
||||
arrayConstructorPhase,
|
||||
constPhase1,
|
||||
|
||||
// TODO: merge the next three phases together, as visitors behave incorrectly between them
|
||||
// (backing fields moved out of companion objects are reachable by two paths):
|
||||
moveOrCopyCompanionObjectFieldsPhase,
|
||||
propertiesPhase,
|
||||
remapObjectFieldAccesses,
|
||||
|
||||
anonymousObjectSuperConstructorPhase,
|
||||
jvmStandardLibraryBuiltInsPhase,
|
||||
|
||||
|
||||
+184
-17
@@ -16,10 +16,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.LambdaMetafactoryArguments
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.LambdaMetafactoryArgumentsBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.SamDelegatingLambdaBlock
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.SamDelegatingLambdaBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.*
|
||||
import org.jetbrains.kotlin.config.JvmClosureGenerationScheme
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -28,9 +25,11 @@ import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
@@ -88,10 +87,11 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
if (shouldGenerateIndyLambdas) {
|
||||
val lambdaMetafactoryArguments =
|
||||
LambdaMetafactoryArgumentsBuilder(context, crossinlineLambdas)
|
||||
.getLambdaMetafactoryArgumentsOrNull(reference, reference.type, true)
|
||||
if (lambdaMetafactoryArguments != null) {
|
||||
.getLambdaMetafactoryArguments(reference, reference.type, true)
|
||||
if (lambdaMetafactoryArguments is LambdaMetafactoryArguments) {
|
||||
return wrapLambdaReferenceWithIndySamConversion(expression, reference, lambdaMetafactoryArguments)
|
||||
}
|
||||
// TODO MetafactoryArgumentsResult.Failure.FunctionHazard?
|
||||
}
|
||||
|
||||
return FunctionReferenceBuilder(reference).build()
|
||||
@@ -254,8 +254,13 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
val lambdaBlock = SamDelegatingLambdaBuilder(context)
|
||||
.build(invokable, samSuperType, currentScope!!.scope.scopeOwnerSymbol, getDeclarationParentForDelegatingLambda())
|
||||
val lambdaMetafactoryArguments = LambdaMetafactoryArgumentsBuilder(context, crossinlineLambdas)
|
||||
.getLambdaMetafactoryArgumentsOrNull(lambdaBlock.ref, samSuperType, false)
|
||||
?: return super.visitTypeOperator(expression)
|
||||
.getLambdaMetafactoryArguments(lambdaBlock.ref, samSuperType, false)
|
||||
|
||||
if (lambdaMetafactoryArguments !is LambdaMetafactoryArguments) {
|
||||
// TODO MetafactoryArgumentsResult.Failure.FunctionHazard?
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
|
||||
invokable.transformChildrenVoid()
|
||||
return wrapSamDelegatingLambdaWithIndySamConversion(samSuperType, lambdaBlock, lambdaMetafactoryArguments)
|
||||
} else {
|
||||
@@ -267,9 +272,25 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
if (shouldGenerateIndySamConversions) {
|
||||
val lambdaMetafactoryArguments =
|
||||
LambdaMetafactoryArgumentsBuilder(context, crossinlineLambdas)
|
||||
.getLambdaMetafactoryArgumentsOrNull(reference, samSuperType, false)
|
||||
if (lambdaMetafactoryArguments != null) {
|
||||
return wrapSamConversionArgumentWithIndySamConversion(expression, lambdaMetafactoryArguments)
|
||||
.getLambdaMetafactoryArguments(reference, samSuperType, false)
|
||||
if (lambdaMetafactoryArguments is LambdaMetafactoryArguments) {
|
||||
return wrapSamConversionArgumentWithIndySamConversion(expression) { samType ->
|
||||
wrapWithIndySamConversion(samType, lambdaMetafactoryArguments)
|
||||
}
|
||||
} else if (lambdaMetafactoryArguments is MetafactoryArgumentsResult.Failure.FunctionHazard) {
|
||||
// Try wrapping function with a proxy local function and see if that helps.
|
||||
val proxyLocalFunBlock = createProxyLocalFunctionForIndySamConversion(reference)
|
||||
val proxyLocalFunRef = proxyLocalFunBlock.statements[proxyLocalFunBlock.statements.size - 1] as IrFunctionReference
|
||||
val proxyLambdaMetafactoryArguments = LambdaMetafactoryArgumentsBuilder(context, crossinlineLambdas)
|
||||
.getLambdaMetafactoryArguments(proxyLocalFunRef, samSuperType, false)
|
||||
if (proxyLambdaMetafactoryArguments is LambdaMetafactoryArguments) {
|
||||
return wrapSamConversionArgumentWithIndySamConversion(expression) { samType ->
|
||||
proxyLocalFunBlock.statements[proxyLocalFunBlock.statements.size - 1] =
|
||||
wrapWithIndySamConversion(samType, proxyLambdaMetafactoryArguments)
|
||||
proxyLocalFunBlock.type = samType
|
||||
proxyLocalFunBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +301,152 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
return FunctionReferenceBuilder(reference, erasedSamSuperType).build()
|
||||
}
|
||||
|
||||
private fun createProxyLocalFunctionForIndySamConversion(reference: IrFunctionReference): IrBlock {
|
||||
val startOffset = reference.startOffset
|
||||
val endOffset = reference.endOffset
|
||||
val targetFun = reference.symbol.owner
|
||||
|
||||
// For a function reference with possibly bound value parameters
|
||||
// [ dispatchReceiver = ..., extensionReceiver = ..., ... ]::foo
|
||||
// create a proxy wrapper:
|
||||
// {
|
||||
// val tmp_proxy_0 = <bound_argument_value_0>
|
||||
// ...
|
||||
// val tmp_proxy_N = <bound_argument_value_N>
|
||||
// fun `$proxy`(p_0: TP_0, ..., p_M: TP_M): TR =
|
||||
// foo(... arg_J ...)
|
||||
// // here for each J arg_J is either 'tmp_proxy_K' or 'p_K' for some K
|
||||
// ::`$proxy`
|
||||
// }
|
||||
|
||||
val temporaryVals = ArrayList<IrVariable>()
|
||||
|
||||
val targetCall: IrFunctionAccessExpression =
|
||||
when (targetFun) {
|
||||
is IrSimpleFunction ->
|
||||
IrCallImpl.fromSymbolOwner(startOffset, endOffset, targetFun.symbol)
|
||||
is IrConstructor ->
|
||||
IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, targetFun.returnType, targetFun.symbol)
|
||||
else ->
|
||||
throw AssertionError("Unexpected callable reference target: ${targetFun.render()}")
|
||||
}
|
||||
|
||||
val proxyFun = context.irFactory.buildFun {
|
||||
name = Name.identifier("\$proxy")
|
||||
returnType = targetFun.returnType
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
modality = Modality.FINAL
|
||||
isSuspend = false
|
||||
isInline = false
|
||||
origin = JvmLoweredDeclarationOrigin.PROXY_FUN_FOR_INDY_SAM_CONVESION
|
||||
}.also { proxyFun ->
|
||||
proxyFun.parent = currentDeclarationParent
|
||||
?: throw AssertionError("No declaration parent when processing $reference")
|
||||
|
||||
var temporaryValIndex = 0
|
||||
var proxyParameterIndex = 0
|
||||
|
||||
fun addAndGetTemporaryVal(initializer: IrExpression): IrGetValue {
|
||||
val tmpVal = IrVariableImpl(
|
||||
startOffset, endOffset,
|
||||
IrDeclarationOrigin.IR_TEMPORARY_VARIABLE,
|
||||
IrVariableSymbolImpl(),
|
||||
Name.identifier("tmp_proxy_${temporaryValIndex++}"),
|
||||
initializer.type,
|
||||
isVar = false, isConst = false, isLateinit = false
|
||||
)
|
||||
tmpVal.initializer = initializer
|
||||
temporaryVals.add(tmpVal)
|
||||
return IrGetValueImpl(startOffset, endOffset, tmpVal.symbol)
|
||||
}
|
||||
|
||||
fun addAndGetProxyValueParameter(originalParameter: IrValueParameter): IrGetValue {
|
||||
val proxyParameter = buildValueParameter(proxyFun) {
|
||||
updateFrom(originalParameter)
|
||||
name = Name.identifier("p$proxyParameterIndex\$${originalParameter.name.asString()}")
|
||||
index = proxyParameterIndex
|
||||
proxyParameterIndex++
|
||||
}.apply {
|
||||
parent = proxyFun
|
||||
}
|
||||
proxyFun.valueParameters += proxyParameter
|
||||
return IrGetValueImpl(startOffset, endOffset, proxyParameter.symbol)
|
||||
}
|
||||
|
||||
fun getTargetCallArgument(boundValue: IrExpression?, originalParameter: IrValueParameter?): IrExpression? =
|
||||
when {
|
||||
boundValue != null ->
|
||||
addAndGetTemporaryVal(boundValue)
|
||||
originalParameter != null ->
|
||||
addAndGetProxyValueParameter(originalParameter)
|
||||
else ->
|
||||
null
|
||||
}
|
||||
|
||||
targetCall.dispatchReceiver = getTargetCallArgument(reference.dispatchReceiver, targetFun.dispatchReceiverParameter)
|
||||
targetCall.extensionReceiver = getTargetCallArgument(reference.extensionReceiver, targetFun.extensionReceiverParameter)
|
||||
for ((valueParameterIndex, valueParameter) in targetFun.valueParameters.withIndex()) {
|
||||
targetCall.putValueArgument(
|
||||
valueParameterIndex,
|
||||
getTargetCallArgument(
|
||||
reference.getValueArgument(valueParameterIndex),
|
||||
valueParameter
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val proxyFunBody = IrBlockBodyImpl(startOffset, endOffset).also { proxyFun.body = it }
|
||||
when {
|
||||
targetFun.isArrayOf() -> {
|
||||
// Lower arrayOf-like function in place.
|
||||
// TODO it seems that VarargLowering does a little bit too much.
|
||||
// Maybe worth decomposing it into varargs part and arrayOf intrinsics part.
|
||||
val varargParameter = proxyFun.valueParameters.singleOrNull { it.isVararg }
|
||||
?: throw AssertionError(
|
||||
"Proxy for *arrayOf should have single vararg parameter:\n" +
|
||||
proxyFun.valueParameters.joinToString(separator = "\n") { it.dump() }
|
||||
)
|
||||
proxyFunBody.statements.add(
|
||||
IrReturnImpl(
|
||||
startOffset, endOffset,
|
||||
context.irBuiltIns.nothingType,
|
||||
proxyFun.symbol,
|
||||
IrGetValueImpl(startOffset, endOffset, varargParameter.symbol, null)
|
||||
)
|
||||
)
|
||||
}
|
||||
targetFun.returnType.isUnit() -> {
|
||||
proxyFunBody.statements.add(targetCall)
|
||||
}
|
||||
else -> {
|
||||
proxyFunBody.statements.add(
|
||||
IrReturnImpl(
|
||||
startOffset, endOffset,
|
||||
context.irBuiltIns.nothingType,
|
||||
proxyFun.symbol,
|
||||
targetCall
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val proxyFunRef = IrFunctionReferenceImpl(
|
||||
startOffset, endOffset,
|
||||
reference.type,
|
||||
proxyFun.symbol,
|
||||
0, // TODO generic function reference?
|
||||
proxyFun.valueParameters.size
|
||||
)
|
||||
|
||||
return IrBlockImpl(
|
||||
startOffset, endOffset,
|
||||
reference.type,
|
||||
origin = null,
|
||||
temporaryVals + proxyFun + proxyFunRef
|
||||
)
|
||||
}
|
||||
|
||||
private fun canGenerateIndySamConversionOnFunctionalExpression(samSuperType: IrType, expression: IrExpression): Boolean {
|
||||
val samClass = samSuperType.classOrNull
|
||||
?: throw AssertionError("Class type expected: ${samSuperType.render()}")
|
||||
@@ -302,24 +469,24 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
|
||||
private fun wrapSamConversionArgumentWithIndySamConversion(
|
||||
expression: IrTypeOperatorCall,
|
||||
lambdaMetafactoryArguments: LambdaMetafactoryArguments
|
||||
produceSamConversion: (IrType) -> IrExpression
|
||||
): IrExpression {
|
||||
val samType = expression.typeOperand
|
||||
return when (val argument = expression.argument) {
|
||||
is IrFunctionReference ->
|
||||
wrapWithIndySamConversion(samType, lambdaMetafactoryArguments)
|
||||
produceSamConversion(samType)
|
||||
is IrBlock ->
|
||||
wrapFunctionReferenceInsideBlockWithIndySamConversion(samType, lambdaMetafactoryArguments, argument)
|
||||
wrapFunctionReferenceInsideBlockWithIndySamConversion(samType, argument, produceSamConversion)
|
||||
else -> throw AssertionError("Block or function reference expected: ${expression.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapFunctionReferenceInsideBlockWithIndySamConversion(
|
||||
samType: IrType,
|
||||
lambdaMetafactoryArguments: LambdaMetafactoryArguments,
|
||||
block: IrBlock
|
||||
block: IrBlock,
|
||||
produceSamConversion: (IrType) -> IrExpression
|
||||
): IrExpression {
|
||||
val indySamConversion = wrapWithIndySamConversion(samType, lambdaMetafactoryArguments)
|
||||
val indySamConversion = produceSamConversion(samType)
|
||||
block.statements[block.statements.size - 1] = indySamConversion
|
||||
block.type = indySamConversion.type
|
||||
return block
|
||||
|
||||
+101
-30
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
|
||||
import org.jetbrains.kotlin.backend.common.lower.parents
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.findSuperDeclaration
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod
|
||||
@@ -34,12 +35,49 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
internal sealed class MetafactoryArgumentsResult {
|
||||
abstract val isSuccess: Boolean
|
||||
|
||||
abstract class Success : MetafactoryArgumentsResult() {
|
||||
override val isSuccess: Boolean
|
||||
get() = true
|
||||
}
|
||||
|
||||
abstract class Failure : MetafactoryArgumentsResult() {
|
||||
override val isSuccess: Boolean
|
||||
get() = false
|
||||
|
||||
// Objects produced by j.l.invoke.LambdaMetafactory have different semantics (e.g., different 'equals' method)
|
||||
// that prevents using them in place of objects of corresponding Kotlin classes.
|
||||
// TODO might use Kotlin-specific metafactory if there would be one.
|
||||
object LambdaMetafactorySemanticsHazard : Failure()
|
||||
|
||||
// There's an ABI incompatibility between classes generated by j.l.invoke.LambdaMetafactory and classes generatede by Kotlin.
|
||||
// TODO might use Kotlin-specific metafactory if there would be one.
|
||||
object LambdaMetafactoryAbiHazard : Failure()
|
||||
|
||||
// An object would be created inside an inline function, which currently doesn't work well with 'invokedynamic'.
|
||||
// TODO make sure indy and Kotlin bytecode inliner work well together
|
||||
object InliningHazard : Failure()
|
||||
|
||||
// Resulting object should be Serializable.
|
||||
// TODO implement serialization support required by j.l.invoke.LambdaMetafactory
|
||||
object SerializationHazard : Failure()
|
||||
|
||||
// There's something special about a function we are referencing.
|
||||
// Wrapping it into a proxy local function might help.
|
||||
object FunctionHazard : Failure()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class LambdaMetafactoryArguments(
|
||||
val samMethod: IrSimpleFunction,
|
||||
val fakeInstanceMethod: IrSimpleFunction,
|
||||
val implMethodReference: IrFunctionReference,
|
||||
val extraOverriddenMethods: List<IrSimpleFunction>
|
||||
)
|
||||
) : MetafactoryArgumentsResult.Success()
|
||||
|
||||
|
||||
internal class LambdaMetafactoryArgumentsBuilder(
|
||||
private val context: JvmBackendContext,
|
||||
@@ -48,34 +86,44 @@ internal class LambdaMetafactoryArgumentsBuilder(
|
||||
/**
|
||||
* @see java.lang.invoke.LambdaMetafactory
|
||||
*/
|
||||
fun getLambdaMetafactoryArgumentsOrNull(
|
||||
fun getLambdaMetafactoryArguments(
|
||||
reference: IrFunctionReference,
|
||||
samType: IrType,
|
||||
plainLambda: Boolean
|
||||
): LambdaMetafactoryArguments? {
|
||||
): MetafactoryArgumentsResult {
|
||||
val samClass = samType.getClass()
|
||||
?: throw AssertionError("SAM type is not a class: ${samType.render()}")
|
||||
|
||||
var semanticsHazard = false
|
||||
var abiHazard = false
|
||||
var inliningHazard = false
|
||||
var shouldBeSerializable = false
|
||||
var functionHazard = false
|
||||
|
||||
// Can't use JDK LambdaMetafactory for function references by default (because of 'equals').
|
||||
// TODO special mode that would generate indy everywhere?
|
||||
if (!reference.origin.isLambda && !samClass.isFromJava())
|
||||
return null
|
||||
if (!reference.origin.isLambda && !samClass.isFromJava()) {
|
||||
semanticsHazard = true
|
||||
}
|
||||
|
||||
// Don't use JDK LambdaMetafactory for serializable lambdas
|
||||
// TODO implement support for serializable lambdas with LambdaMetafactory (requires additional code for deserialization)
|
||||
if (samClass.isInheritedFromSerializable())
|
||||
return null
|
||||
if (samClass.isInheritedFromSerializable()) {
|
||||
shouldBeSerializable = true
|
||||
}
|
||||
|
||||
val samMethod = samClass.getSingleAbstractMethod()
|
||||
?: throw AssertionError("SAM class has no single abstract method: ${samClass.render()}")
|
||||
|
||||
// Can't use JDK LambdaMetafactory for fun interface with suspend fun.
|
||||
if (samMethod.isSuspend)
|
||||
return null
|
||||
if (samMethod.isSuspend) {
|
||||
abiHazard = true
|
||||
}
|
||||
|
||||
// Can't use JDK LambdaMetafactory for fun interfaces that require delegation to $DefaultImpls.
|
||||
if (samClass.requiresDelegationToDefaultImpls())
|
||||
return null
|
||||
if (samClass.requiresDelegationToDefaultImpls()) {
|
||||
abiHazard = true
|
||||
}
|
||||
|
||||
// TODO in cases where LambdaMetafactory is unusable directly due to problems with the implementation function,
|
||||
// we can still avoid generating an entire class by converting the function reference into a lambda first;
|
||||
@@ -83,32 +131,38 @@ internal class LambdaMetafactoryArgumentsBuilder(
|
||||
|
||||
val implFun = reference.symbol.owner
|
||||
|
||||
if (implFun.typeParameters.any { it.isReified }) {
|
||||
// TODO support reified type parameters in proxy wrappers somehow?
|
||||
abiHazard = true
|
||||
}
|
||||
|
||||
// Don't generate references to intrinsic functions as invokedynamic (no such method exists at run-time).
|
||||
if (context.irIntrinsics.getIntrinsic(implFun.symbol) != null)
|
||||
return null
|
||||
if (context.irIntrinsics.getIntrinsic(implFun.symbol) != null) {
|
||||
functionHazard = true
|
||||
}
|
||||
|
||||
// Can't use invokedynamic if the referenced function has to be inlined for correct semantics
|
||||
// Also in some cases like `private inline fun` we'd need accessors, which `SyntheticAccessorLowering`
|
||||
// won't generate under the assumption that the inline function will be inlined. Plus if the function
|
||||
// is in a different module we should probably copy it anyway (and regenerate all objects in it).
|
||||
if (implFun.isInline)
|
||||
return null
|
||||
if (implFun.isInline) {
|
||||
functionHazard = true
|
||||
}
|
||||
|
||||
if (implFun is IrConstructor && DescriptorVisibilities.isPrivate(implFun.visibility)) {
|
||||
// Kotlin generates constructor accessors differently from Java.
|
||||
// TODO more precise accessibility check (see SyntheticAccessorLowering::isAccessible)
|
||||
return null
|
||||
// We don't do exact accessibility check here, just
|
||||
functionHazard = true
|
||||
}
|
||||
|
||||
// It's possible to reference through a child class a method declared in a package-private base Java class.
|
||||
// In this case the corresponding method might be inaccessible in the context where it's referenced (see KT-48954).
|
||||
// For now, just prohibit referencing methods from package-private Java classes through indy (without precise accessibility check).
|
||||
// TODO wrap into lambda?
|
||||
if (implFun is IrSimpleFunction) {
|
||||
val baseFun = findSuperDeclaration(implFun, false, context.state.jvmDefaultMode)
|
||||
val baseFunClass = baseFun.parent as? IrClass
|
||||
if (baseFunClass != null && baseFunClass.visibility == JavaDescriptorVisibilities.PACKAGE_VISIBILITY) {
|
||||
return null
|
||||
functionHazard = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,40 +171,52 @@ internal class LambdaMetafactoryArgumentsBuilder(
|
||||
// LambdaMetafactory treats multifile class part members as non-accessible,
|
||||
// even if the member is referenced via facade,
|
||||
// because corresponding part class is non-accessible
|
||||
return null
|
||||
functionHazard = true
|
||||
}
|
||||
|
||||
// Can't use JDK LambdaMetafactory for annotated lambdas.
|
||||
// JDK LambdaMetafactory doesn't copy annotations from implementation method to an instance method in a
|
||||
// corresponding synthetic class, which doesn't look like a binary compatible change.
|
||||
// TODO relaxed mode?
|
||||
if (reference.origin.isLambda && implFun.annotations.isNotEmpty())
|
||||
return null
|
||||
if (reference.origin.isLambda && implFun.annotations.isNotEmpty()) {
|
||||
abiHazard = true
|
||||
}
|
||||
|
||||
// Don't use JDK LambdaMetafactory for big arity lambdas.
|
||||
if (plainLambda) {
|
||||
var parametersCount = implFun.valueParameters.size
|
||||
if (implFun.extensionReceiverParameter != null) ++parametersCount
|
||||
if (parametersCount >= BuiltInFunctionArity.BIG_ARITY)
|
||||
return null
|
||||
abiHazard = true
|
||||
}
|
||||
|
||||
// Can't use indy-based SAM conversion inside inline fun (Ok in inline lambda).
|
||||
if (implFun.parents.any { it.isInlineFunction() || it.isCrossinlineLambda() })
|
||||
return null
|
||||
if (implFun.parents.any { it.isInlineFunction() || it.isCrossinlineLambda() }) {
|
||||
inliningHazard = true
|
||||
}
|
||||
|
||||
// Don't try to use indy on SAM types with non-invariant projections because buildFakeOverrideMember doesn't support such supertypes
|
||||
// (and rightly so: supertypes in Kotlin can't have projections in immediate type arguments). This can happen for example in case
|
||||
// the SAM type is instantiated with an intersection type in arguments, which is approximated to an out-projection in psi2ir.
|
||||
if (samType is IrSimpleType) {
|
||||
if (samType.arguments.any { it is IrStarProjection || it is IrTypeProjection && it.variance != Variance.INVARIANT })
|
||||
return null
|
||||
if (samType.arguments.any { it is IrStarProjection || it is IrTypeProjection && it.variance != Variance.INVARIANT }) {
|
||||
abiHazard = true
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
semanticsHazard -> return MetafactoryArgumentsResult.Failure.LambdaMetafactorySemanticsHazard
|
||||
abiHazard -> return MetafactoryArgumentsResult.Failure.LambdaMetafactoryAbiHazard
|
||||
inliningHazard -> return MetafactoryArgumentsResult.Failure.InliningHazard
|
||||
shouldBeSerializable -> return MetafactoryArgumentsResult.Failure.SerializationHazard
|
||||
functionHazard -> return MetafactoryArgumentsResult.Failure.FunctionHazard
|
||||
}
|
||||
|
||||
// Do the hard work of matching Kotlin functional interface hierarchy against LambdaMetafactory constraints.
|
||||
// Briefly: sometimes we have to force boxing on the primitive and inline class values, sometimes we have to keep them unboxed.
|
||||
// If this results in conflicting requirements, we can't use INVOKEDYNAMIC with LambdaMetafactory for creating a closure.
|
||||
return getLambdaMetafactoryArgsOrNullInner(reference, samMethod, samType, implFun)
|
||||
?: MetafactoryArgumentsResult.Failure.FunctionHazard
|
||||
}
|
||||
|
||||
private val javaIoSerializableFqn =
|
||||
@@ -258,7 +324,7 @@ internal class LambdaMetafactoryArgumentsBuilder(
|
||||
return null
|
||||
|
||||
adaptFakeInstanceMethodSignature(fakeInstanceMethod, signatureAdaptationConstraints)
|
||||
if (implFun.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) {
|
||||
if (implFun.origin in adaptableFunctionOrigins) {
|
||||
adaptLambdaSignature(implFun as IrSimpleFunction, fakeInstanceMethod, signatureAdaptationConstraints)
|
||||
} else if (
|
||||
!checkMethodSignatureCompliance(implFun, fakeInstanceMethod, signatureAdaptationConstraints, reference)
|
||||
@@ -319,13 +385,18 @@ internal class LambdaMetafactoryArgumentsBuilder(
|
||||
TypeAdaptationConstraint.CONFLICT -> false
|
||||
}
|
||||
|
||||
private val adaptableFunctionOrigins = setOf(
|
||||
IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA,
|
||||
JvmLoweredDeclarationOrigin.PROXY_FUN_FOR_INDY_SAM_CONVESION
|
||||
)
|
||||
|
||||
private fun adaptLambdaSignature(
|
||||
implFun: IrSimpleFunction,
|
||||
fakeInstanceMethod: IrSimpleFunction,
|
||||
constraints: SignatureAdaptationConstraints
|
||||
) {
|
||||
if (implFun.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) {
|
||||
throw AssertionError("Can't adapt non-lambda function signature: ${implFun.render()}")
|
||||
if (implFun.origin !in adaptableFunctionOrigins) {
|
||||
throw AssertionError("Function origin should be one of ${adaptableFunctionOrigins}: ${implFun.dump()}")
|
||||
}
|
||||
|
||||
val implParameters = collectValueParameters(implFun)
|
||||
|
||||
+2
-1
@@ -37,7 +37,7 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object INTERFACE_COMPANION_PRIVATE_INSTANCE : IrDeclarationOriginImpl("INTERFACE_COMPANION_PRIVATE_INSTANCE", isSynthetic = true)
|
||||
object POLYMORPHIC_SIGNATURE_INSTANTIATION : IrDeclarationOriginImpl("POLYMORPHIC_SIGNATURE_INSTANTIATION", isSynthetic = true)
|
||||
object ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER : IrDeclarationOriginImpl("ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER", isSynthetic = true)
|
||||
object OBJECT_SUPER_CONSTRUCTOR_PARAMETER : IrDeclarationOriginImpl("OBJECT_SUPER_CONSTURCTOR_PARAMETER", isSynthetic = true)
|
||||
object OBJECT_SUPER_CONSTRUCTOR_PARAMETER : IrDeclarationOriginImpl("OBJECT_SUPER_CONSTRUCTOR_PARAMETER", isSynthetic = true)
|
||||
object CONTINUATION_CLASS : IrDeclarationOriginImpl("CONTINUATION_CLASS")
|
||||
object SUSPEND_LAMBDA : IrDeclarationOriginImpl("SUSPEND_LAMBDA")
|
||||
object FOR_INLINE_STATE_MACHINE_TEMPLATE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE")
|
||||
@@ -48,4 +48,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object ABSTRACT_BRIDGE_STUB : IrDeclarationOriginImpl("ABSTRACT_BRIDGE_STUB")
|
||||
object INVOVEDYNAMIC_CALL_TARGET : IrDeclarationOriginImpl("INVOVEDYNAMIC_CALL_TARGET")
|
||||
object INLINE_LAMBDA : IrDeclarationOriginImpl("INLINE_LAMBDA")
|
||||
object PROXY_FUN_FOR_INDY_SAM_CONVESION : IrDeclarationOriginImpl("PROXY_FUN_FOR_INDY_SAM_CONVESION")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user