[Fir2Ir] Support rebuilding fake overrides over IR
^KT-61514
This commit is contained in:
committed by
Space Team
parent
44c2a749c9
commit
bf9cae6410
+2
-1
@@ -159,7 +159,8 @@ internal class KtFirCompilerFacility(
|
|||||||
Fir2IrJvmSpecialAnnotationSymbolProvider(),
|
Fir2IrJvmSpecialAnnotationSymbolProvider(),
|
||||||
DefaultBuiltIns.Instance,
|
DefaultBuiltIns.Instance,
|
||||||
Fir2IrCommonMemberStorage(signatureComposerForJvmFir2Ir(false), FirJvmKotlinMangler()),
|
Fir2IrCommonMemberStorage(signatureComposerForJvmFir2Ir(false), FirJvmKotlinMangler()),
|
||||||
initializedIrBuiltIns = null
|
initializedIrBuiltIns = null,
|
||||||
|
::JvmIrTypeSystemContext,
|
||||||
)
|
)
|
||||||
|
|
||||||
patchCodeFragmentIr(fir2IrResult)
|
patchCodeFragmentIr(fir2IrResult)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.backend.jvm.Fir2IrJvmSpecialAnnotationSymbolProv
|
|||||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmKotlinMangler
|
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmKotlinMangler
|
||||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmVisibilityConverter
|
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmVisibilityConverter
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
|
import org.jetbrains.kotlin.fir.moduleData
|
||||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
|
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
|
||||||
@@ -44,6 +45,7 @@ data class ModuleCompilerAnalyzedOutput(
|
|||||||
irMangler: KotlinMangler.IrMangler,
|
irMangler: KotlinMangler.IrMangler,
|
||||||
visibilityConverter: Fir2IrVisibilityConverter,
|
visibilityConverter: Fir2IrVisibilityConverter,
|
||||||
kotlinBuiltIns: KotlinBuiltIns,
|
kotlinBuiltIns: KotlinBuiltIns,
|
||||||
|
typeContextProvider: (IrBuiltIns) -> IrTypeSystemContext,
|
||||||
): Fir2IrResult {
|
): Fir2IrResult {
|
||||||
return Fir2IrConverter.createIrModuleFragment(
|
return Fir2IrConverter.createIrModuleFragment(
|
||||||
session, scopeSession, fir,
|
session, scopeSession, fir,
|
||||||
@@ -52,7 +54,8 @@ data class ModuleCompilerAnalyzedOutput(
|
|||||||
Fir2IrJvmSpecialAnnotationSymbolProvider(), // TODO KT-60526: replace with appropriate (probably empty) implementation for other backends.
|
Fir2IrJvmSpecialAnnotationSymbolProvider(), // TODO KT-60526: replace with appropriate (probably empty) implementation for other backends.
|
||||||
kotlinBuiltIns = kotlinBuiltIns,
|
kotlinBuiltIns = kotlinBuiltIns,
|
||||||
commonMemberStorage = commonMemberStorage,
|
commonMemberStorage = commonMemberStorage,
|
||||||
initializedIrBuiltIns = irBuiltIns
|
initializedIrBuiltIns = irBuiltIns,
|
||||||
|
typeContextProvider = typeContextProvider
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,6 +120,7 @@ fun FirResult.convertToIrAndActualize(
|
|||||||
irMangler,
|
irMangler,
|
||||||
visibilityConverter,
|
visibilityConverter,
|
||||||
kotlinBuiltIns,
|
kotlinBuiltIns,
|
||||||
|
actualizerTypeContextProvider,
|
||||||
).also { result ->
|
).also { result ->
|
||||||
fir2IrResultPostCompute(result)
|
fir2IrResultPostCompute(result)
|
||||||
}
|
}
|
||||||
@@ -129,12 +133,15 @@ fun FirResult.convertToIrAndActualize(
|
|||||||
val commonIrOutputs = commonOutputs.map {
|
val commonIrOutputs = commonOutputs.map {
|
||||||
it.convertToIr(
|
it.convertToIr(
|
||||||
fir2IrExtensions,
|
fir2IrExtensions,
|
||||||
fir2IrConfiguration,
|
// We need to build all modules before rebuilding fake overrides
|
||||||
|
// to avoid fixing declaration storages
|
||||||
|
fir2IrConfiguration.copy(useIrFakeOverrideBuilder = false),
|
||||||
commonMemberStorage = commonMemberStorage,
|
commonMemberStorage = commonMemberStorage,
|
||||||
irBuiltIns = irBuiltIns,
|
irBuiltIns = irBuiltIns,
|
||||||
irMangler,
|
irMangler,
|
||||||
visibilityConverter,
|
visibilityConverter,
|
||||||
kotlinBuiltIns,
|
kotlinBuiltIns,
|
||||||
|
actualizerTypeContextProvider,
|
||||||
).also { result ->
|
).also { result ->
|
||||||
fir2IrResultPostCompute(result)
|
fir2IrResultPostCompute(result)
|
||||||
if (irBuiltIns == null) {
|
if (irBuiltIns == null) {
|
||||||
@@ -150,6 +157,7 @@ fun FirResult.convertToIrAndActualize(
|
|||||||
irMangler,
|
irMangler,
|
||||||
visibilityConverter,
|
visibilityConverter,
|
||||||
kotlinBuiltIns,
|
kotlinBuiltIns,
|
||||||
|
actualizerTypeContextProvider,
|
||||||
).also {
|
).also {
|
||||||
fir2IrResultPostCompute(it)
|
fir2IrResultPostCompute(it)
|
||||||
}
|
}
|
||||||
@@ -159,7 +167,11 @@ fun FirResult.convertToIrAndActualize(
|
|||||||
commonIrOutputs.map { it.irModuleFragment },
|
commonIrOutputs.map { it.irModuleFragment },
|
||||||
fir2IrConfiguration.diagnosticReporter,
|
fir2IrConfiguration.diagnosticReporter,
|
||||||
actualizerTypeContextProvider(fir2IrResult.irModuleFragment.irBuiltins),
|
actualizerTypeContextProvider(fir2IrResult.irModuleFragment.irBuiltins),
|
||||||
fir2IrConfiguration.languageVersionSettings
|
fir2IrConfiguration.languageVersionSettings,
|
||||||
|
commonMemberStorage.symbolTable,
|
||||||
|
irMangler,
|
||||||
|
Fir2IrConverter.friendModulesMap(outputs.last().session),
|
||||||
|
fir2IrConfiguration.useIrFakeOverrideBuilder
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import org.jetbrains.kotlin.incremental.components.InlineConstTracker
|
|||||||
* It means asking for an IR element of a non-cached declaration is a sign of inconsistent state.
|
* It means asking for an IR element of a non-cached declaration is a sign of inconsistent state.
|
||||||
* Code generation in the IDE is trickier, though, as declarations from any module can be potentially referenced.
|
* Code generation in the IDE is trickier, though, as declarations from any module can be potentially referenced.
|
||||||
* For such a scenario, there is a flag that relaxes consistency checks.
|
* For such a scenario, there is a flag that relaxes consistency checks.
|
||||||
|
*
|
||||||
|
* @param useIrFakeOverrideBuilder enables creation of fake-overrides using IR f/o generator instead of FIR2IR one. KT-61514
|
||||||
*/
|
*/
|
||||||
data class Fir2IrConfiguration(
|
data class Fir2IrConfiguration(
|
||||||
val languageVersionSettings: LanguageVersionSettings,
|
val languageVersionSettings: LanguageVersionSettings,
|
||||||
@@ -35,4 +37,5 @@ data class Fir2IrConfiguration(
|
|||||||
val evaluatedConstTracker: EvaluatedConstTracker,
|
val evaluatedConstTracker: EvaluatedConstTracker,
|
||||||
val inlineConstTracker: InlineConstTracker?,
|
val inlineConstTracker: InlineConstTracker?,
|
||||||
val allowNonCachedDeclarations: Boolean,
|
val allowNonCachedDeclarations: Boolean,
|
||||||
|
val useIrFakeOverrideBuilder: Boolean = false
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
|
|||||||
import org.jetbrains.kotlin.KtPsiSourceFileLinesMapping
|
import org.jetbrains.kotlin.KtPsiSourceFileLinesMapping
|
||||||
import org.jetbrains.kotlin.KtSourceFileLinesMappingFromLineStartOffsets
|
import org.jetbrains.kotlin.KtSourceFileLinesMappingFromLineStartOffsets
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendErrors
|
import org.jetbrains.kotlin.backend.common.CommonBackendErrors
|
||||||
|
import org.jetbrains.kotlin.backend.common.actualizer.FakeOverrideRebuilder
|
||||||
import org.jetbrains.kotlin.backend.common.sourceElement
|
import org.jetbrains.kotlin.backend.common.sourceElement
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
@@ -30,6 +31,7 @@ import org.jetbrains.kotlin.fir.extensions.generatedNestedClassifiers
|
|||||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
import org.jetbrains.kotlin.fir.symbols.lazyDeclarationResolver
|
import org.jetbrains.kotlin.fir.symbols.lazyDeclarationResolver
|
||||||
import org.jetbrains.kotlin.ir.PsiIrFileEntry
|
import org.jetbrains.kotlin.ir.PsiIrFileEntry
|
||||||
|
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
||||||
@@ -40,6 +42,7 @@ import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
|
|||||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
|
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
|
||||||
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
|
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
|
||||||
import org.jetbrains.kotlin.ir.interpreter.transformer.transformConst
|
import org.jetbrains.kotlin.ir.interpreter.transformer.transformConst
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorPublicSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorPublicSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionPublicSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionPublicSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.util.KotlinMangler
|
import org.jetbrains.kotlin.ir.util.KotlinMangler
|
||||||
@@ -530,6 +533,13 @@ class Fir2IrConverter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: drop this function in favor of using [IrModuleDescriptor::shouldSeeInternalsOf] in FakeOverrideBuilder KT-61384
|
||||||
|
fun friendModulesMap(session: FirSession) = mapOf(
|
||||||
|
session.moduleData.name.asStringStripSpecialMarkers() to session.moduleData.friendDependencies.map {
|
||||||
|
it.name.asStringStripSpecialMarkers()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
fun createIrModuleFragment(
|
fun createIrModuleFragment(
|
||||||
session: FirSession,
|
session: FirSession,
|
||||||
scopeSession: ScopeSession,
|
scopeSession: ScopeSession,
|
||||||
@@ -542,7 +552,8 @@ class Fir2IrConverter(
|
|||||||
specialSymbolProvider: Fir2IrSpecialSymbolProvider,
|
specialSymbolProvider: Fir2IrSpecialSymbolProvider,
|
||||||
kotlinBuiltIns: KotlinBuiltIns,
|
kotlinBuiltIns: KotlinBuiltIns,
|
||||||
commonMemberStorage: Fir2IrCommonMemberStorage,
|
commonMemberStorage: Fir2IrCommonMemberStorage,
|
||||||
initializedIrBuiltIns: IrBuiltInsOverFir?
|
initializedIrBuiltIns: IrBuiltInsOverFir?,
|
||||||
|
typeContextProvider: (IrBuiltIns) -> IrTypeSystemContext
|
||||||
): Fir2IrResult {
|
): Fir2IrResult {
|
||||||
session.lazyDeclarationResolver.disableLazyResolveContractChecks()
|
session.lazyDeclarationResolver.disableLazyResolveContractChecks()
|
||||||
val moduleDescriptor = FirModuleDescriptor(session, kotlinBuiltIns)
|
val moduleDescriptor = FirModuleDescriptor(session, kotlinBuiltIns)
|
||||||
@@ -564,6 +575,16 @@ class Fir2IrConverter(
|
|||||||
allFirFiles, irModuleFragment, components.fir2IrVisitor, runPreCacheBuiltinClasses = initializedIrBuiltIns == null
|
allFirFiles, irModuleFragment, components.fir2IrVisitor, runPreCacheBuiltinClasses = initializedIrBuiltIns == null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (fir2IrConfiguration.useIrFakeOverrideBuilder) {
|
||||||
|
FakeOverrideRebuilder(
|
||||||
|
commonMemberStorage.symbolTable,
|
||||||
|
irMangler,
|
||||||
|
typeContextProvider(components.irBuiltIns),
|
||||||
|
irModuleFragment,
|
||||||
|
friendModulesMap(session)
|
||||||
|
).rebuildFakeOverrides()
|
||||||
|
}
|
||||||
|
|
||||||
return Fir2IrResult(irModuleFragment, components, moduleDescriptor)
|
return Fir2IrResult(irModuleFragment, components, moduleDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ dependencies {
|
|||||||
api(project(":compiler:backend-common"))
|
api(project(":compiler:backend-common"))
|
||||||
api(project(":compiler:ir.tree"))
|
api(project(":compiler:ir.tree"))
|
||||||
api(project(":compiler:ir.interpreter"))
|
api(project(":compiler:ir.interpreter"))
|
||||||
|
implementation(project(":compiler:ir.serialization.common"))
|
||||||
compileOnly(intellijCore())
|
compileOnly(intellijCore())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+251
@@ -0,0 +1,251 @@
|
|||||||
|
/*
|
||||||
|
* 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.backend.common.actualizer
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.linkage.partial.PartialLinkageSupportForLinker
|
||||||
|
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.common.overrides.FileLocalAwareLinker
|
||||||
|
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrLocalDelegatedPropertyReference
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
||||||
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After actualization, fake overrides can be incorrect.
|
||||||
|
*
|
||||||
|
* Fake overrides can change even in classes, which don't have expect/actuals in their superclasses.
|
||||||
|
* And this change can be more untrivial than just substitute expect class with actual.
|
||||||
|
*
|
||||||
|
* For example, if an expect class is actualized to common class, or several expect classes are actualized to same class,
|
||||||
|
* several fake overrides can be merged with each other, or with real functions.
|
||||||
|
*
|
||||||
|
* To fix that, we are just rebuilding fake overrides from stretch, after actualization is done.
|
||||||
|
*
|
||||||
|
* This is done in 3 steps:
|
||||||
|
* 1. Remove all fake overrides from all classes
|
||||||
|
* 2. Build new fake overrides and map old one to some function now existing in class (possible real function, not fake override)
|
||||||
|
* 3. Remap callsites of all functions using the map collected on step 2.
|
||||||
|
*
|
||||||
|
* Steps 1 and 3 are kinda trivial, expect we should remove all fake overrides from symbol table.
|
||||||
|
*
|
||||||
|
* For matching in step 2, classes are processed in such order. That superclass is processed before class.
|
||||||
|
* That allows us to match using overridden symbols. In particular, f/o is mapped to only function
|
||||||
|
* overriding any of functions (possibly already mapped), which was overriden by initial fake override.
|
||||||
|
*
|
||||||
|
* Here we assume that functions can be only merged, not split, i.e. if the same fake override was overriding several
|
||||||
|
* super-functions, it's impossible to have different function overriding them after actualization.
|
||||||
|
*/
|
||||||
|
class FakeOverrideRebuilder(
|
||||||
|
val symbolTable: SymbolTable,
|
||||||
|
val mangler: KotlinMangler.IrMangler,
|
||||||
|
val typeSystemContext: IrTypeSystemContext,
|
||||||
|
val irModule: IrModuleFragment,
|
||||||
|
// TODO: drop this argument in favor of using [IrModuleDescriptor::shouldSeeInternalsOf] in FakeOverrideBuilder KT-61384
|
||||||
|
friendModules: Map<String, List<String>>
|
||||||
|
) {
|
||||||
|
private val removedFakeOverrides = mutableMapOf<IrClassSymbol, List<IrSymbol>>()
|
||||||
|
private val processedClasses = mutableSetOf<IrClass>()
|
||||||
|
private val fakeOverrideMap = mutableMapOf<IrSymbol, IrSymbol>()
|
||||||
|
|
||||||
|
private val fakeOverrideBuilder = FakeOverrideBuilder(
|
||||||
|
LocalFakeOverridesStorage(),
|
||||||
|
symbolTable,
|
||||||
|
mangler,
|
||||||
|
typeSystemContext,
|
||||||
|
friendModules,
|
||||||
|
PartialLinkageSupportForLinker.DISABLED
|
||||||
|
)
|
||||||
|
|
||||||
|
fun rebuildFakeOverrides() {
|
||||||
|
irModule.acceptVoid(RemoveFakeOverridesVisitor(removedFakeOverrides, symbolTable))
|
||||||
|
for (clazz in removedFakeOverrides.keys) {
|
||||||
|
rebuildClassFakeOverrides(clazz.owner)
|
||||||
|
}
|
||||||
|
irModule.acceptVoid(RemapFakeOverridesVisitor(fakeOverrideMap))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectOverriddenDeclarations(
|
||||||
|
member: IrOverridableDeclaration<*>,
|
||||||
|
result: MutableSet<IrOverridableDeclaration<*>>,
|
||||||
|
visited: MutableSet<IrOverridableDeclaration<*>>,
|
||||||
|
stopAtReal: Boolean,
|
||||||
|
) {
|
||||||
|
if (!visited.add(member)) return
|
||||||
|
if (member.isReal) {
|
||||||
|
result.add(member)
|
||||||
|
if (stopAtReal) return
|
||||||
|
} else {
|
||||||
|
require(member.overriddenSymbols.isNotEmpty()) { "No overridden symbols found for (fake override) ${member.render()}" }
|
||||||
|
}
|
||||||
|
for (overridden in member.overriddenSymbols) {
|
||||||
|
collectOverriddenDeclarations(overridden.owner as IrOverridableDeclaration<*>, result, visited, stopAtReal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rebuildClassFakeOverrides(irClass: IrClass) {
|
||||||
|
if (irClass is IrLazyDeclarationBase) return
|
||||||
|
if (!processedClasses.add(irClass)) return
|
||||||
|
val oldList = removedFakeOverrides[irClass.symbol] ?: return
|
||||||
|
for (c in irClass.superTypes) {
|
||||||
|
c.getClass()?.let { rebuildClassFakeOverrides(it) }
|
||||||
|
}
|
||||||
|
fakeOverrideBuilder.provideFakeOverrides(irClass, CompatibilityMode.CURRENT)
|
||||||
|
val overriddenMap = mutableMapOf<IrSymbol, IrSymbol>()
|
||||||
|
irClass.properties
|
||||||
|
.forEach { prop ->
|
||||||
|
val allOverridden = mutableSetOf<IrOverridableDeclaration<*>>()
|
||||||
|
collectOverriddenDeclarations(prop, allOverridden, mutableSetOf(), false)
|
||||||
|
for (overridden in allOverridden) {
|
||||||
|
require(overriddenMap.put(overridden.symbol, prop.symbol) == null) { "Multiple overrides for ${overridden.render()} "}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
irClass.simpleFunctions()
|
||||||
|
.forEach { func ->
|
||||||
|
val allOverridden = mutableSetOf<IrOverridableDeclaration<*>>()
|
||||||
|
collectOverriddenDeclarations(func, allOverridden, mutableSetOf(), false)
|
||||||
|
for (overridden in allOverridden) {
|
||||||
|
require(overriddenMap.put(overridden.symbol, func.symbol) == null) { "Multiple overrides for ${overridden.render()} "}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (old in oldList) {
|
||||||
|
fakeOverrideMap[old] = mutableSetOf<IrOverridableDeclaration<*>>()
|
||||||
|
.apply { collectOverriddenDeclarations(old.owner as IrOverridableDeclaration<*>, this, mutableSetOf(), true) }
|
||||||
|
.map { overriddenMap[it.symbol] }
|
||||||
|
.distinct()
|
||||||
|
.single() ?: error("${old.owner.render()} is not overridden in ${irClass.render()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LocalFakeOverridesStorage : FileLocalAwareLinker {
|
||||||
|
val funStorage = mutableMapOf<Pair<IrDeclaration, IdSignature>, IrSimpleFunctionSymbolImpl>()
|
||||||
|
val propertyStorage = mutableMapOf<Pair<IrDeclaration, IdSignature>, IrPropertySymbol>()
|
||||||
|
override fun tryReferencingSimpleFunctionByLocalSignature(
|
||||||
|
parent: IrDeclaration,
|
||||||
|
idSignature: IdSignature,
|
||||||
|
): IrSimpleFunctionSymbol? {
|
||||||
|
if (idSignature.isPubliclyVisible) return null
|
||||||
|
return funStorage.getOrPut(parent to idSignature) {
|
||||||
|
IrSimpleFunctionSymbolImpl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun tryReferencingPropertyByLocalSignature(parent: IrDeclaration, idSignature: IdSignature): IrPropertySymbol? {
|
||||||
|
if (idSignature.isPubliclyVisible) return null
|
||||||
|
return propertyStorage.getOrPut(parent to idSignature) {
|
||||||
|
IrPropertySymbolImpl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RemoveFakeOverridesVisitor(
|
||||||
|
val removedOverrides: MutableMap<IrClassSymbol, List<IrSymbol>>,
|
||||||
|
val symbolTable: SymbolTable
|
||||||
|
) : IrElementVisitorVoid {
|
||||||
|
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(DelicateSymbolTableApi::class)
|
||||||
|
override fun visitClass(declaration: IrClass) {
|
||||||
|
val curList = mutableListOf<IrSymbol>()
|
||||||
|
declaration.declarations.removeIf {
|
||||||
|
if (it is IrOverridableDeclaration<*> && it.isFakeOverride) {
|
||||||
|
when (it) {
|
||||||
|
is IrProperty -> {
|
||||||
|
curList.add(it.symbol)
|
||||||
|
symbolTable.removeProperty(it.symbol)
|
||||||
|
it.getter?.symbol?.let { getter ->
|
||||||
|
curList.add(getter)
|
||||||
|
symbolTable.removeSimpleFunction(getter)
|
||||||
|
}
|
||||||
|
it.setter?.symbol?.let { setter ->
|
||||||
|
curList.add(setter)
|
||||||
|
symbolTable.removeSimpleFunction(setter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IrSimpleFunction -> {
|
||||||
|
curList.add(it.symbol)
|
||||||
|
symbolTable.removeSimpleFunction(it.symbol)
|
||||||
|
}
|
||||||
|
else -> shouldNotBeCalled("Only simple functions and properties can be overridden")
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (curList.isNotEmpty()) {
|
||||||
|
removedOverrides[declaration.symbol] = curList
|
||||||
|
}
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: KT-61561 it seams, this class is too generic to be here.
|
||||||
|
// By some reason, we don't have utility class doing that, but we probably should, as we have DeepCopy util classes
|
||||||
|
// Probably, it should also be generated, to avoid missing some plaves, where symbols can happen.
|
||||||
|
private class RemapFakeOverridesVisitor(val fakeOverridesMap: Map<IrSymbol, IrSymbol>) : IrElementVisitorVoid {
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
|
||||||
|
declaration.overriddenSymbols = declaration.overriddenSymbols.map {
|
||||||
|
(fakeOverridesMap[it] as? IrSimpleFunctionSymbol) ?: it
|
||||||
|
}
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitProperty(declaration: IrProperty) {
|
||||||
|
declaration.overriddenSymbols = declaration.overriddenSymbols.map {
|
||||||
|
(fakeOverridesMap[it] as? IrPropertySymbol) ?: it
|
||||||
|
}
|
||||||
|
declaration.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall) {
|
||||||
|
expression.symbol = (fakeOverridesMap[expression.symbol] as? IrSimpleFunctionSymbol) ?: expression.symbol
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunctionReference(expression: IrFunctionReference) {
|
||||||
|
expression.symbol = (fakeOverridesMap[expression.symbol] as? IrSimpleFunctionSymbol) ?: expression.symbol
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitPropertyReference(expression: IrPropertyReference) {
|
||||||
|
expression.symbol = (fakeOverridesMap[expression.symbol] as? IrPropertySymbol) ?: expression.symbol
|
||||||
|
expression.getter = (expression.getter?.let { fakeOverridesMap[it] } as? IrSimpleFunctionSymbol) ?: expression.getter
|
||||||
|
expression.setter = (expression.setter?.let { fakeOverridesMap[it] } as? IrSimpleFunctionSymbol) ?: expression.setter
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference) {
|
||||||
|
expression.getter = (fakeOverridesMap[expression.getter] as? IrSimpleFunctionSymbol) ?: expression.getter
|
||||||
|
expression.setter = (expression.setter?.let { fakeOverridesMap[it] } as? IrSimpleFunctionSymbol) ?: expression.setter
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
-13
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
|||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
||||||
import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
|
||||||
data class IrActualizedResult(val actualizedExpectDeclarations: List<IrDeclaration>)
|
data class IrActualizedResult(val actualizedExpectDeclarations: List<IrDeclaration>)
|
||||||
|
|
||||||
@@ -21,7 +21,12 @@ object IrActualizer {
|
|||||||
dependentFragments: List<IrModuleFragment>,
|
dependentFragments: List<IrModuleFragment>,
|
||||||
diagnosticReporter: DiagnosticReporter,
|
diagnosticReporter: DiagnosticReporter,
|
||||||
typeSystemContext: IrTypeSystemContext,
|
typeSystemContext: IrTypeSystemContext,
|
||||||
languageVersionSettings: LanguageVersionSettings
|
languageVersionSettings: LanguageVersionSettings,
|
||||||
|
symbolTable: SymbolTable,
|
||||||
|
mangler: KotlinMangler.IrMangler,
|
||||||
|
// TODO: drop this argument in favor of using [IrModuleDescriptor::shouldSeeInternalsOf] in FakeOverrideBuilder KT-61384
|
||||||
|
friendModules: Map<String, List<String>>,
|
||||||
|
useIrFakeOverrideBuilder: Boolean
|
||||||
): IrActualizedResult {
|
): IrActualizedResult {
|
||||||
val ktDiagnosticReporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, languageVersionSettings)
|
val ktDiagnosticReporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, languageVersionSettings)
|
||||||
|
|
||||||
@@ -40,18 +45,20 @@ object IrActualizer {
|
|||||||
// Also, it doesn't remove unactualized expect declarations marked with @OptionalExpectation
|
// Also, it doesn't remove unactualized expect declarations marked with @OptionalExpectation
|
||||||
val removedExpectDeclarations = removeExpectDeclarations(dependentFragments, expectActualMap)
|
val removedExpectDeclarations = removeExpectDeclarations(dependentFragments, expectActualMap)
|
||||||
|
|
||||||
// 3. Actualize expect fake overrides in non-expect classes inside common or multi-platform module.
|
if (!useIrFakeOverrideBuilder) {
|
||||||
// It's probably important to run FakeOverridesActualizer before ActualFakeOverridesAdder
|
// 3. Actualize expect fake overrides in non-expect classes inside common or multi-platform module.
|
||||||
FakeOverridesActualizer(expectActualMap).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
// It's probably important to run FakeOverridesActualizer before ActualFakeOverridesAdder
|
||||||
|
FakeOverridesActualizer(expectActualMap).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
||||||
|
|
||||||
// 4. Add fake overrides to non-expect classes inside common or multi-platform module,
|
// 4. Add fake overrides to non-expect classes inside common or multi-platform module,
|
||||||
// taken from these non-expect classes actualized super classes.
|
// taken from these non-expect classes actualized super classes.
|
||||||
ActualFakeOverridesAdder(
|
ActualFakeOverridesAdder(
|
||||||
expectActualMap,
|
expectActualMap,
|
||||||
actualDeclarations.actualClasses,
|
actualDeclarations.actualClasses,
|
||||||
ktDiagnosticReporter,
|
ktDiagnosticReporter,
|
||||||
typeSystemContext
|
typeSystemContext
|
||||||
).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
||||||
|
}
|
||||||
|
|
||||||
// 5. Copy and actualize function parameter default values from expect functions
|
// 5. Copy and actualize function parameter default values from expect functions
|
||||||
val symbolRemapper = ActualizerSymbolRemapper(expectActualMap)
|
val symbolRemapper = ActualizerSymbolRemapper(expectActualMap)
|
||||||
@@ -65,6 +72,11 @@ object IrActualizer {
|
|||||||
// 7. Merge dependent fragments into the main one
|
// 7. Merge dependent fragments into the main one
|
||||||
mergeIrFragments(mainFragment, dependentFragments)
|
mergeIrFragments(mainFragment, dependentFragments)
|
||||||
|
|
||||||
|
if (useIrFakeOverrideBuilder) {
|
||||||
|
// 8. Rebuild fake overrides from stretch, as they could become invalid during actualization
|
||||||
|
FakeOverrideRebuilder(symbolTable, mangler, typeSystemContext, mainFragment, friendModules).rebuildFakeOverrides()
|
||||||
|
}
|
||||||
|
|
||||||
return IrActualizedResult(removedExpectDeclarations)
|
return IrActualizedResult(removedExpectDeclarations)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,3 +110,4 @@ object IrActualizer {
|
|||||||
mainFragment.files.addAll(0, dependentFragments.flatMap { it.files })
|
mainFragment.files.addAll(0, dependentFragments.flatMap { it.files })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -12,7 +12,9 @@ import org.jetbrains.kotlin.config.LanguageFeature
|
|||||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.fir.pipeline.applyIrGenerationExtensions
|
import org.jetbrains.kotlin.fir.pipeline.applyIrGenerationExtensions
|
||||||
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
|
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
|
||||||
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||||
|
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
||||||
import org.jetbrains.kotlin.test.model.*
|
import org.jetbrains.kotlin.test.model.*
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
||||||
@@ -33,7 +35,11 @@ class IrActualizerAndPluginsFacade(
|
|||||||
inputArtifact.dependentIrModuleFragments,
|
inputArtifact.dependentIrModuleFragments,
|
||||||
inputArtifact.diagnosticReporter,
|
inputArtifact.diagnosticReporter,
|
||||||
typeSystemContext,
|
typeSystemContext,
|
||||||
testServices.compilerConfigurationProvider.getCompilerConfiguration(module).languageVersionSettings
|
testServices.compilerConfigurationProvider.getCompilerConfiguration(module).languageVersionSettings,
|
||||||
|
inputArtifact.irPluginContext.symbolTable as SymbolTable, // TODO: it's strange place to get it.
|
||||||
|
inputArtifact.irMangler,
|
||||||
|
mapOf(module.name to module.friendDependencies.map { it.moduleName }),
|
||||||
|
useIrFakeOverrideBuilder = false,
|
||||||
)
|
)
|
||||||
inputArtifact.irActualizerResult = result
|
inputArtifact.irActualizerResult = result
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.ir.backend.js.incrementalDataProvider
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
|
||||||
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
|
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
|
||||||
import org.jetbrains.kotlin.library.unresolvedDependencies
|
import org.jetbrains.kotlin.library.unresolvedDependencies
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||||
@@ -180,7 +181,8 @@ fun ModuleCompilerAnalyzedOutput.convertToJsIr(
|
|||||||
irBuiltIns,
|
irBuiltIns,
|
||||||
JsManglerIr,
|
JsManglerIr,
|
||||||
Fir2IrVisibilityConverter.Default,
|
Fir2IrVisibilityConverter.Default,
|
||||||
builtIns ?: DefaultBuiltIns.Instance // TODO: consider passing externally,
|
builtIns ?: DefaultBuiltIns.Instance, // TODO: consider passing externally,
|
||||||
|
::IrTypeSystemContextImpl
|
||||||
).also {
|
).also {
|
||||||
(it.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = dependencies }
|
(it.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = dependencies }
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.test.frontend.fir
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmIrDeserializerImpl
|
import org.jetbrains.kotlin.backend.jvm.JvmIrDeserializerImpl
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext
|
||||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
|
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
|
||||||
@@ -113,8 +114,9 @@ class Fir2IrJvmResultsConverter(
|
|||||||
irBuiltIns,
|
irBuiltIns,
|
||||||
irMangler,
|
irMangler,
|
||||||
FirJvmVisibilityConverter,
|
FirJvmVisibilityConverter,
|
||||||
DefaultBuiltIns.Instance
|
DefaultBuiltIns.Instance,
|
||||||
)
|
typeContextProvider = ::JvmIrTypeSystemContext,
|
||||||
|
)
|
||||||
irBuiltIns = components.irBuiltIns
|
irBuiltIns = components.irBuiltIns
|
||||||
|
|
||||||
if (index < inputArtifact.partsForDependsOnModules.size - 1) {
|
if (index < inputArtifact.partsForDependsOnModules.size - 1) {
|
||||||
|
|||||||
+3
-1
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.ir.backend.js.incrementalDataProvider
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
|
||||||
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
|
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
|
||||||
import org.jetbrains.kotlin.library.unresolvedDependencies
|
import org.jetbrains.kotlin.library.unresolvedDependencies
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||||
@@ -176,7 +177,8 @@ fun ModuleCompilerAnalyzedOutput.convertToWasmIr(
|
|||||||
irBuiltIns,
|
irBuiltIns,
|
||||||
JsManglerIr,
|
JsManglerIr,
|
||||||
Fir2IrVisibilityConverter.Default,
|
Fir2IrVisibilityConverter.Default,
|
||||||
builtIns ?: DefaultBuiltIns.Instance // TODO: consider passing externally,
|
builtIns ?: DefaultBuiltIns.Instance, // TODO: consider passing externally
|
||||||
|
typeContextProvider = ::IrTypeSystemContextImpl,
|
||||||
).also {
|
).also {
|
||||||
(it.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = dependencies }
|
(it.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = dependencies }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.analyzer.AnalysisResult
|
|||||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmIrDeserializerImpl
|
import org.jetbrains.kotlin.backend.jvm.JvmIrDeserializerImpl
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext
|
||||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
@@ -149,7 +150,8 @@ object GenerationUtils {
|
|||||||
irBuiltIns = null,
|
irBuiltIns = null,
|
||||||
JvmIrMangler,
|
JvmIrMangler,
|
||||||
FirJvmVisibilityConverter,
|
FirJvmVisibilityConverter,
|
||||||
DefaultBuiltIns.Instance
|
DefaultBuiltIns.Instance,
|
||||||
|
::JvmIrTypeSystemContext
|
||||||
)
|
)
|
||||||
val dummyBindingContext = NoScopeRecordCliBindingTrace().bindingContext
|
val dummyBindingContext = NoScopeRecordCliBindingTrace().bindingContext
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user