From e22359cc100bb5b13fa78e53bd0c2c1d6e5dfc25 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 16 Jan 2023 23:24:23 +0100 Subject: [PATCH] [FIR] Add MetadataSymbolProvider Extract common code from K1 to MetadataUtil --- .../stub/file/DirectoryBasedClassFinder.kt | 3 + .../cli/jvm/compiler/CliVirtualFileFinder.kt | 16 ++++ .../JvmClassFileBasedSymbolProvider.kt | 8 +- .../deserialization/MetadataSymbolProvider.kt | 84 +++++++++++++++++++ .../fir/java/deserialization/ProviderUtils.kt | 12 +++ .../deserialization/KotlinMetadataFinder.kt | 2 + .../components/ReflectKotlinClassFinder.kt | 3 + .../deserialization/MetadataUtil.kt | 44 ++++++++++ .../MetadataPackageFragmentProvider.kt | 30 +------ 9 files changed, 166 insertions(+), 36 deletions(-) create mode 100644 compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/MetadataSymbolProvider.kt create mode 100644 compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/ProviderUtils.kt create mode 100644 core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/MetadataUtil.kt diff --git a/analysis/decompiled/decompiler-to-file-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/file/DirectoryBasedClassFinder.kt b/analysis/decompiled/decompiler-to-file-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/file/DirectoryBasedClassFinder.kt index ce4f60dc4d6..894cb2013b5 100644 --- a/analysis/decompiled/decompiler-to-file-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/file/DirectoryBasedClassFinder.kt +++ b/analysis/decompiled/decompiler-to-file-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/file/DirectoryBasedClassFinder.kt @@ -37,6 +37,9 @@ class DirectoryBasedClassFinder( // TODO override fun findMetadata(classId: ClassId): InputStream? = null + // TODO + override fun findMetadataTopLevelClassesInPackage(packageFqName: FqName): Set? = null + // TODO override fun hasMetadataPackage(fqName: FqName): Boolean = false diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliVirtualFileFinder.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliVirtualFileFinder.kt index 7460704566c..af5cb568ecd 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliVirtualFileFinder.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliVirtualFileFinder.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.cli.jvm.compiler import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.search.GlobalSearchScope +import gnu.trove.THashSet import org.jetbrains.kotlin.cli.jvm.index.JavaRoot import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder @@ -49,6 +50,21 @@ class CliVirtualFileFinder( )?.inputStream } + override fun findMetadataTopLevelClassesInPackage(packageFqName: FqName): Set { + val result = THashSet() + index.traverseDirectoriesInPackage(packageFqName, continueSearch = { dir, _ -> + for (child in dir.children) { + if (child.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION) { + result.add(child.nameWithoutExtension) + } + } + + true + }) + + return result + } + override fun hasMetadataPackage(fqName: FqName): Boolean { var found = false index.traverseDirectoriesInPackage(fqName, continueSearch = { dir, _ -> diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt index f4eff91fe33..9f7e9f60687 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt @@ -34,12 +34,6 @@ import org.jetbrains.kotlin.utils.toMetadataVersion import java.nio.file.Path import java.nio.file.Paths -/** - * Any top level declarations in core/builtins/src are also available from FirBuiltinSymbolProvider (or FirIdeBuiltinSymbolProvider) for IDE - * so we filter them out to avoid providing the "same" symbols twice. - */ -private val kotlinBuiltins = setOf("kotlin/ArrayIntrinsicsKt", "kotlin/internal/ProgressionUtilKt") - // This symbol provider loads JVM classes, reading extra info from Kotlin `@Metadata` annotations // if present. Use it for library and incremental compilation sessions. For source sessions use // `JavaSymbolProvider`, as Kotlin classes should be parsed first. @@ -59,7 +53,7 @@ class JvmClassFileBasedSymbolProvider( override fun computePackagePartsInfos(packageFqName: FqName): List { return packagePartProvider.findPackageParts(packageFqName.asString()).mapNotNull { partName -> - if (partName in kotlinBuiltins) return@mapNotNull null + if (partName in KotlinBuiltins) return@mapNotNull null val classId = ClassId.topLevel(JvmClassName.byInternalName(partName).fqNameForTopLevelClassMaybeWithDollars) if (!javaFacade.hasTopLevelClassOf(classId)) return@mapNotNull null val jvmMetadataVersion = session.languageVersionSettings.languageVersion.toMetadataVersion() diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/MetadataSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/MetadataSymbolProvider.kt new file mode 100644 index 00000000000..ef6e119ee6c --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/MetadataSymbolProvider.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.java.deserialization + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.ThreadSafeMutableState +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.deserialization.* +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.serialization.deserialization.KotlinMetadataFinder +import org.jetbrains.kotlin.serialization.deserialization.MetadataClassDataFinder +import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol +import org.jetbrains.kotlin.serialization.deserialization.readProto + +@ThreadSafeMutableState +class MetadataSymbolProvider( + session: FirSession, + moduleDataProvider: ModuleDataProvider, + kotlinScopeProvider: FirKotlinScopeProvider, + private val packagePartProvider: PackagePartProvider, + private val kotlinClassFinder: KotlinMetadataFinder, + defaultDeserializationOrigin: FirDeclarationOrigin = FirDeclarationOrigin.Library +) : AbstractFirDeserializedSymbolProvider( + session, moduleDataProvider, kotlinScopeProvider, defaultDeserializationOrigin, BuiltInSerializerProtocol +) { + private val metadataPartProvider = packagePartProvider as MetadataPartProvider + + private val classDataFinder = MetadataClassDataFinder(kotlinClassFinder) + + private val annotationDeserializer = FirBuiltinAnnotationDeserializer(session) + + private val constDeserializer = FirConstDeserializer(session, BuiltInSerializerProtocol) + + override fun computePackagePartsInfos(packageFqName: FqName): List { + return metadataPartProvider.findMetadataPackageParts(packageFqName.asString()).mapNotNull { partName -> + if (partName in KotlinBuiltins) return@mapNotNull null + val classId = ClassId(packageFqName, Name.identifier(partName)) + + val stream = kotlinClassFinder.findMetadata(classId) ?: return@mapNotNull null + val (proto, nameResolver, _) = readProto(stream) + + val context = FirDeserializationContext.createForPackage( + packageFqName, + proto.`package`, + nameResolver, + moduleDataProvider.allModuleData.last(), + annotationDeserializer = annotationDeserializer, + constDeserializer = constDeserializer, + containerSource = null + ) + + return@mapNotNull PackagePartsCacheData(proto.`package`, context) + } + } + + override fun computePackageSetWithNonClassDeclarations() = packagePartProvider.computePackageSetWithNonClassDeclarations() + + override fun knownTopLevelClassesInPackage(packageFqName: FqName) = kotlinClassFinder.findMetadataTopLevelClassesInPackage(packageFqName) + + override fun extractClassMetadata(classId: ClassId, parentContext: FirDeserializationContext?): ClassMetadataFindResult? { + val classData = classDataFinder.findClassData(classId) ?: return null + return ClassMetadataFindResult.Metadata( + classData.nameResolver, + classData.classProto, + annotationDeserializer = null, + moduleDataProvider.allModuleData.last(), + sourceElement = null, + classPostProcessor = null + ) + } + + override fun isNewPlaceForBodyGeneration(classProto: ProtoBuf.Class) = false + + override fun getPackage(fqName: FqName) = null +} diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/ProviderUtils.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/ProviderUtils.kt new file mode 100644 index 00000000000..3f8d54b6f22 --- /dev/null +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/ProviderUtils.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.java.deserialization + +/** + * Any top level declarations in core/builtins/src are also available from FirBuiltinSymbolProvider (or FirIdeBuiltinSymbolProvider) for IDE + * so we filter them out to avoid providing the "same" symbols twice. + */ +val KotlinBuiltins = setOf("kotlin/ArrayIntrinsicsKt", "kotlin/internal/ProgressionUtilKt") \ No newline at end of file diff --git a/core/compiler.common/src/org/jetbrains/kotlin/serialization/deserialization/KotlinMetadataFinder.kt b/core/compiler.common/src/org/jetbrains/kotlin/serialization/deserialization/KotlinMetadataFinder.kt index 1f059523e28..ae7b8ce5450 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/serialization/deserialization/KotlinMetadataFinder.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/serialization/deserialization/KotlinMetadataFinder.kt @@ -27,6 +27,8 @@ interface KotlinMetadataFinder { */ fun findMetadata(classId: ClassId): InputStream? + fun findMetadataTopLevelClassesInPackage(packageFqName: FqName): Set? + /** * @return `true` iff this finder is able to locate the package with the given [fqName], containing .kotlin_metadata files. * Note that returning `true` makes [MetadataPackageFragmentProvider] construct the package fragment for the package, diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/ReflectKotlinClassFinder.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/ReflectKotlinClassFinder.kt index 3f273db94f6..aaf61fc6216 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/ReflectKotlinClassFinder.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/components/ReflectKotlinClassFinder.kt @@ -45,6 +45,9 @@ class ReflectKotlinClassFinder(private val classLoader: ClassLoader) : KotlinCla // TODO override fun findMetadata(classId: ClassId): InputStream? = null + // TODO + override fun findMetadataTopLevelClassesInPackage(packageFqName: FqName): Set? = null + // TODO override fun hasMetadataPackage(fqName: FqName): Boolean = false diff --git a/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/MetadataUtil.kt b/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/MetadataUtil.kt new file mode 100644 index 00000000000..3bb6609369d --- /dev/null +++ b/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/MetadataUtil.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.serialization.deserialization + +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion +import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol +import java.io.InputStream + +class MetadataClassDataFinder(val finder: KotlinMetadataFinder) : ClassDataFinder { + override fun findClassData(classId: ClassId): ClassData? { + val topLevelClassId = generateSequence(classId, ClassId::getOuterClassId).last() + val stream = finder.findMetadata(topLevelClassId) ?: return null + val (message, nameResolver, version) = readProto(stream) + return message.class_List.firstOrNull { classProto -> + nameResolver.getClassId(classProto.fqName) == classId + }?.let { classProto -> + ClassData(nameResolver, classProto, version, SourceElement.NO_SOURCE) + } + } +} + +fun readProto(stream: InputStream): Triple { + val version = BuiltInsBinaryVersion.readFrom(stream) + + if (!version.isCompatibleWithCurrentCompilerVersion()) { + // TODO: report a proper diagnostic + throw UnsupportedOperationException( + "Kotlin metadata definition format version is not supported: " + + "expected ${BuiltInsBinaryVersion.INSTANCE}, actual $version. " + + "Please update Kotlin" + ) + } + + val message = ProtoBuf.PackageFragment.parseFrom(stream, BuiltInSerializerProtocol.extensionRegistry) + val nameResolver = NameResolverImpl(message.strings, message.qualifiedNames) + return Triple(message, nameResolver, version) +} \ No newline at end of file diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MetadataPackageFragmentProvider.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MetadataPackageFragmentProvider.kt index fe677a8e0c5..2d1e52229de 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MetadataPackageFragmentProvider.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MetadataPackageFragmentProvider.kt @@ -18,7 +18,6 @@ package org.jetbrains.kotlin.serialization.deserialization import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.NotFoundClasses -import org.jetbrains.kotlin.descriptors.SourceElement import org.jetbrains.kotlin.descriptors.deserialization.AdditionalClassPartsProvider import org.jetbrains.kotlin.descriptors.deserialization.PlatformDependentDeclarationFilter import org.jetbrains.kotlin.incremental.components.LookupTracker @@ -36,7 +35,6 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker import org.jetbrains.kotlin.types.extensions.TypeAttributeTranslators -import java.io.InputStream class MetadataPackageFragmentProvider( storageManager: StorageManager, @@ -85,16 +83,7 @@ class MetadataPackageFragment( private val metadataPartProvider: MetadataPartProvider, private val finder: KotlinMetadataFinder ) : DeserializedPackageFragment(fqName, storageManager, module) { - override val classDataFinder = ClassDataFinder { classId -> - val topLevelClassId = generateSequence(classId, ClassId::getOuterClassId).last() - val stream = finder.findMetadata(topLevelClassId) ?: return@ClassDataFinder null - val (message, nameResolver, version) = readProto(stream) - message.class_List.firstOrNull { classProto -> - nameResolver.getClassId(classProto.fqName) == classId - }?.let { classProto -> - ClassData(nameResolver, classProto, version, SourceElement.NO_SOURCE) - } - } + override val classDataFinder = MetadataClassDataFinder(finder) private lateinit var components: DeserializationComponents @@ -152,23 +141,6 @@ class MetadataPackageFragment( return true } - private fun readProto(stream: InputStream): Triple { - val version = BuiltInsBinaryVersion.readFrom(stream) - - if (!version.isCompatibleWithCurrentCompilerVersion()) { - // TODO: report a proper diagnostic - throw UnsupportedOperationException( - "Kotlin metadata definition format version is not supported: " + - "expected ${BuiltInsBinaryVersion.INSTANCE}, actual $version. " + - "Please update Kotlin" - ) - } - - val message = ProtoBuf.PackageFragment.parseFrom(stream, BuiltInSerializerProtocol.extensionRegistry) - val nameResolver = NameResolverImpl(message.strings, message.qualifiedNames) - return Triple(message, nameResolver, version) - } - companion object { const val METADATA_FILE_EXTENSION = "kotlin_metadata" const val DOT_METADATA_FILE_EXTENSION = ".$METADATA_FILE_EXTENSION"