[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:
Mads Ager
2020-11-04 15:59:26 +01:00
committed by Alexander Udalov
parent 3d2f5f4bc1
commit 7b4e0b2f5d
7 changed files with 82 additions and 19 deletions
@@ -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");
@@ -129,6 +129,8 @@ class JvmBackendContext(
val suspendFunctionOriginalToView = mutableMapOf<IrFunction, IrFunction>()
val fakeContinuation: IrExpression = createFakeContinuation(this)
val jvmStaticObjectFunctionToStaticFunctionMap = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
val staticDefaultStubs = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory)
@@ -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.
@@ -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)()
@@ -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");
@@ -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");
@@ -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");