[decompiler] get rid of indexers in decompiled text

This commit is contained in:
Anna Kozlova
2023-04-27 11:33:20 +02:00
committed by teamcity
parent 1102dc1d0e
commit 8106505166
8 changed files with 8 additions and 252 deletions
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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())
}
@@ -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) {
@@ -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())
)
@@ -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)