Move decompiler from IJ repository
This commit just moves files and fixes imports
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":compiler:psi"))
|
||||
implementation(project(":compiler:frontend.java"))
|
||||
implementation(project(":core:compiler.common"))
|
||||
implementation(project(":compiler:light-classes"))
|
||||
implementation(project(":analysis:analysis-api-providers"))
|
||||
implementation(project(":analysis:analysis-api"))
|
||||
implementation(project(":analysis:analysis-internal-utils"))
|
||||
implementation(project(":analysis:project-structure"))
|
||||
implementation(project(":compiler:psi:cls-psi-stub-builder"))
|
||||
implementation(project(":compiler:psi:cls-psi-file-stub-builder"))
|
||||
implementation(intellijCore())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.diagnostic.Logger
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.ContractDeserializerImpl
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.NotFoundClasses
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.load.kotlin.*
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.stubs.file.builder.ClsKotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.psi.stubs.file.builder.DirectoryBasedClassFinder
|
||||
import org.jetbrains.kotlin.psi.stubs.file.builder.DirectoryBasedDataFinder
|
||||
import org.jetbrains.kotlin.resolve.sam.SamConversionResolverImpl
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
|
||||
|
||||
fun DeserializerForClassfileDecompiler(classFile: VirtualFile): DeserializerForClassfileDecompiler {
|
||||
val kotlinClassHeaderInfo =
|
||||
ClsKotlinBinaryClassCache.getInstance().getKotlinBinaryClassHeaderData(classFile)
|
||||
?: error("Decompiled data factory shouldn't be called on an unsupported file: $classFile")
|
||||
val packageFqName = kotlinClassHeaderInfo.classId.packageFqName
|
||||
return DeserializerForClassfileDecompiler(classFile.parent!!, packageFqName)
|
||||
}
|
||||
|
||||
class DeserializerForClassfileDecompiler(
|
||||
packageDirectory: VirtualFile,
|
||||
directoryPackageFqName: FqName
|
||||
) : DeserializerForDecompilerBase(directoryPackageFqName) {
|
||||
override val builtIns: KotlinBuiltIns get() = DefaultBuiltIns.Instance
|
||||
|
||||
private val classFinder = DirectoryBasedClassFinder(packageDirectory, directoryPackageFqName)
|
||||
|
||||
override val deserializationComponents: DeserializationComponents
|
||||
|
||||
init {
|
||||
val classDataFinder = DirectoryBasedDataFinder(classFinder, LOG)
|
||||
val notFoundClasses = NotFoundClasses(storageManager, moduleDescriptor)
|
||||
val annotationAndConstantLoader =
|
||||
BinaryClassAnnotationAndConstantLoaderImpl(moduleDescriptor, notFoundClasses, storageManager, classFinder)
|
||||
|
||||
val configuration = object : DeserializationConfiguration {
|
||||
override val readDeserializedContracts: Boolean = true
|
||||
override val preserveDeclarationsOrdering: Boolean = true
|
||||
}
|
||||
|
||||
deserializationComponents = DeserializationComponents(
|
||||
storageManager, moduleDescriptor, configuration, classDataFinder, annotationAndConstantLoader,
|
||||
packageFragmentProvider, ResolveEverythingToKotlinAnyLocalClassifierResolver(builtIns), LoggingErrorReporter(LOG),
|
||||
LookupTracker.DO_NOTHING, JavaFlexibleTypeDeserializer, emptyList(), notFoundClasses,
|
||||
ContractDeserializerImpl(configuration, storageManager),
|
||||
extensionRegistryLite = JvmProtoBufUtil.EXTENSION_REGISTRY,
|
||||
samConversionResolver = SamConversionResolverImpl(storageManager, samWithReceiverResolvers = emptyList())
|
||||
)
|
||||
}
|
||||
|
||||
override fun resolveDeclarationsInFacade(facadeFqName: FqName): List<DeclarationDescriptor> {
|
||||
val packageFqName = facadeFqName.parent()
|
||||
assert(packageFqName == directoryPackageFqName) {
|
||||
"Was called for $facadeFqName; only members of $directoryPackageFqName package are expected."
|
||||
}
|
||||
val binaryClassForPackageClass = classFinder.findKotlinClass(ClassId.topLevel(facadeFqName))
|
||||
val header = binaryClassForPackageClass?.classHeader
|
||||
val annotationData = header?.data
|
||||
val strings = header?.strings
|
||||
if (annotationData == null || strings == null) {
|
||||
LOG.error("Could not read annotation data for $facadeFqName from ${binaryClassForPackageClass?.classId}")
|
||||
return emptyList()
|
||||
}
|
||||
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(annotationData, strings)
|
||||
val dummyPackageFragment = createDummyPackageFragment(header.packageName?.let(::FqName) ?: facadeFqName.parent())
|
||||
val membersScope = DeserializedPackageMemberScope(
|
||||
dummyPackageFragment,
|
||||
packageProto, nameResolver, header.metadataVersion,
|
||||
JvmPackagePartSource(binaryClassForPackageClass, packageProto, nameResolver), deserializationComponents,
|
||||
"scope of dummyPackageFragment ${dummyPackageFragment.fqName} in module $moduleDescriptor @DeserializerForClassfileDecompiler"
|
||||
) { emptyList() }
|
||||
return membersScope.getContributedDescriptors().toList()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(DeserializerForClassfileDecompiler::class.java)
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// 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 org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentProviderOptimized
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents
|
||||
import org.jetbrains.kotlin.serialization.deserialization.LocalClassifierTypeSettings
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
abstract class DeserializerForDecompilerBase(val directoryPackageFqName: FqName) : ResolverForDecompiler {
|
||||
protected abstract val deserializationComponents: DeserializationComponents
|
||||
|
||||
protected abstract val builtIns: KotlinBuiltIns
|
||||
|
||||
protected val storageManager: StorageManager = LockBasedStorageManager.NO_LOCKS
|
||||
|
||||
protected val moduleDescriptor: ModuleDescriptorImpl = createDummyModule("module for building decompiled sources")
|
||||
|
||||
protected val packageFragmentProvider: PackageFragmentProvider = object : PackageFragmentProviderOptimized {
|
||||
override fun collectPackageFragments(fqName: FqName, packageFragments: MutableCollection<PackageFragmentDescriptor>) {
|
||||
packageFragments.add(createDummyPackageFragment(fqName))
|
||||
}
|
||||
|
||||
override fun isEmpty(fqName: FqName): Boolean = false
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
override fun getPackageFragments(fqName: FqName): List<PackageFragmentDescriptor> {
|
||||
return listOf(createDummyPackageFragment(fqName))
|
||||
}
|
||||
|
||||
override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection<FqName> {
|
||||
throw UnsupportedOperationException("This method is not supposed to be called.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun resolveTopLevelClass(classId: ClassId) = deserializationComponents.deserializeClass(classId)
|
||||
|
||||
protected fun createDummyPackageFragment(fqName: FqName): MutablePackageFragmentDescriptor =
|
||||
MutablePackageFragmentDescriptor(moduleDescriptor, fqName)
|
||||
|
||||
private fun createDummyModule(name: String) = ModuleDescriptorImpl(Name.special("<$name>"), storageManager, builtIns)
|
||||
|
||||
init {
|
||||
moduleDescriptor.initialize(packageFragmentProvider)
|
||||
moduleDescriptor.setDependencies(moduleDescriptor, moduleDescriptor.builtIns.builtInsModule)
|
||||
}
|
||||
}
|
||||
|
||||
class ResolveEverythingToKotlinAnyLocalClassifierResolver(private val builtIns: KotlinBuiltIns) : LocalClassifierTypeSettings {
|
||||
override val replacementTypeForLocalClassifiers: SimpleType?
|
||||
get() = builtIns.anyType
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.compiled.ClassFileDecompilers
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
|
||||
import org.jetbrains.kotlin.psi.stubs.file.builder.ClsClassFinder.isKotlinInternalCompiledFile
|
||||
import org.jetbrains.kotlin.psi.stubs.file.builder.ClsKotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.psi.stubs.file.builder.KotlinClsStubBuilder
|
||||
|
||||
class KotlinClassFileDecompiler : ClassFileDecompilers.Full() {
|
||||
private val stubBuilder = KotlinClsStubBuilder()
|
||||
|
||||
override fun accepts(file: VirtualFile) = ClsKotlinBinaryClassCache.getInstance().isKotlinJvmCompiledFile(file)
|
||||
|
||||
override fun getStubBuilder() = stubBuilder
|
||||
|
||||
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): KotlinDecompiledFileViewProvider {
|
||||
return KotlinDecompiledFileViewProvider(manager, file, physical) factory@{ provider ->
|
||||
val virtualFile = provider.virtualFile
|
||||
|
||||
if (isKotlinInternalCompiledFile(virtualFile))
|
||||
null
|
||||
else
|
||||
KtClsFile(provider)
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// 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.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiInvalidElementAccessException
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.SingleRootFileViewProvider
|
||||
import com.intellij.psi.impl.DebugUtil
|
||||
import com.intellij.psi.impl.source.PsiFileImpl
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtDecompiledFile
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.utils.concurrent.block.LockedClearableLazyValue
|
||||
|
||||
class KotlinDecompiledFileViewProvider(
|
||||
manager: PsiManager,
|
||||
file: VirtualFile,
|
||||
physical: Boolean,
|
||||
private val factory: (KotlinDecompiledFileViewProvider) -> KtDecompiledFile?
|
||||
) : SingleRootFileViewProvider(manager, file, physical, KotlinLanguage.INSTANCE) {
|
||||
val content: LockedClearableLazyValue<String> = LockedClearableLazyValue(Any()) {
|
||||
val psiFile = createFile(manager.project, file, KotlinFileType.INSTANCE)
|
||||
val text = psiFile?.text ?: ""
|
||||
|
||||
DebugUtil.performPsiModification<PsiInvalidElementAccessException>("Invalidating throw-away copy of file that was used for getting text") {
|
||||
(psiFile as? PsiFileImpl)?.markInvalidated()
|
||||
}
|
||||
|
||||
text
|
||||
}
|
||||
|
||||
override fun createFile(project: Project, file: VirtualFile, fileType: FileType): PsiFile? {
|
||||
return factory(this)
|
||||
}
|
||||
|
||||
override fun createCopy(copy: VirtualFile) = KotlinDecompiledFileViewProvider(manager, copy, false, factory)
|
||||
|
||||
override fun getContents() = content.get()
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// 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.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
|
||||
|
||||
class LoggingErrorReporter(private val log: Logger) : ErrorReporter {
|
||||
override fun reportIncompleteHierarchy(descriptor: ClassDescriptor, unresolvedSuperClasses: List<String>) {
|
||||
// This is absolutely fine for the decompiler
|
||||
}
|
||||
|
||||
override fun reportCannotInferVisibility(descriptor: CallableMemberDescriptor) {
|
||||
log.error("Could not infer visibility for $descriptor")
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// 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 org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
interface ResolverForDecompiler {
|
||||
fun resolveTopLevelClass(classId: ClassId): ClassDescriptor?
|
||||
|
||||
fun resolveDeclarationsInFacade(facadeFqName: FqName): List<DeclarationDescriptor>
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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.file
|
||||
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinDecompiledFileViewProvider
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.buildDecompiledTextForClassFile
|
||||
|
||||
class KtClsFile(provider: KotlinDecompiledFileViewProvider) : KtDecompiledFile(provider, ::buildDecompiledTextForClassFile)
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// 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.file
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinDecompiledFileViewProvider
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.DecompiledText
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.DecompiledTextIndexer
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.utils.concurrent.block.LockedClearableLazyValue
|
||||
|
||||
open class KtDecompiledFile(
|
||||
private val provider: KotlinDecompiledFileViewProvider,
|
||||
buildDecompiledText: (VirtualFile) -> DecompiledText
|
||||
) : KtFile(provider, true) {
|
||||
|
||||
private val decompiledText = LockedClearableLazyValue(Any()) {
|
||||
buildDecompiledText(provider.virtualFile)
|
||||
}
|
||||
|
||||
override fun getText(): String? {
|
||||
return decompiledText.get().text
|
||||
}
|
||||
|
||||
override fun onContentReload() {
|
||||
super.onContentReload()
|
||||
|
||||
provider.content.drop()
|
||||
decompiledText.drop()
|
||||
}
|
||||
|
||||
fun <T : Any> getDeclaration(indexer: DecompiledTextIndexer<T>, key: T): KtDeclaration? {
|
||||
val range = decompiledText.get().index.getRange(indexer, key) ?: return null
|
||||
return PsiTreeUtil.findElementOfClassAtRange(this@KtDecompiledFile, range.startOffset, range.endOffset, KtDeclaration::class.java)
|
||||
}
|
||||
|
||||
fun <T : Any> hasDeclarationWithKey(indexer: DecompiledTextIndexer<T>, key: T): Boolean {
|
||||
return decompiledText.get().index.getRange(indexer, key) != null
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.text
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
|
||||
data class DecompiledText(val text: String, val index: DecompiledTextIndex)
|
||||
|
||||
interface DecompiledTextIndexer<out T : Any> {
|
||||
fun indexDescriptor(descriptor: DeclarationDescriptor): Collection<T>
|
||||
}
|
||||
|
||||
// In-memory HashMap-based index of decompiled text
|
||||
// allows navigation features
|
||||
class DecompiledTextIndex(private val indexers: Collection<DecompiledTextIndexer<*>>) {
|
||||
private val indexerToMap: Map<DecompiledTextIndexer<*>, MutableMap<Any, TextRange>> = indexers.keysToMap { hashMapOf<Any, TextRange>() }
|
||||
|
||||
fun addToIndex(descriptor: DeclarationDescriptor, textRange: TextRange) {
|
||||
indexers.forEach { mapper ->
|
||||
val correspondingMap = indexerToMap.getValue(mapper)
|
||||
mapper.indexDescriptor(descriptor).forEach { key ->
|
||||
correspondingMap[key] = textRange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Any> getRange(mapper: DecompiledTextIndexer<T>, key: T): TextRange? = indexerToMap[mapper]?.get(key)
|
||||
|
||||
companion object {
|
||||
val Empty = DecompiledTextIndex(listOf())
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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.text
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.contracts.description.ContractProviderKey
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
|
||||
private const val DECOMPILED_CODE_COMMENT = "/* compiled code */"
|
||||
private const val DECOMPILED_COMMENT_FOR_PARAMETER = "/* = compiled code */"
|
||||
private const val FLEXIBLE_TYPE_COMMENT = "/* platform type */"
|
||||
private const val DECOMPILED_CONTRACT_STUB = "contract { /* compiled contract */ }"
|
||||
|
||||
fun DescriptorRendererOptions.defaultDecompilerRendererOptions() {
|
||||
withDefinedIn = false
|
||||
classWithPrimaryConstructor = true
|
||||
secondaryConstructorsAsPrimary = false
|
||||
modifiers = DescriptorRendererModifier.ALL
|
||||
excludedTypeAnnotationClasses = emptySet()
|
||||
alwaysRenderModifiers = true
|
||||
parameterNamesInFunctionalTypes = false // to support parameters names in decompiled text we need to load annotation arguments
|
||||
}
|
||||
|
||||
fun buildDecompiledText(
|
||||
packageFqName: FqName,
|
||||
descriptors: List<DeclarationDescriptor>,
|
||||
descriptorRenderer: DescriptorRenderer,
|
||||
indexers: Collection<DecompiledTextIndexer<*>>,
|
||||
): DecompiledText {
|
||||
val builder = StringBuilder()
|
||||
|
||||
fun appendDecompiledTextAndPackageName() {
|
||||
builder.append("// IntelliJ API Decompiler stub source generated from a class file\n" + "// Implementation of methods is not available")
|
||||
builder.append("\n\n")
|
||||
if (!packageFqName.isRoot) {
|
||||
builder.append("package ").append(packageFqName.render()).append("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
val textIndex = DecompiledTextIndex(indexers)
|
||||
|
||||
fun indexDescriptor(descriptor: DeclarationDescriptor, startOffset: Int, endOffset: Int) {
|
||||
textIndex.addToIndex(descriptor, TextRange(startOffset, endOffset))
|
||||
}
|
||||
|
||||
fun CallableMemberDescriptor.isConsideredSynthetic(): Boolean {
|
||||
return when (kind) {
|
||||
CallableMemberDescriptor.Kind.DECLARATION -> false
|
||||
|
||||
CallableMemberDescriptor.Kind.FAKE_OVERRIDE,
|
||||
CallableMemberDescriptor.Kind.DELEGATION -> true
|
||||
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED -> {
|
||||
// Of all synthesized functions, only `component*` functions are rendered (for historical reasons)
|
||||
!DataClassDescriptorResolver.isComponentLike(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun appendDescriptor(descriptor: DeclarationDescriptor, indent: String, lastEnumEntry: Boolean? = null) {
|
||||
val startOffset = builder.length
|
||||
if (isEnumEntry(descriptor)) {
|
||||
for (annotation in descriptor.annotations) {
|
||||
builder.append(descriptorRenderer.renderAnnotation(annotation))
|
||||
builder.append(" ")
|
||||
}
|
||||
builder.append(descriptor.name.asString().quoteIfNeeded())
|
||||
builder.append(if (lastEnumEntry!!) ";" else ",")
|
||||
} else {
|
||||
builder.append(descriptorRenderer.render(descriptor).replace("= ...", DECOMPILED_COMMENT_FOR_PARAMETER))
|
||||
}
|
||||
var endOffset = builder.length
|
||||
|
||||
if (descriptor is CallableDescriptor) {
|
||||
//NOTE: assuming that only return types can be flexible
|
||||
if (descriptor.returnType!!.isFlexible()) {
|
||||
builder.append(" ").append(FLEXIBLE_TYPE_COMMENT)
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor is FunctionDescriptor || descriptor is PropertyDescriptor) {
|
||||
if ((descriptor as MemberDescriptor).modality != Modality.ABSTRACT) {
|
||||
if (descriptor is FunctionDescriptor) {
|
||||
with(builder) {
|
||||
append(" { ")
|
||||
if (descriptor.getUserData(ContractProviderKey)?.getContractDescription() != null) {
|
||||
append(DECOMPILED_CONTRACT_STUB).append("; ")
|
||||
}
|
||||
append(DECOMPILED_CODE_COMMENT).append(" }")
|
||||
}
|
||||
} else {
|
||||
// descriptor instanceof PropertyDescriptor
|
||||
builder.append(" ").append(DECOMPILED_CODE_COMMENT)
|
||||
}
|
||||
endOffset = builder.length
|
||||
}
|
||||
} else if (descriptor is ClassDescriptor && !isEnumEntry(descriptor)) {
|
||||
builder.append(" {\n")
|
||||
|
||||
val subindent = "$indent "
|
||||
|
||||
var firstPassed = false
|
||||
fun newlineExceptFirst() {
|
||||
if (firstPassed) {
|
||||
builder.append("\n")
|
||||
} else {
|
||||
firstPassed = true
|
||||
}
|
||||
}
|
||||
|
||||
val allDescriptors = descriptor.secondaryConstructors + descriptor.defaultType.memberScope.getContributedDescriptors()
|
||||
val (enumEntries, members) = allDescriptors.partition(::isEnumEntry)
|
||||
|
||||
for ((index, enumEntry) in enumEntries.withIndex()) {
|
||||
newlineExceptFirst()
|
||||
builder.append(subindent)
|
||||
appendDescriptor(enumEntry, subindent, index == enumEntries.lastIndex)
|
||||
}
|
||||
|
||||
val companionObject = descriptor.companionObjectDescriptor
|
||||
if (companionObject != null) {
|
||||
newlineExceptFirst()
|
||||
builder.append(subindent)
|
||||
appendDescriptor(companionObject, subindent)
|
||||
}
|
||||
|
||||
for (member in members) {
|
||||
if (member.containingDeclaration != descriptor) {
|
||||
continue
|
||||
}
|
||||
if (member == companionObject) {
|
||||
continue
|
||||
}
|
||||
if (member is CallableMemberDescriptor && member.isConsideredSynthetic()) {
|
||||
continue
|
||||
}
|
||||
newlineExceptFirst()
|
||||
builder.append(subindent)
|
||||
appendDescriptor(member, subindent)
|
||||
}
|
||||
|
||||
builder.append(indent).append("}")
|
||||
endOffset = builder.length
|
||||
}
|
||||
|
||||
builder.append("\n")
|
||||
indexDescriptor(descriptor, startOffset, endOffset)
|
||||
|
||||
if (descriptor is ClassDescriptor) {
|
||||
val primaryConstructor = descriptor.unsubstitutedPrimaryConstructor
|
||||
if (primaryConstructor != null) {
|
||||
indexDescriptor(primaryConstructor, startOffset, endOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendDecompiledTextAndPackageName()
|
||||
for (member in descriptors) {
|
||||
appendDescriptor(member, "")
|
||||
builder.append("\n")
|
||||
}
|
||||
|
||||
return DecompiledText(builder.toString(), textIndex)
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.text
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.DeserializerForClassfileDecompiler
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.ResolverForDecompiler
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.stubs.file.builder.ClsClassFinder.findMultifileClassParts
|
||||
import org.jetbrains.kotlin.psi.stubs.file.builder.ClsKotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.types.asFlexibleType
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
|
||||
fun buildDecompiledTextForClassFile(
|
||||
classFile: VirtualFile,
|
||||
resolver: ResolverForDecompiler = DeserializerForClassfileDecompiler(classFile)
|
||||
): DecompiledText {
|
||||
val classHeader =
|
||||
ClsKotlinBinaryClassCache.getInstance().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)
|
||||
}
|
||||
|
||||
fun buildText(declarations: List<DeclarationDescriptor>) = buildDecompiledText(
|
||||
classHeader.packageName?.let(::FqName) ?: classId.packageFqName,
|
||||
declarations, decompilerRendererForClassFiles, listOf(ByDescriptorIndexer, BySignatureIndexer)
|
||||
)
|
||||
|
||||
return when (classHeader.kind) {
|
||||
KotlinClassHeader.Kind.FILE_FACADE ->
|
||||
buildText(resolver.resolveDeclarationsInFacade(classId.asSingleFqName()))
|
||||
KotlinClassHeader.Kind.CLASS -> {
|
||||
buildText(listOfNotNull(resolver.resolveTopLevelClass(classId)))
|
||||
}
|
||||
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
|
||||
val partClasses = findMultifileClassParts(classFile, classId, classHeader.partNamesIfMultifileFacade)
|
||||
val partMembers = partClasses.flatMap { partClass ->
|
||||
resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName())
|
||||
}
|
||||
buildText(partMembers)
|
||||
}
|
||||
else ->
|
||||
throw UnsupportedOperationException("Unknown header kind: $classHeader, class $classId")
|
||||
}
|
||||
}
|
||||
|
||||
private val decompilerRendererForClassFiles = DescriptorRenderer.withOptions {
|
||||
defaultDecompilerRendererOptions()
|
||||
typeNormalizer = { type -> if (type.isFlexible()) type.asFlexibleType().lowerBound else type }
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// 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.text
|
||||
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
|
||||
private const val FILE_ABI_VERSION_MARKER: String = "FILE_ABI"
|
||||
private const val CURRENT_ABI_VERSION_MARKER: String = "CURRENT_ABI"
|
||||
|
||||
const val INCOMPATIBLE_ABI_VERSION_GENERAL_COMMENT: String =
|
||||
"// This class file was compiled with different version of Kotlin compiler and can't be decompiled."
|
||||
|
||||
private const val INCOMPATIBLE_ABI_VERSION_COMMENT: String = "$INCOMPATIBLE_ABI_VERSION_GENERAL_COMMENT\n" +
|
||||
"//\n" +
|
||||
"// Current compiler ABI version is $CURRENT_ABI_VERSION_MARKER\n" +
|
||||
"// File ABI version is $FILE_ABI_VERSION_MARKER"
|
||||
|
||||
fun <V : BinaryVersion> createIncompatibleAbiVersionDecompiledText(expectedVersion: V, actualVersion: V): DecompiledText = DecompiledText(
|
||||
INCOMPATIBLE_ABI_VERSION_COMMENT.replace(CURRENT_ABI_VERSION_MARKER, expectedVersion.toString())
|
||||
.replace(FILE_ABI_VERSION_MARKER, actualVersion.toString()),
|
||||
DecompiledTextIndex.Empty
|
||||
)
|
||||
+2
-1
@@ -498,7 +498,8 @@ include ":generators:analysis-api-generator",
|
||||
":analysis:analysis-internal-utils",
|
||||
":analysis:symbol-light-classes",
|
||||
":analysis:project-structure",
|
||||
":analysis:analysis-api-fe10"
|
||||
":analysis:analysis-api-fe10",
|
||||
":analysis:decompiled:decompiler-to-psi"
|
||||
|
||||
|
||||
if (buildProperties.inJpsBuildIdeaSync) {
|
||||
|
||||
Reference in New Issue
Block a user