[FIR2IR] Generate IR for unbound symbols in allowNonCachedDeclarations mode
`allowNonCachedDeclarations` mode allows referring source declaration which does not belong to the set of sources passed to fir2ir (e.g. for debugger support). So if code refers to such declaration, fir2ir creates symbol for it but not the IR declaration itself
This commit is contained in:
committed by
Space Team
parent
c9ff0c41fc
commit
3dff232710
+12
@@ -208,6 +208,12 @@ public class FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerate
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/local.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localFunction.kt")
|
||||
public void testLocalFunction() throws Exception {
|
||||
@@ -262,6 +268,12 @@ public class FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerate
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localMutated.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localVariable.kt")
|
||||
public void testLocalVariable() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("multipleClassAndFunctionContextReceivers.kt")
|
||||
public void testMultipleClassAndFunctionContextReceivers() throws Exception {
|
||||
|
||||
+12
@@ -208,6 +208,12 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/local.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localFunction.kt")
|
||||
public void testLocalFunction() throws Exception {
|
||||
@@ -262,6 +268,12 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localMutated.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localVariable.kt")
|
||||
public void testLocalVariable() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("multipleClassAndFunctionContextReceivers.kt")
|
||||
public void testMultipleClassAndFunctionContextReceivers() throws Exception {
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
LocalDelegate[name: x; isMutated: true; displayText: x$delegate]
|
||||
lvar x: R|kotlin/String|
|
||||
R|kotlin/String|
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
x = "O"
|
||||
x + "K"
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
MODULE_FRAGMENT
|
||||
FILE fqName:<root> fileName:fragment.kt
|
||||
CLASS CLASS name:CodeFragment modality:FINAL visibility:public superTypes:[kotlin.Any]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.CodeFragment
|
||||
CONSTRUCTOR visibility:public <> () returnType:<root>.CodeFragment [primary]
|
||||
BLOCK_BODY
|
||||
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
|
||||
FUN name:run visibility:public modality:FINAL <> (p0:kotlin.String) returnType:kotlin.String
|
||||
VALUE_PARAMETER SHARED_VARIABLE_IN_EVALUATOR_FRAGMENT name:p0 index:0 type:kotlin.String [assignable]
|
||||
EXPRESSION_BODY
|
||||
BLOCK type=kotlin.String origin=null
|
||||
SET_VAR 'p0: kotlin.String [assignable] declared in <root>.CodeFragment.run' type=kotlin.Unit origin=null
|
||||
CONST String type=kotlin.String value="O"
|
||||
CALL 'public final fun plus (other: kotlin.Any?): kotlin.String [operator] declared in kotlin.String' type=kotlin.String origin=PLUS
|
||||
$this: GET_VAR 'p0: kotlin.String [assignable] declared in <root>.CodeFragment.run' type=kotlin.String origin=null
|
||||
other: CONST String type=kotlin.String value="K"
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Delegate(private var value: String) {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
|
||||
return value
|
||||
}
|
||||
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
|
||||
this.value = value
|
||||
}
|
||||
}
|
||||
|
||||
fun test() {
|
||||
var x by Delegate("a")
|
||||
<caret>x
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public final class CodeFragment {
|
||||
// source: 'fragment.kt'
|
||||
public method <init>(): void
|
||||
public final static method run(p0: java.lang.String): java.lang.String
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Local[name: x; isMutated: true; displayText: x]
|
||||
lvar x: R|kotlin/String|
|
||||
R|kotlin/String|
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
x = "O"
|
||||
x + "K"
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
MODULE_FRAGMENT
|
||||
FILE fqName:<root> fileName:fragment.kt
|
||||
CLASS CLASS name:CodeFragment modality:FINAL visibility:public superTypes:[kotlin.Any]
|
||||
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.CodeFragment
|
||||
CONSTRUCTOR visibility:public <> () returnType:<root>.CodeFragment [primary]
|
||||
BLOCK_BODY
|
||||
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
|
||||
FUN name:run visibility:public modality:FINAL <> (p0:kotlin.String) returnType:kotlin.String
|
||||
VALUE_PARAMETER SHARED_VARIABLE_IN_EVALUATOR_FRAGMENT name:p0 index:0 type:kotlin.String [assignable]
|
||||
EXPRESSION_BODY
|
||||
BLOCK type=kotlin.String origin=null
|
||||
SET_VAR 'p0: kotlin.String [assignable] declared in <root>.CodeFragment.run' type=kotlin.Unit origin=null
|
||||
CONST String type=kotlin.String value="O"
|
||||
CALL 'public final fun plus (other: kotlin.Any?): kotlin.String [operator] declared in kotlin.String' type=kotlin.String origin=PLUS
|
||||
$this: GET_VAR 'p0: kotlin.String [assignable] declared in <root>.CodeFragment.run' type=kotlin.String origin=null
|
||||
other: CONST String type=kotlin.String value="K"
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
fun test() {
|
||||
var x = "a"
|
||||
<caret>x
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public final class CodeFragment {
|
||||
// source: 'fragment.kt'
|
||||
public method <init>(): void
|
||||
public final static method run(p0: java.lang.String): java.lang.String
|
||||
}
|
||||
+12
@@ -84,6 +84,12 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/local.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localDelegatedProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localFunction.kt")
|
||||
public void testLocalFunction() throws Exception {
|
||||
@@ -138,6 +144,12 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localMutated.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("localVariable.kt")
|
||||
public void testLocalVariable() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/localVariable.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("multipleClassAndFunctionContextReceivers.kt")
|
||||
public void testMultipleClassAndFunctionContextReceivers() throws Exception {
|
||||
|
||||
@@ -122,6 +122,12 @@ class Fir2IrConverter(
|
||||
declarationStorage.generateUnboundFakeOverrides()
|
||||
}
|
||||
|
||||
if (configuration.allowNonCachedDeclarations) {
|
||||
// See the comment to fillUnboundSymbols function itself
|
||||
@OptIn(LeakedDeclarationCaches::class)
|
||||
declarationStorage.fillUnboundSymbols()
|
||||
}
|
||||
|
||||
evaluateConstants(irModuleFragment, components)
|
||||
}
|
||||
|
||||
|
||||
+74
-18
@@ -9,14 +9,12 @@ import org.jetbrains.kotlin.builtins.StandardNames.BUILT_INS_PACKAGE_FQ_NAMES
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||
import org.jetbrains.kotlin.fir.backend.generators.isExternalParent
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.descriptors.FirBuiltInsPackageFragment
|
||||
import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor
|
||||
import org.jetbrains.kotlin.fir.java.symbols.FirJavaOverriddenSyntheticPropertySymbol
|
||||
@@ -1177,22 +1175,75 @@ class Fir2IrDeclarationStorage(
|
||||
for ((identifier, symbol) in irForFirSessionDependantDeclarationMap) {
|
||||
if (symbol.isBound) continue
|
||||
val (originalSymbol, dispatchReceiverLookupTag, _) = identifier
|
||||
val irParent = findIrParent(originalSymbol.fir, dispatchReceiverLookupTag)
|
||||
when (originalSymbol) {
|
||||
is FirPropertySymbol -> createAndCacheIrProperty(
|
||||
originalSymbol.fir,
|
||||
irParent,
|
||||
fakeOverrideOwnerLookupTag = dispatchReceiverLookupTag
|
||||
)
|
||||
generateDeclaration(originalSymbol, dispatchReceiverLookupTag)
|
||||
}
|
||||
}
|
||||
|
||||
is FirNamedFunctionSymbol -> createAndCacheIrFunction(
|
||||
originalSymbol.fir,
|
||||
irParent,
|
||||
fakeOverrideOwnerLookupTag = dispatchReceiverLookupTag
|
||||
)
|
||||
/**
|
||||
* This function iterates over all non f/o callable symbols created in declaration storage and binds all unbound symbols
|
||||
*
|
||||
* Usually all symbols are bound after fir2ir conversion is over, but it's not true for `allowNonCachedDeclarations`, when
|
||||
* we convert to IR only part of sources from code fragments.
|
||||
*
|
||||
* ```
|
||||
* // Original code
|
||||
* fun foo(x: Int) {} // (1)
|
||||
*
|
||||
* fun bar() {
|
||||
* 1.let { // (2)
|
||||
* <context of code fragment>
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Code fragment
|
||||
* foo(this@let)
|
||||
*
|
||||
* Here in the body of the code fragment we reference function (1) and lambda (2), which leads to creation of their symbols,
|
||||
* but not to generation of their IR. And since the original code won't be processed by fir2ir, we need to manually create
|
||||
* IR for all symbols from it, to avoid publication of unbound symbols after fir2ri conversion is over
|
||||
*
|
||||
* Note that in the code fragment we may capture even local functions and lambdas, which are stored not in global caches,
|
||||
* but in `localStorage`, which is getting cleared after leaving from corresponding scope. So to generate IR for them we need
|
||||
* to call this function not only after fir2ir conversion, but also after leaving each local scope (see `leaveScope` function)
|
||||
*/
|
||||
@LeakedDeclarationCaches
|
||||
internal fun fillUnboundSymbols() {
|
||||
fillUnboundSymbols(functionCache)
|
||||
fillUnboundSymbols(propertyCache)
|
||||
}
|
||||
|
||||
else -> error("Unexpected declaration: $originalSymbol")
|
||||
}
|
||||
@LeakedDeclarationCaches
|
||||
private fun fillUnboundSymbols(cache: Map<out FirCallableDeclaration, IrSymbol>) {
|
||||
for ((firDeclaration, irSymbol) in cache) {
|
||||
if (irSymbol.isBound) continue
|
||||
generateDeclaration(firDeclaration.symbol, dispatchReceiverLookupTag = null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateDeclaration(
|
||||
originalSymbol: FirBasedSymbol<*>,
|
||||
dispatchReceiverLookupTag: ConeClassLikeLookupTag?,
|
||||
) {
|
||||
val irParent = findIrParent(
|
||||
originalSymbol.packageFqName(),
|
||||
dispatchReceiverLookupTag ?: originalSymbol.getContainingClassSymbol(session)?.toLookupTag(),
|
||||
originalSymbol,
|
||||
originalSymbol.origin
|
||||
)
|
||||
when (originalSymbol) {
|
||||
is FirPropertySymbol -> createAndCacheIrProperty(
|
||||
originalSymbol.fir,
|
||||
irParent,
|
||||
fakeOverrideOwnerLookupTag = dispatchReceiverLookupTag
|
||||
)
|
||||
|
||||
is FirNamedFunctionSymbol -> createAndCacheIrFunction(
|
||||
originalSymbol.fir,
|
||||
irParent,
|
||||
fakeOverrideOwnerLookupTag = dispatchReceiverLookupTag
|
||||
)
|
||||
|
||||
else -> error("Unexpected declaration: $originalSymbol")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1240,6 +1291,11 @@ class Fir2IrDeclarationStorage(
|
||||
symbol is IrEnumEntrySymbol ||
|
||||
symbol is IrScriptSymbol
|
||||
) {
|
||||
if (configuration.allowNonCachedDeclarations) {
|
||||
// See KDoc to `fillUnboundSymbols` function
|
||||
@OptIn(LeakedDeclarationCaches::class)
|
||||
fillUnboundSymbols(localStorage.lastCache.localFunctions)
|
||||
}
|
||||
localStorage.leaveCallable()
|
||||
}
|
||||
symbolTable.leaveScope(symbol)
|
||||
|
||||
@@ -19,6 +19,10 @@ class Fir2IrLocalCallableStorage {
|
||||
cacheStack += Fir2IrScopeCache()
|
||||
}
|
||||
|
||||
@LeakedDeclarationCaches
|
||||
val lastCache: Fir2IrScopeCache
|
||||
get() = cacheStack.last()
|
||||
|
||||
fun leaveCallable() {
|
||||
cacheStack.last().clear()
|
||||
cacheStack.removeAt(cacheStack.size - 1)
|
||||
|
||||
@@ -17,6 +17,9 @@ class Fir2IrScopeCache {
|
||||
|
||||
private val localFunctionCache = mutableMapOf<FirFunction, IrSimpleFunctionSymbol>()
|
||||
|
||||
val localFunctions: Map<FirFunction, IrSimpleFunctionSymbol>
|
||||
get() = localFunctionCache
|
||||
|
||||
private val delegatedPropertyCache = mutableMapOf<FirProperty, IrLocalDelegatedPropertySymbol>()
|
||||
|
||||
fun getParameter(parameter: FirValueParameter): IrValueParameterSymbol? {
|
||||
|
||||
@@ -464,15 +464,6 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun FirBasedSymbol<*>.packageFqName(): FqName {
|
||||
return when (this) {
|
||||
is FirClassLikeSymbol<*> -> classId.packageFqName
|
||||
is FirPropertyAccessorSymbol -> propertySymbol.packageFqName()
|
||||
is FirCallableSymbol<*> -> callableId.packageName
|
||||
else -> error("No package fq name for $this")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val FirSession.moduleVisibilityChecker: FirModuleVisibilityChecker? by FirSession.nullableSessionComponentAccessor()
|
||||
|
||||
@@ -20,6 +20,10 @@ import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
|
||||
import org.jetbrains.kotlin.fir.renderer.FirRenderer
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.builder.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.*
|
||||
@@ -273,3 +277,12 @@ val FirExpression.isStatementLikeExpression: Boolean
|
||||
|
||||
private val FirExpression.isIndexedAssignment: Boolean
|
||||
get() = this is FirBlock && statements.lastOrNull()?.source?.kind == KtFakeSourceElementKind.ImplicitUnit.IndexedAssignmentCoercion
|
||||
|
||||
fun FirBasedSymbol<*>.packageFqName(): FqName {
|
||||
return when (this) {
|
||||
is FirClassLikeSymbol<*> -> classId.packageFqName
|
||||
is FirPropertyAccessorSymbol -> propertySymbol.packageFqName()
|
||||
is FirCallableSymbol<*> -> callableId.packageName
|
||||
else -> error("No package fq name for $this")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user