[FE] Use FQN instead of KClass in TypeRegistry

TypeRegistry is the static map which contains mapping between type of
  session component and its index in the session array. Originally
  `KClass` of a component was used as a key in this map, which worked
  pretty well for compiler components. But there was a problem with
  components from plugins and Kotlin daemon: in compilation with daemon
  we keep TypeRegistry between compilations. But for each compilation
  we load plugins from jar, which brings to the situation when after N
  compilations there is N entries in TypeRegistry map with different
  KClass'es for same extension component. This not only causes AIOBE
  but also introduces the memory leak, because we keep reference to
  KClass which is not used anymore

So to fix this issue it's enough to just store FQN of component in
  TypeRegistry instead of KClass

There are no tests in this commit, but they will be added in next
  commit relevant to scripting. With those commits we will have gradle
  integration tests which uses Kotlin compiler daemon and registers
  a compiler plugin for scripting

^KT-55023 Fixed
This commit is contained in:
Dmitriy Novozhilov
2022-12-20 10:27:48 +02:00
committed by Space Team
parent 890418f6b7
commit c44993e586
4 changed files with 29 additions and 23 deletions
@@ -7,13 +7,12 @@ package org.jetbrains.kotlin.fir.util
import org.jetbrains.kotlin.util.TypeRegistry
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
abstract class ConeTypeRegistry<K : Any, V : Any> : TypeRegistry<K, V>() {
override fun <T : K> ConcurrentHashMap<KClass<out K>, Int>.customComputeIfAbsent(
kClass: KClass<T>,
compute: (KClass<out K>) -> Int
override fun ConcurrentHashMap<String, Int>.customComputeIfAbsent(
key: String,
compute: (String) -> Int
): Int {
return this.computeIfAbsent(kClass, compute)
return this.computeIfAbsent(key, compute)
}
}
@@ -6,26 +6,27 @@
package org.jetbrains.kotlin.fir.renderer
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataKey
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataRegistry
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import kotlin.reflect.KClass
open class FirDeclarationRendererWithAttributes : FirDeclarationRenderer() {
override fun FirDeclaration.renderDeclarationAttributes() {
if (attributes.isNotEmpty()) {
val attributes = getAttributesWithValues().mapNotNull { (klass, value) ->
value?.let { klass.simpleName to value.renderAsDeclarationAttributeValue() }
value?.let { klass to value.renderAsDeclarationAttributeValue() }
}.joinToString { (name, value) -> "$name=$value" }
printer.print("[$attributes] ")
}
}
private fun FirDeclaration.getAttributesWithValues(): List<Pair<KClass<out FirDeclarationDataKey>, Any?>> {
private fun FirDeclaration.getAttributesWithValues(): List<Pair<String, Any?>> {
val attributesMap = FirDeclarationDataRegistry.allValuesThreadUnsafeForRendering()
return attributesMap.entries.sortedBy { it.key.simpleName }.map { (klass, index) -> klass to attributes[index] }
return attributesMap.entries
.map { it.key.substringAfterLast(".") to it.value }
.sortedBy { it.first }
.map { (klass, index) -> klass to attributes[index] }
}
private fun Any.renderAsDeclarationAttributeValue() = when (this) {
@@ -34,4 +35,4 @@ open class FirDeclarationRendererWithAttributes : FirDeclarationRenderer() {
is FirProperty -> symbol.callableId.toString()
else -> toString()
}
}
}
@@ -61,10 +61,9 @@ class NullableArrayMapAccessor<K : Any, V : Any, T : V>(
}
abstract class TypeRegistry<K : Any, V : Any> {
private val idPerType = ConcurrentHashMap<KClass<out K>, Int>()
private val idPerType = ConcurrentHashMap<String, Int>()
private val idCounter = AtomicInteger(0)
fun <T : V, KK : K> generateAccessor(kClass: KClass<KK>, default: T? = null): ArrayMapAccessor<K, V, T> {
return ArrayMapAccessor(kClass, getId(kClass), default)
}
@@ -78,15 +77,22 @@ abstract class TypeRegistry<K : Any, V : Any> {
}
fun <T : K> getId(kClass: KClass<T>): Int {
return idPerType.customComputeIfAbsent(kClass) { idCounter.getAndIncrement() }
return idPerType.customComputeIfAbsent(kClass.qualifiedName!!) { idCounter.getAndIncrement() }
}
abstract fun <T : K> ConcurrentHashMap<KClass<out K>, Int>.customComputeIfAbsent(
kClass: KClass<T>,
compute: (KClass<out K>) -> Int
/*
* This function is needed for compatibility with JDK 6
* ArrayMap and other infrastructure is used in KotlinType, declared in :core:descriptors module, which is
* compiled against JDK 6 (because it's used in kotlin-reflect, which is still compatible with Java 6)
* So the problem is that JDK 6 does not have thread-safe computeIfAbsent for ConcurrentHashMap,
* and we need this method to add ability to provide thread-safe implementation by hand
*/
abstract fun ConcurrentHashMap<String, Int>.customComputeIfAbsent(
key: String,
compute: (String) -> Int
): Int
fun allValuesThreadUnsafeForRendering(): Map<KClass<out K>, Int> {
fun allValuesThreadUnsafeForRendering(): Map<String, Int> {
return idPerType
}
@@ -42,12 +42,12 @@ class TypeAttributes private constructor(attributes: List<TypeAttribute<*>>) : A
return generateNullableAccessor<TypeAttribute<*>, T>(T::class) as ReadOnlyProperty<TypeAttributes, T?>
}
override fun <T : TypeAttribute<*>> ConcurrentHashMap<KClass<out TypeAttribute<*>>, Int>.customComputeIfAbsent(
kClass: KClass<T>,
compute: (KClass<out TypeAttribute<*>>) -> Int
override fun ConcurrentHashMap<String, Int>.customComputeIfAbsent(
key: String,
compute: (String) -> Int
): Int {
return this[kClass] ?: synchronized(this) {
this[kClass] ?: compute(kClass).also { this.putIfAbsent(kClass, it) }
return this[key] ?: synchronized(this) {
this[key] ?: compute(key).also { this.putIfAbsent(key, it) }
}
}