From 064600d3518ae5bfce59d98dd3d778e7cce2654c Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Thu, 27 Jul 2023 15:59:10 +0200 Subject: [PATCH] [KLIB] API4ABI: Avoid CCE in HashMap.computeIfAbsent() ^KT-54402 --- .../library/abi/impl/LibraryAbiReaderImpl.kt | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiReaderImpl.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiReaderImpl.kt index d4a6236a631..0869c9abca2 100644 --- a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiReaderImpl.kt +++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiReaderImpl.kt @@ -23,6 +23,9 @@ import org.jetbrains.kotlin.library.abi.AbiTypeNullability.* import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.storage.CacheWithNotNullValues +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.utils.* import org.jetbrains.kotlin.utils.addToStdlib.ifTrue @@ -116,13 +119,20 @@ private class LibraryDeserializer( val packageFQN = fileReader.deserializeFqName(proto.fqNameList) packageName = AbiCompoundName(packageFQN) + val fileName = if (proto.hasFileEntry() && proto.fileEntry.hasName()) proto.fileEntry.name else "" + val fileSignature = FileSignature( id = Any(), // Just an unique object. fqName = FqName(packageFQN), - fileName = if (proto.hasFileEntry() && proto.fileEntry.hasName()) proto.fileEntry.name else "" + fileName = fileName ) signatureDeserializer = IdSignatureDeserializer(fileReader, fileSignature, interner) - typeDeserializer = TypeDeserializer(fileReader, signatureDeserializer) + + typeDeserializer = TypeDeserializer( + storageManager = LockBasedStorageManager("file '$fileName', package '$packageFQN'"), + libraryFile = fileReader, + signatureDeserializer = signatureDeserializer + ) } fun deserializeTo(output: MutableList) { @@ -551,10 +561,22 @@ private class LibraryDeserializer( } private class TypeDeserializer( + storageManager: StorageManager, private val libraryFile: IrLibraryFile, private val signatureDeserializer: IdSignatureDeserializer ) { - private val typeIdToTypeCache = HashMap() + /** + * Type Id -> [AbiType] cache. + * + * Implementation detail: The cache is backed by [CacheWithNotNullValues] instance provided by + * [LockBasedStorageManager.createCacheWithNotNullValues]. Unlike regular [java.util.Map] implementations + * that may throw an exception (preferably [java.util.ConcurrentModificationException]) if a mapping + * function in [java.util.Map.computeIfAbsent] attempts to modify the same map, or even may lead to + * 1-thread deadlocks (a real case with [java.util.concurrent.ConcurrentHashMap]), the cache based on + * [CacheWithNotNullValues] is always safe: It allows recursive calls of the mapping function to + * compute values for nested entities by design. + */ + private val typeIdToTypeCache = storageManager.createCacheWithNotNullValues() private val nonPublicTopLevelClassNames = HashSet() fun deserializeType(typeId: Int, typeParameterResolver: TypeParameterResolver): AbiType {