Extract common parts of built-ins and JS decompilers
This commit is contained in:
@@ -31,6 +31,9 @@ class BuiltInsBinaryVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
|
||||
@JvmField
|
||||
val INSTANCE = BuiltInsBinaryVersion(1, 0, 0)
|
||||
|
||||
@JvmField
|
||||
val INVALID_VERSION = BuiltInsBinaryVersion()
|
||||
|
||||
fun readFrom(stream: InputStream): BuiltInsBinaryVersion {
|
||||
val dataInput = DataInputStream(stream)
|
||||
return BuiltInsBinaryVersion(*(1..dataInput.readInt()).map { dataInput.readInt() }.toIntArray())
|
||||
|
||||
+24
-95
@@ -18,130 +18,59 @@ package org.jetbrains.kotlin.idea.decompiler.builtIns
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.FileViewProvider
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.compiled.ClassFileDecompilers
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.builtins.BuiltInsBinaryVersion
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
|
||||
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.KotlinMetadataDeserializerForDecompiler
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.createIncompatibleAbiVersionDecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.FileWithMetadata
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.KotlinMetadataDecompiler
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ClassDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
class KotlinBuiltInDecompiler : ClassFileDecompilers.Full() {
|
||||
private val stubBuilder = KotlinBuiltInStubBuilder()
|
||||
|
||||
override fun accepts(file: VirtualFile): Boolean {
|
||||
return file.fileType == KotlinBuiltInFileType
|
||||
}
|
||||
|
||||
override fun getStubBuilder() = stubBuilder
|
||||
|
||||
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): FileViewProvider {
|
||||
return KotlinDecompiledFileViewProvider(manager, file, physical) { provider ->
|
||||
if (BuiltInDefinitionFile.read(provider.virtualFile) == null) {
|
||||
null
|
||||
}
|
||||
else {
|
||||
KtDecompiledFile(provider) { file ->
|
||||
buildDecompiledTextForBuiltIns(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
class KotlinBuiltInDecompiler : KotlinMetadataDecompiler<BuiltInsBinaryVersion>(
|
||||
KotlinBuiltInFileType, KotlinBuiltInStubBuilder(), TargetPlatform.Default, BuiltInSerializerProtocol,
|
||||
FlexibleTypeDeserializer.ThrowException, BuiltInsBinaryVersion.INSTANCE, BuiltInsBinaryVersion.INVALID_VERSION
|
||||
) {
|
||||
override fun readFile(bytes: ByteArray, file: VirtualFile): FileWithMetadata? {
|
||||
return BuiltInDefinitionFile.read(bytes, file)
|
||||
}
|
||||
}
|
||||
|
||||
private val decompilerRendererForBuiltIns = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
|
||||
|
||||
fun buildDecompiledTextForBuiltIns(builtInFile: VirtualFile): DecompiledText {
|
||||
if (builtInFile.fileType != KotlinBuiltInFileType) {
|
||||
error("Unexpected file type ${builtInFile.fileType}")
|
||||
}
|
||||
|
||||
val file = BuiltInDefinitionFile.read(builtInFile)
|
||||
?: error("Unexpectedly empty built-in file: $builtInFile")
|
||||
|
||||
when (file) {
|
||||
is BuiltInDefinitionFile.Incompatible -> {
|
||||
return createIncompatibleAbiVersionDecompiledText(BuiltInsBinaryVersion.INSTANCE, file.version)
|
||||
}
|
||||
is BuiltInDefinitionFile.Compatible -> {
|
||||
val packageFqName = file.packageFqName
|
||||
val resolver = KotlinMetadataDeserializerForDecompiler(
|
||||
packageFqName, file.proto, file.nameResolver,
|
||||
TargetPlatform.Default, BuiltInSerializerProtocol, FlexibleTypeDeserializer.ThrowException
|
||||
)
|
||||
val declarations = arrayListOf<DeclarationDescriptor>()
|
||||
declarations.addAll(resolver.resolveDeclarationsInFacade(packageFqName))
|
||||
for (classProto in file.classesToDecompile) {
|
||||
val classId = file.nameResolver.getClassId(classProto.fqName)
|
||||
declarations.addIfNotNull(resolver.resolveTopLevelClass(classId))
|
||||
class BuiltInDefinitionFile(
|
||||
proto: ProtoBuf.PackageFragment,
|
||||
val packageDirectory: VirtualFile,
|
||||
val isMetadata: Boolean
|
||||
) : FileWithMetadata.Compatible(proto, BuiltInSerializerProtocol) {
|
||||
override val classesToDecompile: List<ProtoBuf.Class>
|
||||
get() = super.classesToDecompile.let { classes ->
|
||||
if (isMetadata || !FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES) classes
|
||||
else classes.filter { classProto ->
|
||||
shouldDecompileBuiltInClass(nameResolver.getClassId(classProto.fqName))
|
||||
}
|
||||
return buildDecompiledText(packageFqName, declarations, decompilerRendererForBuiltIns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class BuiltInDefinitionFile {
|
||||
class Incompatible(val version: BuiltInsBinaryVersion) : BuiltInDefinitionFile()
|
||||
|
||||
class Compatible(val proto: ProtoBuf.PackageFragment,
|
||||
val packageDirectory: VirtualFile,
|
||||
val isMetadata: Boolean) : BuiltInDefinitionFile() {
|
||||
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
|
||||
val packageFqName = nameResolver.getPackageFqName(proto.`package`.getExtension(BuiltInsProtoBuf.packageFqName))
|
||||
|
||||
val classesToDecompile =
|
||||
proto.class_List.filter { proto ->
|
||||
val classId = nameResolver.getClassId(proto.fqName)
|
||||
!classId.isNestedClass && classId !in ClassDeserializer.BLACK_LIST
|
||||
}.let { classes ->
|
||||
if (isMetadata || !FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES) classes
|
||||
else classes.filter { classProto ->
|
||||
shouldDecompileBuiltInClass(nameResolver.getClassId(classProto.fqName))
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldDecompileBuiltInClass(classId: ClassId): Boolean {
|
||||
val realJvmClassFileName = classId.shortClassName.asString() + "." + JavaClassFileType.INSTANCE.defaultExtension
|
||||
return packageDirectory.findChild(realJvmClassFileName) == null
|
||||
}
|
||||
private fun shouldDecompileBuiltInClass(classId: ClassId): Boolean {
|
||||
val realJvmClassFileName = classId.shortClassName.asString() + "." + JavaClassFileType.INSTANCE.defaultExtension
|
||||
return packageDirectory.findChild(realJvmClassFileName) == null
|
||||
}
|
||||
|
||||
companion object {
|
||||
var FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES = true
|
||||
@TestOnly set
|
||||
|
||||
fun read(file: VirtualFile): BuiltInDefinitionFile? {
|
||||
return read(file.contentsToByteArray(), file)
|
||||
}
|
||||
|
||||
fun read(contents: ByteArray, file: VirtualFile): BuiltInDefinitionFile? {
|
||||
fun read(contents: ByteArray, file: VirtualFile): FileWithMetadata? {
|
||||
val stream = ByteArrayInputStream(contents)
|
||||
|
||||
val version = BuiltInsBinaryVersion.readFrom(stream)
|
||||
if (!version.isCompatible()) {
|
||||
return BuiltInDefinitionFile.Incompatible(version)
|
||||
return FileWithMetadata.Incompatible(version)
|
||||
}
|
||||
|
||||
val proto = ProtoBuf.PackageFragment.parseFrom(stream, BuiltInSerializerProtocol.extensionRegistry)
|
||||
val result = BuiltInDefinitionFile.Compatible(proto, file.parent, file.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION)
|
||||
val result = BuiltInDefinitionFile(proto, file.parent, file.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION)
|
||||
val packageProto = result.proto.`package`
|
||||
if (result.classesToDecompile.isEmpty() &&
|
||||
packageProto.typeAliasCount == 0 && packageProto.functionCount == 0 && packageProto.propertyCount == 0) {
|
||||
|
||||
+4
-3
@@ -21,10 +21,11 @@ import com.intellij.psi.impl.compiled.ClassFileStubBuilder
|
||||
import com.intellij.psi.stubs.PsiFileStub
|
||||
import com.intellij.util.indexing.FileContent
|
||||
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ProtoBasedClassDataFinder
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.AnnotationLoaderForStubBuilderImpl
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.FileWithMetadata
|
||||
import org.jetbrains.kotlin.idea.decompiler.stubBuilder.*
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinStubVersions
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ProtoBasedClassDataFinder
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
||||
|
||||
@@ -37,10 +38,10 @@ class KotlinBuiltInStubBuilder : ClsStubBuilder() {
|
||||
val file = BuiltInDefinitionFile.read(content.content, virtualFile) ?: return null
|
||||
|
||||
when (file) {
|
||||
is BuiltInDefinitionFile.Incompatible -> {
|
||||
is FileWithMetadata.Incompatible -> {
|
||||
return createIncompatibleAbiVersionFileStub()
|
||||
}
|
||||
is BuiltInDefinitionFile.Compatible -> {
|
||||
is FileWithMetadata.Compatible -> {
|
||||
val packageProto = file.proto.`package`
|
||||
val packageFqName = file.packageFqName
|
||||
val nameResolver = file.nameResolver
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.decompiler.common
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.FileViewProvider
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.compiled.ClassFileDecompilers
|
||||
import com.intellij.psi.compiled.ClsStubBuilder
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
|
||||
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
|
||||
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ClassDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.IOException
|
||||
|
||||
abstract class KotlinMetadataDecompiler<out V : BinaryVersion>(
|
||||
private val fileType: FileType,
|
||||
private val stubBuilder: ClsStubBuilder,
|
||||
private val targetPlatform: TargetPlatform,
|
||||
private val serializerProtocol: SerializerExtensionProtocol,
|
||||
private val flexibleTypeDeserializer: FlexibleTypeDeserializer,
|
||||
private val expectedBinaryVersion: V,
|
||||
private val invalidBinaryVersion: V
|
||||
) : ClassFileDecompilers.Full() {
|
||||
private val renderer = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
|
||||
|
||||
abstract fun readFile(bytes: ByteArray, file: VirtualFile): FileWithMetadata?
|
||||
|
||||
override fun accepts(file: VirtualFile) = file.fileType == fileType
|
||||
|
||||
override fun getStubBuilder() = stubBuilder
|
||||
|
||||
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): FileViewProvider {
|
||||
return KotlinDecompiledFileViewProvider(manager, file, physical) { provider ->
|
||||
if (readFile(provider.virtualFile) == null) {
|
||||
null
|
||||
}
|
||||
else {
|
||||
KtDecompiledFile(provider, this::buildDecompiledText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readFile(file: VirtualFile): FileWithMetadata? {
|
||||
if (!file.isValid) return null
|
||||
|
||||
return try {
|
||||
readFile(file.contentsToByteArray(), file)
|
||||
}
|
||||
catch (e: IOException) {
|
||||
// This is needed because sometimes we're given VirtualFile instances that point to non-existent .jar entries.
|
||||
// Such files are valid (isValid() returns true), but an attempt to read their contents results in a FileNotFoundException.
|
||||
// Note that although calling "refresh()" instead of catching an exception would seem more correct here,
|
||||
// it's not always allowed and also is likely to degrade performance
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDecompiledText(virtualFile: VirtualFile): DecompiledText {
|
||||
if (virtualFile.fileType != fileType) {
|
||||
error("Unexpected file type ${virtualFile.fileType}")
|
||||
}
|
||||
|
||||
val file = readFile(virtualFile)
|
||||
|
||||
return when (file) {
|
||||
null -> {
|
||||
createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, invalidBinaryVersion)
|
||||
}
|
||||
is FileWithMetadata.Incompatible -> {
|
||||
createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, file.version)
|
||||
}
|
||||
is FileWithMetadata.Compatible -> {
|
||||
val packageFqName = file.packageFqName
|
||||
val resolver = KotlinMetadataDeserializerForDecompiler(
|
||||
packageFqName, file.proto, file.nameResolver,
|
||||
targetPlatform, serializerProtocol, flexibleTypeDeserializer
|
||||
)
|
||||
val declarations = arrayListOf<DeclarationDescriptor>()
|
||||
declarations.addAll(resolver.resolveDeclarationsInFacade(packageFqName))
|
||||
for (classProto in file.classesToDecompile) {
|
||||
val classId = file.nameResolver.getClassId(classProto.fqName)
|
||||
declarations.addIfNotNull(resolver.resolveTopLevelClass(classId))
|
||||
}
|
||||
buildDecompiledText(packageFqName, declarations, renderer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class FileWithMetadata {
|
||||
class Incompatible(val version: BinaryVersion) : FileWithMetadata()
|
||||
|
||||
open class Compatible(
|
||||
val proto: ProtoBuf.PackageFragment,
|
||||
serializerProtocol: SerializerExtensionProtocol
|
||||
) : FileWithMetadata() {
|
||||
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
|
||||
val packageFqName = nameResolver.getPackageFqName(proto.`package`.getExtension(serializerProtocol.packageFqName))
|
||||
|
||||
open val classesToDecompile: List<ProtoBuf.Class> =
|
||||
proto.class_List.filter { proto ->
|
||||
val classId = nameResolver.getClassId(proto.fqName)
|
||||
!classId.isNestedClass && classId !in ClassDeserializer.BLACK_LIST
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-99
@@ -17,123 +17,38 @@
|
||||
package org.jetbrains.kotlin.idea.decompiler.js
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.FileViewProvider
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.compiled.ClassFileDecompilers
|
||||
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
|
||||
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.KotlinMetadataDeserializerForDecompiler
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.createIncompatibleAbiVersionDecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.FileWithMetadata
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.KotlinMetadataDecompiler
|
||||
import org.jetbrains.kotlin.js.resolve.JsPlatform
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl
|
||||
import org.jetbrains.kotlin.serialization.js.DynamicTypeDeserializer
|
||||
import org.jetbrains.kotlin.serialization.js.JsProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol
|
||||
import org.jetbrains.kotlin.utils.JsMetadataVersion
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
|
||||
class KotlinJavaScriptMetaFileDecompiler : ClassFileDecompilers.Full() {
|
||||
private val stubBuilder = KotlinJavaScriptStubBuilder()
|
||||
|
||||
override fun accepts(file: VirtualFile): Boolean {
|
||||
return file.fileType == KotlinJavaScriptMetaFileType
|
||||
}
|
||||
|
||||
override fun getStubBuilder() = stubBuilder
|
||||
|
||||
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): FileViewProvider {
|
||||
return KotlinDecompiledFileViewProvider(manager, file, physical) { provider ->
|
||||
KtDecompiledFile(provider, ::buildDecompiledTextFromJsMetadata)
|
||||
}
|
||||
class KotlinJavaScriptMetaFileDecompiler : KotlinMetadataDecompiler<JsMetadataVersion>(
|
||||
KotlinJavaScriptMetaFileType, KotlinJavaScriptStubBuilder(), JsPlatform, JsSerializerProtocol, DynamicTypeDeserializer,
|
||||
JsMetadataVersion.INSTANCE, JsMetadataVersion.INVALID_VERSION
|
||||
) {
|
||||
override fun readFile(bytes: ByteArray, file: VirtualFile): FileWithMetadata? {
|
||||
return KjsmFile.read(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
private val decompilerRendererForJS = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
|
||||
|
||||
// TODO: deduplicate code with KotlinBuiltInDecompiler
|
||||
fun buildDecompiledTextFromJsMetadata(kjsmFile: VirtualFile): DecompiledText {
|
||||
if (kjsmFile.fileType != KotlinJavaScriptMetaFileType) {
|
||||
error("Unexpected file type ${kjsmFile.fileType}")
|
||||
}
|
||||
|
||||
val file = KjsmFile.read(kjsmFile)
|
||||
|
||||
when (file) {
|
||||
null -> {
|
||||
return createIncompatibleAbiVersionDecompiledText(JsMetadataVersion.INSTANCE, JsMetadataVersion.INVALID_VERSION)
|
||||
}
|
||||
is KjsmFile.Incompatible -> {
|
||||
return createIncompatibleAbiVersionDecompiledText(JsMetadataVersion.INSTANCE, file.version)
|
||||
}
|
||||
is KjsmFile.Compatible -> {
|
||||
val packageFqName = file.packageFqName
|
||||
val resolver = KotlinMetadataDeserializerForDecompiler(
|
||||
packageFqName, file.proto, file.nameResolver,
|
||||
JsPlatform, JsSerializerProtocol, DynamicTypeDeserializer
|
||||
)
|
||||
val declarations = arrayListOf<DeclarationDescriptor>()
|
||||
declarations.addAll(resolver.resolveDeclarationsInFacade(packageFqName))
|
||||
for (klass in file.classesToDecompile) {
|
||||
val classId = file.nameResolver.getClassId(klass.fqName)
|
||||
declarations.addIfNotNull(resolver.resolveTopLevelClass(classId))
|
||||
}
|
||||
return buildDecompiledText(packageFqName, declarations, decompilerRendererForJS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: deduplicate code with BuiltInDefinitionFile
|
||||
sealed class KjsmFile {
|
||||
class Incompatible(val version: JsMetadataVersion) : KjsmFile()
|
||||
|
||||
class Compatible(val header: JsProtoBuf.Header, val proto: ProtoBuf.PackageFragment) : KjsmFile() {
|
||||
val packageFqName = FqName(header.packageFqName)
|
||||
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
|
||||
|
||||
val classesToDecompile =
|
||||
proto.class_List.filterNot { proto -> nameResolver.getClassId(proto.fqName).isNestedClass }
|
||||
}
|
||||
|
||||
class KjsmFile(proto: ProtoBuf.PackageFragment) : FileWithMetadata.Compatible(proto, JsSerializerProtocol) {
|
||||
companion object {
|
||||
fun read(file: VirtualFile): KjsmFile? {
|
||||
val stream = ByteArrayInputStream(readFileContentsSafely(file) ?: return null)
|
||||
fun read(bytes: ByteArray): FileWithMetadata? {
|
||||
val stream = ByteArrayInputStream(bytes)
|
||||
|
||||
val version = JsMetadataVersion.readFrom(stream)
|
||||
if (!version.isCompatible()) {
|
||||
return Incompatible(version)
|
||||
return FileWithMetadata.Incompatible(version)
|
||||
}
|
||||
|
||||
val header = JsProtoBuf.Header.parseDelimitedFrom(stream)
|
||||
val proto = ProtoBuf.PackageFragment.parseFrom(stream, JsSerializerProtocol.extensionRegistry)
|
||||
JsProtoBuf.Header.parseDelimitedFrom(stream)
|
||||
|
||||
return Compatible(header, proto)
|
||||
}
|
||||
|
||||
private fun readFileContentsSafely(file: VirtualFile): ByteArray? {
|
||||
if (!file.isValid) return null
|
||||
|
||||
return try {
|
||||
file.contentsToByteArray()
|
||||
}
|
||||
catch (e: IOException) {
|
||||
// This is needed because sometimes we're given VirtualFile instances that point to non-existent .jar entries.
|
||||
// Such files are valid (isValid() returns true), but an attempt to read their contents results in a FileNotFoundException.
|
||||
// Note that although calling "refresh()" instead of catching an exception would seem more correct here,
|
||||
// it's not always allowed and also is likely to degrade performance
|
||||
null
|
||||
}
|
||||
return KjsmFile(ProtoBuf.PackageFragment.parseFrom(stream, JsSerializerProtocol.extensionRegistry))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -20,10 +20,11 @@ import com.intellij.psi.compiled.ClsStubBuilder
|
||||
import com.intellij.psi.impl.compiled.ClassFileStubBuilder
|
||||
import com.intellij.psi.stubs.PsiFileStub
|
||||
import com.intellij.util.indexing.FileContent
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ProtoBasedClassDataFinder
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.AnnotationLoaderForStubBuilderImpl
|
||||
import org.jetbrains.kotlin.idea.decompiler.common.FileWithMetadata
|
||||
import org.jetbrains.kotlin.idea.decompiler.stubBuilder.*
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinStubVersions
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ProtoBasedClassDataFinder
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
||||
import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol
|
||||
@@ -35,13 +36,13 @@ class KotlinJavaScriptStubBuilder : ClsStubBuilder() {
|
||||
override fun buildFileStub(content: FileContent): PsiFileStub<*>? {
|
||||
val virtualFile = content.file
|
||||
assert(virtualFile.fileType == KotlinJavaScriptMetaFileType) { "Unexpected file type ${virtualFile.fileType}" }
|
||||
val file = KjsmFile.read(virtualFile) ?: return null
|
||||
val file = KjsmFile.read(content.content) ?: return null
|
||||
|
||||
when (file) {
|
||||
is KjsmFile.Incompatible -> {
|
||||
is FileWithMetadata.Incompatible -> {
|
||||
return createIncompatibleAbiVersionFileStub()
|
||||
}
|
||||
is KjsmFile.Compatible -> {
|
||||
is FileWithMetadata.Compatible -> {
|
||||
val packageProto = file.proto.`package`
|
||||
val packageFqName = file.packageFqName
|
||||
val nameResolver = file.nameResolver
|
||||
|
||||
@@ -127,7 +127,7 @@ open class KotlinMetadataFileIndexBase<T>(classOfIndex: Class<T>, indexFunction:
|
||||
private val INDEXER = indexer { fileContent ->
|
||||
if (fileContent.fileType == KotlinBuiltInFileType && fileContent.fileName.endsWith(MetadataPackageFragment.DOT_METADATA_FILE_EXTENSION)) {
|
||||
val builtins = BuiltInDefinitionFile.read(fileContent.content, fileContent.file.parent)
|
||||
(builtins as? BuiltInDefinitionFile.Compatible)?.let { builtinDefFile ->
|
||||
(builtins as? BuiltInDefinitionFile)?.let { builtinDefFile ->
|
||||
val proto = builtinDefFile.proto
|
||||
proto.class_List.singleOrNull()?.let { cls ->
|
||||
indexFunction(builtinDefFile.nameResolver.getClassId(cls.fqName))
|
||||
|
||||
Reference in New Issue
Block a user