Do not generate references as adapted with -Xno-optimized-callable-references
The reason for this is that this flag is used right now in 'cli-common' to workaround the problem that this module is compiled with API version 1.4, but runs with stdlib of version 1.3 (bundled to Gradle). The same problem would appear with adapted function references, since we use kotlin/jvm/internal/AdaptedFunctionReference in the bytecode, only available since 1.4. The fix is to generate adapted references in this case as subclasses of the already existing kotlin/jvm/internal/FunctionReference. This can change behavior in some extreme corner cases (because such references can now be observed to have reflection capabilities), but it's an -X argument anyway. Another option would be to introduce another compiler argument specifically for this, but it looks like it would only complicate things without much benefit.
This commit is contained in:
@@ -154,8 +154,10 @@ class JvmRuntimeTypes(
|
||||
|
||||
val suspendFunctionType = if (referencedFunction.isSuspend) suspendFunctionInterface?.defaultType else null
|
||||
val superClass = when {
|
||||
isAdaptedCallableReference || isSuspendConversion -> adaptedFunctionReference
|
||||
generateOptimizedCallableReferenceSuperClasses -> functionReferenceImpl
|
||||
generateOptimizedCallableReferenceSuperClasses -> when {
|
||||
isAdaptedCallableReference || isSuspendConversion -> adaptedFunctionReference
|
||||
else -> functionReferenceImpl
|
||||
}
|
||||
else -> functionReference
|
||||
}
|
||||
return listOfNotNull(superClass.defaultType, functionType, suspendFunctionType)
|
||||
@@ -178,4 +180,4 @@ class JvmRuntimeTypes(
|
||||
|
||||
return classes[arity].defaultType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+5
@@ -2070,6 +2070,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt")
|
||||
public void testNoAdaptedReferencesIfNoOptimizedReferencesEnabled() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noReflectionForAdaptedCallableReferences.kt")
|
||||
public void testNoReflectionForAdaptedCallableReferences() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt");
|
||||
|
||||
+15
-20
@@ -124,10 +124,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
private val useOptimizedSuperClass =
|
||||
context.state.generateOptimizedCallableReferenceSuperClasses
|
||||
|
||||
private val adaptedReferenceOriginalTarget: IrFunction?
|
||||
private val adapteeCall: IrFunctionAccessExpression?
|
||||
|
||||
init {
|
||||
private val adapteeCall: IrFunctionAccessExpression? =
|
||||
if (callee.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) {
|
||||
// The body of a callable reference adapter contains either only a call, or an IMPLICIT_COERCION_TO_UNIT type operator
|
||||
// applied to a call. That call's target is the original function which we need to get owner/name/signature.
|
||||
@@ -144,24 +141,26 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
if (call !is IrFunctionAccessExpression) {
|
||||
throw UnsupportedOperationException("Unknown structure of ADAPTER_FOR_CALLABLE_REFERENCE: ${callee.render()}")
|
||||
}
|
||||
adapteeCall = call
|
||||
adaptedReferenceOriginalTarget = call.symbol.owner
|
||||
call
|
||||
} else {
|
||||
adapteeCall = null
|
||||
adaptedReferenceOriginalTarget = null
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private val adaptedReferenceOriginalTarget: IrFunction? = adapteeCall?.symbol?.owner
|
||||
private val isAdaptedReference = adaptedReferenceOriginalTarget != null
|
||||
|
||||
private val needToGenerateSamEqualsHashCodeMethods =
|
||||
samSuperType != null &&
|
||||
samSuperType.getClass()?.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB &&
|
||||
(adaptedReferenceOriginalTarget != null || !isLambda)
|
||||
(isAdaptedReference || !isLambda)
|
||||
|
||||
private val superType =
|
||||
samSuperType ?: when {
|
||||
adaptedReferenceOriginalTarget != null -> context.ir.symbols.adaptedFunctionReference
|
||||
isLambda -> context.ir.symbols.lambdaClass
|
||||
useOptimizedSuperClass -> context.ir.symbols.functionReferenceImpl
|
||||
useOptimizedSuperClass -> when {
|
||||
isAdaptedReference -> context.ir.symbols.adaptedFunctionReference
|
||||
else -> context.ir.symbols.functionReferenceImpl
|
||||
}
|
||||
else -> context.ir.symbols.functionReference
|
||||
}.defaultType
|
||||
|
||||
@@ -258,7 +257,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
|
||||
SamEqualsHashCodeMethodsGenerator(backendContext, functionReferenceClass, samSuperType) { receiver ->
|
||||
val internalClass = when {
|
||||
adaptedReferenceOriginalTarget != null -> backendContext.ir.symbols.adaptedFunctionReference
|
||||
isAdaptedReference -> backendContext.ir.symbols.adaptedFunctionReference
|
||||
else -> backendContext.ir.symbols.functionReferenceImpl
|
||||
}
|
||||
val constructor = internalClass.owner.constructors.single {
|
||||
@@ -299,7 +298,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
context.irBuiltIns.anyClass.owner.constructors.single()
|
||||
} else {
|
||||
val expectedArity =
|
||||
if (isLambda && adaptedReferenceOriginalTarget == null) 1
|
||||
if (isLambda && !isAdaptedReference) 1
|
||||
else 1 + (if (boundReceiver != null) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0)
|
||||
superType.getClass()!!.constructors.single {
|
||||
it.valueParameters.size == expectedArity
|
||||
@@ -327,12 +326,8 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
if (boundReceiver != null) {
|
||||
call.putValueArgument(index++, generateBoundReceiver())
|
||||
}
|
||||
val callableReferenceTarget = when {
|
||||
adaptedReferenceOriginalTarget != null -> adaptedReferenceOriginalTarget
|
||||
!isLambda && useOptimizedSuperClass -> callee
|
||||
else -> null
|
||||
}
|
||||
if (callableReferenceTarget != null) {
|
||||
if (!isLambda && useOptimizedSuperClass) {
|
||||
val callableReferenceTarget = adaptedReferenceOriginalTarget ?: callee
|
||||
val owner = calculateOwnerKClass(callableReferenceTarget.parent, backendContext)
|
||||
call.putValueArgument(index++, kClassToJavaClass(owner, backendContext))
|
||||
call.putValueArgument(index++, irString(callableReferenceTarget.originalName.asString()))
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// KOTLIN_CONFIGURATION_FLAGS: +JVM.NO_OPTIMIZED_CALLABLE_REFERENCES
|
||||
|
||||
class A {
|
||||
fun target(): Int = 42
|
||||
}
|
||||
|
||||
fun foo(f: () -> Unit): Any = f
|
||||
|
||||
fun box(): String {
|
||||
val o = foo(A()::target)
|
||||
if (o is kotlin.jvm.internal.AdaptedFunctionReference ||
|
||||
o !is kotlin.jvm.internal.FunctionReference)
|
||||
return "Fail: we shouldn't generate reference to AdaptedFunctionReference if -Xno-optimized-callable-references is enabled"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+5
@@ -2090,6 +2090,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt")
|
||||
public void testNoAdaptedReferencesIfNoOptimizedReferencesEnabled() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noReflectionForAdaptedCallableReferences.kt")
|
||||
public void testNoReflectionForAdaptedCallableReferences() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt");
|
||||
|
||||
+5
@@ -2090,6 +2090,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt")
|
||||
public void testNoAdaptedReferencesIfNoOptimizedReferencesEnabled() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noReflectionForAdaptedCallableReferences.kt")
|
||||
public void testNoReflectionForAdaptedCallableReferences() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt");
|
||||
|
||||
+5
@@ -2070,6 +2070,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt")
|
||||
public void testNoAdaptedReferencesIfNoOptimizedReferencesEnabled() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noReflectionForAdaptedCallableReferences.kt")
|
||||
public void testNoReflectionForAdaptedCallableReferences() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt");
|
||||
|
||||
Reference in New Issue
Block a user