Avoid retaining whole KotlinClassHeader in IDEKotlinBinaryClassCache
Before this change there was a retention chain VirtualFile -> KotlinBinaryData -> KotlinBinaryHeaderData -> KotlinClassHeader The latter one contains all binary metadata (a lot of String[]) while only a small part of it is used. The used parts are moved straight to KotlinBinaryClassHeaderData in this change. #KT-19484 Fixed
This commit is contained in:
+20
-8
@@ -22,14 +22,22 @@ import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileWithId
|
||||
import com.intellij.reference.SoftReference
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
object IDEKotlinBinaryClassCache {
|
||||
data class KotlinBinaryHeaderData(val classHeader: KotlinClassHeader, val classId: ClassId)
|
||||
data class KotlinBinaryData(val isKotlinBinary: Boolean, val timestamp: Long, val headerData: KotlinBinaryHeaderData?)
|
||||
class KotlinBinaryClassHeaderData(
|
||||
val classId: ClassId,
|
||||
val kind: KotlinClassHeader.Kind,
|
||||
val metadataVersion: JvmMetadataVersion,
|
||||
val partNamesIfMultifileFacade: List<String>,
|
||||
val packageName: String?
|
||||
)
|
||||
|
||||
data class KotlinBinaryData(val isKotlinBinary: Boolean, val timestamp: Long, val headerData: KotlinBinaryClassHeaderData?)
|
||||
|
||||
/**
|
||||
* Checks if this file is a compiled Kotlin class file (not necessarily ABI-compatible with the current plugin)
|
||||
@@ -67,7 +75,7 @@ object IDEKotlinBinaryClassCache {
|
||||
return kotlinBinaryClass
|
||||
}
|
||||
|
||||
fun getKotlinBinaryClassHeaderData(file: VirtualFile, fileContent: ByteArray? = null): KotlinBinaryHeaderData? {
|
||||
fun getKotlinBinaryClassHeaderData(file: VirtualFile, fileContent: ByteArray? = null): KotlinBinaryClassHeaderData? {
|
||||
val cached = getKotlinBinaryFromCache(file)
|
||||
if (cached != null) {
|
||||
if (!cached.isKotlinBinary) {
|
||||
@@ -84,13 +92,17 @@ object IDEKotlinBinaryClassCache {
|
||||
|
||||
private val attributeService = ServiceManager.getService(FileAttributeService::class.java)
|
||||
|
||||
private fun createHeaderInfo(kotlinBinaryClass: KotlinJvmBinaryClass?): KotlinBinaryHeaderData? {
|
||||
val header = kotlinBinaryClass?.classHeader
|
||||
val classId = kotlinBinaryClass?.classId
|
||||
private fun createHeaderInfo(kotlinBinaryClass: KotlinJvmBinaryClass?): KotlinBinaryClassHeaderData? {
|
||||
val classId = kotlinBinaryClass?.classId ?: return null
|
||||
|
||||
return if (header != null && classId != null) KotlinBinaryHeaderData(header, classId) else null
|
||||
return kotlinBinaryClass.classHeader.toLightHeader(classId)
|
||||
}
|
||||
|
||||
private fun KotlinClassHeader.toLightHeader(classId: ClassId) =
|
||||
KotlinBinaryClassHeaderData(
|
||||
classId, kind, metadataVersion, multifilePartNames, packageName
|
||||
)
|
||||
|
||||
private val KOTLIN_IS_COMPILED_FILE_ATTRIBUTE: String = "kotlin-is-binary-compiled".apply {
|
||||
ServiceManager.getService(FileAttributeService::class.java)?.register(this, 1)
|
||||
}
|
||||
@@ -120,4 +132,4 @@ object IDEKotlinBinaryClassCache {
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -42,7 +42,7 @@ fun isKotlinWithCompatibleAbiVersion(file: VirtualFile): Boolean {
|
||||
if (!IDEKotlinBinaryClassCache.isKotlinJvmCompiledFile(file)) return false
|
||||
|
||||
val kotlinClass = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file)
|
||||
return kotlinClass != null && kotlinClass.classHeader.metadataVersion.isCompatible()
|
||||
return kotlinClass != null && kotlinClass.metadataVersion.isCompatible()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,18 +69,18 @@ fun isKotlinInternalCompiledFile(file: VirtualFile, fileContent: ByteArray? = nu
|
||||
return true
|
||||
}
|
||||
|
||||
val (header, classId) = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file, fileContent) ?: return false
|
||||
if (classId.isLocal) return true
|
||||
val header = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file, fileContent) ?: return false
|
||||
if (header.classId.isLocal) return true
|
||||
|
||||
return header.kind == KotlinClassHeader.Kind.SYNTHETIC_CLASS ||
|
||||
header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|
||||
}
|
||||
|
||||
fun findMultifileClassParts(file: VirtualFile, classId: ClassId, header: KotlinClassHeader): List<KotlinJvmBinaryClass> {
|
||||
fun findMultifileClassParts(file: VirtualFile, classId: ClassId, partNames: List<String>): List<KotlinJvmBinaryClass> {
|
||||
val packageFqName = classId.packageFqName
|
||||
val partsFinder = DirectoryBasedClassFinder(file.parent!!, packageFqName)
|
||||
val partNames = header.data ?: return emptyList()
|
||||
|
||||
return partNames.mapNotNull {
|
||||
partsFinder.findKotlinClass(ClassId(packageFqName, Name.identifier(it.substringAfterLast('/'))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -67,9 +67,11 @@ fun buildDecompiledTextForClassFile(
|
||||
classFile: VirtualFile,
|
||||
resolver: ResolverForDecompiler = DeserializerForClassfileDecompiler(classFile)
|
||||
): DecompiledText {
|
||||
val (classHeader, classId) = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(classFile)
|
||||
val classHeader = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(classFile)
|
||||
?: error("Decompiled data factory shouldn't be called on an unsupported file: " + classFile)
|
||||
|
||||
val classId = classHeader.classId
|
||||
|
||||
if (!classHeader.metadataVersion.isCompatible()) {
|
||||
return createIncompatibleAbiVersionDecompiledText(JvmMetadataVersion.INSTANCE, classHeader.metadataVersion)
|
||||
}
|
||||
@@ -85,7 +87,7 @@ fun buildDecompiledTextForClassFile(
|
||||
buildText(listOfNotNull(resolver.resolveTopLevelClass(classId)))
|
||||
}
|
||||
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
|
||||
val partClasses = findMultifileClassParts(classFile, classId, classHeader)
|
||||
val partClasses = findMultifileClassParts(classFile, classId, classHeader.partNamesIfMultifileFacade)
|
||||
val partMembers = partClasses.flatMap { partClass ->
|
||||
resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName())
|
||||
}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ open class KotlinClsStubBuilder : ClsStubBuilder() {
|
||||
|
||||
val components = createStubBuilderComponents(file, packageFqName, fileContent)
|
||||
if (header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
|
||||
val partFiles = findMultifileClassParts(file, classId, header)
|
||||
val partFiles = findMultifileClassParts(file, classId, header.multifilePartNames)
|
||||
return createMultifileClassStub(header, partFiles, classId.asSingleFqName(), components)
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ object KotlinClassFileIndex : KotlinFileIndexBase<KotlinClassFileIndex>(KotlinCl
|
||||
|
||||
private val INDEXER = indexer { fileContent ->
|
||||
val headerInfo = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(fileContent.file, fileContent.content)
|
||||
if (headerInfo != null && headerInfo.classHeader.metadataVersion.isCompatible()) headerInfo.classId.asSingleFqName() else null
|
||||
if (headerInfo != null && headerInfo.metadataVersion.isCompatible()) headerInfo.classId.asSingleFqName() else null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -26,9 +26,9 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.junit.Assert
|
||||
|
||||
abstract class AbstractInternalCompiledClassesTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
private fun isFileWithHeader(predicate: (KotlinClassHeader, ClassId) -> Boolean) : VirtualFile.() -> Boolean = {
|
||||
private fun isFileWithHeader(predicate: (IDEKotlinBinaryClassCache.KotlinBinaryClassHeaderData, ClassId) -> Boolean) : VirtualFile.() -> Boolean = {
|
||||
val info = IDEKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(this)
|
||||
info != null && predicate(info.classHeader, info.classId)
|
||||
info != null && predicate(info, info.classId)
|
||||
}
|
||||
|
||||
protected fun isSyntheticClass(): VirtualFile.() -> Boolean =
|
||||
|
||||
Reference in New Issue
Block a user