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 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
}
}
}
@@ -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");
@@ -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()))
@@ -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");
}
@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");
@@ -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");
@@ -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");