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:analysis-internal-utils",
|
||||||
":analysis:symbol-light-classes",
|
":analysis:symbol-light-classes",
|
||||||
":analysis:project-structure",
|
":analysis:project-structure",
|
||||||
":analysis:analysis-api-fe10"
|
":analysis:analysis-api-fe10",
|
||||||
|
":analysis:decompiled:decompiler-to-psi"
|
||||||
|
|
||||||
|
|
||||||
if (buildProperties.inJpsBuildIdeaSync) {
|
if (buildProperties.inJpsBuildIdeaSync) {
|
||||||
|
|||||||
Reference in New Issue
Block a user