[decompiler] get rid of indexers in decompiled text
This commit is contained in:
-3
@@ -3,11 +3,8 @@
|
||||
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
|
||||
|
||||
|
||||
+1
-5
@@ -28,11 +28,7 @@ import org.jetbrains.kotlin.types.typeUtil.isNullableAny
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
|
||||
object ByDescriptorIndexer : DecompiledTextIndexer<String> {
|
||||
override fun indexDescriptor(descriptor: DeclarationDescriptor): Collection<String> {
|
||||
return listOf(descriptor.toStringKey())
|
||||
}
|
||||
|
||||
object ByDescriptorIndexer {
|
||||
fun getDeclarationForDescriptor(descriptor: DeclarationDescriptor, file: KtDecompiledFile): KtDeclaration? {
|
||||
val original = descriptor.original
|
||||
|
||||
|
||||
-184
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* 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.psi.PsiMember
|
||||
import com.intellij.psi.impl.compiled.SignatureParsing
|
||||
import com.intellij.util.cls.ClsFormatException
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.kotlin.MemberSignature
|
||||
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
|
||||
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
|
||||
import java.text.CharacterIterator
|
||||
import java.text.StringCharacterIterator
|
||||
|
||||
private object ByJvmSignatureIndexer : DecompiledTextIndexer<ClassNameAndSignature> {
|
||||
override fun indexDescriptor(descriptor: DeclarationDescriptor): Collection<ClassNameAndSignature> {
|
||||
val signatures = arrayListOf<ClassNameAndSignature>()
|
||||
fun save(id: List<Name>, signature: MemberSignature) {
|
||||
signatures.add(ClassNameAndSignature(id, signature))
|
||||
}
|
||||
|
||||
fun ClassDescriptor.apply() {
|
||||
when (kind) {
|
||||
ClassKind.ENUM_ENTRY -> {
|
||||
val enumClass = containingDeclaration as ClassDescriptor
|
||||
val signature = MemberSignature.fromFieldNameAndDesc(name.asString(), enumClass.desc())
|
||||
save(enumClass.relativeClassName(), signature)
|
||||
}
|
||||
ClassKind.OBJECT -> {
|
||||
val instanceFieldSignature = MemberSignature.fromFieldNameAndDesc(JvmAbi.INSTANCE_FIELD, desc())
|
||||
save(relativeClassName(), instanceFieldSignature)
|
||||
if (isCompanionObject) {
|
||||
val signature = MemberSignature.fromFieldNameAndDesc(name.asString(), desc())
|
||||
save((containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty(), signature)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun DeserializedClassConstructorDescriptor.apply() {
|
||||
JvmProtoBufUtil.getJvmConstructorSignature(proto, nameResolver, typeTable)?.let {
|
||||
val id = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
|
||||
val signature = MemberSignature.fromJvmMemberSignature(it)
|
||||
save(id, signature)
|
||||
}
|
||||
}
|
||||
|
||||
fun DeserializedSimpleFunctionDescriptor.apply() {
|
||||
JvmProtoBufUtil.getJvmMethodSignature(proto, nameResolver, typeTable)?.let {
|
||||
val id = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
|
||||
|
||||
val signature = MemberSignature.fromJvmMemberSignature(it)
|
||||
save(id, signature)
|
||||
|
||||
if (findJvmOverloadsAnnotation() == null) return
|
||||
|
||||
val extensionShift = if (isExtension) 1 else 0
|
||||
|
||||
val omittedList = mutableListOf<Int>()
|
||||
valueParameters.asReversed().forEach { parameter ->
|
||||
if (parameter.hasDefaultValue()) {
|
||||
omittedList.add(parameter.index + extensionShift)
|
||||
val newDescriptor = excludeParametersFromDescriptor(it.desc, omittedList)
|
||||
if (newDescriptor != null) {
|
||||
val overloadedSignature = MemberSignature.fromMethodNameAndDesc(it.name, newDescriptor)
|
||||
save(id, overloadedSignature)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun DeserializedPropertyDescriptor.apply() {
|
||||
val className = (containingDeclaration as? ClassDescriptor)?.relativeClassName().orEmpty()
|
||||
val signature = proto.getExtensionOrNull(JvmProtoBuf.propertySignature)
|
||||
if (signature != null) {
|
||||
val fieldSignature = JvmProtoBufUtil.getJvmFieldSignature(proto, nameResolver, typeTable)
|
||||
if (fieldSignature != null) {
|
||||
save(className, MemberSignature.fromJvmMemberSignature(fieldSignature))
|
||||
}
|
||||
if (signature.hasGetter()) {
|
||||
save(className, MemberSignature.fromMethod(nameResolver, signature.getter))
|
||||
}
|
||||
if (signature.hasSetter()) {
|
||||
save(className, MemberSignature.fromMethod(nameResolver, signature.setter))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when (descriptor) {
|
||||
is ClassDescriptor -> descriptor.apply()
|
||||
is DeserializedClassConstructorDescriptor -> descriptor.apply()
|
||||
is DeserializedSimpleFunctionDescriptor -> descriptor.apply()
|
||||
is DeserializedPropertyDescriptor -> descriptor.apply()
|
||||
}
|
||||
|
||||
return signatures
|
||||
}
|
||||
}
|
||||
|
||||
private fun excludeParametersFromDescriptor(descriptor: String, omittedParameters: List<Int>): String? {
|
||||
|
||||
fun tryParseParametersAndReturnType(): Pair<List<String>, String>? {
|
||||
val iterator = StringCharacterIterator(descriptor)
|
||||
|
||||
fun parseTypeString(): String? {
|
||||
val begin = iterator.index
|
||||
try {
|
||||
SignatureParsing.parseTypeString(iterator) { it }
|
||||
} catch (e: ClsFormatException) {
|
||||
return null
|
||||
}
|
||||
val end = iterator.index
|
||||
return descriptor.substring(begin, end)
|
||||
}
|
||||
|
||||
if (iterator.current() != '(') return null
|
||||
iterator.next()
|
||||
|
||||
if (iterator.current() == ')') {
|
||||
iterator.next()
|
||||
val returnType = parseTypeString() ?: return null
|
||||
return emptyList<String>() to returnType
|
||||
}
|
||||
|
||||
val parameterTypes = mutableListOf<String>()
|
||||
while (iterator.current() != ')' && iterator.current() != CharacterIterator.DONE) {
|
||||
parameterTypes += parseTypeString() ?: return null
|
||||
}
|
||||
|
||||
if (iterator.current() != ')') return null
|
||||
iterator.next()
|
||||
|
||||
val returnType = parseTypeString() ?: return null
|
||||
return parameterTypes to returnType
|
||||
}
|
||||
|
||||
val (parameterTypes, returnType) = tryParseParametersAndReturnType() ?: return null
|
||||
|
||||
val parametersList = parameterTypes
|
||||
.filterIndexed { index, _ -> index !in omittedParameters }
|
||||
.joinToString("")
|
||||
|
||||
return "($parametersList)$returnType"
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.desc(): String = "L" + JvmClassName.byClassId(classId!!).internalName + ";"
|
||||
|
||||
fun PsiMember.relativeClassName(): List<Name> {
|
||||
return generateSequence(this.containingClass) { it.containingClass }.toList().dropLast(1).reversed().map { Name.identifier(it.name!!) }
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.relativeClassName(): List<Name> {
|
||||
return classId!!.relativeClassName.pathSegments().drop(1)
|
||||
}
|
||||
|
||||
// every member is represented by its jvm signature and relative class name (which is easy to obtain from descriptors or cls psi)
|
||||
|
||||
// relative class name is a path containing inner/nested class names from top level class to the class containing this member (excluding top level class name)
|
||||
// Examples: for top level function or function in a top level class relativeClassName is empty
|
||||
// For: class TopLevel { class A { class B { fun f() } } }
|
||||
// relativeClassName for function 'f' will be [A, B]
|
||||
data class ClassNameAndSignature(val relativeClassName: List<Name>, val memberSignature: MemberSignature)
|
||||
|
||||
// expose with different type
|
||||
val BySignatureIndexer: DecompiledTextIndexer<*> = ByJvmSignatureIndexer
|
||||
+1
-30
@@ -5,33 +5,4 @@
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
data class DecompiledText(val text: String)
|
||||
+1
-21
@@ -66,7 +66,6 @@ fun buildDecompiledText(
|
||||
packageFqName: FqName,
|
||||
descriptors: List<DeclarationDescriptor>,
|
||||
descriptorRenderer: DescriptorRenderer,
|
||||
indexers: Collection<DecompiledTextIndexer<*>> = listOf(ByDescriptorIndexer),
|
||||
): DecompiledText {
|
||||
val builder = StringBuilder()
|
||||
|
||||
@@ -78,14 +77,7 @@ fun buildDecompiledText(
|
||||
}
|
||||
}
|
||||
|
||||
val textIndex = DecompiledTextIndex(indexers)
|
||||
|
||||
fun indexDescriptor(descriptor: DeclarationDescriptor, startOffset: Int, endOffset: Int) {
|
||||
textIndex.addToIndex(descriptor, TextRange(startOffset, endOffset))
|
||||
}
|
||||
|
||||
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))
|
||||
@@ -96,7 +88,6 @@ fun buildDecompiledText(
|
||||
} 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
|
||||
@@ -119,7 +110,6 @@ fun buildDecompiledText(
|
||||
// descriptor instanceof PropertyDescriptor
|
||||
builder.append(" ").append(DECOMPILED_CODE_COMMENT)
|
||||
}
|
||||
endOffset = builder.length
|
||||
}
|
||||
if (descriptor is PropertyDescriptor) {
|
||||
for (accessor in descriptor.accessors) {
|
||||
@@ -152,7 +142,6 @@ fun buildDecompiledText(
|
||||
builder.append(")")
|
||||
builder.append(" {").append(DECOMPILED_CODE_COMMENT).append(" }")
|
||||
}
|
||||
endOffset = builder.length
|
||||
}
|
||||
}
|
||||
} else if (descriptor is ClassDescriptor && !isEnumEntry(descriptor)) {
|
||||
@@ -201,18 +190,9 @@ fun buildDecompiledText(
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -221,5 +201,5 @@ fun buildDecompiledText(
|
||||
builder.append("\n")
|
||||
}
|
||||
|
||||
return DecompiledText(builder.toString(), textIndex)
|
||||
return DecompiledText(builder.toString())
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ fun buildDecompiledTextForClassFile(
|
||||
|
||||
fun buildText(declarations: List<DeclarationDescriptor>) = buildDecompiledText(
|
||||
classHeader.packageNameWithFallback,
|
||||
declarations, decompilerRendererForClassFiles, listOf(ByDescriptorIndexer, BySignatureIndexer)
|
||||
declarations, decompilerRendererForClassFiles
|
||||
)
|
||||
|
||||
return when (classHeader.kind) {
|
||||
|
||||
+1
-2
@@ -17,6 +17,5 @@ private const val INCOMPATIBLE_ABI_VERSION_COMMENT: String = "$INCOMPATIBLE_ABI_
|
||||
|
||||
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
|
||||
.replace(FILE_ABI_VERSION_MARKER, actualVersion.toString())
|
||||
)
|
||||
|
||||
+3
-6
@@ -6,19 +6,16 @@
|
||||
package org.jetbrains.kotlin.analysis.decompiled.light.classes.fe10
|
||||
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.analysis.decompiled.light.classes.origin.KotlinDeclarationInCompiledFileSearcher
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.BySignatureIndexer
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.ClassNameAndSignature
|
||||
import org.jetbrains.kotlin.analysis.decompiler.psi.text.relativeClassName
|
||||
import org.jetbrains.kotlin.load.kotlin.MemberSignature
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
|
||||
class KotlinDeclarationInCompiledFileSearcherFE10Impl : KotlinDeclarationInCompiledFileSearcher() {
|
||||
override fun findDeclarationInCompiledFile(file: KtClsFile, member: PsiMember, signature: MemberSignature): KtDeclaration? {
|
||||
val relativeClassName = member.relativeClassName()
|
||||
val relativeClassName = generateSequence(member.containingClass) { it.containingClass }.toList().dropLast(1).reversed()
|
||||
.map { Name.identifier(it.name!!) }
|
||||
|
||||
val memberName = member.name ?: return null
|
||||
return findByStubs(file, relativeClassName, member, memberName)
|
||||
|
||||
Reference in New Issue
Block a user