[JVM_IR] Deal with lowering ordering issues for JvmStatic function references.
Do not destructively update the @JvmStatic function, instead create a copy on first access, and replace the original with the copy in the jvm static lowering. This ensures that the original function is seen in other lowerings independently of file lowering order.
This commit is contained in:
committed by
Alexander Udalov
parent
3d2f5f4bc1
commit
7b4e0b2f5d
Generated
+5
@@ -17779,6 +17779,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/jvmStatic/funAccess.kt");
|
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")
|
@TestMetadata("importStaticMemberFromObject.kt")
|
||||||
public void testImportStaticMemberFromObject() throws Exception {
|
public void testImportStaticMemberFromObject() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");
|
runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ class JvmBackendContext(
|
|||||||
val suspendFunctionOriginalToView = mutableMapOf<IrFunction, IrFunction>()
|
val suspendFunctionOriginalToView = mutableMapOf<IrFunction, IrFunction>()
|
||||||
val fakeContinuation: IrExpression = createFakeContinuation(this)
|
val fakeContinuation: IrExpression = createFakeContinuation(this)
|
||||||
|
|
||||||
|
val jvmStaticObjectFunctionToStaticFunctionMap = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
|
||||||
|
|
||||||
val staticDefaultStubs = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
val staticDefaultStubs = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
|
||||||
|
|
||||||
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory)
|
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory)
|
||||||
|
|||||||
+47
-19
@@ -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.copyCorrespondingPropertyFrom
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.isInCurrentModule
|
import org.jetbrains.kotlin.backend.jvm.ir.isInCurrentModule
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
|
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.DescriptorVisibilities
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||||
import org.jetbrains.kotlin.ir.builders.irCall
|
import org.jetbrains.kotlin.ir.builders.irCall
|
||||||
@@ -127,36 +127,62 @@ private class CompanionObjectJvmStaticLowering(val context: JvmBackendContext) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SingletonObjectJvmStaticLowering(
|
private class SingletonObjectJvmStaticLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||||
val context: JvmBackendContext
|
|
||||||
) : ClassLoweringPass {
|
|
||||||
override fun lower(irClass: IrClass) {
|
override fun lower(irClass: IrClass) {
|
||||||
if (!irClass.isObject || irClass.isCompanion) return
|
if (!irClass.isObject || irClass.isCompanion) return
|
||||||
|
|
||||||
irClass.declarations.filter(::isJvmStaticFunction).forEach {
|
val jvmStaticFunctionsToReplace = irClass.declarations.filter {
|
||||||
val jvmStaticFunction = it as IrSimpleFunction
|
|
||||||
// dispatch receiver parameter is already null for synthetic property annotation methods
|
// dispatch receiver parameter is already null for synthetic property annotation methods
|
||||||
jvmStaticFunction.dispatchReceiverParameter?.let { oldDispatchReceiverParameter ->
|
isJvmStaticFunction(it) && it is IrSimpleFunction && it.dispatchReceiverParameter != null
|
||||||
jvmStaticFunction.dispatchReceiverParameter = null
|
}
|
||||||
jvmStaticFunction.body = jvmStaticFunction.body?.replaceThisByStaticReference(
|
jvmStaticFunctionsToReplace.forEach { function ->
|
||||||
context.cachedDeclarations,
|
val replacement = createReplacement(context, function as IrSimpleFunction)
|
||||||
irClass,
|
// Set dispatch receiver parameter for body move operation.
|
||||||
oldDispatchReceiverParameter
|
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 {
|
private fun IrFunction.isJvmStaticInSingleton(): Boolean {
|
||||||
val parentClass = parent as? IrClass ?: return false
|
val parentClass = parent as? IrClass ?: return false
|
||||||
return isJvmStaticFunction(this) && parentClass.isObject && !parentClass.isCompanion
|
return isJvmStaticFunction(this) && parentClass.isObject && !parentClass.isCompanion
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MakeCallsStatic(
|
private class MakeCallsStatic(val context: JvmBackendContext) : IrElementTransformerVoid() {
|
||||||
val context: JvmBackendContext
|
|
||||||
) : IrElementTransformerVoid() {
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
if (expression.symbol.owner.isJvmStaticInSingleton() && expression.dispatchReceiver != null) {
|
if (expression.symbol.owner.isJvmStaticInSingleton() && expression.dispatchReceiver != null) {
|
||||||
// Imported functions do not have their receiver parameter nulled by SingletonObjectJvmStaticLowering,
|
// Imported functions do not have their receiver parameter nulled by SingletonObjectJvmStaticLowering,
|
||||||
@@ -165,7 +191,9 @@ private class MakeCallsStatic(
|
|||||||
val callee = expression.symbol.owner
|
val callee = expression.symbol.owner
|
||||||
val newCallee = if (!callee.isInCurrentModule()) {
|
val newCallee = if (!callee.isInCurrentModule()) {
|
||||||
callee.copyRemovingDispatchReceiver() // TODO: cache these
|
callee.copyRemovingDispatchReceiver() // TODO: cache these
|
||||||
} else callee
|
} else {
|
||||||
|
createReplacement(context, callee)
|
||||||
|
}
|
||||||
|
|
||||||
return context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset).irBlock(expression) {
|
return context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset).irBlock(expression) {
|
||||||
// OldReceiver has to be evaluated for its side effects.
|
// OldReceiver has to be evaluated for its side effects.
|
||||||
|
|||||||
@@ -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)()
|
||||||
+5
@@ -19179,6 +19179,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/jvmStatic/funAccess.kt");
|
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")
|
@TestMetadata("importStaticMemberFromObject.kt")
|
||||||
public void testImportStaticMemberFromObject() throws Exception {
|
public void testImportStaticMemberFromObject() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");
|
runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");
|
||||||
|
|||||||
+5
@@ -19179,6 +19179,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/jvmStatic/funAccess.kt");
|
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")
|
@TestMetadata("importStaticMemberFromObject.kt")
|
||||||
public void testImportStaticMemberFromObject() throws Exception {
|
public void testImportStaticMemberFromObject() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");
|
runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");
|
||||||
|
|||||||
+5
@@ -17779,6 +17779,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/jvmStatic/funAccess.kt");
|
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")
|
@TestMetadata("importStaticMemberFromObject.kt")
|
||||||
public void testImportStaticMemberFromObject() throws Exception {
|
public void testImportStaticMemberFromObject() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");
|
runTest("compiler/testData/codegen/box/jvmStatic/importStaticMemberFromObject.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user