Move decompiler from IJ repository

This commit just moves files and fixes imports
This commit is contained in:
Ilya Kirillov
2021-12-17 15:46:01 +03:00
parent a367fee5cf
commit fceb46924e
14 changed files with 639 additions and 1 deletions
@@ -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() }
}
@@ -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)
}
}
@@ -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
}
@@ -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)
}
}
}
@@ -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()
}
@@ -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")
}
}
@@ -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>
}
@@ -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)
@@ -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
}
}
@@ -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())
}
}
@@ -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)
}
@@ -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 }
}
@@ -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
View File
@@ -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) {