[JS IR] Use a copy of an original inline function for inlining
If inline function A calls another inline function B, we must use the original version of inline function A for inlining, which doesn’t have inlined function B. Because during the inlining process, we remap all occurrences of inline function A to a temporary copy of function A, and if the function B somehow uses function A (e.g. callable reference), the built IR will have a reference to the temporary function, not the original one. All these things lead to broken cross-module references. This patch saves the original versions of all inline functions before inlining and provides them during the inline process. ^KT-55930 Fixed
This commit is contained in:
committed by
Space Team
parent
82d934d873
commit
a5c8e30bb1
+6
-1
@@ -42,6 +42,7 @@ fun IrExpression.isAdaptedFunctionReference() =
|
||||
|
||||
interface InlineFunctionResolver {
|
||||
fun getFunctionDeclaration(symbol: IrFunctionSymbol): IrFunction
|
||||
fun getFunctionSymbol(irFunction: IrFunction): IrFunctionSymbol
|
||||
}
|
||||
|
||||
fun IrFunction.isTopLevelInPackage(name: String, packageName: String): Boolean {
|
||||
@@ -72,6 +73,10 @@ open class DefaultInlineFunctionResolver(open val context: CommonBackendContext)
|
||||
else -> (symbol.owner as? IrSimpleFunction)?.resolveFakeOverride() ?: symbol.owner
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFunctionSymbol(irFunction: IrFunction): IrFunctionSymbol {
|
||||
return irFunction.symbol
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionInlining(
|
||||
@@ -206,7 +211,7 @@ class FunctionInlining(
|
||||
symbol = irReturnableBlockSymbol,
|
||||
origin = null,
|
||||
statements = newStatements,
|
||||
inlineFunctionSymbol = callee.symbol
|
||||
inlineFunctionSymbol = inlineFunctionResolver.getFunctionSymbol(callee)
|
||||
).apply {
|
||||
transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
|
||||
@@ -254,10 +254,10 @@ private val wrapInlineDeclarationsWithReifiedTypeParametersLowering = makeBodyLo
|
||||
description = "Wrap inline declarations with reified type parameters"
|
||||
)
|
||||
|
||||
private val functionInliningPhase = makeBodyLoweringPhase(
|
||||
{ FunctionInlining(it, it.innerClassesSupport) },
|
||||
name = "FunctionInliningPhase",
|
||||
description = "Perform function inlining",
|
||||
private val saveInlineFunctionsBeforeInlining = makeDeclarationTransformerPhase(
|
||||
::SaveInlineFunctionsBeforeInlining,
|
||||
name = "SaveInlineFunctionsBeforeInlining",
|
||||
description = "Save inline function before inlining",
|
||||
prerequisite = setOf(
|
||||
expectDeclarationsRemovingPhase, sharedVariablesLoweringPhase,
|
||||
localClassesInInlineLambdasPhase, localClassesExtractionFromInlineFunctionsPhase,
|
||||
@@ -265,6 +265,13 @@ private val functionInliningPhase = makeBodyLoweringPhase(
|
||||
)
|
||||
)
|
||||
|
||||
private val functionInliningPhase = makeBodyLoweringPhase(
|
||||
{ FunctionInlining(it, JsInlineFunctionResolver(it), it.innerClassesSupport) },
|
||||
name = "FunctionInliningPhase",
|
||||
description = "Perform function inlining",
|
||||
prerequisite = setOf(saveInlineFunctionsBeforeInlining)
|
||||
)
|
||||
|
||||
private val copyInlineFunctionBodyLoweringPhase = makeDeclarationTransformerPhase(
|
||||
::CopyInlineFunctionBodyLowering,
|
||||
name = "CopyInlineFunctionBody",
|
||||
@@ -843,6 +850,7 @@ val loweringList = listOf<Lowering>(
|
||||
localClassesExtractionFromInlineFunctionsPhase,
|
||||
syntheticAccessorLoweringPhase,
|
||||
wrapInlineDeclarationsWithReifiedTypeParametersLowering,
|
||||
saveInlineFunctionsBeforeInlining,
|
||||
functionInliningPhase,
|
||||
copyInlineFunctionBodyLoweringPhase,
|
||||
removeInlineDeclarationsWithReifiedTypeParametersLoweringPhase,
|
||||
|
||||
@@ -33,6 +33,8 @@ class JsMapping : DefaultMapping() {
|
||||
|
||||
val suspendArityStore = DefaultDelegateFactory.newDeclarationToDeclarationCollectionMapping<IrClass, Collection<IrSimpleFunction>>()
|
||||
|
||||
val inlineFunctionsBeforeInlining = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrFunction, IrFunction>()
|
||||
|
||||
// Wasm mappings
|
||||
val wasmJsInteropFunctionToWrapper =
|
||||
DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
|
||||
@@ -45,4 +47,4 @@ class JsMapping : DefaultMapping() {
|
||||
|
||||
val wasmExternalClassToInstanceCheck =
|
||||
DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||
}
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower.inline
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
|
||||
import org.jetbrains.kotlin.backend.common.lower.inline.DefaultInlineFunctionResolver
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||
|
||||
internal class SaveInlineFunctionsBeforeInlining(context: JsIrBackendContext) : DeclarationTransformer {
|
||||
private val inlineFunctionsBeforeInlining = context.mapping.inlineFunctionsBeforeInlining
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (declaration is IrFunction && declaration.isInline) {
|
||||
inlineFunctionsBeforeInlining[declaration] = declaration.deepCopyWithVariables().also {
|
||||
it.patchDeclarationParents(declaration.parent)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
internal class JsInlineFunctionResolver(context: JsIrBackendContext) : DefaultInlineFunctionResolver(context) {
|
||||
private val inlineFunctionsBeforeInlining = context.mapping.inlineFunctionsBeforeInlining
|
||||
private val inlineFunctionsBeforeInliningSymbols = hashMapOf<IrFunction, IrFunctionSymbol>()
|
||||
|
||||
override fun getFunctionDeclaration(symbol: IrFunctionSymbol): IrFunction {
|
||||
val function = super.getFunctionDeclaration(symbol)
|
||||
val functionBeforeInlining = inlineFunctionsBeforeInlining[function] ?: return function
|
||||
inlineFunctionsBeforeInliningSymbols[functionBeforeInlining] = function.symbol
|
||||
return functionBeforeInlining
|
||||
}
|
||||
|
||||
override fun getFunctionSymbol(irFunction: IrFunction): IrFunctionSymbol {
|
||||
return inlineFunctionsBeforeInliningSymbols[irFunction] ?: super.getFunctionSymbol(irFunction)
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -117,7 +117,8 @@ class CrossModuleDependenciesResolver(
|
||||
if (tag in header.optionalCrossModuleImports) {
|
||||
continue
|
||||
}
|
||||
error("Internal error: cannot find external signature '$tag' for module ${header.moduleName}")
|
||||
val name = header.nameBindings[tag] ?: "<unknown name>"
|
||||
error("Internal error: cannot find external signature '$tag' for name '$name' in module ${header.moduleName}")
|
||||
}
|
||||
|
||||
builder.imports += CrossModuleRef(fromModuleBuilder, tag)
|
||||
|
||||
@@ -5162,6 +5162,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/inline/privateProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("recursiveDependency.kt")
|
||||
public void testRecursiveDependency() throws Exception {
|
||||
runTest("js/js.translator/testData/box/inline/recursiveDependency.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rootConstructor.kt")
|
||||
public void testRootConstructor() throws Exception {
|
||||
|
||||
+6
@@ -5784,6 +5784,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
|
||||
runTest("js/js.translator/testData/box/inline/privateProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("recursiveDependency.kt")
|
||||
public void testRecursiveDependency() throws Exception {
|
||||
runTest("js/js.translator/testData/box/inline/recursiveDependency.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rootConstructor.kt")
|
||||
public void testRootConstructor() throws Exception {
|
||||
|
||||
+6
@@ -5784,6 +5784,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/inline/privateProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("recursiveDependency.kt")
|
||||
public void testRecursiveDependency() throws Exception {
|
||||
runTest("js/js.translator/testData/box/inline/recursiveDependency.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("rootConstructor.kt")
|
||||
public void testRootConstructor() throws Exception {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// MODULE: lib
|
||||
// FILE: A.kt
|
||||
inline fun funA(flag: Boolean): String {
|
||||
if (flag) {
|
||||
return funB(flag)
|
||||
}
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// FILE: B.kt
|
||||
inline fun funB(flag: Boolean): String {
|
||||
val f = ::funA
|
||||
if (flag) {
|
||||
return f(false)
|
||||
}
|
||||
return "NOT OK"
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
fun box(): String {
|
||||
return funA(true)
|
||||
}
|
||||
Reference in New Issue
Block a user