diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/IDEKotlinBinaryClassCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/IDEKotlinBinaryClassCache.kt index 5ac5e5194b5..eb54abc81f7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/IDEKotlinBinaryClassCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/IDEKotlinBinaryClassCache.kt @@ -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, + 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 } -} \ No newline at end of file +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/ClassFileDecompilerUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/ClassFileDecompilerUtil.kt index 7b4f87d0cd1..cd53f6fddcc 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/ClassFileDecompilerUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/ClassFileDecompilerUtil.kt @@ -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 { +fun findMultifileClassParts(file: VirtualFile, classId: ClassId, partNames: List): List { 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('/')))) } -} \ No newline at end of file +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClassFileDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClassFileDecompiler.kt index 14fa2bac4b7..1b978006721 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClassFileDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClassFileDecompiler.kt @@ -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()) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClsStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClsStubBuilder.kt index ace7f7dc180..10db6bc77da 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClsStubBuilder.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClsStubBuilder.kt @@ -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) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/vfilefinder/fileIndexes.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/vfilefinder/fileIndexes.kt index ff6d123a660..8cde604956a 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/vfilefinder/fileIndexes.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/vfilefinder/fileIndexes.kt @@ -93,7 +93,7 @@ object KotlinClassFileIndex : KotlinFileIndexBase(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 } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/AbstractInternalCompiledClassesTest.kt b/idea/tests/org/jetbrains/kotlin/idea/decompiler/AbstractInternalCompiledClassesTest.kt index 75e27e4cc39..ff2da49949f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/decompiler/AbstractInternalCompiledClassesTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/AbstractInternalCompiledClassesTest.kt @@ -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 =