From c40d6af1e558a0e9bfdc1c4ef9a672ff29de15ce Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Thu, 3 Dec 2015 18:10:45 +0300 Subject: [PATCH] Decompile builtins (kotlin_class and kotlin_package files) The main purpose of this is to be able to index builtin declarations and enable go to symbol/class functionality in ide Also it makes sense to show builtin classes (Any, Int ...) as classes when browsing kotlin-runtime.jar Refactor decompiler code to eliminate existing and potential duplication Share the code between js (kjsm) decompilation and builtin decompilation heavily --- .../BuiltInsSerializedResourcePaths.kt | 6 +- .../builtIns/KotlinBuiltInDecompiler.kt | 118 ++++++++++++++++++ .../KotlinBuiltInDeserializerForDecompiler.kt | 57 +++++++++ .../builtIns/KotlinBuiltInStubBuilder.kt | 106 ++++++++++++++++ .../idea/decompiler/builtIns/fileTypes.kt | 54 ++++++++ .../DeserializerForClassfileDecompiler.kt | 4 +- .../classFile/KotlinClassFileDecompiler.kt | 29 ++--- .../AnnotationLoaderForStubBuilderImpl.kt} | 22 ++-- .../idea/decompiler/common/ProtoBufUtil.kt | 16 ++- .../common/directoryBasedFinders.kt | 49 ++++++++ ...tlinJavaScriptDeserializerForDecompiler.kt | 56 ++------- .../js/KotlinJavaScriptMetaFileDecompiler.kt | 15 +-- .../js/KotlinJavaScriptStubBuilder.kt | 23 ++-- .../textBuilder/DecompiledTextFactory.kt | 10 ++ .../DeserializerForDecompilerBase.kt | 41 ++++-- .../textBuilder/ResolverForDecompiler.kt | 2 +- .../kotlin/idea/util/ProjectRootsUtil.kt | 14 ++- .../jetbrains/kotlin/idea/test/testUtils.kt | 4 +- idea/src/META-INF/plugin.xml | 14 ++- .../idea/KotlinBuiltInFileTypeFactory.kt | 28 +++++ .../textBuilder/TextConsistencyBaseTest.kt | 2 +- 21 files changed, 533 insertions(+), 137 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInDecompiler.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInDeserializerForDecompiler.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInStubBuilder.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/fileTypes.kt rename idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/{js/AnnotationLoaderForKotlinJavaScriptStubBuilder.kt => common/AnnotationLoaderForStubBuilderImpl.kt} (75%) rename js/js.serializer/src/org/jetbrains/kotlin/serialization/js/jsProtoBufUtil.kt => idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/ProtoBufUtil.kt (70%) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/directoryBasedFinders.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/KotlinBuiltInFileTypeFactory.kt diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsSerializedResourcePaths.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsSerializedResourcePaths.kt index 5531c86eb65..e40ad991905 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsSerializedResourcePaths.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltInsSerializedResourcePaths.kt @@ -30,9 +30,9 @@ public object BuiltInsSerializedResourcePaths : SerializedResourcePaths() { BuiltInsProtoBuf.registerAllExtensions(extensionRegistry) } - private val CLASS_METADATA_FILE_EXTENSION = "kotlin_class" - private val PACKAGE_FILE_EXTENSION = "kotlin_package" - private val STRING_TABLE_FILE_EXTENSION = "kotlin_string_table" + val CLASS_METADATA_FILE_EXTENSION = "kotlin_class" + val PACKAGE_FILE_EXTENSION = "kotlin_package" + val STRING_TABLE_FILE_EXTENSION = "kotlin_string_table" public override fun getClassMetadataPath(classId: ClassId): String { return packageFqNameToPath(classId.getPackageFqName()) + "/" + classId.getRelativeClassName().asString() + diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInDecompiler.kt new file mode 100644 index 00000000000..d225f98a005 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInDecompiler.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2010-2015 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.builtIns + +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiManager +import com.intellij.psi.compiled.ClassFileDecompilers +import org.jetbrains.kotlin.builtins.BuiltInsSerializedResourcePaths +import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider +import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile +import org.jetbrains.kotlin.idea.decompiler.common.toClassProto +import org.jetbrains.kotlin.idea.decompiler.common.toPackageProto +import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText +import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText +import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf +import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl + +class KotlinBuiltInDecompiler : ClassFileDecompilers.Full() { + private val stubBuilder = KotlinBuiltInStubBuilder() + + override fun accepts(file: VirtualFile): Boolean { + return file.fileType == KotlinBuiltInClassFileType || file.fileType == KotlinBuiltInPackageFileType + } + + override fun getStubBuilder() = stubBuilder + + override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): FileViewProvider { + return KotlinDecompiledFileViewProvider(manager, file, physical) { provider -> + if (isInternalBuiltInFile(provider.virtualFile)) { + null + } + else { + KtDecompiledFile(provider) { file -> + buildDecompiledTextForBuiltIns(file) + } + } + } + } + + companion object { + val LOG = Logger.getInstance(KotlinBuiltInDecompiler::class.java) + } +} + +private val decompilerRendererForBuiltIns = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() } + +public fun buildDecompiledTextForBuiltIns( + builtInFile: VirtualFile +): DecompiledText { + val directory = builtInFile.parent!! + val nameResolver = readStringTable(directory, KotlinBuiltInDecompiler.LOG)!! + val content = builtInFile.contentsToByteArray() + return when (builtInFile.fileType) { + KotlinBuiltInPackageFileType -> { + val packageFqName = content.toPackageProto(BuiltInsSerializedResourcePaths.extensionRegistry).packageFqName(nameResolver) + val resolver = KotlinBuiltInDeserializerForDecompiler(directory, packageFqName, nameResolver) + buildDecompiledText(packageFqName, resolver.resolveDeclarationsInFacade(packageFqName), decompilerRendererForBuiltIns) + } + KotlinBuiltInClassFileType -> { + val classProto = content.toClassProto(BuiltInsSerializedResourcePaths.extensionRegistry) + val classId = nameResolver.getClassId(classProto.fqName) + val packageFqName = classId.packageFqName + val resolver = KotlinBuiltInDeserializerForDecompiler(directory, packageFqName, nameResolver) + buildDecompiledText( + packageFqName, + listOfNotNull(resolver.resolveTopLevelClass(classId)), + decompilerRendererForBuiltIns + ) + } + else -> error("Unexpected filetype ${builtInFile.fileType}") + } +} + +fun ProtoBuf.Package.packageFqName(nameResolver: NameResolverImpl): FqName { + return nameResolver.getPackageFqName(getExtension(BuiltInsProtoBuf.packageFqName)) +} + +fun isInternalBuiltInFile(virtualFile: VirtualFile): Boolean { + when (virtualFile.fileType) { + KotlinBuiltInPackageFileType -> { + // do not accept kotlin_package files without packageFqName extension to avoid failing on older runtimes + // this check may be costly but there are few kotlin_package files + val packageProto = virtualFile.contentsToByteArray().toPackageProto(BuiltInsSerializedResourcePaths.extensionRegistry) + return !packageProto.hasExtension(BuiltInsProtoBuf.packageFqName) + } + KotlinBuiltInClassFileType -> { + val correspondsToInnerClass = virtualFile.nameWithoutExtension.contains('.') + if (correspondsToInnerClass) { + return true + } + val classFileName = virtualFile.nameWithoutExtension + "." + JavaClassFileType.INSTANCE.defaultExtension + val classFileForTheSameClassIsPresent = virtualFile.parent!!.findChild(classFileName) != null + return classFileForTheSameClassIsPresent + } + else -> error("Unexpected filetype ${virtualFile.fileType}") + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInDeserializerForDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInDeserializerForDecompiler.kt new file mode 100644 index 00000000000..31dd5978473 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInDeserializerForDecompiler.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2015 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.builtIns + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol +import org.jetbrains.kotlin.builtins.BuiltInsSerializedResourcePaths +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.decompiler.common.DirectoryBasedClassDataFinder +import org.jetbrains.kotlin.idea.decompiler.textBuilder.DeserializerForDecompilerBase +import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter +import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassResolver +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.TargetPlatform +import org.jetbrains.kotlin.serialization.deserialization.* + +public class KotlinBuiltInDeserializerForDecompiler( + packageDirectory: VirtualFile, + packageFqName: FqName, + private val nameResolver: NameResolver +) : DeserializerForDecompilerBase(packageDirectory, packageFqName) { + override val targetPlatform: TargetPlatform get() = TargetPlatform.Default + + private val classDataFinder = DirectoryBasedClassDataFinder( + packageDirectory, directoryPackageFqName, nameResolver, BuiltInsSerializedResourcePaths + ) + + override val deserializationComponents = DeserializationComponents( + storageManager, moduleDescriptor, classDataFinder, AnnotationAndConstantLoaderImpl(moduleDescriptor, BuiltInSerializerProtocol), + packageFragmentProvider, ResolveEverythingToKotlinAnyLocalClassResolver(targetPlatform.builtIns), LoggingErrorReporter(LOG), + LookupTracker.DO_NOTHING, FlexibleTypeCapabilitiesDeserializer.ThrowException, ClassDescriptorFactory.EMPTY + ) + + override fun resolveDeclarationsInFacade(facadeFqName: FqName): List { + return getDescriptorsFromPackageFile(facadeFqName, BuiltInsSerializedResourcePaths, LOG, nameResolver) + } + + companion object { + private val LOG = Logger.getInstance(KotlinBuiltInDeserializerForDecompiler::class.java) + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInStubBuilder.kt new file mode 100644 index 00000000000..d8c58d22a0d --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/KotlinBuiltInStubBuilder.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2010-2015 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.builtIns + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.compiled.ClsStubBuilder +import com.intellij.psi.impl.compiled.ClassFileStubBuilder +import com.intellij.psi.stubs.PsiFileStub +import com.intellij.util.indexing.FileContent +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol +import org.jetbrains.kotlin.builtins.BuiltInsSerializedResourcePaths +import org.jetbrains.kotlin.idea.decompiler.common.AnnotationLoaderForStubBuilderImpl +import org.jetbrains.kotlin.idea.decompiler.common.DirectoryBasedClassDataFinder +import org.jetbrains.kotlin.idea.decompiler.common.toClassProto +import org.jetbrains.kotlin.idea.decompiler.common.toPackageProto +import org.jetbrains.kotlin.idea.decompiler.stubBuilder.ClsStubBuilderComponents +import org.jetbrains.kotlin.idea.decompiler.stubBuilder.createPackageFacadeStub +import org.jetbrains.kotlin.idea.decompiler.stubBuilder.createTopLevelClassStub +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.stubs.impl.KotlinFileStubImpl +import org.jetbrains.kotlin.serialization.deserialization.NameResolver +import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl +import org.jetbrains.kotlin.serialization.deserialization.TypeTable +import java.io.ByteArrayInputStream + +public class KotlinBuiltInStubBuilder : ClsStubBuilder() { + override fun getStubVersion() = ClassFileStubBuilder.STUB_VERSION + 1 + + override fun buildFileStub(content: FileContent): PsiFileStub<*>? { + + if (isInternalBuiltInFile(content.file)) return null + + return doBuildFileStub(content) + } + + @TestOnly + fun doBuildFileStub(content: FileContent): KotlinFileStubImpl? { + val file = content.file + val directory = file.parent!! + val nameResolver = readStringTable(directory, LOG) ?: return null + val contentAsBytes = content.content + + return when (file.fileType) { + KotlinBuiltInPackageFileType -> { + val packageProto = contentAsBytes.toPackageProto(BuiltInsSerializedResourcePaths.extensionRegistry) + val packageFqName = packageProto.packageFqName(nameResolver) + val components = createStubBuilderComponents(file, packageFqName, nameResolver) + val context = components.createContext( + nameResolver, packageFqName, TypeTable(packageProto.typeTable) + ) + createPackageFacadeStub(packageProto, packageFqName, context) + } + KotlinBuiltInClassFileType -> { + val classProto = contentAsBytes.toClassProto(BuiltInsSerializedResourcePaths.extensionRegistry) + val classId = nameResolver.getClassId(classProto.fqName) + val packageFqName = classId.packageFqName + val components = createStubBuilderComponents(file, packageFqName, nameResolver) + val context = components.createContext(nameResolver, packageFqName, TypeTable(classProto.typeTable)) + createTopLevelClassStub(classId, classProto, context) + } + else -> error("Unexpected filetype ${file.fileType}") + } + } + + private fun createStubBuilderComponents(file: VirtualFile, packageFqName: FqName, nameResolver: NameResolver): ClsStubBuilderComponents { + val finder = DirectoryBasedClassDataFinder(file.parent!!, packageFqName, nameResolver, BuiltInsSerializedResourcePaths) + val annotationLoader = AnnotationLoaderForStubBuilderImpl(BuiltInSerializerProtocol) + return ClsStubBuilderComponents(finder, annotationLoader) + } + + companion object { + val LOG = Logger.getInstance(KotlinBuiltInStubBuilder::class.java) + } +} + +fun readStringTable(directory: VirtualFile, log: Logger): NameResolverImpl? { + val stringsFileName = "${directory.name}.${BuiltInsSerializedResourcePaths.STRING_TABLE_FILE_EXTENSION}" + val stringTableFile = directory.findFileByRelativePath(stringsFileName) ?: return run { + log.error("$stringsFileName not found in $directory") + null + } + val nameResolver = try { + NameResolverImpl.read(ByteArrayInputStream(stringTableFile.contentsToByteArray(false))) + } + catch (e: Exception) { + log.error("Error reading data from $stringTableFile ", e) + null + } + return nameResolver +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/fileTypes.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/fileTypes.kt new file mode 100644 index 00000000000..809b9331131 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/builtIns/fileTypes.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2015 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.builtIns + +import com.intellij.openapi.fileTypes.FileType +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.builtins.BuiltInsSerializedResourcePaths +import org.jetbrains.kotlin.idea.KotlinIcons + +public object KotlinBuiltInClassFileType : FileType { + override fun getName() = "kotlin_class" + + override fun getDescription() = "Kotlin builtin class" + + override fun getDefaultExtension() = BuiltInsSerializedResourcePaths.CLASS_METADATA_FILE_EXTENSION + + override fun getIcon() = KotlinIcons.CLASS + + override fun isBinary() = true + + override fun isReadOnly() = true + + override fun getCharset(file: VirtualFile, content: ByteArray) = null +} + +public object KotlinBuiltInPackageFileType : FileType { + override fun getName() = "kotlin_package" + + override fun getDescription() = "Kotlin builtin package" + + override fun getDefaultExtension() = BuiltInsSerializedResourcePaths.PACKAGE_FILE_EXTENSION + + override fun getIcon() = KotlinIcons.FILE + + override fun isBinary() = true + + override fun isReadOnly() = true + + override fun getCharset(file: VirtualFile, content: ByteArray) = null +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt index 6a41c3c2c0c..0c07c1b4c6a 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/DeserializerForClassfileDecompiler.kt @@ -64,7 +64,7 @@ public class DeserializerForClassfileDecompiler( LookupTracker.DO_NOTHING, JavaFlexibleTypeCapabilitiesDeserializer, ClassDescriptorFactory.EMPTY ) - override fun resolveDeclarationsInFacade(facadeFqName: FqName): Collection { + override fun resolveDeclarationsInFacade(facadeFqName: FqName): List { val packageFqName = facadeFqName.parent() assert(packageFqName == directoryPackageFqName) { "Was called for $facadeFqName; only members of $directoryPackageFqName package are expected." @@ -81,7 +81,7 @@ public class DeserializerForClassfileDecompiler( val membersScope = DeserializedPackageMemberScope( createDummyPackageFragment(packageFqName), packageProto, nameResolver, deserializationComponents ) { emptyList() } - return membersScope.getContributedDescriptors() + return membersScope.getContributedDescriptors().toList() } companion object { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClassFileDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClassFileDecompiler.kt index 76a9f51a9a8..22035a81342 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClassFileDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/classFile/KotlinClassFileDecompiler.kt @@ -26,14 +26,13 @@ import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolverForDecompiler import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText +import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.renderer.DescriptorRendererModifier -import org.jetbrains.kotlin.renderer.ExcludedTypeAnnotations import org.jetbrains.kotlin.types.flexibility import org.jetbrains.kotlin.types.isFlexible import java.util.* @@ -50,14 +49,10 @@ public class KotlinClassFileDecompiler : ClassFileDecompilers.Full() { return KotlinDecompiledFileViewProvider(manager, file, physical) { provider -> val virtualFile = provider.virtualFile val fileIndex = ServiceManager.getService(project, FileIndexFacade::class.java) - if (!fileIndex.isInLibraryClasses(virtualFile) && fileIndex.isInSource(virtualFile)) { - null - } - else if (isKotlinInternalCompiledFile(virtualFile)) { - null - } - else { - KtClsFile(provider) + when { + !fileIndex.isInLibraryClasses(virtualFile) && fileIndex.isInSource(virtualFile) -> null + isKotlinInternalCompiledFile(virtualFile) -> null + else -> KtClsFile(provider) } } } @@ -65,13 +60,9 @@ public class KotlinClassFileDecompiler : ClassFileDecompilers.Full() { class KtClsFile(provider: KotlinDecompiledFileViewProvider) : KtDecompiledFile(provider, { file -> buildDecompiledTextForClassFile(file) }) -private val descriptorRendererForClassFileDecompiler = DescriptorRenderer.withOptions { - withDefinedIn = false - classWithPrimaryConstructor = true +private val decompilerRendererForClassFiles = DescriptorRenderer.withOptions { + defaultDecompilerRendererOptions() typeNormalizer = { type -> if (type.isFlexible()) type.flexibility().lowerBound else type } - secondaryConstructorsAsPrimary = false - modifiers = DescriptorRendererModifier.ALL - excludedTypeAnnotationClasses = ExcludedTypeAnnotations.annotationsForNullabilityAndMutability } private val FILE_ABI_VERSION_MARKER: String = "FILE_ABI" @@ -103,13 +94,13 @@ public fun buildDecompiledTextForClassFile( mapOf()) } classHeader.isCompatibleFileFacadeKind() -> - buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInFacade(classId.asSingleFqName())), descriptorRendererForClassFileDecompiler) + buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInFacade(classId.asSingleFqName())), decompilerRendererForClassFiles) classHeader.isCompatibleClassKind() -> - buildDecompiledText(packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)), descriptorRendererForClassFileDecompiler) + buildDecompiledText(packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)), decompilerRendererForClassFiles) classHeader.isCompatibleMultifileClassKind() -> { val partClasses = findMultifileClassParts(classFile, kotlinClass) val partMembers = partClasses.flatMap { partClass -> resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName()) } - buildDecompiledText(packageFqName, partMembers, descriptorRendererForClassFileDecompiler) + buildDecompiledText(packageFqName, partMembers, decompilerRendererForClassFiles) } else -> throw UnsupportedOperationException("Unknown header kind: ${classHeader.kind} ${classHeader.isCompatibleAbiVersion}") diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/AnnotationLoaderForKotlinJavaScriptStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/AnnotationLoaderForStubBuilderImpl.kt similarity index 75% rename from idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/AnnotationLoaderForKotlinJavaScriptStubBuilder.kt rename to idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/AnnotationLoaderForStubBuilderImpl.kt index 1e3f04c1069..a7d701515ca 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/AnnotationLoaderForKotlinJavaScriptStubBuilder.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/AnnotationLoaderForStubBuilderImpl.kt @@ -14,25 +14,27 @@ * limitations under the License. */ -package org.jetbrains.kotlin.idea.decompiler.js +package org.jetbrains.kotlin.idea.decompiler.common import com.google.protobuf.MessageLite import org.jetbrains.kotlin.idea.decompiler.stubBuilder.ClassIdWithTarget import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol import org.jetbrains.kotlin.serialization.deserialization.AnnotatedCallableKind import org.jetbrains.kotlin.serialization.deserialization.AnnotationAndConstantLoader import org.jetbrains.kotlin.serialization.deserialization.NameResolver import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer -import org.jetbrains.kotlin.serialization.js.JsProtoBuf import org.jetbrains.kotlin.types.KotlinType -public class AnnotationLoaderForKotlinJavaScriptStubBuilder() : AnnotationAndConstantLoader { +public class AnnotationLoaderForStubBuilderImpl( + private val protocol: SerializerExtensionProtocol +) : AnnotationAndConstantLoader { override fun loadClassAnnotations( classProto: ProtoBuf.Class, nameResolver: NameResolver ): List = - classProto.getExtension(JsProtoBuf.classAnnotation).orEmpty().map { nameResolver.getClassId(it.id) } + classProto.getExtension(protocol.classAnnotation).orEmpty().map { nameResolver.getClassId(it.id) } override fun loadCallableAnnotations( container: ProtoContainer, @@ -40,9 +42,9 @@ public class AnnotationLoaderForKotlinJavaScriptStubBuilder() : AnnotationAndCon kind: AnnotatedCallableKind ): List { val annotations = when (proto) { - is ProtoBuf.Constructor -> proto.getExtension(JsProtoBuf.constructorAnnotation) - is ProtoBuf.Function -> proto.getExtension(JsProtoBuf.functionAnnotation) - is ProtoBuf.Property -> proto.getExtension(JsProtoBuf.propertyAnnotation) + is ProtoBuf.Constructor -> proto.getExtension(protocol.constructorAnnotation) + is ProtoBuf.Function -> proto.getExtension(protocol.functionAnnotation) + is ProtoBuf.Property -> proto.getExtension(protocol.propertyAnnotation) else -> error("Unknown message: $proto") }.orEmpty() return annotations.map { @@ -57,7 +59,7 @@ public class AnnotationLoaderForKotlinJavaScriptStubBuilder() : AnnotationAndCon parameterIndex: Int, proto: ProtoBuf.ValueParameter ): List = - proto.getExtension(JsProtoBuf.parameterAnnotation).orEmpty().map { + proto.getExtension(protocol.parameterAnnotation).orEmpty().map { container.nameResolver.getClassId(it.id) } @@ -71,10 +73,10 @@ public class AnnotationLoaderForKotlinJavaScriptStubBuilder() : AnnotationAndCon proto: ProtoBuf.Type, nameResolver: NameResolver ): List = - proto.getExtension(JsProtoBuf.typeAnnotation).orEmpty().map { nameResolver.getClassId(it.id) } + proto.getExtension(protocol.typeAnnotation).orEmpty().map { nameResolver.getClassId(it.id) } override fun loadTypeParameterAnnotations(proto: ProtoBuf.TypeParameter, nameResolver: NameResolver): List = - proto.getExtension(JsProtoBuf.typeParameterAnnotation).orEmpty().map { nameResolver.getClassId(it.id) } + proto.getExtension(protocol.typeParameterAnnotation).orEmpty().map { nameResolver.getClassId(it.id) } override fun loadPropertyConstant( container: ProtoContainer, diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/jsProtoBufUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/ProtoBufUtil.kt similarity index 70% rename from js/js.serializer/src/org/jetbrains/kotlin/serialization/js/jsProtoBufUtil.kt rename to idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/ProtoBufUtil.kt index 76c7da6fe53..450e58e048f 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/jsProtoBufUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/ProtoBufUtil.kt @@ -14,18 +14,16 @@ * limitations under the License. */ -package org.jetbrains.kotlin.serialization.js +package org.jetbrains.kotlin.idea.decompiler.common +import com.google.protobuf.ExtensionRegistryLite import org.jetbrains.kotlin.serialization.ProtoBuf import java.io.ByteArrayInputStream -public fun ByteArray.toPackageProto(): ProtoBuf.Package { - val registry = KotlinJavascriptSerializedResourcePaths.extensionRegistry - return ProtoBuf.Package.parseFrom(ByteArrayInputStream(this), registry)!! -} - -public fun ByteArray.toClassProto(): ProtoBuf.Class { - val registry = KotlinJavascriptSerializedResourcePaths.extensionRegistry - return ProtoBuf.Class.parseFrom(ByteArrayInputStream(this), registry) +fun ByteArray.toPackageProto(extensionRegistry: ExtensionRegistryLite): ProtoBuf.Package { + return ProtoBuf.Package.parseFrom(ByteArrayInputStream(this), extensionRegistry)!! } +fun ByteArray.toClassProto(extensionRegistry: ExtensionRegistryLite): ProtoBuf.Class { + return ProtoBuf.Class.parseFrom(ByteArrayInputStream(this), extensionRegistry) +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/directoryBasedFinders.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/directoryBasedFinders.kt new file mode 100644 index 00000000000..c53b7c5fe83 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/common/directoryBasedFinders.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2015 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.common + +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.serialization.ClassData +import org.jetbrains.kotlin.serialization.ClassDataWithSource +import org.jetbrains.kotlin.serialization.SerializedResourcePaths +import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder +import org.jetbrains.kotlin.serialization.deserialization.NameResolver + +class DirectoryBasedClassDataFinder( + private val packageDirectory: VirtualFile, + private val directoryPackageFqName: FqName, + private val nameResolver: NameResolver, + private val paths: SerializedResourcePaths +) : ClassDataFinder { + fun findFileForClass(classId: ClassId): VirtualFile? { + if (classId.packageFqName != directoryPackageFqName) return null + + val pathRelativeToDir = paths.getClassMetadataPath(classId).substringAfterLast("/") + return packageDirectory.findChild(pathRelativeToDir) + } + + override fun findClassData(classId: ClassId): ClassDataWithSource? { + val virtualFile = findFileForClass(classId) ?: return null + + val content = virtualFile.contentsToByteArray(false) + val classProto = content.toClassProto(paths.extensionRegistry) + return ClassDataWithSource(ClassData(nameResolver, classProto)) + } +} + diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptDeserializerForDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptDeserializerForDecompiler.kt index 1eedf5cddfd..3cf88014fda 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptDeserializerForDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptDeserializerForDecompiler.kt @@ -19,19 +19,17 @@ package org.jetbrains.kotlin.idea.decompiler.js import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.decompiler.common.DirectoryBasedClassDataFinder import org.jetbrains.kotlin.idea.decompiler.textBuilder.DeserializerForDecompilerBase import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassResolver import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.js.resolve.JsPlatform -import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.TargetPlatform -import org.jetbrains.kotlin.serialization.ClassData -import org.jetbrains.kotlin.serialization.ClassDataWithSource import org.jetbrains.kotlin.serialization.deserialization.* -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope -import org.jetbrains.kotlin.serialization.js.* +import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol +import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializedResourcePaths import java.io.ByteArrayInputStream public class KotlinJavaScriptDeserializerForDecompiler( @@ -48,62 +46,22 @@ public class KotlinJavaScriptDeserializerForDecompiler( override val targetPlatform: TargetPlatform get() = JsPlatform - private val metaFileFinder = DirectoryBasedKotlinJavaScriptMetaFileFinder(packageDirectory, directoryPackageFqName, nameResolver) - - private val classDataFinder = DirectoryBasedKotlinJavaScriptDataFinder(metaFileFinder, LOG) + private val finder = DirectoryBasedClassDataFinder(packageDirectory, directoryPackageFqName, nameResolver, KotlinJavascriptSerializedResourcePaths) private val annotationAndConstantLoader = AnnotationAndConstantLoaderImpl(moduleDescriptor, JsSerializerProtocol) override val deserializationComponents = DeserializationComponents( - storageManager, moduleDescriptor, classDataFinder, annotationAndConstantLoader, packageFragmentProvider, + storageManager, moduleDescriptor, finder, annotationAndConstantLoader, packageFragmentProvider, ResolveEverythingToKotlinAnyLocalClassResolver(targetPlatform.builtIns), LoggingErrorReporter(LOG), LookupTracker.DO_NOTHING, FlexibleTypeCapabilitiesDeserializer.Dynamic, ClassDescriptorFactory.EMPTY ) - override fun resolveDeclarationsInFacade(facadeFqName: FqName): Collection { + override fun resolveDeclarationsInFacade(facadeFqName: FqName): List { val packageFqName = facadeFqName.parent() - assert(packageFqName == directoryPackageFqName) { - "Was called for $facadeFqName; only members of $directoryPackageFqName package are expected." - } - val file = metaFileFinder.findKotlinJavascriptMetaFile(ClassId.topLevel(facadeFqName)) - if (file == null) { - LOG.error("Could not read data for $facadeFqName") - return emptyList() - } - - val content = file.contentsToByteArray(false) - val membersScope = DeserializedPackageMemberScope( - createDummyPackageFragment(packageFqName), content.toPackageProto(), nameResolver, deserializationComponents - ) { emptyList() } - return membersScope.getContributedDescriptors() + return getDescriptorsFromPackageFile(packageFqName, KotlinJavascriptSerializedResourcePaths, LOG, nameResolver) } companion object { private val LOG = Logger.getInstance(KotlinJavaScriptDeserializerForDecompiler::class.java) } } - -class DirectoryBasedKotlinJavaScriptMetaFileFinder( - val packageDirectory: VirtualFile, - val directoryPackageFqName: FqName, - val nameResolver: NameResolver -) { - fun findKotlinJavascriptMetaFile(classId: ClassId): VirtualFile? { - if (classId.getPackageFqName() != directoryPackageFqName) return null - - val targetName = classId.getRelativeClassName().pathSegments().joinToString(".", postfix = "." + KotlinJavascriptSerializationUtil.CLASS_METADATA_FILE_EXTENSION) - return packageDirectory.findChild(targetName) - } -} - -class DirectoryBasedKotlinJavaScriptDataFinder( - val classFinder: DirectoryBasedKotlinJavaScriptMetaFileFinder, - val log: Logger -) : ClassDataFinder { - override fun findClassData(classId: ClassId): ClassDataWithSource? { - val file = classFinder.findKotlinJavascriptMetaFile(classId) ?: return null - - val content = file.contentsToByteArray(false) - return ClassDataWithSource(ClassData(classFinder.nameResolver, content.toClassProto())) - } -} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptMetaFileDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptMetaFileDecompiler.kt index f14a30a17e3..cecaff07398 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptMetaFileDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptMetaFileDecompiler.kt @@ -26,11 +26,10 @@ import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolverForDecompiler import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText +import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions import org.jetbrains.kotlin.load.kotlin.OldPackageFacadeClassUtils import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.renderer.DescriptorRendererModifier -import org.jetbrains.kotlin.renderer.ExcludedTypeAnnotations import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil import java.util.* @@ -57,13 +56,7 @@ class KotlinJavaScriptMetaFileDecompiler : ClassFileDecompilers.Full() { } } -private val descriptorRendererForKotlinJavascriptDecompiler = DescriptorRenderer.withOptions { - withDefinedIn = false - classWithPrimaryConstructor = true - secondaryConstructorsAsPrimary = false - modifiers = DescriptorRendererModifier.ALL - excludedTypeAnnotationClasses = ExcludedTypeAnnotations.annotationsForNullabilityAndMutability -} +private val decompilerRendererForJS = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() } public fun buildDecompiledTextFromJsMetadata( classFile: VirtualFile, @@ -75,11 +68,11 @@ public fun buildDecompiledTextFromJsMetadata( if (isPackageHeader) { return buildDecompiledText(packageFqName, resolveDeclarationsInPackage(packageFqName, resolver), - descriptorRendererForKotlinJavascriptDecompiler) + decompilerRendererForJS) } else { val classId = JsMetaFileUtils.getClassId(classFile) - return buildDecompiledText(packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)), descriptorRendererForKotlinJavascriptDecompiler) + return buildDecompiledText(packageFqName, listOfNotNull(resolver.resolveTopLevelClass(classId)), decompilerRendererForJS) } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptStubBuilder.kt index c2996e02454..9f344683a10 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptStubBuilder.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/js/KotlinJavaScriptStubBuilder.kt @@ -16,12 +16,15 @@ package org.jetbrains.kotlin.idea.decompiler.js -import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.compiled.ClsStubBuilder import com.intellij.psi.impl.compiled.ClassFileStubBuilder import com.intellij.psi.stubs.PsiFileStub import com.intellij.util.indexing.FileContent +import org.jetbrains.kotlin.idea.decompiler.common.AnnotationLoaderForStubBuilderImpl +import org.jetbrains.kotlin.idea.decompiler.common.DirectoryBasedClassDataFinder +import org.jetbrains.kotlin.idea.decompiler.common.toClassProto +import org.jetbrains.kotlin.idea.decompiler.common.toPackageProto import org.jetbrains.kotlin.idea.decompiler.stubBuilder.ClsStubBuilderComponents import org.jetbrains.kotlin.idea.decompiler.stubBuilder.createPackageFacadeStub import org.jetbrains.kotlin.idea.decompiler.stubBuilder.createTopLevelClassStub @@ -30,9 +33,8 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.serialization.deserialization.NameResolver import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl import org.jetbrains.kotlin.serialization.deserialization.TypeTable +import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializedResourcePaths -import org.jetbrains.kotlin.serialization.js.toClassProto -import org.jetbrains.kotlin.serialization.js.toPackageProto import java.io.ByteArrayInputStream public class KotlinJavaScriptStubBuilder : ClsStubBuilder() { @@ -61,14 +63,14 @@ public class KotlinJavaScriptStubBuilder : ClsStubBuilder() { val components = createStubBuilderComponents(file, packageFqName, nameResolver) if (isPackageHeader) { - val packageProto = content.toPackageProto() + val packageProto = content.toPackageProto(KotlinJavascriptSerializedResourcePaths.extensionRegistry) val context = components.createContext( nameResolver, packageFqName, TypeTable(packageProto.typeTable) ) return createPackageFacadeStub(packageProto, packageFqName, context) } else { - val classProto = content.toClassProto() + val classProto = content.toClassProto(KotlinJavascriptSerializedResourcePaths.extensionRegistry) val context = components.createContext(nameResolver, packageFqName, TypeTable(classProto.typeTable)) val classId = JsMetaFileUtils.getClassId(file) return createTopLevelClassStub(classId, classProto, context) @@ -76,13 +78,10 @@ public class KotlinJavaScriptStubBuilder : ClsStubBuilder() { } private fun createStubBuilderComponents(file: VirtualFile, packageFqName: FqName, nameResolver: NameResolver): ClsStubBuilderComponents { - val metaFileFinder = DirectoryBasedKotlinJavaScriptMetaFileFinder(file.parent!!, packageFqName, nameResolver) - val classDataFinder = DirectoryBasedKotlinJavaScriptDataFinder(metaFileFinder, LOG) - val annotationLoader = AnnotationLoaderForKotlinJavaScriptStubBuilder() + val classDataFinder = DirectoryBasedClassDataFinder( + file.parent!!, packageFqName, nameResolver, KotlinJavascriptSerializedResourcePaths + ) + val annotationLoader = AnnotationLoaderForStubBuilderImpl(JsSerializerProtocol) return ClsStubBuilderComponents(classDataFinder, annotationLoader) } - - companion object { - val LOG = Logger.getInstance(KotlinJavaScriptStubBuilder::class.java) - } } 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/DecompiledTextFactory.kt index 6e3e427ca0d..9928c0389fa 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/DecompiledTextFactory.kt @@ -21,6 +21,8 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRendererModifier +import org.jetbrains.kotlin.renderer.DescriptorRendererOptions +import org.jetbrains.kotlin.renderer.ExcludedTypeAnnotations import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike import org.jetbrains.kotlin.resolve.descriptorUtil.secondaryConstructors @@ -42,6 +44,14 @@ public fun descriptorToKey(descriptor: DeclarationDescriptor): String { public data class DecompiledText(public val text: String, public val renderedDescriptorsToRange: Map) +fun DescriptorRendererOptions.defaultDecompilerRendererOptions() { + withDefinedIn = false + classWithPrimaryConstructor = true + secondaryConstructorsAsPrimary = false + modifiers = DescriptorRendererModifier.ALL + excludedTypeAnnotationClasses = ExcludedTypeAnnotations.annotationsForNullabilityAndMutability +} + public fun buildDecompiledText( packageFqName: FqName, descriptors: List, diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DeserializerForDecompilerBase.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DeserializerForDecompilerBase.kt index 9360fee9fa5..315600d3310 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DeserializerForDecompilerBase.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DeserializerForDecompilerBase.kt @@ -16,25 +16,22 @@ package org.jetbrains.kotlin.idea.decompiler.textBuilder +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ModuleParameters -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor -import org.jetbrains.kotlin.descriptors.PackageFragmentProvider -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor +import org.jetbrains.kotlin.idea.decompiler.common.toPackageProto import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.TargetPlatform -import org.jetbrains.kotlin.resolve.constants.ConstantValue -import org.jetbrains.kotlin.serialization.deserialization.AnnotationAndConstantLoader -import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder +import org.jetbrains.kotlin.serialization.SerializedResourcePaths import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents import org.jetbrains.kotlin.serialization.deserialization.LocalClassResolver +import org.jetbrains.kotlin.serialization.deserialization.NameResolver +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager @@ -63,7 +60,31 @@ public abstract class DeserializerForDecompilerBase( override fun resolveTopLevelClass(classId: ClassId) = deserializationComponents.deserializeClass(classId) protected fun createDummyPackageFragment(fqName: FqName): MutablePackageFragmentDescriptor = - MutablePackageFragmentDescriptor(moduleDescriptor, fqName) + MutablePackageFragmentDescriptor(moduleDescriptor, fqName) + + protected fun getDescriptorsFromPackageFile( + packageFqName: FqName, + paths: SerializedResourcePaths, + log: Logger, + nameResolver: NameResolver + ): List { + assert(packageFqName == directoryPackageFqName) { + "Was called for $packageFqName; only members of $directoryPackageFqName package are expected." + } + val packageFilePath = paths.getPackageFilePath(directoryPackageFqName).substringAfterLast("/") + val file = packageDirectory.findChild(packageFilePath) + if (file == null) { + log.error("Could not read data for package $packageFqName; $packageFilePath absent in $packageDirectory") + return emptyList() + } + + val content = file.contentsToByteArray(false) + val packageProto = content.toPackageProto(paths.extensionRegistry) + val membersScope = DeserializedPackageMemberScope( + createDummyPackageFragment(packageFqName), packageProto, nameResolver, deserializationComponents + ) { emptyList() } + return membersScope.getContributedDescriptors().toList() + } private fun createDummyModule(name: String) = ModuleDescriptorImpl(Name.special("<$name>"), storageManager, ModuleParameters.Empty, targetPlatform.builtIns) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/ResolverForDecompiler.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/ResolverForDecompiler.kt index 9250a6d13e6..61b967e45b8 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/ResolverForDecompiler.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/ResolverForDecompiler.kt @@ -24,5 +24,5 @@ import org.jetbrains.kotlin.name.ClassId public interface ResolverForDecompiler { public fun resolveTopLevelClass(classId: ClassId): ClassDescriptor? - public fun resolveDeclarationsInFacade(facadeFqName: FqName): Collection + public fun resolveDeclarationsInFacade(facadeFqName: FqName): List } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ProjectRootsUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ProjectRootsUtil.kt index fbde5fcad72..70a2c4a498b 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ProjectRootsUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ProjectRootsUtil.kt @@ -16,14 +16,18 @@ package org.jetbrains.kotlin.idea.util -import com.intellij.openapi.roots.ProjectFileIndex -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.idea.util.application.runReadAction +import com.intellij.ide.highlighter.JavaClassFileType import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiDirectory -import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.caches.resolve.JsProjectDetector +import org.jetbrains.kotlin.idea.decompiler.builtIns.KotlinBuiltInClassFileType +import org.jetbrains.kotlin.idea.decompiler.builtIns.KotlinBuiltInPackageFileType +import org.jetbrains.kotlin.idea.util.application.runReadAction + +private val classFileLike = setOf(JavaClassFileType.INSTANCE, KotlinBuiltInClassFileType, KotlinBuiltInPackageFileType) public object ProjectRootsUtil { @JvmStatic @@ -42,7 +46,7 @@ public object ProjectRootsUtil { || (includeLibraryClasses && fileIndex.isLibraryClassFile(file)) } // NOTE: the following is a workaround for cases when class files are under library source roots and source files are under class roots - val isClassFile = file.getFileType() == JavaClassFileType.INSTANCE + val isClassFile = file.fileType in classFileLike return (includeLibraryClasses && isClassFile && fileIndex.isInLibraryClasses(file)) || (includeLibrarySource && !isClassFile && fileIndex.isInLibrarySource(file)) } diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/testUtils.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/testUtils.kt index f2a226eb0df..cce5cd0f074 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/testUtils.kt +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/testUtils.kt @@ -33,7 +33,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages import org.jetbrains.kotlin.idea.caches.resolve.LibraryModificationTracker import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider -import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile +import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile import org.jetbrains.kotlin.idea.references.BuiltInsReferenceResolver import org.jetbrains.kotlin.psi.KtFile import java.util.* @@ -106,7 +106,7 @@ public fun unInvalidateBuiltinsAndStdLib(project: Project, runnable: RunnableWit builtInsSources.forEach { unInvalidateFile(it) } stdLibViewProviders.forEach { - it.allFiles.forEach { unInvalidateFile(it as KtClsFile) } + it.allFiles.forEach { unInvalidateFile(it as KtDecompiledFile) } vFileToViewProviderMap.set(it.virtualFile, it) } } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 28fb856adbb..903a44b0704 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -275,7 +275,7 @@ - + @@ -293,6 +293,7 @@ + @@ -560,6 +561,7 @@ + @@ -1300,11 +1302,17 @@ - - + + + + + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinBuiltInFileTypeFactory.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinBuiltInFileTypeFactory.kt new file mode 100644 index 00000000000..e24b66636dd --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinBuiltInFileTypeFactory.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2015 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 + +import com.intellij.openapi.fileTypes.FileTypeConsumer +import com.intellij.openapi.fileTypes.FileTypeFactory +import org.jetbrains.kotlin.idea.decompiler.builtIns.KotlinBuiltInClassFileType +import org.jetbrains.kotlin.idea.decompiler.builtIns.KotlinBuiltInPackageFileType + +class KotlinBuiltInFileTypeFactory : FileTypeFactory() { + override fun createFileTypes(consumer: FileTypeConsumer) { + consumer.consume(KotlinBuiltInClassFileType, KotlinBuiltInClassFileType.defaultExtension) + consumer.consume(KotlinBuiltInPackageFileType, KotlinBuiltInPackageFileType.defaultExtension) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/TextConsistencyBaseTest.kt b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/TextConsistencyBaseTest.kt index 2180b66537b..210b99e06eb 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/TextConsistencyBaseTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/TextConsistencyBaseTest.kt @@ -77,7 +77,7 @@ public abstract class TextConsistencyBaseTest : KotlinLightCodeInsightFixtureTes override fun resolveTopLevelClass(classId: ClassId): ClassDescriptor? = module.resolveTopLevelClass(classId.asSingleFqName(), NoLookupLocation.FROM_TEST) - override fun resolveDeclarationsInFacade(facadeFqName: FqName): Collection = + override fun resolveDeclarationsInFacade(facadeFqName: FqName): List = module.getPackage(facadeFqName.parent()).memberScope.getContributedDescriptors().filter { it is CallableMemberDescriptor && it.module != module.builtIns.builtInsModule &&