From ae664d37ae05c29b7a81d3d4a8a944db1c15a317 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Wed, 21 Feb 2024 20:04:52 +0200 Subject: [PATCH] [Tests] Support KLIB metadata for stdlib in test compile utility Create dependency provider able to resolve code from .knm metadata files to simplify migration from the legacy kotlin-stdlib-common artifact. KTIJ-28668 KTI-1457 --- .../common/CommonResolverForModuleFactory.kt | 3 +- .../org/jetbrains/kotlin/test/KlibTestUtil.kt | 121 +++++++++++++++++- 2 files changed, 116 insertions(+), 8 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/CommonResolverForModuleFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/CommonResolverForModuleFactory.kt index 1bdfa8667a9..8b292a4f12e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/CommonResolverForModuleFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/CommonResolverForModuleFactory.kt @@ -146,6 +146,7 @@ class CommonResolverForModuleFactory( targetEnvironment: TargetEnvironment, capabilities: Map, Any?> = emptyMap(), dependenciesContainer: CommonDependenciesContainer? = null, + explicitProjectContext: ProjectContext? = null, metadataPartProviderFactory: (ModuleContent) -> MetadataPartProvider ): AnalysisResult { val moduleInfo = SourceModuleInfo( @@ -173,7 +174,7 @@ class CommonResolverForModuleFactory( dependenciesContainer ) - val projectContext = ProjectContext(project, "metadata serializer") + val projectContext = explicitProjectContext ?: ProjectContext(project, "metadata serializer") val resolver = ResolverForSingleModuleProject( "sources for metadata serializer", diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KlibTestUtil.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KlibTestUtil.kt index b073a098fe5..2b33fc0f0c4 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KlibTestUtil.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KlibTestUtil.kt @@ -6,6 +6,9 @@ package org.jetbrains.kotlin.test import com.intellij.openapi.util.Disposer +import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer +import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer import org.jetbrains.kotlin.builtins.DefaultBuiltIns @@ -21,24 +24,29 @@ import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl -import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION -import org.jetbrains.kotlin.library.KotlinLibraryVersioning -import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.ir.backend.js.moduleName +import org.jetbrains.kotlin.library.* import org.jetbrains.kotlin.library.impl.BuiltInsPlatform import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutForWriter import org.jetbrains.kotlin.library.impl.KotlinLibraryWriterImpl -import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories -import org.jetbrains.kotlin.library.metadata.KlibMetadataVersion -import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer -import org.jetbrains.kotlin.library.resolveSingleFileKlib +import org.jetbrains.kotlin.library.metadata.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.CommonPlatforms +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration import org.jetbrains.kotlin.resolve.CompilerEnvironment +import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.util.DummyLogger import java.io.File +import java.io.IOException +import java.nio.file.Path import org.jetbrains.kotlin.konan.file.File as KFile object KlibTestUtil { @@ -53,6 +61,7 @@ object KlibTestUtil { configuration.put(CommonConfigurationKeys.MODULE_NAME, libraryName) configuration.addKotlinSourceRoots(sourceFiles.map { it.absolutePath }) val stdlibFile = ForTestCompileRuntime.stdlibCommonForTests() + // support for the legacy version of kotlin-stdlib-common (JAR with .kotlin_metadata) configuration.addJvmClasspathRoot(stdlibFile) val rootDisposable = Disposer.newDisposable("Disposable for ${KlibTestUtil::class.simpleName}.compileCommonSourcesToKlib") @@ -63,6 +72,8 @@ object KlibTestUtil { extensionConfigs = EnvironmentConfigFiles.METADATA_CONFIG_FILES ) + val projectContext = ProjectContext(environment.project, "Compile common sources to KLIB metadata") + val analyzer = AnalyzerWithCompilerReport( configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY), configuration.languageVersionSettings, @@ -77,6 +88,9 @@ object KlibTestUtil { environment.configuration.languageVersionSettings, CommonPlatforms.defaultCommonPlatform, CompilerEnvironment, + explicitProjectContext = projectContext, + // support for the KLIB version of kotlin-stdlib-common (KLIB with .knm metadata) + dependenciesContainer = createDependencyContainerForStdlibIfKlib(stdlibFile.toPath(), environment, projectContext), ) { content -> environment.createPackagePartProvider(content.moduleContentScope) } @@ -153,3 +167,96 @@ object KlibTestUtil { return module } } + +/** + * Creates a dependency container that includes common stdlib, if the passed file path points to a metadata KLIB in the supported format. + * Note that [resolveSingleFileKlib] is sensitive to the library layout and to the file extension. + * In the case of a custom klib layout or file extension other than .klib, the library won't be resolved even if it contains .knm files. + * For the current kotlin-stdlib-common.jar it will always return null, the purpose of the function is to simplify future migration. + * It's been checked that the dependency container for a library with the supported layout is functional. + * See KTI-1457. + */ +private fun createDependencyContainerForStdlibIfKlib( + stdlibFilePath: Path, + environment: KotlinCoreEnvironment, + projectContext: ProjectContext, +): CommonDependenciesContainerImpl? { + val stdlibKlib = resolveSingleFileKlib(KFile(stdlibFilePath), strategy = ToolingSingleFileKlibResolveStrategy).also { + try { + it.moduleName // trigger an exception in case of unsupported file/layout + } catch (e: IOException) { + return null + } + } + + val stdlibModuleDescriptor = createAndInitializeKlibBasedStdlibCommonDescriptor(stdlibKlib, environment, projectContext) + return CommonDependenciesContainerImpl(listOf(stdlibModuleDescriptor)) +} + +private fun createAndInitializeKlibBasedStdlibCommonDescriptor( + stdlibKlib: KotlinLibrary, + environment: KotlinCoreEnvironment, + projectContext: ProjectContext, +): ModuleDescriptor { + val stdlibCommonDescriptor = ModuleDescriptorImpl( + Name.special(""), + projectContext.storageManager, DefaultBuiltIns.Instance, CommonPlatforms.defaultCommonPlatform + ).also { + it.setDependencies(it) + } + + val metadataModuleDescriptorFactory: KlibMetadataModuleDescriptorFactory = KlibMetadataFactories( + { DefaultBuiltIns.Instance }, + NullFlexibleTypeDeserializer, + ).DefaultDeserializedDescriptorFactory + + val klibPackageFragmentProvider = metadataModuleDescriptorFactory.createPackageFragmentProvider( + library = stdlibKlib, + packageAccessHandler = null, + packageFragmentNames = parseModuleHeader(stdlibKlib.moduleHeaderData).packageFragmentNameList, + storageManager = projectContext.storageManager, + moduleDescriptor = stdlibCommonDescriptor, + configuration = CompilerDeserializationConfiguration(environment.configuration.languageVersionSettings), + compositePackageFragmentAddend = null, + lookupTracker = LookupTracker.DO_NOTHING, + ) + + stdlibCommonDescriptor.initialize( + CompositePackageFragmentProvider( + listOf( + klibPackageFragmentProvider, + DefaultBuiltIns.Instance.builtInsModule.packageFragmentProvider, + ), + "Test provider for .knm metadata and built-in declarations of kotlin-stdlib-common" + ) + ) + + return stdlibCommonDescriptor +} + +private class CommonDependenciesContainerImpl(dependencies: Collection) : CommonDependenciesContainer { + private class ModuleInfoImpl(val module: ModuleDescriptor) : ModuleInfo { + override val name: Name get() = module.name + + override fun dependencies(): List = listOf(this) + override fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns = ModuleInfo.DependencyOnBuiltIns.LAST + + override val platform: TargetPlatform get() = CommonPlatforms.defaultCommonPlatform + override val analyzerServices: PlatformDependentAnalyzerServices get() = CommonPlatformAnalyzerServices + } + + private val moduleDescriptorByModuleInfo: Map = dependencies.associateBy(::ModuleInfoImpl) + + override val moduleInfos: List get() = moduleDescriptorByModuleInfo.keys.toList() + + override fun moduleDescriptorForModuleInfo(moduleInfo: ModuleInfo): ModuleDescriptor { + return moduleDescriptorByModuleInfo[moduleInfo] + ?: error("Unknown module info $moduleInfo") + } + + override fun registerDependencyForAllModules(moduleInfo: ModuleInfo, descriptorForModule: ModuleDescriptorImpl) = Unit + override fun packageFragmentProviderForModuleInfo(moduleInfo: ModuleInfo): PackageFragmentProvider? = null + + override val friendModuleInfos: List get() = emptyList() + override val refinesModuleInfos: List get() = emptyList() +}