From c44993e5869f2e14cc33af1d4b0e11951013e617 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 20 Dec 2022 10:27:48 +0200 Subject: [PATCH] [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 --- .../kotlin/fir/util/ConeTypeRegistry.kt | 9 ++++----- .../FirDeclarationRendererWithAttributes.kt | 13 ++++++------ .../jetbrains/kotlin/util/ArrayMapOwner.kt | 20 ++++++++++++------- .../jetbrains/kotlin/types/TypeAttributes.kt | 10 +++++----- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/util/ConeTypeRegistry.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/util/ConeTypeRegistry.kt index 194235f427c..8fe9e499a9b 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/util/ConeTypeRegistry.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/util/ConeTypeRegistry.kt @@ -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 : TypeRegistry() { - override fun ConcurrentHashMap, Int>.customComputeIfAbsent( - kClass: KClass, - compute: (KClass) -> Int + override fun ConcurrentHashMap.customComputeIfAbsent( + key: String, + compute: (String) -> Int ): Int { - return this.computeIfAbsent(kClass, compute) + return this.computeIfAbsent(key, compute) } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/renderer/FirDeclarationRendererWithAttributes.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/renderer/FirDeclarationRendererWithAttributes.kt index d13976b923c..b24c6430f82 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/renderer/FirDeclarationRendererWithAttributes.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/renderer/FirDeclarationRendererWithAttributes.kt @@ -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, Any?>> { + private fun FirDeclaration.getAttributesWithValues(): List> { 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() } -} \ No newline at end of file +} diff --git a/core/compiler.common/src/org/jetbrains/kotlin/util/ArrayMapOwner.kt b/core/compiler.common/src/org/jetbrains/kotlin/util/ArrayMapOwner.kt index 33674768728..1b0dfef625a 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/util/ArrayMapOwner.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/util/ArrayMapOwner.kt @@ -61,10 +61,9 @@ class NullableArrayMapAccessor( } abstract class TypeRegistry { - private val idPerType = ConcurrentHashMap, Int>() + private val idPerType = ConcurrentHashMap() private val idCounter = AtomicInteger(0) - fun generateAccessor(kClass: KClass, default: T? = null): ArrayMapAccessor { return ArrayMapAccessor(kClass, getId(kClass), default) } @@ -78,15 +77,22 @@ abstract class TypeRegistry { } fun getId(kClass: KClass): Int { - return idPerType.customComputeIfAbsent(kClass) { idCounter.getAndIncrement() } + return idPerType.customComputeIfAbsent(kClass.qualifiedName!!) { idCounter.getAndIncrement() } } - abstract fun ConcurrentHashMap, Int>.customComputeIfAbsent( - kClass: KClass, - compute: (KClass) -> 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.customComputeIfAbsent( + key: String, + compute: (String) -> Int ): Int - fun allValuesThreadUnsafeForRendering(): Map, Int> { + fun allValuesThreadUnsafeForRendering(): Map { return idPerType } diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeAttributes.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeAttributes.kt index 9d202a1214b..2657cac2217 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeAttributes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeAttributes.kt @@ -42,12 +42,12 @@ class TypeAttributes private constructor(attributes: List>) : A return generateNullableAccessor, T>(T::class) as ReadOnlyProperty } - override fun > ConcurrentHashMap>, Int>.customComputeIfAbsent( - kClass: KClass, - compute: (KClass>) -> Int + override fun ConcurrentHashMap.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) } } }