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:
Denis Zharkov
2018-03-19 12:19:35 +03:00
parent dca7323627
commit d8538db719
6 changed files with 34 additions and 20 deletions
@@ -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
}
}
}
@@ -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('/'))))
}
}
}
@@ -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())
}
@@ -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
}
}
@@ -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 =