Memory optimization
diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt index 7faccd8..fd10c9e 100644 --- a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt +++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt
@@ -9,6 +9,7 @@ import com.intellij.openapi.components.ApplicationComponent import com.intellij.openapi.vfs.* import com.intellij.util.containers.ContainerUtil.createConcurrentSoftValueMap +import com.intellij.util.containers.ContainerUtil.createConcurrentWeakValueMap import org.jetbrains.kotlin.konan.library.KLIB_METADATA_FILE_EXTENSION import org.jetbrains.kotlin.konan.library.KLIB_MODULE_METADATA_FILE_NAME import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf @@ -23,7 +24,7 @@ ApplicationManager.getApplication().getComponent(KotlinNativeLoadingMetadataCache::class.java) } - private val packageFragmentCache = createConcurrentSoftValueMap<VirtualFile, KonanProtoBuf.LinkDataPackageFragment>() + private val packageFragmentCache = createConcurrentWeakValueMap<VirtualFile, KonanProtoBuf.LinkDataPackageFragment>() private val moduleHeaderCache = createConcurrentSoftValueMap<VirtualFile, KonanProtoBuf.LinkDataLibrary>() fun getCachedPackageFragment(virtualFile: VirtualFile): KonanProtoBuf.LinkDataPackageFragment =
diff --git a/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt b/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt index e86679c..d9b71c8 100644 --- a/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt +++ b/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt
@@ -33,3 +33,21 @@ return ClassData(nameResolver, foundClass, KonanMetadataVersion.INSTANCE, SourceElement.NO_SOURCE) } } + +class KotlinNativeProtoBasedClassDataFinder( + proto: KonanProtoBuf.LinkDataPackageFragment, + private val nameResolver: NameResolver, + private val classSource: (ClassId) -> SourceElement = { SourceElement.NO_SOURCE } +) : ClassDataFinder { + private val classIdToProto = + proto.classes.classesList.associateBy { klass -> + nameResolver.getClassId(klass.fqName) + } + + internal val allClassIds: Collection<ClassId> get() = classIdToProto.keys + + override fun findClassData(classId: ClassId): ClassData? { + val classProto = classIdToProto[classId] ?: return null + return ClassData(nameResolver, classProto, KonanMetadataVersion.INSTANCE, classSource(classId)) + } +}
diff --git a/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt b/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt index 6e332f5..eba2c56 100644 --- a/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt +++ b/konan/library-reader/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt
@@ -25,7 +25,7 @@ private val packageAccessedHandler: PackageAccessedHandler?, storageManager: StorageManager, module: ModuleDescriptor, - partName: String + val partName: String ) : DeserializedPackageFragment(fqName, storageManager, module) { lateinit var components: DeserializationComponents @@ -36,7 +36,11 @@ // The proto field is lazy so that we can load only needed // packages from the library. - private val protoForNames: KonanProtoBuf.LinkDataPackageFragment by lazy { library.packageMetadata(fqName.asString(), partName) } + private val protoForNames: KonanProtoBuf.LinkDataPackageFragment + get() = + protoForNamesStore ?: library.packageMetadata(fqName.asString(), partName).also { protoForNamesStore = it } + + private var protoForNamesStore: KonanProtoBuf.LinkDataPackageFragment? = null val proto: KonanProtoBuf.LinkDataPackageFragment get() = protoForNames.also { packageAccessedHandler?.markPackageAccessed(fqName) } @@ -46,35 +50,37 @@ } override val classDataFinder by lazy { - KonanClassDataFinder(proto, nameResolver) + KotlinNativeProtoBasedClassDataFinder(proto, nameResolver) } private val _memberScope by lazy { + val classNameList = protoForNames.classes.classNameList /* TODO: we fake proto binary versioning for now. */ - DeserializedPackageMemberScope( + val memberScope = DeserializedPackageMemberScope( this, proto.getPackage(), nameResolver, KonanMetadataVersion.INSTANCE, /* containerSource = */ null, components - ) { loadClassNames() } + ) { loadClassNames(classNameList) } + // Clear hard ref to proto, as class fully initialized + protoForNamesStore = null + memberScope } override fun getMemberScope(): DeserializedPackageMemberScope = _memberScope private val classifierNames: Set<Name> by lazy { val result = mutableSetOf<Name>() - result.addAll(loadClassNames()) + result.addAll(loadClassNames(protoForNames.classes.classNameList)) protoForNames.getPackage().typeAliasList.mapTo(result) { nameResolver.getName(it.name) } result } fun hasTopLevelClassifier(name: Name): Boolean = name in classifierNames - private fun loadClassNames(): Collection<Name> { - - val classNameList = protoForNames.classes.classNameList + private fun loadClassNames(classNameList: List<Int>): Collection<Name> { val names = classNameList.mapNotNull { val classId = nameResolver.getClassId(it)