[FIR2IR] Properly cache generated data/value class members

FIR instances of generated toString/hashCode/equals are session dependant,
  so we need some cache, which operates only session-independent stuff.
  Pair of `FirClass` and `Name` was chosen here

Previously SymbolTable played the role of this cache (with signatures as
  keys). But it should be replaced in scope of KT-66341 and KT-64990
This commit is contained in:
Dmitriy Novozhilov
2024-03-05 11:43:07 +02:00
committed by Space Team
parent 52f6868b74
commit e8f8399af4
4 changed files with 47 additions and 7 deletions
@@ -33,6 +33,7 @@ class Fir2IrCommonMemberStorage(val firSignatureComposer: FirBasedSignatureCompo
val localClassCache: MutableMap<FirClass, IrClass> = mutableMapOf()
val functionCache: ConcurrentHashMap<FirFunction, IrSimpleFunctionSymbol> = ConcurrentHashMap()
val dataClassGeneratedFunctionsCache: ConcurrentHashMap<FirClass, Fir2IrDeclarationStorage.DataClassGeneratedFunctionsStorage> = ConcurrentHashMap()
val constructorCache: ConcurrentHashMap<FirConstructor, IrConstructorSymbol> = ConcurrentHashMap()
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.backend
import org.jetbrains.kotlin.builtins.StandardNames.BUILT_INS_PACKAGE_FQ_NAMES
import org.jetbrains.kotlin.builtins.StandardNames.DATA_CLASS_COPY
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.fir.*
@@ -16,10 +17,7 @@ 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
@@ -27,6 +25,7 @@ import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyConstructor
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyProperty
import org.jetbrains.kotlin.fir.lazy.Fir2IrLazySimpleFunction
import org.jetbrains.kotlin.fir.resolve.getContainingClass
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
import org.jetbrains.kotlin.fir.resolve.toSymbol
@@ -54,6 +53,7 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerAbiStability
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
import org.jetbrains.kotlin.utils.threadLocal
@@ -78,7 +78,14 @@ class Fir2IrDeclarationStorage(
private val scriptCache: ConcurrentHashMap<FirScript, IrScript> = ConcurrentHashMap()
class DataClassGeneratedFunctionsStorage {
var hashCodeSymbol: IrSimpleFunctionSymbol? = null
var toStringSymbol: IrSimpleFunctionSymbol? = null
var equalsSymbol: IrSimpleFunctionSymbol? = null
}
private val functionCache: ConcurrentHashMap<FirFunction, IrSimpleFunctionSymbol> = commonMemberStorage.functionCache
private val dataClassGeneratedFunctionsCache: ConcurrentHashMap<FirClass, DataClassGeneratedFunctionsStorage> = commonMemberStorage.dataClassGeneratedFunctionsCache
private val constructorCache: ConcurrentHashMap<FirConstructor, IrConstructorSymbol> = commonMemberStorage.constructorCache
@@ -109,6 +116,7 @@ class Fir2IrDeclarationStorage(
}
}
}
private val propertyCache = PropertyCacheStorage(commonMemberStorage.propertyCache, commonMemberStorage.syntheticPropertyCache)
private val getterForPropertyCache: ConcurrentHashMap<IrSymbol, IrSimpleFunctionSymbol> =
commonMemberStorage.getterForPropertyCache
@@ -347,6 +355,17 @@ class Fir2IrDeclarationStorage(
if (function.visibility == Visibilities.Local) {
return localStorage.getLocalFunctionSymbol(function)
}
runIf(function.origin.generatedAnyMethod) {
val containingClass = function.getContainingClass(session)!!
val cache = dataClassGeneratedFunctionsCache.computeIfAbsent(containingClass) { DataClassGeneratedFunctionsStorage() }
val cachedFunction = when (function.nameOrSpecialName) {
OperatorNameConventions.EQUALS -> cache.equalsSymbol
OperatorNameConventions.HASH_CODE -> cache.hashCodeSymbol
OperatorNameConventions.TO_STRING -> cache.toStringSymbol
else -> return@runIf // componentN functions are the same for all sessions
}
return cachedFunction?.let(symbolsMappingForLazyClasses::remapFunctionSymbol)
}
val cachedIrCallable = getCachedIrCallableSymbol(
function,
fakeOverrideOwnerLookupTag,
@@ -441,6 +460,14 @@ class Fir2IrDeclarationStorage(
irForFirSessionDependantDeclarationMap[key] = irFunctionSymbol
}
function.origin.generatedAnyMethod -> {
val name = function.nameOrSpecialName
require(OperatorNameConventions.isComponentN(name) || name == DATA_CLASS_COPY) {
"Only componentN functions should be cached this way, but got: $name"
}
functionCache[function] = irFunctionSymbol
}
else -> {
functionCache[function] = irFunctionSymbol
}
@@ -463,7 +490,15 @@ class Fir2IrDeclarationStorage(
}
internal fun cacheGeneratedFunction(firFunction: FirSimpleFunction, irFunction: IrSimpleFunction) {
functionCache[firFunction] = irFunction.symbol
val containingClass = firFunction.getContainingClass(session)!!
val cache = dataClassGeneratedFunctionsCache.computeIfAbsent(containingClass) { DataClassGeneratedFunctionsStorage() }
val irSymbol = irFunction.symbol
when (val name = firFunction.nameOrSpecialName) {
OperatorNameConventions.EQUALS -> cache.equalsSymbol = irSymbol
OperatorNameConventions.HASH_CODE -> cache.hashCodeSymbol = irSymbol
OperatorNameConventions.TO_STRING -> cache.toStringSymbol = irSymbol
else -> error("Only toString, hashCode and equals should be cached this way, but got $name")
}
}
// ------------------------------------ constructors ------------------------------------
@@ -310,8 +310,7 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
otherParameterNeeded: Boolean = false,
isOperator: Boolean = false,
): IrSimpleFunction {
val signature = if (klass.symbol.classId.isLocal) null else components.signatureComposer.composeSignature(syntheticCounterpart)
val symbol = components.declarationStorage.createFunctionSymbol(signature)
val symbol = components.declarationStorage.createFunctionSymbol(signature = null)
return components.irFactory.createSimpleFunction(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
@@ -113,4 +113,9 @@ object OperatorNameConventions {
RANGE_TO to "..",
RANGE_UNTIL to "..<",
)
fun isComponentN(name: Name): Boolean {
val identifier = name.identifierOrNullIfSpecial ?: return false
return COMPONENT_REGEX.matches(identifier)
}
}