diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/KtDecompiledFile.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/KtDecompiledFile.kt index 8a6337249dd..e7c9fd77a0b 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/KtDecompiledFile.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/KtDecompiledFile.kt @@ -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 { - return decompiledText.get().renderedDescriptorsToRange + fun getDeclaration(indexer: DecompiledTextIndexer, 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) } } - diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/incompatibleAbiVersion.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/incompatibleAbiVersion.kt index 671b10fa547..bb2864d5380 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/incompatibleAbiVersion.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/incompatibleAbiVersion.kt @@ -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 createIncompatibleAbiVersionDecompiledText(expectedVersi INCOMPATIBLE_ABI_VERSION_COMMENT .replace(CURRENT_ABI_VERSION_MARKER, expectedVersion.toString()) .replace(FILE_ABI_VERSION_MARKER, actualVersion.toString()), - mapOf() + DecompiledTextIndex.Empty ) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/findDecompiledDeclaration.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/findDecompiledDeclaration.kt index c873f14851e..779677763bc 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/findDecompiledDeclaration.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/findDecompiledDeclaration.kt @@ -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 { + override fun indexDescriptor(descriptor: DeclarationDescriptor): Collection { + 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 + } +} + diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledText.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledText.kt new file mode 100644 index 00000000000..fa628724d32 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledText.kt @@ -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 { + fun indexDescriptor(descriptor: DeclarationDescriptor): Collection +} + +// In-memory HashMap-based index of decompiled text +// allows navigation features +class DecompiledTextIndex(private val indexers: Collection>) { + private val indexerToMap: Map, MutableMap> = indexers.keysToMap { hashMapOf() } + + fun addToIndex(descriptor: DeclarationDescriptor, textRange: TextRange) { + indexers.forEach { mapper -> + val correspondingMap = indexerToMap[mapper]!! + mapper.indexDescriptor(descriptor).forEach { key -> + correspondingMap[key] = textRange + } + } + } + + fun getRange(mapper: DecompiledTextIndexer, key: T): TextRange? { + return indexerToMap[mapper]?.get(key) + } + + companion object { + val Empty = DecompiledTextIndex(listOf()) + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextFactory.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/buildDecompiledText.kt similarity index 86% rename from idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextFactory.kt rename to idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/buildDecompiledText.kt index 36a585c5802..4707fcb8781 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextFactory.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/buildDecompiledText.kt @@ -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) - fun DescriptorRendererOptions.defaultDecompilerRendererOptions() { withDefinedIn = false classWithPrimaryConstructor = true @@ -56,10 +45,10 @@ fun DescriptorRendererOptions.defaultDecompilerRendererOptions() { fun buildDecompiledText( packageFqName: FqName, descriptors: List, - descriptorRenderer: DescriptorRenderer + descriptorRenderer: DescriptorRenderer, + indexers: Collection> = listOf(ByDescriptorIndexer) ): DecompiledText { val builder = StringBuilder() - val renderedDescriptorsToRange = HashMap() 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) }