Migrate stub and psi builders by classfiles form IJ repository
This commit is contained in:
+74
@@ -0,0 +1,74 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package org.jetbrains.kotlin.analysis.decompiler.psi
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinMetadataStubBuilder
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinStubVersions
|
||||
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
|
||||
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getClassId
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
class KotlinBuiltInDecompiler : KotlinMetadataDecompiler<BuiltInsBinaryVersion>(
|
||||
KotlinBuiltInFileType, { BuiltInSerializerProtocol },
|
||||
FlexibleTypeDeserializer.ThrowException, { BuiltInsBinaryVersion.INSTANCE }, { BuiltInsBinaryVersion.INVALID_VERSION },
|
||||
KotlinStubVersions.BUILTIN_STUB_VERSION
|
||||
) {
|
||||
override fun readFile(bytes: ByteArray, file: VirtualFile): KotlinMetadataStubBuilder.FileWithMetadata? {
|
||||
return BuiltInDefinitionFile.read(bytes, file)
|
||||
}
|
||||
}
|
||||
|
||||
class BuiltInDefinitionFile(
|
||||
proto: ProtoBuf.PackageFragment,
|
||||
version: BuiltInsBinaryVersion,
|
||||
val packageDirectory: VirtualFile,
|
||||
val isMetadata: Boolean
|
||||
) : KotlinMetadataStubBuilder.FileWithMetadata.Compatible(proto, version, 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))
|
||||
}
|
||||
}
|
||||
|
||||
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(contents: ByteArray, file: VirtualFile): KotlinMetadataStubBuilder.FileWithMetadata? {
|
||||
val stream = ByteArrayInputStream(contents)
|
||||
|
||||
val version = BuiltInsBinaryVersion.readFrom(stream)
|
||||
if (!version.isCompatible()) {
|
||||
return Incompatible(version)
|
||||
}
|
||||
|
||||
val proto = ProtoBuf.PackageFragment.parseFrom(stream, BuiltInSerializerProtocol.extensionRegistry)
|
||||
val result =
|
||||
BuiltInDefinitionFile(proto, version, 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
|
||||
) {
|
||||
// No declarations to decompile: should skip this file
|
||||
return null
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package org.jetbrains.kotlin.analysis.decompiler.psi
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.KotlinIconProviderService
|
||||
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
|
||||
import javax.swing.Icon
|
||||
|
||||
object KotlinBuiltInFileType : FileType {
|
||||
override fun getName() = "kotlin_builtins"
|
||||
|
||||
override fun getDescription(): String =
|
||||
KotlinLabelProviderService.getService()?.getLabelForBuiltInFileType()
|
||||
?: DEFAULT_DESCRIPTION
|
||||
|
||||
override fun getDefaultExtension() = BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION
|
||||
|
||||
override fun getIcon(): Icon = KotlinIconProviderService.getInstance().builtInFileIcon
|
||||
|
||||
override fun isBinary() = true
|
||||
|
||||
override fun isReadOnly() = true
|
||||
|
||||
override fun getCharset(file: VirtualFile, content: ByteArray): String? = null
|
||||
|
||||
private const val DEFAULT_DESCRIPTION = "Kotlin built-in declarations"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.decompiler.psi
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
|
||||
abstract class KotlinLabelProviderService {
|
||||
abstract fun getLabelForBuiltInFileType(): String
|
||||
|
||||
companion object {
|
||||
fun getService(): KotlinLabelProviderService? =
|
||||
ApplicationManager.getApplication().getServiceIfCreated(KotlinLabelProviderService::class.java)
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package org.jetbrains.kotlin.analysis.decompiler.psi
|
||||
|
||||
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 org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtDecompiledFile
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.DecompiledText
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.buildDecompiledText
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.createIncompatibleAbiVersionDecompiledText
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.defaultDecompilerRendererOptions
|
||||
import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinMetadataStubBuilder
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
|
||||
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getClassId
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.IOException
|
||||
|
||||
abstract class KotlinMetadataDecompiler<out V : BinaryVersion>(
|
||||
private val fileType: FileType,
|
||||
private val serializerProtocol: () -> SerializerExtensionProtocol,
|
||||
private val flexibleTypeDeserializer: FlexibleTypeDeserializer,
|
||||
private val expectedBinaryVersion: () -> V,
|
||||
private val invalidBinaryVersion: () -> V,
|
||||
stubVersion: Int
|
||||
) : ClassFileDecompilers.Full() {
|
||||
private val metadataStubBuilder: KotlinMetadataStubBuilder =
|
||||
KotlinMetadataStubBuilder(stubVersion, fileType, serializerProtocol, ::readFileSafely)
|
||||
|
||||
private val renderer: DescriptorRenderer by lazy {
|
||||
DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
|
||||
}
|
||||
|
||||
abstract fun readFile(bytes: ByteArray, file: VirtualFile): KotlinMetadataStubBuilder.FileWithMetadata?
|
||||
|
||||
override fun accepts(file: VirtualFile) = file.fileType == fileType
|
||||
|
||||
override fun getStubBuilder() = metadataStubBuilder
|
||||
|
||||
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): FileViewProvider {
|
||||
return KotlinDecompiledFileViewProvider(manager, file, physical) { provider ->
|
||||
val virtualFile = provider.virtualFile
|
||||
readFileSafely(virtualFile)?.let { fileWithMetadata ->
|
||||
KtDecompiledFile(provider) {
|
||||
check(it == virtualFile) {
|
||||
"Unexpected file $it, expected ${virtualFile.fileType}"
|
||||
}
|
||||
buildDecompiledText(fileWithMetadata)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun readFile(file: VirtualFile) = readFileSafely(file)
|
||||
|
||||
private fun readFileSafely(file: VirtualFile, content: ByteArray? = null): KotlinMetadataStubBuilder.FileWithMetadata? {
|
||||
if (!file.isValid) return null
|
||||
|
||||
return try {
|
||||
readFile(content ?: file.contentsToByteArray(false), 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(file: KotlinMetadataStubBuilder.FileWithMetadata): DecompiledText {
|
||||
return when (file) {
|
||||
is KotlinMetadataStubBuilder.FileWithMetadata.Incompatible -> {
|
||||
createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion(), file.version)
|
||||
}
|
||||
is KotlinMetadataStubBuilder.FileWithMetadata.Compatible -> {
|
||||
val packageFqName = file.packageFqName
|
||||
val resolver = KotlinMetadataDeserializerForDecompiler(
|
||||
packageFqName, file.proto, file.nameResolver, file.version,
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package org.jetbrains.kotlin.analysis.decompiler.psi
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.NotFoundClasses
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.sam.SamConversionResolverImpl
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
|
||||
import org.jetbrains.kotlin.serialization.deserialization.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
|
||||
|
||||
class KotlinMetadataDeserializerForDecompiler(
|
||||
packageFqName: FqName,
|
||||
private val proto: ProtoBuf.PackageFragment,
|
||||
private val nameResolver: NameResolver,
|
||||
private val metadataVersion: BinaryVersion,
|
||||
serializerProtocol: SerializerExtensionProtocol,
|
||||
flexibleTypeDeserializer: FlexibleTypeDeserializer
|
||||
) : DeserializerForDecompilerBase(packageFqName) {
|
||||
override val builtIns: KotlinBuiltIns get() = DefaultBuiltIns.Instance
|
||||
|
||||
override val deserializationComponents: DeserializationComponents
|
||||
|
||||
init {
|
||||
val notFoundClasses = NotFoundClasses(storageManager, moduleDescriptor)
|
||||
|
||||
deserializationComponents = DeserializationComponents(
|
||||
storageManager, moduleDescriptor, DeserializationConfiguration.Default,
|
||||
ProtoBasedClassDataFinder(proto, nameResolver, metadataVersion),
|
||||
AnnotationAndConstantLoaderImpl(moduleDescriptor, notFoundClasses, serializerProtocol), packageFragmentProvider,
|
||||
ResolveEverythingToKotlinAnyLocalClassifierResolver(builtIns), LoggingErrorReporter(LOG),
|
||||
LookupTracker.DO_NOTHING, flexibleTypeDeserializer, emptyList(), notFoundClasses,
|
||||
ContractDeserializer.DEFAULT,
|
||||
extensionRegistryLite = serializerProtocol.extensionRegistry,
|
||||
samConversionResolver = SamConversionResolverImpl(storageManager, samWithReceiverResolvers = emptyList())
|
||||
)
|
||||
}
|
||||
|
||||
override fun resolveDeclarationsInFacade(facadeFqName: FqName): List<DeclarationDescriptor> {
|
||||
assert(facadeFqName == directoryPackageFqName) {
|
||||
"Was called for $facadeFqName; only members of $directoryPackageFqName package are expected."
|
||||
}
|
||||
|
||||
val dummyPackageFragment = createDummyPackageFragment(facadeFqName)
|
||||
val membersScope = DeserializedPackageMemberScope(
|
||||
dummyPackageFragment, proto.`package`, nameResolver, metadataVersion, containerSource = null,
|
||||
components = deserializationComponents,
|
||||
debugName = "scope of dummyPackageFragment ${dummyPackageFragment.fqName} in module " +
|
||||
"${deserializationComponents.moduleDescriptor} @KotlinMetadataDeserializerForDecompiler"
|
||||
) { emptyList() }
|
||||
|
||||
return membersScope.getContributedDescriptors().toList()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KotlinMetadataDeserializerForDecompiler::class.java)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ object ByDescriptorIndexer : DecompiledTextIndexer<String> {
|
||||
return listOf(descriptor.toStringKey())
|
||||
}
|
||||
|
||||
internal fun getDeclarationForDescriptor(descriptor: DeclarationDescriptor, file: KtDecompiledFile): KtDeclaration? {
|
||||
fun getDeclarationForDescriptor(descriptor: DeclarationDescriptor, file: KtDecompiledFile): KtDeclaration? {
|
||||
val original = descriptor.original
|
||||
|
||||
if (original is TypeAliasConstructorDescriptor) {
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ fun buildDecompiledText(
|
||||
packageFqName: FqName,
|
||||
descriptors: List<DeclarationDescriptor>,
|
||||
descriptorRenderer: DescriptorRenderer,
|
||||
indexers: Collection<DecompiledTextIndexer<*>>,
|
||||
indexers: Collection<DecompiledTextIndexer<*>> = listOf(ByDescriptorIndexer),
|
||||
): DecompiledText {
|
||||
val builder = StringBuilder()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user