From 54f9f130e2b4f866d554557e902a736537e17855 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 30 Apr 2020 21:22:18 +0200 Subject: [PATCH] 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. --- .../kotlin/codegen/JvmRuntimeTypes.kt | 8 +++-- .../ir/FirBlackBoxCodegenTestGenerated.java | 5 +++ .../jvm/lower/FunctionReferenceLowering.kt | 35 ++++++++----------- ...eferencesIfNoOptimizedReferencesEnabled.kt | 19 ++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 +++ .../LightAnalysisModeTestGenerated.java | 5 +++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 +++ 7 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 compiler/testData/codegen/box/callableReference/adaptedReferences/noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index 5e8d9872b71..d099417addf 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -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 } -} \ No newline at end of file +} diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 4902f0b4377..5ee1b42ad0e 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 86bd62b2c0c..6aeec2ec748 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.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())) diff --git a/compiler/testData/codegen/box/callableReference/adaptedReferences/noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt b/compiler/testData/codegen/box/callableReference/adaptedReferences/noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt new file mode 100644 index 00000000000..cc0dd0863a5 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/adaptedReferences/noAdaptedReferencesIfNoOptimizedReferencesEnabled.kt @@ -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" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index d1faece6135..ce9be7fd49c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index e99154a61b9..954534d0290 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index d096009b640..61020652f46 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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");