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 b7a9c69695a..e060073994a 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 @@ -17779,6 +17779,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/jvmStatic/funAccess.kt"); } + @TestMetadata("functionReference.kt") + public void testFunctionReference() throws Exception { + runTest("compiler/testData/codegen/box/jvmStatic/functionReference.kt"); + } + @TestMetadata("importStaticMemberFromObject.kt") public void testImportStaticMemberFromObject() throws Exception { runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt"); diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 5fc528ef78e..75fd6b0263c 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -129,6 +129,8 @@ class JvmBackendContext( val suspendFunctionOriginalToView = mutableMapOf() val fakeContinuation: IrExpression = createFakeContinuation(this) + val jvmStaticObjectFunctionToStaticFunctionMap = mutableMapOf() + val staticDefaultStubs = mutableMapOf() val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStaticAnnotationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStaticAnnotationLowering.kt index a1c0d7be115..81cf15b7cd3 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStaticAnnotationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmStaticAnnotationLowering.kt @@ -17,8 +17,8 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.ir.copyCorrespondingPropertyFrom import org.jetbrains.kotlin.backend.jvm.ir.isInCurrentModule import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference -import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.irCall @@ -127,36 +127,62 @@ private class CompanionObjectJvmStaticLowering(val context: JvmBackendContext) : } } -private class SingletonObjectJvmStaticLowering( - val context: JvmBackendContext -) : ClassLoweringPass { +private class SingletonObjectJvmStaticLowering(val context: JvmBackendContext) : ClassLoweringPass { override fun lower(irClass: IrClass) { if (!irClass.isObject || irClass.isCompanion) return - irClass.declarations.filter(::isJvmStaticFunction).forEach { - val jvmStaticFunction = it as IrSimpleFunction + val jvmStaticFunctionsToReplace = irClass.declarations.filter { // dispatch receiver parameter is already null for synthetic property annotation methods - jvmStaticFunction.dispatchReceiverParameter?.let { oldDispatchReceiverParameter -> - jvmStaticFunction.dispatchReceiverParameter = null - jvmStaticFunction.body = jvmStaticFunction.body?.replaceThisByStaticReference( - context.cachedDeclarations, - irClass, - oldDispatchReceiverParameter - ) - } + isJvmStaticFunction(it) && it is IrSimpleFunction && it.dispatchReceiverParameter != null + } + jvmStaticFunctionsToReplace.forEach { function -> + val replacement = createReplacement(context, function as IrSimpleFunction) + // Set dispatch receiver parameter for body move operation. + replacement.dispatchReceiverParameter = function.dispatchReceiverParameter + replacement.body = function.moveBodyTo(replacement)?.replaceThisByStaticReference( + context.cachedDeclarations, + irClass, + function.dispatchReceiverParameter!! + ) + // Clear dispatch receiver parameter again after body move operation. + replacement.dispatchReceiverParameter = null + irClass.declarations.remove(function) + irClass.declarations.add(replacement) } } - } +private fun createReplacement( + context: JvmBackendContext, + jvmStaticFunction: IrSimpleFunction +): IrSimpleFunction = + context.jvmStaticObjectFunctionToStaticFunctionMap.getOrPut(jvmStaticFunction) { + val irClass = jvmStaticFunction.parentAsClass + val newFunction = context.irFactory.buildFun { + updateFrom(jvmStaticFunction) + name = jvmStaticFunction.name + returnType = jvmStaticFunction.returnType + }.apply { + parent = irClass + copyTypeParametersFrom(jvmStaticFunction) + copyAnnotationsFrom(jvmStaticFunction) + extensionReceiverParameter = jvmStaticFunction.extensionReceiverParameter?.copyTo(this) + valueParameters = jvmStaticFunction.valueParameters.map { it.copyTo(this) } + copyAttributes(jvmStaticFunction) + copyCorrespondingPropertyFrom(jvmStaticFunction) + metadata = jvmStaticFunction.metadata + } + context.jvmStaticObjectFunctionToStaticFunctionMap[jvmStaticFunction] = newFunction + newFunction + } + + private fun IrFunction.isJvmStaticInSingleton(): Boolean { val parentClass = parent as? IrClass ?: return false return isJvmStaticFunction(this) && parentClass.isObject && !parentClass.isCompanion } -private class MakeCallsStatic( - val context: JvmBackendContext -) : IrElementTransformerVoid() { +private class MakeCallsStatic(val context: JvmBackendContext) : IrElementTransformerVoid() { override fun visitCall(expression: IrCall): IrExpression { if (expression.symbol.owner.isJvmStaticInSingleton() && expression.dispatchReceiver != null) { // Imported functions do not have their receiver parameter nulled by SingletonObjectJvmStaticLowering, @@ -165,7 +191,9 @@ private class MakeCallsStatic( val callee = expression.symbol.owner val newCallee = if (!callee.isInCurrentModule()) { callee.copyRemovingDispatchReceiver() // TODO: cache these - } else callee + } else { + createReplacement(context, callee) + } return context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset).irBlock(expression) { // OldReceiver has to be evaluated for its side effects. diff --git a/compiler/testData/codegen/box/jvmStatic/functionReference.kt b/compiler/testData/codegen/box/jvmStatic/functionReference.kt new file mode 100644 index 00000000000..93f187f3904 --- /dev/null +++ b/compiler/testData/codegen/box/jvmStatic/functionReference.kt @@ -0,0 +1,13 @@ +// TARGET_BACKEND: JVM + +// WITH_RUNTIME +// FILE: u1.kt + +object O { + @JvmStatic + fun foo() = "OK" +} + +// FILE: u2.kt + +fun box() = (O::foo)() diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index aeb7c5cf9b2..bfb847b9f6e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -19179,6 +19179,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/jvmStatic/funAccess.kt"); } + @TestMetadata("functionReference.kt") + public void testFunctionReference() throws Exception { + runTest("compiler/testData/codegen/box/jvmStatic/functionReference.kt"); + } + @TestMetadata("importStaticMemberFromObject.kt") public void testImportStaticMemberFromObject() throws Exception { runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 06ce5e9fda9..96d8a6ee25b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -19179,6 +19179,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/jvmStatic/funAccess.kt"); } + @TestMetadata("functionReference.kt") + public void testFunctionReference() throws Exception { + runTest("compiler/testData/codegen/box/jvmStatic/functionReference.kt"); + } + @TestMetadata("importStaticMemberFromObject.kt") public void testImportStaticMemberFromObject() throws Exception { runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 3afc9182e33..bcf94dd10fe 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -17779,6 +17779,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/jvmStatic/funAccess.kt"); } + @TestMetadata("functionReference.kt") + public void testFunctionReference() throws Exception { + runTest("compiler/testData/codegen/box/jvmStatic/functionReference.kt"); + } + @TestMetadata("importStaticMemberFromObject.kt") public void testImportStaticMemberFromObject() throws Exception { runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");