diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CommonPlatformKindResolution.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CommonPlatformKindResolution.kt index afbbb35ed4f..62aa23896a9 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CommonPlatformKindResolution.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CommonPlatformKindResolution.kt @@ -5,27 +5,40 @@ package org.jetbrains.kotlin.caches.resolve +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.roots.libraries.Library import com.intellij.openapi.roots.libraries.PersistentLibraryKind import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.PathUtil import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion +import org.jetbrains.kotlin.backend.common.serialization.metadata.metadataVersion import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.context.ProjectContext +import org.jetbrains.kotlin.idea.caches.project.LibraryInfo import org.jetbrains.kotlin.idea.caches.project.SdkInfo import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey import org.jetbrains.kotlin.idea.framework.CommonLibraryKind +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.library.* +import org.jetbrains.kotlin.library.impl.createKotlinLibrary +import org.jetbrains.kotlin.platform.CommonPlatforms +import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind import org.jetbrains.kotlin.resolve.TargetEnvironment -import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment +import java.io.IOException +import java.util.* class CommonPlatformKindResolution : IdePlatformKindResolution { override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean { - return virtualFile.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION + return virtualFile.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION || virtualFile.isMetadataKlib } override val libraryKind: PersistentLibraryKind<*>? @@ -39,6 +52,20 @@ class CommonPlatformKindResolution : IdePlatformKindResolution { return DefaultBuiltIns.Instance } + override fun createLibraryInfo(project: Project, library: Library): List { + val klibFiles = library.getFiles(OrderRootType.CLASSES).filter { it.isMetadataKlib } + + return if (klibFiles.isNotEmpty()) { + klibFiles.mapNotNull { + val path = PathUtil.getLocalPath(it) ?: return@mapNotNull null + CommonKlibLibraryInfo(project, library, path) + } + } else { + // No klib files <=> old metadata-library <=> create usual LibraryInfo + listOf(LibraryInfo(project, library)) + } + } + override fun createResolverForModuleFactory( settings: PlatformAnalysisParameters, environment: TargetEnvironment, @@ -51,4 +78,69 @@ class CommonPlatformKindResolution : IdePlatformKindResolution { shouldCheckExpectActual = true ) } -} \ No newline at end of file +} + +// TODO(dsavvinov): unify with NativeLibraryInfo +class CommonKlibLibraryInfo(project: Project, library: Library, val libraryRoot: String) : LibraryInfo(project, library) { + + sealed class MetadataInfo { + abstract val isCompatible: Boolean + + object Compatible : MetadataInfo() { + override val isCompatible get() = true + } + + class Incompatible(val isOlder: Boolean) : MetadataInfo() { + override val isCompatible get() = false + } + } + + val commonLibrary = createKotlinLibrary(File(libraryRoot)) + + val metadataInfo by lazy { + val metadataVersion = commonLibrary.safeMetadataVersion + when { + metadataVersion == null -> MetadataInfo.Incompatible(true) // too old KLIB format, even doesn't have metadata version + !metadataVersion.isCompatible() -> MetadataInfo.Incompatible(!metadataVersion.isAtLeast(KlibMetadataVersion.INSTANCE)) + else -> MetadataInfo.Compatible + } + } + + override fun getLibraryRoots() = listOf(libraryRoot) + + override val platform: TargetPlatform + get() = CommonPlatforms.defaultCommonPlatform + + override fun toString() = "Common" + super.toString() + + companion object { + internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion } + + private fun KotlinLibrary.readSafe(defaultValue: T, action: KotlinLibrary.() -> T) = try { + action() + } catch (_: IOException) { + defaultValue + } + } +} + + +val VirtualFile.isMetadataKlib: Boolean + get() { + val extension = extension + if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false + + val manifestFile = findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false + + // native libraries + val irFile = findChild(KLIB_IR_FOLDER_NAME) + if (irFile != null && irFile.children.isNotEmpty()) return false + + val manifestProperties = try { + manifestFile.inputStream.use { Properties().apply { load(it) } } + } catch (_: IOException) { + return false + } + + return manifestProperties.containsKey(KLIB_PROPERTY_UNIQUE_NAME) + } \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt index 7627ec01f35..b6149809a24 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CompositeResolverForModuleFactory.kt @@ -10,9 +10,10 @@ package org.jetbrains.kotlin.caches.resolve import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters -import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices import org.jetbrains.kotlin.analyzer.common.configureCommonSpecificComponents +import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.container.* import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.descriptors.ModuleDescriptor @@ -25,6 +26,8 @@ import org.jetbrains.kotlin.frontend.java.di.configureJavaSpecificComponents import org.jetbrains.kotlin.frontend.java.di.initializeJavaSpecificComponents import org.jetbrains.kotlin.idea.project.IdeaEnvironment import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.konan.util.KlibMetadataFactories +import org.jetbrains.kotlin.library.metadata.parseModuleHeader import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl import org.jetbrains.kotlin.load.kotlin.PackagePartProvider @@ -39,7 +42,6 @@ import org.jetbrains.kotlin.platform.jvm.JvmPlatform import org.jetbrains.kotlin.platform.konan.KonanPlatform import org.jetbrains.kotlin.platform.konan.KonanPlatforms import org.jetbrains.kotlin.resolve.* -import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker import org.jetbrains.kotlin.resolve.checkers.ExperimentalMarkerDeclarationAnnotationChecker import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters @@ -50,6 +52,9 @@ import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmen import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil import org.jetbrains.kotlin.serialization.js.createKotlinJavascriptPackageFragmentProvider +import org.jetbrains.kotlin.serialization.konan.NullFlexibleTypeDeserializer +import org.jetbrains.kotlin.serialization.konan.impl.KlibMetadataModuleDescriptorFactoryImpl +import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils @@ -108,7 +113,7 @@ class CompositeResolverForModuleFactory( val packageFragmentProviders = sequence { yield(container.get().packageFragmentProvider) - yieldAll(getCommonProvidersIfAny(container)) + yieldAll(getCommonProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container)) // todo: module context yieldAll(getJsProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container)) yieldAll(getJvmProvidersIfAny(container)) yieldAll(getKonanProvidersIfAny(moduleInfo, container)) @@ -117,8 +122,45 @@ class CompositeResolverForModuleFactory( return ResolverForModule(CompositePackageFragmentProvider(packageFragmentProviders), container) } - private fun getCommonProvidersIfAny(container: StorageComponentContainer): List = - if (targetPlatform.isCommon()) listOf(container.get()) else emptyList() + private fun getCommonProvidersIfAny( + moduleInfo: ModuleInfo, + moduleContext: ModuleContext, + moduleDescriptor: ModuleDescriptor, + container: StorageComponentContainer + ): List { + if (!targetPlatform.isCommon()) return emptyList() + val metadataProvder = container.get() + var klibMetadataProvider: PackageFragmentProvider? = null + + if (moduleInfo is CommonKlibLibraryInfo && moduleInfo.metadataInfo == CommonKlibLibraryInfo.MetadataInfo.Compatible) { + val library = moduleInfo.commonLibrary + val languageVersionSettings = container.get() + + val packageFragmentNames = parseModuleHeader(library.moduleHeaderData).packageFragmentNameList + + val MetadataFactories = KlibMetadataFactories( + { DefaultBuiltIns.Instance }, + NullFlexibleTypeDeserializer + ) + + val klibMetadataModuleDescriptorFactory = KlibMetadataModuleDescriptorFactoryImpl( + MetadataFactories.DefaultDescriptorFactory, + MetadataFactories.DefaultPackageFragmentsFactory, + MetadataFactories.flexibleTypeDeserializer + ) + + klibMetadataProvider = klibMetadataModuleDescriptorFactory.createPackageFragmentProvider( + library, + packageAccessHandler = null, + packageFragmentNames = packageFragmentNames, + storageManager = moduleContext.storageManager, + moduleDescriptor = moduleDescriptor, + configuration = CompilerDeserializationConfiguration(languageVersionSettings), + compositePackageFragmentAddend = null + ) + } + return listOfNotNull(metadataProvder, klibMetadataProvider) + } private fun getJvmProvidersIfAny(container: StorageComponentContainer): List = if (targetPlatform.has()) listOf(container.get().packageFragmentProvider) else emptyList() diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt index 895a2bbbe13..9319bcca035 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/project/IdeaModuleInfos.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.idea.framework.getLibraryPlatform import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.project.findAnalyzerServices import org.jetbrains.kotlin.idea.project.getStableName +import org.jetbrains.kotlin.idea.project.isHMPPEnabled import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected import org.jetbrains.kotlin.idea.util.rootManager @@ -122,10 +123,14 @@ private fun ideaModelDependencies( platform: TargetPlatform ): List { fun TargetPlatform.canDependOn(other: TargetPlatform): Boolean { - return this.isJvm() && other.isJvm() || - this.isJs() && other.isJs() || - this.isNative() && other.isNative() || - this.isCommon() && other.isCommon() + return if (module.isHMPPEnabled) { + other.componentPlatforms.containsAll(this.componentPlatforms) + } else { + this.isJvm() && other.isJvm() || + this.isJs() && other.isJs() || + this.isNative() && other.isNative() || + this.isCommon() && other.isCommon() + } } //NOTE: lib dependencies can be processed several times during recursive traversal