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:
Alexander Udalov
2020-04-30 21:22:18 +02:00
parent 4f7599076c
commit 54f9f130e2
7 changed files with 59 additions and 23 deletions
@@ -154,8 +154,10 @@ class JvmRuntimeTypes(
val suspendFunctionType = if (referencedFunction.isSuspend) suspendFunctionInterface?.defaultType else null val suspendFunctionType = if (referencedFunction.isSuspend) suspendFunctionInterface?.defaultType else null
val superClass = when { val superClass = when {
isAdaptedCallableReference || isSuspendConversion -> adaptedFunctionReference generateOptimizedCallableReferenceSuperClasses -> when {
generateOptimizedCallableReferenceSuperClasses -> functionReferenceImpl isAdaptedCallableReference || isSuspendConversion -> adaptedFunctionReference
else -> functionReferenceImpl
}
else -> functionReference else -> functionReference
} }
return listOfNotNull(superClass.defaultType, functionType, suspendFunctionType) return listOfNotNull(superClass.defaultType, functionType, suspendFunctionType)
@@ -2070,6 +2070,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt"); 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") @TestMetadata("noReflectionForAdaptedCallableReferences.kt")
public void testNoReflectionForAdaptedCallableReferences() throws Exception { public void testNoReflectionForAdaptedCallableReferences() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt"); runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt");
@@ -124,10 +124,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
private val useOptimizedSuperClass = private val useOptimizedSuperClass =
context.state.generateOptimizedCallableReferenceSuperClasses context.state.generateOptimizedCallableReferenceSuperClasses
private val adaptedReferenceOriginalTarget: IrFunction? private val adapteeCall: IrFunctionAccessExpression? =
private val adapteeCall: IrFunctionAccessExpression?
init {
if (callee.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) { 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 // 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. // 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) { if (call !is IrFunctionAccessExpression) {
throw UnsupportedOperationException("Unknown structure of ADAPTER_FOR_CALLABLE_REFERENCE: ${callee.render()}") throw UnsupportedOperationException("Unknown structure of ADAPTER_FOR_CALLABLE_REFERENCE: ${callee.render()}")
} }
adapteeCall = call call
adaptedReferenceOriginalTarget = call.symbol.owner
} else { } else {
adapteeCall = null null
adaptedReferenceOriginalTarget = null
} }
}
private val adaptedReferenceOriginalTarget: IrFunction? = adapteeCall?.symbol?.owner
private val isAdaptedReference = adaptedReferenceOriginalTarget != null
private val needToGenerateSamEqualsHashCodeMethods = private val needToGenerateSamEqualsHashCodeMethods =
samSuperType != null && samSuperType != null &&
samSuperType.getClass()?.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && samSuperType.getClass()?.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB &&
(adaptedReferenceOriginalTarget != null || !isLambda) (isAdaptedReference || !isLambda)
private val superType = private val superType =
samSuperType ?: when { samSuperType ?: when {
adaptedReferenceOriginalTarget != null -> context.ir.symbols.adaptedFunctionReference
isLambda -> context.ir.symbols.lambdaClass 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 else -> context.ir.symbols.functionReference
}.defaultType }.defaultType
@@ -258,7 +257,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
SamEqualsHashCodeMethodsGenerator(backendContext, functionReferenceClass, samSuperType) { receiver -> SamEqualsHashCodeMethodsGenerator(backendContext, functionReferenceClass, samSuperType) { receiver ->
val internalClass = when { val internalClass = when {
adaptedReferenceOriginalTarget != null -> backendContext.ir.symbols.adaptedFunctionReference isAdaptedReference -> backendContext.ir.symbols.adaptedFunctionReference
else -> backendContext.ir.symbols.functionReferenceImpl else -> backendContext.ir.symbols.functionReferenceImpl
} }
val constructor = internalClass.owner.constructors.single { val constructor = internalClass.owner.constructors.single {
@@ -299,7 +298,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
context.irBuiltIns.anyClass.owner.constructors.single() context.irBuiltIns.anyClass.owner.constructors.single()
} else { } else {
val expectedArity = val expectedArity =
if (isLambda && adaptedReferenceOriginalTarget == null) 1 if (isLambda && !isAdaptedReference) 1
else 1 + (if (boundReceiver != null) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0) else 1 + (if (boundReceiver != null) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0)
superType.getClass()!!.constructors.single { superType.getClass()!!.constructors.single {
it.valueParameters.size == expectedArity it.valueParameters.size == expectedArity
@@ -327,12 +326,8 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
if (boundReceiver != null) { if (boundReceiver != null) {
call.putValueArgument(index++, generateBoundReceiver()) call.putValueArgument(index++, generateBoundReceiver())
} }
val callableReferenceTarget = when { if (!isLambda && useOptimizedSuperClass) {
adaptedReferenceOriginalTarget != null -> adaptedReferenceOriginalTarget val callableReferenceTarget = adaptedReferenceOriginalTarget ?: callee
!isLambda && useOptimizedSuperClass -> callee
else -> null
}
if (callableReferenceTarget != null) {
val owner = calculateOwnerKClass(callableReferenceTarget.parent, backendContext) val owner = calculateOwnerKClass(callableReferenceTarget.parent, backendContext)
call.putValueArgument(index++, kClassToJavaClass(owner, backendContext)) call.putValueArgument(index++, kClassToJavaClass(owner, backendContext))
call.putValueArgument(index++, irString(callableReferenceTarget.originalName.asString())) call.putValueArgument(index++, irString(callableReferenceTarget.originalName.asString()))
@@ -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"
}
@@ -2090,6 +2090,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt"); 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") @TestMetadata("noReflectionForAdaptedCallableReferences.kt")
public void testNoReflectionForAdaptedCallableReferences() throws Exception { public void testNoReflectionForAdaptedCallableReferences() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt"); runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt");
@@ -2090,6 +2090,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt"); 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") @TestMetadata("noReflectionForAdaptedCallableReferences.kt")
public void testNoReflectionForAdaptedCallableReferences() throws Exception { public void testNoReflectionForAdaptedCallableReferences() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt"); runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt");
@@ -2070,6 +2070,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/manyDefaultsAndVararg.kt"); 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") @TestMetadata("noReflectionForAdaptedCallableReferences.kt")
public void testNoReflectionForAdaptedCallableReferences() throws Exception { public void testNoReflectionForAdaptedCallableReferences() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt"); runTest("compiler/testData/codegen/box/callableReference/adaptedReferences/noReflectionForAdaptedCallableReferences.kt");