JVM_IR wrap function into a proxy if indy SAM-conversion becomes valid

This commit is contained in:
Dmitry Petrov
2021-10-06 18:53:29 +03:00
committed by teamcityserver
parent 9ad62eb1fd
commit 40fe67880b
21 changed files with 583 additions and 66 deletions
@@ -22233,6 +22233,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/invokedynamic/sam/genericLambdaSignature.kt");
}
@Test
@TestMetadata("inlineFunWithPrivateMethod.kt")
public void testInlineFunWithPrivateMethod() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/inlineFunWithPrivateMethod.kt");
}
@Test
@TestMetadata("inlineOnly.kt")
public void testInlineOnly() throws Exception {
@@ -22281,6 +22287,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/invokedynamic/sam/primitiveVsWrapperInSam.kt");
}
@Test
@TestMetadata("reifiedTypeParameter.kt")
public void testReifiedTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/reifiedTypeParameter.kt");
}
@Test
@TestMetadata("samConversionInsideSamConvertedLambda.kt")
public void testSamConversionInsideSamConvertedLambda() throws Exception {
@@ -22480,6 +22492,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/enhancedNullability.kt");
}
@Test
@TestMetadata("genericBoundInnerConstructorRef.kt")
public void testGenericBoundInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/genericBoundInnerConstructorRef.kt");
}
@Test
@TestMetadata("genericInnerConstructorRef.kt")
public void testGenericInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/genericInnerConstructorRef.kt");
}
@Test
@TestMetadata("innerConstructorRef.kt")
public void testInnerConstructorRef() throws Exception {
@@ -22534,6 +22558,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/multifileClassMemberFromStdlib.kt");
}
@Test
@TestMetadata("multipleProxyWrappersForSameFunction.kt")
public void testMultipleProxyWrappersForSameFunction() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/multipleProxyWrappersForSameFunction.kt");
}
@Test
@TestMetadata("nonTrivialReceiver.kt")
public void testNonTrivialReceiver() throws Exception {
@@ -22621,6 +22651,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/enumValues.kt");
}
@Test
@TestMetadata("intArrayOf.kt")
public void testIntArrayOf() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/intArrayOf.kt");
}
@Test
@TestMetadata("intPlus.kt")
public void testIntPlus() throws Exception {
@@ -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,
@@ -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
@@ -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)
@@ -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")
}
@@ -0,0 +1,21 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 1 java/lang/invoke/LambdaMetafactory
// FILE: genericBoundInnerConstructorRef.kt
class Outer<TO>(val s1: TO) {
inner class Inner<TI>(val s2: TI) {
fun t() = s1.toString() + s2.toString()
}
}
fun box() = Sam(Outer("O")::Inner).get("K").t()
// FILE: Sam.java
public interface Sam<TO, TI> {
Outer<TO>.Inner<TI> get(String s);
}
@@ -0,0 +1,24 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 1 java/lang/invoke/LambdaMetafactory
// FILE: genericInnerConstructorRef.kt
// Can't resolve generic type argument for unbound constructor reference,
// so 'Outer' has no type parameters.
class Outer(val s1: String) {
inner class Inner<TI>(val s2: TI) {
fun t() = s1 + s2.toString()
}
}
fun box() = Sam(Outer::Inner).get(Outer("O"), "K").t()
// FILE: Sam.java
public interface Sam<TI> {
Outer.Inner<TI> get(Outer outer, String s);
}
@@ -5,7 +5,7 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 0 java/lang/invoke/LambdaMetafactory
// 1 java/lang/invoke/LambdaMetafactory
// IGNORE_BACKEND_FIR: JVM_IR
// ^ OVERLOAD_RESOLUTION_AMBIGUITY: Overload resolution ambiguity between candidates: [kotlin/collections/plus, kotlin/collections/plus]
@@ -0,0 +1,30 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 4 java/lang/invoke/LambdaMetafactory
// FILE: voidReturnTypeAsObject.kt
var t = "Failed"
fun ok(s: String) { t = s }
fun box(): String {
Sam(::ok).get("1")
Sam(::ok).get("2")
Sam(::ok).get("3")
val r = Sam(::ok).get("OK")
if (r != Unit) {
return "Failed: $r"
}
return t
}
// FILE: Sam.java
public interface Sam {
Object get(String s);
}
@@ -4,7 +4,7 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 0 java/lang/invoke/LambdaMetafactory
// 1 java/lang/invoke/LambdaMetafactory
// FILE: anyNToString.kt
fun box() =
@@ -4,7 +4,7 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 0 java/lang/invoke/LambdaMetafactory
// 1 java/lang/invoke/LambdaMetafactory
// FILE: arrayConstructor.kt
fun box(): String {
@@ -4,7 +4,7 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 0 java/lang/invoke/LambdaMetafactory
// 1 java/lang/invoke/LambdaMetafactory
// FILE: charArrayOf.kt
fun box(): String {
@@ -0,0 +1,19 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 1 java/lang/invoke/LambdaMetafactory
// FILE: intArrayOf.kt
fun box(): String {
val sam = Sam(::intArrayOf)
val arr = sam.get(intArrayOf('O'.toInt(), 'K'.toInt()))
return "${arr[0].toChar()}${arr[1].toChar()}"
}
// FILE: Sam.java
public interface Sam {
int[] get(int[] s);
}
@@ -4,7 +4,7 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 0 java/lang/invoke/LambdaMetafactory
// 1 java/lang/invoke/LambdaMetafactory
// FILE: stringNPlus.kt
fun test(x: String?, y: Any?) =
@@ -4,8 +4,7 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 0 java/lang/invoke/LambdaMetafactory
// 1 final synthetic class VoidReturnTypeAsObjectKt\$box\$r\$[0-9]+
// 1 java/lang/invoke/LambdaMetafactory
// FILE: voidReturnTypeAsObject.kt
var t = "Failed"
@@ -0,0 +1,27 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// WITH_RUNTIME
// FULL_JDK
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 1 java/lang/invoke/LambdaMetafactory
// FILE: inlineFunWithPrivateMethod.kt
private inline fun g(x: String) = println(x)
fun call(c: Consumer<String>) = c.accept("")
fun box(): String {
val obj = { call(::g) } // `g` is inaccessible in this scope
obj()
return "OK"
}
// FILE: Consumer.java
public interface Consumer<T> {
void accept(T t);
}
@@ -6,22 +6,13 @@
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 0 java/lang/invoke/LambdaMetafactory
// 1 java/lang/invoke/LambdaMetafactory
// FILE: inlineOnly.kt
inline fun <reified T> f(x: T) =
println("${T::class.simpleName}($x)")
private inline fun g(x: String) = println(x)
fun call(c: Consumer<String>) = c.accept("")
fun box(): String {
call(::println) // `println` is `@InlineOnly`
call(::f) // `f` has a reified type parameter and thus isn't callable directly
val obj = { call(::g) } // `g` is inaccessible in this scope
obj()
call(::println) // 'println' is @InlineOnly
return "OK"
}
@@ -0,0 +1,27 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// WITH_RUNTIME
// FULL_JDK
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 0 java/lang/invoke/LambdaMetafactory
// FILE: reifiedTypeParameter.kt
inline fun <reified T> f(x: T) =
println("${T::class.simpleName}($x)")
fun call(c: Consumer<String>) = c.accept("")
fun box(): String {
call(::f) // `f` has a reified type parameter and thus isn't callable directly
return "OK"
}
// FILE: Consumer.java
public interface Consumer<T> {
void accept(T t);
}
@@ -22095,6 +22095,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/invokedynamic/sam/genericLambdaSignature.kt");
}
@Test
@TestMetadata("inlineFunWithPrivateMethod.kt")
public void testInlineFunWithPrivateMethod() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/inlineFunWithPrivateMethod.kt");
}
@Test
@TestMetadata("inlineOnly.kt")
public void testInlineOnly() throws Exception {
@@ -22143,6 +22149,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/invokedynamic/sam/primitiveVsWrapperInSam.kt");
}
@Test
@TestMetadata("reifiedTypeParameter.kt")
public void testReifiedTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/reifiedTypeParameter.kt");
}
@Test
@TestMetadata("samConversionInsideSamConvertedLambda.kt")
public void testSamConversionInsideSamConvertedLambda() throws Exception {
@@ -22342,6 +22354,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/enhancedNullability.kt");
}
@Test
@TestMetadata("genericBoundInnerConstructorRef.kt")
public void testGenericBoundInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/genericBoundInnerConstructorRef.kt");
}
@Test
@TestMetadata("genericInnerConstructorRef.kt")
public void testGenericInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/genericInnerConstructorRef.kt");
}
@Test
@TestMetadata("innerConstructorRef.kt")
public void testInnerConstructorRef() throws Exception {
@@ -22396,6 +22420,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/multifileClassMemberFromStdlib.kt");
}
@Test
@TestMetadata("multipleProxyWrappersForSameFunction.kt")
public void testMultipleProxyWrappersForSameFunction() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/multipleProxyWrappersForSameFunction.kt");
}
@Test
@TestMetadata("nonTrivialReceiver.kt")
public void testNonTrivialReceiver() throws Exception {
@@ -22483,6 +22513,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/enumValues.kt");
}
@Test
@TestMetadata("intArrayOf.kt")
public void testIntArrayOf() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/intArrayOf.kt");
}
@Test
@TestMetadata("intPlus.kt")
public void testIntPlus() throws Exception {
@@ -22233,6 +22233,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/genericLambdaSignature.kt");
}
@Test
@TestMetadata("inlineFunWithPrivateMethod.kt")
public void testInlineFunWithPrivateMethod() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/inlineFunWithPrivateMethod.kt");
}
@Test
@TestMetadata("inlineOnly.kt")
public void testInlineOnly() throws Exception {
@@ -22281,6 +22287,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/primitiveVsWrapperInSam.kt");
}
@Test
@TestMetadata("reifiedTypeParameter.kt")
public void testReifiedTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/reifiedTypeParameter.kt");
}
@Test
@TestMetadata("samConversionInsideSamConvertedLambda.kt")
public void testSamConversionInsideSamConvertedLambda() throws Exception {
@@ -22480,6 +22492,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/enhancedNullability.kt");
}
@Test
@TestMetadata("genericBoundInnerConstructorRef.kt")
public void testGenericBoundInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/genericBoundInnerConstructorRef.kt");
}
@Test
@TestMetadata("genericInnerConstructorRef.kt")
public void testGenericInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/genericInnerConstructorRef.kt");
}
@Test
@TestMetadata("innerConstructorRef.kt")
public void testInnerConstructorRef() throws Exception {
@@ -22534,6 +22558,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/multifileClassMemberFromStdlib.kt");
}
@Test
@TestMetadata("multipleProxyWrappersForSameFunction.kt")
public void testMultipleProxyWrappersForSameFunction() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/multipleProxyWrappersForSameFunction.kt");
}
@Test
@TestMetadata("nonTrivialReceiver.kt")
public void testNonTrivialReceiver() throws Exception {
@@ -22621,6 +22651,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/enumValues.kt");
}
@Test
@TestMetadata("intArrayOf.kt")
public void testIntArrayOf() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/intArrayOf.kt");
}
@Test
@TestMetadata("intPlus.kt")
public void testIntPlus() throws Exception {
@@ -18495,6 +18495,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/genericFunInterfaceWithPrimitive.kt");
}
@TestMetadata("inlineFunWithPrivateMethod.kt")
public void testInlineFunWithPrivateMethod() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/inlineFunWithPrivateMethod.kt");
}
@TestMetadata("inlineOnly.kt")
public void testInlineOnly() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/inlineOnly.kt");
@@ -18535,6 +18540,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/primitiveVsWrapperInSam.kt");
}
@TestMetadata("reifiedTypeParameter.kt")
public void testReifiedTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/reifiedTypeParameter.kt");
}
@TestMetadata("samConversionInsideSamConvertedLambda.kt")
public void testSamConversionInsideSamConvertedLambda() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/samConversionInsideSamConvertedLambda.kt");
@@ -18705,6 +18715,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/enhancedNullability.kt");
}
@TestMetadata("genericBoundInnerConstructorRef.kt")
public void testGenericBoundInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/genericBoundInnerConstructorRef.kt");
}
@TestMetadata("genericInnerConstructorRef.kt")
public void testGenericInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/genericInnerConstructorRef.kt");
}
@TestMetadata("innerConstructorRef.kt")
public void testInnerConstructorRef() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/innerConstructorRef.kt");
@@ -18750,6 +18770,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/multifileClassMemberFromStdlib.kt");
}
@TestMetadata("multipleProxyWrappersForSameFunction.kt")
public void testMultipleProxyWrappersForSameFunction() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/multipleProxyWrappersForSameFunction.kt");
}
@TestMetadata("nonTrivialReceiver.kt")
public void testNonTrivialReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/nonTrivialReceiver.kt");
@@ -18827,6 +18852,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/enumValues.kt");
}
@TestMetadata("intArrayOf.kt")
public void testIntArrayOf() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/intArrayOf.kt");
}
@TestMetadata("intPlus.kt")
public void testIntPlus() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/specialFunctions/intPlus.kt");