Add ability to decompile multifile classes

A side effect of KotlinClassFileDecompiler#KotlinDecompiledFileViewProvider@factory
that multifile java classes like FilesKt__FilePathComponentsKt.class
becomes not visible with Kotlin plugin as file
jar://..../kotlin-stdlib.jar!/kotlin/io/FilesKt__FilePathComponentsKt.class
is considered as Kotlin internal compiled file and therefore no PsiFile
for this virtualFile

#KTIJ-912

Merge-request: KT-MR-8146
Merged-by: Vladimir Dolzhenko <Vladimir.Dolzhenko@jetbrains.com>
This commit is contained in:
Vladimir Dolzhenko
2023-01-02 15:31:19 +00:00
committed by Space Team
parent 7a9c59aeb5
commit f9f0f561d7
3 changed files with 19 additions and 5 deletions
@@ -29,8 +29,7 @@ object ClsClassFinder {
* which should NOT be decompiled (and, as a result, shown under the library in the Project view, be searchable via Find class, etc.) * which should NOT be decompiled (and, as a result, shown under the library in the Project view, be searchable via Find class, etc.)
*/ */
fun isKotlinInternalCompiledFile(file: VirtualFile, fileContent: ByteArray? = null): Boolean { fun isKotlinInternalCompiledFile(file: VirtualFile, fileContent: ByteArray? = null): Boolean {
// Don't crash on invalid files (EA-97751) if (!file.isValidAndExists(fileContent)) {
if (!file.isValid || fileContent?.size == 0 || !file.exists()) {
return false return false
} }
@@ -65,4 +64,17 @@ object ClsClassFinder {
return header.kind == KotlinClassHeader.Kind.SYNTHETIC_CLASS || return header.kind == KotlinClassHeader.Kind.SYNTHETIC_CLASS ||
header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART header.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
} }
fun isMultifileClassPartFile(file: VirtualFile, fileContent: ByteArray? = null): Boolean {
if (!file.isValidAndExists(fileContent)) {
return false
}
val clsKotlinBinaryClassCache = ClsKotlinBinaryClassCache.getInstance()
val headerData = clsKotlinBinaryClassCache.getKotlinBinaryClassHeaderData(file, fileContent)
return headerData?.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
}
// Don't crash on invalid files (EA-97751)
private fun VirtualFile.isValidAndExists(fileContent: ByteArray? = null): Boolean =
this.isValid && fileContent?.size != 0 && this.exists()
} }
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiManager
import com.intellij.psi.compiled.ClassFileDecompilers import com.intellij.psi.compiled.ClassFileDecompilers
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsClassFinder.isKotlinInternalCompiledFile import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsClassFinder.isKotlinInternalCompiledFile
import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsClassFinder.isMultifileClassPartFile
import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache
import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinClsStubBuilder import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinClsStubBuilder
@@ -24,10 +25,11 @@ class KotlinClassFileDecompiler : ClassFileDecompilers.Full() {
return KotlinDecompiledFileViewProvider(manager, file, physical) factory@{ provider -> return KotlinDecompiledFileViewProvider(manager, file, physical) factory@{ provider ->
val virtualFile = provider.virtualFile val virtualFile = provider.virtualFile
if (isKotlinInternalCompiledFile(virtualFile)) if (isKotlinInternalCompiledFile(virtualFile) && !isMultifileClassPartFile(virtualFile)) {
null null
else } else {
KtClsFile(provider) KtClsFile(provider)
}
} }
} }
} }
@@ -37,7 +37,7 @@ fun buildDecompiledTextForClassFile(
) )
return when (classHeader.kind) { return when (classHeader.kind) {
KotlinClassHeader.Kind.FILE_FACADE -> KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART ->
buildText(resolver.resolveDeclarationsInFacade(classId.asSingleFqName())) buildText(resolver.resolveDeclarationsInFacade(classId.asSingleFqName()))
KotlinClassHeader.Kind.CLASS -> { KotlinClassHeader.Kind.CLASS -> {
buildText(listOfNotNull(resolver.resolveTopLevelClass(classId))) buildText(listOfNotNull(resolver.resolveTopLevelClass(classId)))