Refactor: abstract over decompiled text indexing
Move navigation-related code from KtDecompiledFile
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,25 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.decompiler
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.descriptorToKey
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.kotlin.BuiltInClassesAreSerializableOnJvm
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
|
||||
import org.jetbrains.kotlin.utils.concurrent.block.LockedClearableLazyValue
|
||||
|
||||
open class KtDecompiledFile(
|
||||
@@ -46,38 +33,6 @@ open class KtDecompiledFile(
|
||||
buildDecompiledText(provider.virtualFile)
|
||||
}
|
||||
|
||||
fun getDeclarationForDescriptor(descriptor: DeclarationDescriptor): KtDeclaration? {
|
||||
val original = descriptor.original
|
||||
|
||||
if (original is ValueParameterDescriptor) {
|
||||
val callable = original.containingDeclaration
|
||||
val callableDeclaration = getDeclarationForDescriptor(callable) as? KtCallableDeclaration ?: return null
|
||||
return callableDeclaration.valueParameters[original.index]
|
||||
}
|
||||
|
||||
if (original is ConstructorDescriptor && original.isPrimary) {
|
||||
val classOrObject = getDeclarationForDescriptor(original.containingDeclaration) as? KtClassOrObject
|
||||
return classOrObject?.getPrimaryConstructor() ?: classOrObject
|
||||
}
|
||||
|
||||
return original.findElementForDescriptor() ?: run {
|
||||
if (descriptor !is ClassDescriptor) return null
|
||||
|
||||
val classFqName = descriptor.fqNameSafe
|
||||
if (BuiltInClassesAreSerializableOnJvm.isSerializableInJava(classFqName)) {
|
||||
val builtInDescriptor = TargetPlatform.Default.builtIns.builtInsModule.resolveTopLevelClass(classFqName, NoLookupLocation.FROM_IDE)
|
||||
return builtInDescriptor?.findElementForDescriptor()
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.findElementForDescriptor(): KtDeclaration? {
|
||||
return decompiledText.get().renderedDescriptorsToRange[descriptorToKey(this)]?.let { range ->
|
||||
PsiTreeUtil.findElementOfClassAtRange(this@KtDecompiledFile, range.startOffset, range.endOffset, KtDeclaration::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getText(): String? {
|
||||
return decompiledText.get().text
|
||||
}
|
||||
@@ -89,9 +44,8 @@ open class KtDecompiledFile(
|
||||
decompiledText.drop()
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun getRenderedDescriptorsToRange(): Map<String, TextRange> {
|
||||
return decompiledText.get().renderedDescriptorsToRange
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.idea.decompiler.common
|
||||
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndex
|
||||
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
|
||||
|
||||
private val FILE_ABI_VERSION_MARKER: String = "FILE_ABI"
|
||||
@@ -34,6 +35,6 @@ fun <V : BinaryVersion> createIncompatibleAbiVersionDecompiledText(expectedVersi
|
||||
INCOMPATIBLE_ABI_VERSION_COMMENT
|
||||
.replace(CURRENT_ABI_VERSION_MARKER, expectedVersion.toString())
|
||||
.replace(FILE_ABI_VERSION_MARKER, actualVersion.toString()),
|
||||
mapOf()
|
||||
DecompiledTextIndex.Empty
|
||||
)
|
||||
}
|
||||
|
||||
+54
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,13 +20,22 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelPropertyFqnNameIndex
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.kotlin.BuiltInClassesAreSerializableOnJvm
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import java.util.*
|
||||
|
||||
@@ -40,8 +49,8 @@ fun findDecompiledDeclaration(
|
||||
|
||||
val decompiledFiles = findDecompiledFilesForDescriptor(project, referencedDescriptor)
|
||||
|
||||
return decompiledFiles.asSequence().mapNotNull {
|
||||
it.getDeclarationForDescriptor(referencedDescriptor)
|
||||
return decompiledFiles.asSequence().mapNotNull { file ->
|
||||
ByDescriptorIndexer.getDeclarationForDescriptor(referencedDescriptor, file)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
@@ -92,3 +101,45 @@ private fun findCandidateDeclarationsInIndex(
|
||||
}
|
||||
}
|
||||
|
||||
object ByDescriptorIndexer : DecompiledTextIndexer<String> {
|
||||
override fun indexDescriptor(descriptor: DeclarationDescriptor): Collection<String> {
|
||||
return listOf(descriptor.toStringKey())
|
||||
}
|
||||
|
||||
internal fun getDeclarationForDescriptor(descriptor: DeclarationDescriptor, file: KtDecompiledFile): KtDeclaration? {
|
||||
val original = descriptor.original
|
||||
|
||||
if (original is ValueParameterDescriptor) {
|
||||
val callable = original.containingDeclaration
|
||||
val callableDeclaration = getDeclarationForDescriptor(callable, file) as? KtCallableDeclaration ?: return null
|
||||
return callableDeclaration.valueParameters[original.index]
|
||||
}
|
||||
|
||||
if (original is ConstructorDescriptor && original.isPrimary) {
|
||||
val classOrObject = getDeclarationForDescriptor(original.containingDeclaration, file) as? KtClassOrObject
|
||||
return classOrObject?.getPrimaryConstructor() ?: classOrObject
|
||||
}
|
||||
|
||||
|
||||
return file.getDeclaration(this, original.toStringKey()) ?: run {
|
||||
if (descriptor !is ClassDescriptor) return null
|
||||
|
||||
val classFqName = descriptor.fqNameSafe
|
||||
if (BuiltInClassesAreSerializableOnJvm.isSerializableInJava(classFqName)) {
|
||||
val builtInDescriptor = TargetPlatform.Default.builtIns.builtInsModule.resolveTopLevelClass(classFqName, NoLookupLocation.FROM_IDE)
|
||||
return builtInDescriptor?.let { file.getDeclaration(this, it.toStringKey()) }
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.toStringKey(): String {
|
||||
return descriptorRendererForKeys.render(this)
|
||||
}
|
||||
|
||||
private val descriptorRendererForKeys = DescriptorRenderer.COMPACT_WITH_MODIFIERS.withOptions {
|
||||
modifiers = DescriptorRendererModifier.ALL
|
||||
withDefinedIn = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.decompiler.textBuilder
|
||||
|
||||
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<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[mapper]!!
|
||||
mapper.indexDescriptor(descriptor).forEach { key ->
|
||||
correspondingMap[key] = textRange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Any> getRange(mapper: DecompiledTextIndexer<T>, key: T): TextRange? {
|
||||
return indexerToMap[mapper]?.get(key)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Empty = DecompiledTextIndex(listOf())
|
||||
}
|
||||
}
|
||||
+11
-20
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.decompiler.textBuilder
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.decompiler.navigation.ByDescriptorIndexer
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
|
||||
@@ -27,23 +28,11 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import java.util.*
|
||||
|
||||
private val DECOMPILED_CODE_COMMENT = "/* compiled code */"
|
||||
private val DECOMPILED_COMMENT_FOR_PARAMETER = "/* = compiled code */"
|
||||
private val FLEXIBLE_TYPE_COMMENT = "/* platform type */"
|
||||
|
||||
private val descriptorRendererForKeys = DescriptorRenderer.COMPACT_WITH_MODIFIERS.withOptions {
|
||||
modifiers = DescriptorRendererModifier.ALL
|
||||
withDefinedIn = true
|
||||
}
|
||||
|
||||
fun descriptorToKey(descriptor: DeclarationDescriptor): String {
|
||||
return descriptorRendererForKeys.render(descriptor)
|
||||
}
|
||||
|
||||
data class DecompiledText(val text: String, val renderedDescriptorsToRange: Map<String, TextRange>)
|
||||
|
||||
fun DescriptorRendererOptions.defaultDecompilerRendererOptions() {
|
||||
withDefinedIn = false
|
||||
classWithPrimaryConstructor = true
|
||||
@@ -56,10 +45,10 @@ fun DescriptorRendererOptions.defaultDecompilerRendererOptions() {
|
||||
fun buildDecompiledText(
|
||||
packageFqName: FqName,
|
||||
descriptors: List<DeclarationDescriptor>,
|
||||
descriptorRenderer: DescriptorRenderer
|
||||
descriptorRenderer: DescriptorRenderer,
|
||||
indexers: Collection<DecompiledTextIndexer<*>> = listOf(ByDescriptorIndexer)
|
||||
): DecompiledText {
|
||||
val builder = StringBuilder()
|
||||
val renderedDescriptorsToRange = HashMap<String, TextRange>()
|
||||
|
||||
fun appendDecompiledTextAndPackageName() {
|
||||
builder.append("// IntelliJ API Decompiler stub source generated from a class file\n" + "// Implementation of methods is not available")
|
||||
@@ -69,8 +58,10 @@ fun buildDecompiledText(
|
||||
}
|
||||
}
|
||||
|
||||
fun saveDescriptorToRange(descriptor: DeclarationDescriptor, startOffset: Int, endOffset: Int) {
|
||||
renderedDescriptorsToRange[descriptorToKey(descriptor)] = TextRange(startOffset, endOffset)
|
||||
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) {
|
||||
@@ -161,12 +152,12 @@ fun buildDecompiledText(
|
||||
}
|
||||
|
||||
builder.append("\n")
|
||||
saveDescriptorToRange(descriptor, startOffset, endOffset)
|
||||
indexDescriptor(descriptor, startOffset, endOffset)
|
||||
|
||||
if (descriptor is ClassDescriptor) {
|
||||
val primaryConstructor = descriptor.unsubstitutedPrimaryConstructor
|
||||
if (primaryConstructor != null) {
|
||||
saveDescriptorToRange(primaryConstructor, startOffset, endOffset)
|
||||
indexDescriptor(primaryConstructor, startOffset, endOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,5 +168,5 @@ fun buildDecompiledText(
|
||||
builder.append("\n")
|
||||
}
|
||||
|
||||
return DecompiledText(builder.toString(), renderedDescriptorsToRange)
|
||||
return DecompiledText(builder.toString(), textIndex)
|
||||
}
|
||||
Reference in New Issue
Block a user