Add IDE support for common klibs

This commit is contained in:
Dmitry Savvinov
2020-01-24 15:42:51 +03:00
parent 6d45b048cb
commit d1e14598a6
3 changed files with 151 additions and 12 deletions
@@ -5,27 +5,40 @@
package org.jetbrains.kotlin.caches.resolve 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.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory 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.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.context.ProjectContext 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.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind 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.platform.impl.CommonIdePlatformKind
import org.jetbrains.kotlin.resolve.TargetEnvironment import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
import java.io.IOException
import java.util.*
class CommonPlatformKindResolution : IdePlatformKindResolution { class CommonPlatformKindResolution : IdePlatformKindResolution {
override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean { 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<*>? override val libraryKind: PersistentLibraryKind<*>?
@@ -39,6 +52,20 @@ class CommonPlatformKindResolution : IdePlatformKindResolution {
return DefaultBuiltIns.Instance return DefaultBuiltIns.Instance
} }
override fun createLibraryInfo(project: Project, library: Library): List<LibraryInfo> {
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( override fun createResolverForModuleFactory(
settings: PlatformAnalysisParameters, settings: PlatformAnalysisParameters,
environment: TargetEnvironment, environment: TargetEnvironment,
@@ -51,4 +78,69 @@ class CommonPlatformKindResolution : IdePlatformKindResolution {
shouldCheckExpectActual = true shouldCheckExpectActual = true
) )
} }
} }
// 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 <T> 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)
}
@@ -10,9 +10,10 @@ package org.jetbrains.kotlin.caches.resolve
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters 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.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.config.languageVersionSettings
import org.jetbrains.kotlin.container.* import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor 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.frontend.java.di.initializeJavaSpecificComponents
import org.jetbrains.kotlin.idea.project.IdeaEnvironment import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.incremental.components.LookupTracker 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.ModuleClassResolver
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider 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.KonanPlatform
import org.jetbrains.kotlin.platform.konan.KonanPlatforms import org.jetbrains.kotlin.platform.konan.KonanPlatforms
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.ExperimentalMarkerDeclarationAnnotationChecker import org.jetbrains.kotlin.resolve.checkers.ExperimentalMarkerDeclarationAnnotationChecker
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters 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.deserialization.MetadataPartProvider
import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
import org.jetbrains.kotlin.serialization.js.createKotlinJavascriptPackageFragmentProvider 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.storage.StorageManager
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
@@ -108,7 +113,7 @@ class CompositeResolverForModuleFactory(
val packageFragmentProviders = sequence { val packageFragmentProviders = sequence {
yield(container.get<ResolveSession>().packageFragmentProvider) yield(container.get<ResolveSession>().packageFragmentProvider)
yieldAll(getCommonProvidersIfAny(container)) yieldAll(getCommonProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container)) // todo: module context
yieldAll(getJsProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container)) yieldAll(getJsProvidersIfAny(moduleInfo, moduleContext, moduleDescriptor, container))
yieldAll(getJvmProvidersIfAny(container)) yieldAll(getJvmProvidersIfAny(container))
yieldAll(getKonanProvidersIfAny(moduleInfo, container)) yieldAll(getKonanProvidersIfAny(moduleInfo, container))
@@ -117,8 +122,45 @@ class CompositeResolverForModuleFactory(
return ResolverForModule(CompositePackageFragmentProvider(packageFragmentProviders), container) return ResolverForModule(CompositePackageFragmentProvider(packageFragmentProviders), container)
} }
private fun getCommonProvidersIfAny(container: StorageComponentContainer): List<PackageFragmentProvider> = private fun getCommonProvidersIfAny(
if (targetPlatform.isCommon()) listOf(container.get<MetadataPackageFragmentProvider>()) else emptyList() moduleInfo: ModuleInfo,
moduleContext: ModuleContext,
moduleDescriptor: ModuleDescriptor,
container: StorageComponentContainer
): List<PackageFragmentProvider> {
if (!targetPlatform.isCommon()) return emptyList()
val metadataProvder = container.get<MetadataPackageFragmentProvider>()
var klibMetadataProvider: PackageFragmentProvider? = null
if (moduleInfo is CommonKlibLibraryInfo && moduleInfo.metadataInfo == CommonKlibLibraryInfo.MetadataInfo.Compatible) {
val library = moduleInfo.commonLibrary
val languageVersionSettings = container.get<LanguageVersionSettings>()
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<PackageFragmentProvider> = private fun getJvmProvidersIfAny(container: StorageComponentContainer): List<PackageFragmentProvider> =
if (targetPlatform.has<JvmPlatform>()) listOf(container.get<JavaDescriptorResolver>().packageFragmentProvider) else emptyList() if (targetPlatform.has<JvmPlatform>()) listOf(container.get<JavaDescriptorResolver>().packageFragmentProvider) else emptyList()
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.idea.framework.getLibraryPlatform
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.findAnalyzerServices import org.jetbrains.kotlin.idea.project.findAnalyzerServices
import org.jetbrains.kotlin.idea.project.getStableName 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.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
import org.jetbrains.kotlin.idea.util.rootManager import org.jetbrains.kotlin.idea.util.rootManager
@@ -122,10 +123,14 @@ private fun ideaModelDependencies(
platform: TargetPlatform platform: TargetPlatform
): List<IdeaModuleInfo> { ): List<IdeaModuleInfo> {
fun TargetPlatform.canDependOn(other: TargetPlatform): Boolean { fun TargetPlatform.canDependOn(other: TargetPlatform): Boolean {
return this.isJvm() && other.isJvm() || return if (module.isHMPPEnabled) {
this.isJs() && other.isJs() || other.componentPlatforms.containsAll(this.componentPlatforms)
this.isNative() && other.isNative() || } else {
this.isCommon() && other.isCommon() 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 //NOTE: lib dependencies can be processed several times during recursive traversal