From 9ea344fe76e9f1a685741245fa231c1d6e883988 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Fri, 25 Aug 2023 10:50:29 +0200 Subject: [PATCH] [decompiler] fix ambiguities to builtin members resolve in K2 IDE If a user has multiple kotlin-stdlib libraries in a project, all those stdlib libraries will contain builtins which will result in resolution ambiguities. To prevent such kind of ambiguities inside LLFirDependenciesSymbolProvider, callables are grouped by facade class name. Only matching callables from the first facade are used. Facade is a Kotlin/JVM-term so right now it works only for JVM targets. Builtins are also used in JVM, so the current solution is to have a Facade name also for builtins. ^KTIJ-26760 --- .../stub/file/KotlinMetadataStubBuilder.kt | 15 +++++++++++- .../decompiler/psi/KotlinBuiltInDecompiler.kt | 24 ++++++++++++++++++- .../psi/KotlinMetadataDecompiler.kt | 4 ++-- .../StubBasedFirDeserializedSymbolProvider.kt | 4 ++-- .../StubBasedFirMemberDeserializer.kt | 2 +- .../kotlin/psi/stubs/KotlinStubVersions.kt | 2 +- 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/analysis/decompiled/decompiler-to-file-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/file/KotlinMetadataStubBuilder.kt b/analysis/decompiled/decompiler-to-file-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/file/KotlinMetadataStubBuilder.kt index 6b5dea383fb..00db27260c4 100644 --- a/analysis/decompiled/decompiler-to-file-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/file/KotlinMetadataStubBuilder.kt +++ b/analysis/decompiled/decompiler-to-file-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/file/KotlinMetadataStubBuilder.kt @@ -12,11 +12,17 @@ import com.intellij.psi.impl.compiled.ClassFileStubBuilder import com.intellij.psi.stubs.PsiFileStub import com.intellij.util.indexing.FileContent import org.jetbrains.kotlin.analysis.decompiler.stub.* +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl import org.jetbrains.kotlin.metadata.deserialization.TypeTable +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol import org.jetbrains.kotlin.serialization.deserialization.ClassDeserializer import org.jetbrains.kotlin.serialization.deserialization.ProtoBasedClassDataFinder @@ -56,7 +62,12 @@ open class KotlinMetadataStubBuilder( val fileStub = createFileStub(packageFqName, isScript = false) createPackageDeclarationsStubs( fileStub, context, - ProtoContainer.Package(packageFqName, context.nameResolver, context.typeTable, source = null), + ProtoContainer.Package( + packageFqName, + context.nameResolver, + context.typeTable, + source = createCallableSource(file, content.fileName) + ), packageProto ) for (classProto in file.classesToDecompile) { @@ -69,6 +80,8 @@ open class KotlinMetadataStubBuilder( } } + protected open fun createCallableSource(file: FileWithMetadata.Compatible, filename: String): SourceElement? = null + sealed class FileWithMetadata { class Incompatible(val version: BinaryVersion) : FileWithMetadata() diff --git a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/KotlinBuiltInDecompiler.kt b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/KotlinBuiltInDecompiler.kt index 50f5bce3138..72579aa1987 100644 --- a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/KotlinBuiltInDecompiler.kt +++ b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/KotlinBuiltInDecompiler.kt @@ -6,10 +6,14 @@ import com.intellij.ide.highlighter.JavaClassFileType import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinMetadataStubBuilder +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource +import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.psi.stubs.KotlinStubVersions +import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol @@ -21,16 +25,34 @@ class KotlinBuiltInDecompiler : KotlinMetadataDecompiler( FlexibleTypeDeserializer.ThrowException, { BuiltInsBinaryVersion.INSTANCE }, { BuiltInsBinaryVersion.INVALID_VERSION }, KotlinStubVersions.BUILTIN_STUB_VERSION ) { + override val metadataStubBuilder: KotlinMetadataStubBuilder = + KotlinBuiltInMetadataStubBuilder(::readFileSafely) + override fun readFile(bytes: ByteArray, file: VirtualFile): KotlinMetadataStubBuilder.FileWithMetadata? { return BuiltInDefinitionFile.read(bytes, file) } } +private class KotlinBuiltInMetadataStubBuilder( + readFile: (VirtualFile, ByteArray) -> FileWithMetadata?, +) : KotlinMetadataStubBuilder(KotlinStubVersions.BUILTIN_STUB_VERSION, KotlinBuiltInFileType, { BuiltInSerializerProtocol }, readFile) { + override fun createCallableSource(file: FileWithMetadata.Compatible, filename: String): SourceElement? { + val fileNameForFacade = when (val withoutExtension = filename.removeSuffix(BuiltInSerializerProtocol.DOT_DEFAULT_EXTENSION)) { + // this is the filename used in stdlib, others should match + "kotlin" -> "library" + else -> withoutExtension + } + + val facadeFqName = PackagePartClassUtils.getPackagePartFqName(file.packageFqName, fileNameForFacade) + return JvmPackagePartSource(JvmClassName.byClassId(ClassId.topLevel(facadeFqName)), null, file.proto.`package`, file.nameResolver) + } +} + class BuiltInDefinitionFile( proto: ProtoBuf.PackageFragment, version: BuiltInsBinaryVersion, val packageDirectory: VirtualFile, - val isMetadata: Boolean + val isMetadata: Boolean, ) : KotlinMetadataStubBuilder.FileWithMetadata.Compatible(proto, version, BuiltInSerializerProtocol) { override val classesToDecompile: List get() = super.classesToDecompile.let { classes -> diff --git a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/KotlinMetadataDecompiler.kt b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/KotlinMetadataDecompiler.kt index a5e0fd0b71f..14915b74e21 100644 --- a/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/KotlinMetadataDecompiler.kt +++ b/analysis/decompiled/decompiler-to-psi/src/org/jetbrains/kotlin/analysis/decompiler/psi/KotlinMetadataDecompiler.kt @@ -31,7 +31,7 @@ abstract class KotlinMetadataDecompiler( private val invalidBinaryVersion: () -> V, stubVersion: Int ) : ClassFileDecompilers.Full() { - private val metadataStubBuilder: KotlinMetadataStubBuilder = + protected open val metadataStubBuilder: KotlinMetadataStubBuilder = KotlinMetadataStubBuilder(stubVersion, fileType, serializerProtocol, ::readFileSafely) private val renderer: DescriptorRenderer by lazy { @@ -61,7 +61,7 @@ abstract class KotlinMetadataDecompiler( @TestOnly fun readFile(file: VirtualFile) = readFileSafely(file) - private fun readFileSafely(file: VirtualFile, content: ByteArray? = null): KotlinMetadataStubBuilder.FileWithMetadata? { + protected fun readFileSafely(file: VirtualFile, content: ByteArray? = null): KotlinMetadataStubBuilder.FileWithMetadata? { if (!file.isValid) return null return try { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirDeserializedSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirDeserializedSymbolProvider.kt index 5972c5a701f..16ba6d76c2e 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirDeserializedSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirDeserializedSymbolProvider.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.analysis.providers.createPackageProvider import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.caches.FirCache import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider import org.jetbrains.kotlin.fir.java.deserialization.KotlinBuiltins @@ -28,7 +29,6 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.stubs.impl.* import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty -import org.jetbrains.kotlin.fir.caches.getValue typealias DeserializedTypeAliasPostProcessor = (FirTypeAliasSymbol) -> Unit @@ -124,7 +124,7 @@ internal open class StubBasedFirDeserializedSymbolProvider( StubBasedAnnotationDeserializer(session), kotlinScopeProvider, parentContext = context, - containerSource = if (initialOrigin == FirDeclarationOrigin.BuiltIns) null else JvmStubDeserializedContainerSource(classId), + containerSource = JvmStubDeserializedContainerSource(classId), deserializeNestedClass = this::getClass, initialOrigin = initialOrigin ) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt index 2ebcbb6f070..025d75aaf9e 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/StubBasedFirMemberDeserializer.kt @@ -169,7 +169,7 @@ internal class StubBasedFirDeserializationContext( callableId.packageName, callableId.className, parameterListOwner, - containerSource = containerSource.takeIf { initialOrigin != FirDeclarationOrigin.BuiltIns }, + containerSource = containerSource, outerClassSymbol = null, symbol, initialOrigin diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt index 473422e07f1..b939c2ea78f 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt @@ -36,7 +36,7 @@ object KotlinStubVersions { // BuiltIn stub version should be increased if changes are made to builtIn stub building subsystem (org.jetbrains.kotlin.idea.decompiler.builtIns) // Increasing this version will lead to reindexing of all builtIn files (see KotlinBuiltInFileType). - const val BUILTIN_STUB_VERSION = BINARY_STUB_VERSION + 3 + const val BUILTIN_STUB_VERSION = BINARY_STUB_VERSION + 4 // JS stub version should be increased if changes are made to js stub building subsystem (org.jetbrains.kotlin.idea.decompiler.js) // Increasing this version will lead to reindexing of js binary files (see KotlinJavaScriptMetaFileType).