Load JVM built-ins in IDE from module dependencies

Fix built-ins for JVM platform and make them consistent
with module's dependency on standard library. Changes
don't affect non-JVM platforms.

Previously all built-ins in IDE were loaded from classloader
and were based on the same pre-serialized .kotlin_builtins files.
This approach is generally not correct as built-in declarations
differ for different platforms, but it had been working for a while
without immediately observalble effects (see KT-33233 for more info).
After changes in standard library JvmBuiltins started producing
false errors (see KT-39728).

To fix this, JVM built-ins in IDE now utilize the same technique as
applied in CLI: using dependency on standard library as a module
for built-ins instead of artificial module that considers only
.kotlin_builtins.

Change summary:
- Provide JvmBuiltins with kind FROM_DEPENDENCIES
  for all modules with stdlib dependency in IDE
- Add JvmBuiltinsPackageFragmentProvider to JVM-ish module resolvers
  (JVM and Composite with JVM platform) to support their use as
  built-ins module
- Create KotlinBuiltInsMetadataIndex file index for tracking libraries
  containing .kotlin_builtins to support JvmBuiltinsPackageFragmentProvider
- Create KotlinStdlibIndex file index for tracking kotlin-stdlib(-common),
  which looks for "Kotlin-Runtime-Component" manifest attribute
- Add caching service to track LibraryInfo for kotlin-stdlib(-common)
- Put LibraryInfo for kotlin-stdlib(-common) alongside SDKs
  due to the need to resolve that modules in BuiltInsCache
- Update BuiltInsCache to separate JvmBuiltins by module's dependency
  on stdlib and JDK
- Make platform of KotlinSDK common instead of JVM
- Set built-ins module lazily in IDE

^KT-33233 Verification Pending
This commit is contained in:
Pavel Kirpichenkov
2020-12-14 16:30:29 +03:00
parent 1479c7a270
commit c0dd731818
27 changed files with 440 additions and 77 deletions
@@ -53,13 +53,15 @@ class CommonPlatformKindResolution : IdePlatformKindResolution {
override val kind get() = CommonIdePlatformKind
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?): BuiltInsCacheKey = BuiltInsCacheKey.DefaultBuiltInsKey
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?, stdlibInfo: LibraryInfo?): BuiltInsCacheKey =
BuiltInsCacheKey.DefaultBuiltInsKey
override fun createBuiltIns(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo?
sdkDependency: SdkInfo?,
stdlibDependency: LibraryInfo?,
): KotlinBuiltIns {
return DefaultBuiltIns.Instance
}
@@ -8,9 +8,11 @@
package org.jetbrains.kotlin.caches.resolve
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.containers.addIfNotNull
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
import org.jetbrains.kotlin.analyzer.common.configureCommonSpecificComponents
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsPackageFragmentProvider
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.context.ModuleContext
@@ -22,6 +24,7 @@ import org.jetbrains.kotlin.frontend.di.configureModule
import org.jetbrains.kotlin.frontend.di.configureStandardResolveComponents
import org.jetbrains.kotlin.frontend.java.di.configureJavaSpecificComponents
import org.jetbrains.kotlin.frontend.java.di.initializeJavaSpecificComponents
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolver
import org.jetbrains.kotlin.load.java.lazy.ModuleClassResolverImpl
@@ -32,6 +35,7 @@ import org.jetbrains.kotlin.platform.*
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.JvmPlatform
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.NativePlatform
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.resolve.*
@@ -130,8 +134,14 @@ class CompositeResolverForModuleFactory(
return listOfNotNull(metadataProvider, klibMetadataProvider)
}
@OptIn(ExperimentalStdlibApi::class)
private fun getJvmProvidersIfAny(container: StorageComponentContainer): List<PackageFragmentProvider> =
if (targetPlatform.has<JvmPlatform>()) listOf(container.get<JavaDescriptorResolver>().packageFragmentProvider) else emptyList()
buildList {
if (targetPlatform.has<JvmPlatform>()) add(container.get<JavaDescriptorResolver>().packageFragmentProvider)
// Use JVM built-ins only for completely-JVM modules
addIfNotNull(container.tryGetService(JvmBuiltInsPackageFragmentProvider::class.java))
}
private fun getNativeProvidersIfAny(moduleInfo: ModuleInfo, container: StorageComponentContainer): List<PackageFragmentProvider> {
if (!targetPlatform.has<NativePlatform>()) return emptyList()
@@ -191,9 +201,11 @@ class CompositeResolverForModuleFactory(
// JVM-specific
if (targetPlatform.has<JvmPlatform>()) {
configureJavaSpecificComponents(
moduleContext, moduleClassResolver!!, languageVersionSettings, configureJavaClassFinder = null,
moduleContext, moduleClassResolver!!,
languageVersionSettings,
configureJavaClassFinder = null,
javaClassTracker = null,
useBuiltInsProvider = false
useBuiltInsProvider = IdeBuiltInsLoadingState.isFromDependenciesForJvm && targetPlatform.isJvm() // use JVM BuiltIns only for completely JVM modules
)
}
@@ -42,12 +42,13 @@ import org.jetbrains.kotlin.storage.StorageManager
interface IdePlatformKindResolution {
val kind: IdePlatformKind<*>
fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?): BuiltInsCacheKey
fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?, stdlibInfo: LibraryInfo?): BuiltInsCacheKey
fun createBuiltIns(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo?
sdkDependency: SdkInfo?,
stdlibDependency: LibraryInfo?
): KotlinBuiltIns
fun createResolverForModuleFactory(
@@ -51,7 +51,7 @@ class JsPlatformKindResolution : IdePlatformKindResolution {
override val kind get() = JsIdePlatformKind
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?): BuiltInsCacheKey {
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?, stdlibInfo: LibraryInfo?): BuiltInsCacheKey {
return BuiltInsCacheKey.DefaultBuiltInsKey
}
@@ -59,7 +59,8 @@ class JsPlatformKindResolution : IdePlatformKindResolution {
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo?
sdkDependency: SdkInfo?,
stdlibDependency: LibraryInfo?,
): KotlinBuiltIns {
return DefaultBuiltIns.Instance
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.caches.resolve
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.libraries.Library
@@ -18,10 +19,12 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
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.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.caches.resolve.supportsAdditionalBuiltInsMembers
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind
@@ -30,6 +33,8 @@ import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
import org.jetbrains.kotlin.resolve.jvm.JvmResolverForModuleFactory
private val LOG = Logger.getInstance(JvmPlatformKindResolution::class.java)
class JvmPlatformKindResolution : IdePlatformKindResolution {
override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean {
return false // TODO: No library kind for JVM
@@ -51,32 +56,80 @@ class JvmPlatformKindResolution : IdePlatformKindResolution {
override val kind get() = JvmIdePlatformKind
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?): BuiltInsCacheKey {
return if (sdkInfo != null) CacheKeyBySdk(sdkInfo.sdk) else BuiltInsCacheKey.DefaultBuiltInsKey
override fun getKeyForBuiltIns(moduleInfo: ModuleInfo, sdkInfo: SdkInfo?, stdlibInfo: LibraryInfo?): BuiltInsCacheKey {
if (IdeBuiltInsLoadingState.isFromClassLoader && stdlibInfo != null) {
LOG.error("Standard library ${stdlibInfo.displayedName} provided for built-ins, but loading from dependencies is disabled")
}
return if (sdkInfo != null)
CacheKeyByBuiltInsDependencies(sdkInfo.sdk, stdlibInfo)
else BuiltInsCacheKey.DefaultBuiltInsKey
}
override fun createBuiltIns(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo?
sdkDependency: SdkInfo?,
stdlibDependency: LibraryInfo?,
): KotlinBuiltIns {
return if (sdkDependency != null)
JvmBuiltIns(projectContext.storageManager, JvmBuiltIns.Kind.FROM_CLASS_LOADER).apply {
setPostponedSettingsComputation {
// SDK should be present, otherwise we wouldn't have created JvmBuiltIns in createBuiltIns
val sdkDescriptor = resolverForProject.descriptorForModule(sdkDependency)
val isAdditionalBuiltInsFeaturesSupported =
moduleInfo.supportsAdditionalBuiltInsMembers(projectContext.project)
JvmBuiltIns.Settings(sdkDescriptor, isAdditionalBuiltInsFeaturesSupported)
}
}
else
DefaultBuiltIns.Instance
return when {
sdkDependency == null -> DefaultBuiltIns.Instance
stdlibDependency == null || moduleInfo is SdkInfo ->
createBuiltInsFromClassLoader(moduleInfo, projectContext, resolverForProject, sdkDependency)
else -> createBuiltinsFromModuleDependencies(moduleInfo, projectContext, resolverForProject, sdkDependency, stdlibDependency)
}
}
data class CacheKeyBySdk(val sdk: Sdk) : BuiltInsCacheKey
private fun createBuiltInsFromClassLoader(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo,
): JvmBuiltIns {
return JvmBuiltIns(projectContext.storageManager, JvmBuiltIns.Kind.FROM_CLASS_LOADER).apply {
setPostponedSettingsComputation {
// SDK should be present, otherwise we wouldn't have created JvmBuiltIns in createBuiltIns
val sdkDescriptor = resolverForProject.descriptorForModule(sdkDependency)
val isAdditionalBuiltInsFeaturesSupported =
moduleInfo.supportsAdditionalBuiltInsMembers(projectContext.project)
JvmBuiltIns.Settings(sdkDescriptor, isAdditionalBuiltInsFeaturesSupported)
}
}
}
private fun createBuiltinsFromModuleDependencies(
moduleInfo: IdeaModuleInfo,
projectContext: ProjectContext,
resolverForProject: ResolverForProject<IdeaModuleInfo>,
sdkDependency: SdkInfo,
stdlibDependency: LibraryInfo,
): JvmBuiltIns {
if (IdeBuiltInsLoadingState.isFromClassLoader) {
LOG.error("Incorrect attempt to create built-ins from module dependencies")
}
return JvmBuiltIns(projectContext.storageManager, JvmBuiltIns.Kind.FROM_DEPENDENCIES).apply {
setPostponedBuiltinsModuleComputation {
val stdlibDescriptor = resolverForProject.descriptorForModule(stdlibDependency)
@Suppress("unchecked_cast")
stdlibDescriptor as ModuleDescriptorImpl
}
setPostponedSettingsComputation {
val sdkDescriptor = resolverForProject.descriptorForModule(sdkDependency)
val isAdditionalBuiltInsFeaturesSupported =
moduleInfo.supportsAdditionalBuiltInsMembers(projectContext.project)
JvmBuiltIns.Settings(sdkDescriptor, isAdditionalBuiltInsFeaturesSupported)
}
}
}
data class CacheKeyByBuiltInsDependencies(val sdk: Sdk, val stdlib: LibraryInfo?) : BuiltInsCacheKey
}
class JvmLibraryInfo(project: Project, library: Library) : LibraryInfo(project, library) {
@@ -9,6 +9,7 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.impl.scopes.LibraryScopeBase
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.*
import com.intellij.openapi.roots.impl.libraries.LibraryEx
@@ -23,10 +24,7 @@ import com.intellij.util.SmartList
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.analyzer.CombinedModuleInfo
import org.jetbrains.kotlin.analyzer.LibraryModuleInfo
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.TrackableModuleInfo
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.caches.project.cacheByClassInvalidatingOnRootModifications
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
import org.jetbrains.kotlin.caches.resolve.resolution
@@ -37,8 +35,10 @@ import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
import org.jetbrains.kotlin.idea.caches.resolve.util.enlargedSearchScope
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModificationTracker
import org.jetbrains.kotlin.idea.configuration.BuildSystemType
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.configuration.getBuildSystemType
import org.jetbrains.kotlin.idea.core.isInTestSourceContentKotlinAware
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
import org.jetbrains.kotlin.idea.framework.effectiveKind
import org.jetbrains.kotlin.idea.framework.platform
import org.jetbrains.kotlin.idea.klib.AbstractKlibLibraryInfo
@@ -48,11 +48,8 @@ import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
import org.jetbrains.kotlin.idea.util.rootManager
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.DefaultIdeTargetPlatformKindProvider
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.*
import org.jetbrains.kotlin.platform.compat.toOldPlatform
import org.jetbrains.kotlin.platform.idePlatformKind
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
@@ -356,7 +353,25 @@ abstract class LibraryInfo(override val project: Project, val library: Library)
val (libraries, sdks) = LibraryDependenciesCache.getInstance(project).getLibrariesAndSdksUsedWith(this)
result.addAll(sdks)
result.addAll(libraries)
/*
* When built-ins are created from module dependencies (as opposed to loading them from classloader)
* we must resolve Kotlin standard library containing some of the built-ins declarations in the same
* resolver for project as JDK. This comes from the following requirements:
* - JvmBuiltins need JDK and standard library descriptors -> resolver for project should be able to
* resolve them
* - Builtins are created in BuiltinsCache -> module descriptors should be resolved under lock of the
* SDK resolver to prevent deadlocks
* This means we have to maintain dependencies of the standard library manually or effectively drop
* resolver for SDK otherwise. Libraries depend on superset of their actual dependencies because of
* the inability to get real dependencies from IDEA model. So moving stdlib with all dependencies
* down is a questionable option.
*/
if (!IdeBuiltInsLoadingState.isFromClassLoader && this.isCoreKotlinLibrary(project)) {
libraries.filterTo(result) { it.isCoreKotlinLibrary(project) }
} else {
result.addAll(libraries)
}
return result.toList()
}
@@ -432,10 +447,19 @@ data class SdkInfo(override val project: Project, val sdk: Sdk) : IdeaModuleInfo
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
override val platform: TargetPlatform
get() = JvmPlatforms.unspecifiedJvmPlatform // TODO(dsavvinov): provide proper target version
get() = when {
sdk.sdkType is KotlinSdkType -> CommonPlatforms.defaultCommonPlatform
else -> JvmPlatforms.unspecifiedJvmPlatform // TODO(dsavvinov): provide proper target version
}
override val analyzerServices: PlatformDependentAnalyzerServices
get() = JvmPlatformAnalyzerServices
override val capabilities: Map<ModuleCapability<*>, Any?>
get() = when (this.sdk.sdkType) {
is JavaSdk -> super.capabilities + mapOf(JDK_CAPABILITY to true)
else -> super.capabilities
}
}
object NotUnderContentRootModuleInfo : IdeaModuleInfo {
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2020 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.idea.caches.project
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.indexing.FileBasedIndex
import org.jetbrains.kotlin.caches.project.cacheInvalidatingOnRootModifications
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.vfilefinder.KotlinStdlibIndex
import org.jetbrains.kotlin.name.FqName
import java.util.concurrent.ConcurrentHashMap
interface KotlinStdlibCache {
fun isStdlib(libraryInfo: LibraryInfo): Boolean
fun isStdlibDependency(libraryInfo: LibraryInfo): Boolean
companion object {
fun getInstance(project: Project): KotlinStdlibCache =
if (IdeBuiltInsLoadingState.isFromClassLoader) {
Disabled
} else {
ServiceManager.getService(project, KotlinStdlibCache::class.java)
?: error("Failed to load service ${KotlinStdlibCache::class.java.name}")
}
val Disabled = object : KotlinStdlibCache {
override fun isStdlib(libraryInfo: LibraryInfo) = false
override fun isStdlibDependency(libraryInfo: LibraryInfo) = false
}
}
}
class KotlinStdlibCacheImpl(val project: Project) : KotlinStdlibCache {
private val stdlibCache = project.cacheInvalidatingOnRootModifications {
ConcurrentHashMap<LibraryInfo, Boolean>()
}
private val stdlibDependencyCache = project.cacheInvalidatingOnRootModifications {
ConcurrentHashMap<LibraryInfo, Boolean>()
}
private class LibraryScope(
project: Project,
private val directories: Set<VirtualFile>
) : DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) {
private val fileSystems = directories.mapTo(hashSetOf(), VirtualFile::getFileSystem)
override fun contains(file: VirtualFile): Boolean =
file.fileSystem in fileSystems && generateSequence(file, VirtualFile::getParent).any { it in directories }
override fun toString() = "All files under: $directories"
}
private fun libraryScopeContainsIndexedFilesForNames(libraryInfo: LibraryInfo, names: Collection<FqName>) = runReadAction {
names.any { name ->
FileBasedIndex.getInstance().getContainingFiles(
KotlinStdlibIndex.KEY,
name,
LibraryScope(project, libraryInfo.library.rootProvider.getFiles(OrderRootType.CLASSES).toSet())
).isNotEmpty()
}
}
private fun libraryScopeContainsIndexedFilesForName(libraryInfo: LibraryInfo, name: FqName) =
libraryScopeContainsIndexedFilesForNames(libraryInfo, listOf(name))
override fun isStdlib(libraryInfo: LibraryInfo): Boolean {
return stdlibCache.getOrPut(libraryInfo) {
libraryScopeContainsIndexedFilesForName(libraryInfo, KotlinStdlibIndex.KOTLIN_STDLIB_NAME)
}
}
override fun isStdlibDependency(libraryInfo: LibraryInfo): Boolean {
return stdlibDependencyCache.getOrPut(libraryInfo) {
libraryScopeContainsIndexedFilesForNames(libraryInfo, KotlinStdlibIndex.STANDARD_LIBRARY_DEPENDENCY_NAMES)
}
}
}
fun LibraryInfo.isCoreKotlinLibrary(project: Project): Boolean =
isKotlinStdlib(project) || isKotlinStdlibDependency(project)
fun LibraryInfo.isKotlinStdlib(project: Project): Boolean =
KotlinStdlibCache.getInstance(project).isStdlib(this)
fun LibraryInfo.isKotlinStdlibDependency(project: Project): Boolean =
KotlinStdlibCache.getInstance(project).isStdlibDependency(this)
@@ -74,8 +74,8 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
val otherLibrary = libraryOrderEntry.library
if (otherLibrary is LibraryEx && !otherLibrary.isDisposed) {
val otherLibraryInfos = createLibraryInfo(project, otherLibrary)
otherLibraryInfos.firstOrNull()?.platform?.let { otherLibraryPlatform ->
if (compatiblePlatforms(platform, otherLibraryPlatform)) {
otherLibraryInfos.firstOrNull()?.let { otherLibraryInfo ->
if (compatiblePlatforms(platform, otherLibraryInfo.platform)) {
libraries.addAll(otherLibraryInfos)
}
}
@@ -84,7 +84,10 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
override fun visitJdkOrderEntry(jdkOrderEntry: JdkOrderEntry, value: Unit) {
val jdk = jdkOrderEntry.jdk ?: return
sdks += SdkInfo(project, jdk)
SdkInfo(project, jdk).also { sdkInfo ->
if (compatiblePlatforms(platform, sdkInfo.platform))
sdks += sdkInfo
}
}
}, Unit)
}
@@ -6,14 +6,12 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.caches.resolve.CompositeAnalyzerServices
import org.jetbrains.kotlin.caches.resolve.CompositeResolverForModuleFactory
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.caches.resolve.resolution
import org.jetbrains.kotlin.caches.resolve.*
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -22,6 +20,7 @@ import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.idea.compiler.IdeSealedClassInheritorsProvider
import org.jetbrains.kotlin.idea.project.findAnalyzerServices
@@ -101,6 +100,9 @@ class IdeaResolverForProject(
"Unexpected modules passed through JvmPlatformParameters to IDE resolver ($targetModuleInfo, $referencingModuleInfo)"
}
tryGetResolverForModuleWithResolutionAnchorFallback(targetModuleInfo, referencingModuleInfo)
},
useBuiltinsProviderForModule = {
IdeBuiltInsLoadingState.isFromDependenciesForJvm && it is LibraryInfo && it.isKotlinStdlib(projectContext.project)
}
)
@@ -132,18 +134,28 @@ class IdeaResolverForProject(
fun getOrCreateIfNeeded(module: IdeaModuleInfo): KotlinBuiltIns = projectContextFromSdkResolver.storageManager.compute {
val sdk = resolverForSdk.sdkDependency(module)
val stdlib = findStdlibForModulesBuiltins(module)
val key = module.platform.idePlatformKind.resolution.getKeyForBuiltIns(module, sdk)
val key = module.platform.idePlatformKind.resolution.getKeyForBuiltIns(module, sdk, stdlib)
val cachedBuiltIns = cache[key]
if (cachedBuiltIns != null) return@compute cachedBuiltIns
module.platform.idePlatformKind.resolution
.createBuiltIns(module, projectContextFromSdkResolver, resolverForSdk, sdk)
.createBuiltIns(module, projectContextFromSdkResolver, resolverForSdk, sdk, stdlib)
.also {
// TODO: MemoizedFunction should be used here instead, but for proper we also need a module (for LV settings) that is not contained in the key
cache[key] = it
}
}
private fun findStdlibForModulesBuiltins(module: IdeaModuleInfo): LibraryInfo? {
if (IdeBuiltInsLoadingState.isFromClassLoader)
return null
return module.dependencies().lazyClosure { it.dependencies() }.firstOrNull {
it is LibraryInfo && it.isKotlinStdlib(projectContextFromSdkResolver.project)
} as? LibraryInfo
}
}
private fun tryGetResolverForModuleWithResolutionAnchorFallback(
@@ -43,6 +43,7 @@ import org.jetbrains.kotlin.context.GlobalContextImpl
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
import org.jetbrains.kotlin.idea.caches.resolve.util.GlobalFacadeModuleFilters
import org.jetbrains.kotlin.idea.caches.resolve.util.contextWithCompositeExceptionTracker
import org.jetbrains.kotlin.idea.caches.trackers.outOfBlockModificationCount
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
@@ -174,13 +175,13 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
)
}
private inner class GlobalFacade(settings: PlatformAnalysisSettings) {
private val sdkContext = GlobalContext(resolverForSdkName)
private val moduleFilters = GlobalFacadeModuleFilters(project)
val facadeForSdk = ProjectResolutionFacade(
"facadeForSdk", "$resolverForSdkName with settings=$settings",
project, sdkContext, settings,
moduleFilter = { it is SdkInfo },
moduleFilter = moduleFilters::sdkFacadeFilter,
dependencies = listOf(
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project)
@@ -194,7 +195,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
"facadeForLibraries", "$resolverForLibrariesName with settings=$settings",
project, librariesContext, settings,
reuseDataFrom = facadeForSdk,
moduleFilter = { it is LibraryInfo },
moduleFilter = moduleFilters::libraryFacadeFilter,
invalidateOnOOCB = false,
dependencies = listOf(
LibraryModificationTracker.getInstance(project),
@@ -207,7 +208,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
"facadeForModules", "$resolverForModulesName with settings=$settings",
project, modulesContext, settings,
reuseDataFrom = facadeForLibraries,
moduleFilter = { !it.isLibraryClasses() },
moduleFilter = moduleFilters::moduleFacadeFilter,
dependencies = listOf(
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project)
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2020 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.idea.caches.resolve.util
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.configuration.IdeBuiltInsLoadingState
internal interface ModuleFilters {
fun sdkFacadeFilter(module: IdeaModuleInfo): Boolean
fun libraryFacadeFilter(module: IdeaModuleInfo): Boolean
fun moduleFacadeFilter(module: IdeaModuleInfo): Boolean
}
private object ClassLoaderBuiltInsModuleFilters : ModuleFilters {
override fun sdkFacadeFilter(module: IdeaModuleInfo): Boolean = module is SdkInfo
override fun libraryFacadeFilter(module: IdeaModuleInfo): Boolean = module is LibraryInfo
override fun moduleFacadeFilter(module: IdeaModuleInfo): Boolean = !module.isLibraryClasses()
}
private class DependencyBuiltinsModuleFilters(private val project: Project) : ModuleFilters {
override fun sdkFacadeFilter(module: IdeaModuleInfo): Boolean =
module is SdkInfo || module is LibraryInfo && module.isCoreKotlinLibrary(project)
override fun libraryFacadeFilter(module: IdeaModuleInfo): Boolean =
module is LibraryInfo && !module.isCoreKotlinLibrary(project)
override fun moduleFacadeFilter(module: IdeaModuleInfo): Boolean = !module.isLibraryClasses()
}
internal class GlobalFacadeModuleFilters(project: Project) : ModuleFilters {
private val impl = when (IdeBuiltInsLoadingState.state) {
IdeBuiltInsLoadingState.IdeBuiltInsLoading.FROM_CLASSLOADER -> ClassLoaderBuiltInsModuleFilters
IdeBuiltInsLoadingState.IdeBuiltInsLoading.FROM_DEPENDENCIES_JVM -> DependencyBuiltinsModuleFilters(project)
}
override fun sdkFacadeFilter(module: IdeaModuleInfo): Boolean = impl.sdkFacadeFilter(module)
override fun libraryFacadeFilter(module: IdeaModuleInfo): Boolean = impl.libraryFacadeFilter(module)
override fun moduleFacadeFilter(module: IdeaModuleInfo): Boolean = impl.moduleFacadeFilter(module)
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2020 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.idea.configuration
object IdeBuiltInsLoadingState {
val state: IdeBuiltInsLoading = IdeBuiltInsLoading.FROM_DEPENDENCIES_JVM
enum class IdeBuiltInsLoading {
FROM_CLASSLOADER,
FROM_DEPENDENCIES_JVM;
}
val isFromDependenciesForJvm: Boolean
get() = state == IdeBuiltInsLoading.FROM_DEPENDENCIES_JVM
val isFromClassLoader: Boolean
get() = state == IdeBuiltInsLoading.FROM_CLASSLOADER
}
@@ -18,7 +18,7 @@ import java.io.InputStream
class IDEVirtualFileFinder(private val scope: GlobalSearchScope) : VirtualFileFinder() {
override fun findMetadata(classId: ClassId): InputStream? {
val file = findVirtualFileWithHeader(classId, KotlinMetadataFileIndex.KEY) ?: return null
val file = findVirtualFileWithHeader(classId.asSingleFqName(), KotlinMetadataFileIndex.KEY) ?: return null
return try {
file.inputStream
@@ -29,8 +29,8 @@ class IDEVirtualFileFinder(private val scope: GlobalSearchScope) : VirtualFileFi
override fun hasMetadataPackage(fqName: FqName): Boolean = KotlinMetadataFilePackageIndex.hasSomethingInPackage(fqName, scope)
// TODO: load built-ins metadata from scope
override fun findBuiltInsData(packageFqName: FqName): InputStream? = null
override fun findBuiltInsData(packageFqName: FqName): InputStream? =
findVirtualFileWithHeader(packageFqName, KotlinBuiltInsMetadataIndex.KEY)?.inputStream
init {
if (scope != GlobalSearchScope.EMPTY_SCOPE && scope.project == null) {
@@ -38,10 +38,11 @@ class IDEVirtualFileFinder(private val scope: GlobalSearchScope) : VirtualFileFi
}
}
override fun findVirtualFileWithHeader(classId: ClassId): VirtualFile? = findVirtualFileWithHeader(classId, KotlinClassFileIndex.KEY)
override fun findVirtualFileWithHeader(classId: ClassId): VirtualFile? =
findVirtualFileWithHeader(classId.asSingleFqName(), KotlinClassFileIndex.KEY)
private fun findVirtualFileWithHeader(classId: ClassId, key: ID<FqName, Void>): VirtualFile? =
FileBasedIndex.getInstance().getContainingFiles(key, classId.asSingleFqName(), scope).firstOrNull()
private fun findVirtualFileWithHeader(fqName: FqName, key: ID<FqName, Void>): VirtualFile? =
FileBasedIndex.getInstance().getContainingFiles(key, fqName, scope).firstOrNull()
companion object {
private val LOG = Logger.getInstance(IDEVirtualFileFinder::class.java)
@@ -11,6 +11,7 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.indexing.*
import com.intellij.util.io.IOUtil
import com.intellij.util.io.KeyDescriptor
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsPackageFragmentProvider
import org.jetbrains.kotlin.idea.caches.IDEKotlinBinaryClassCache
import org.jetbrains.kotlin.idea.decompiler.builtIns.BuiltInDefinitionFile
import org.jetbrains.kotlin.idea.decompiler.builtIns.KotlinBuiltInFileType
@@ -25,10 +26,12 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
import org.jetbrains.kotlin.serialization.deserialization.getClassId
import org.jetbrains.kotlin.utils.JsMetadataVersion
import org.jetbrains.lang.manifest.ManifestFileType
import java.io.ByteArrayInputStream
import java.io.DataInput
import java.io.DataOutput
import java.util.*
import java.util.jar.Manifest
abstract class KotlinFileIndexBase<T>(classOfIndex: Class<T>) : ScalarIndexExtension<FqName>() {
val KEY: ID<FqName, Void> = ID.create(classOfIndex.canonicalName)
@@ -142,6 +145,54 @@ object KotlinMetadataFilePackageIndex : KotlinMetadataFileIndexBase<KotlinMetada
KotlinMetadataFilePackageIndex::class.java, ClassId::getPackageFqName
)
object KotlinBuiltInsMetadataIndex : KotlinFileIndexBase<KotlinBuiltInsMetadataIndex>(KotlinBuiltInsMetadataIndex::class.java) {
override fun getIndexer() = INDEXER
override fun getInputFilter() = FileBasedIndex.InputFilter { file -> file.fileType == KotlinBuiltInFileType }
override fun getVersion() = VERSION
private val VERSION = 1
private val INDEXER = indexer { fileContent ->
if (fileContent.fileType == KotlinBuiltInFileType &&
fileContent.fileName.endsWith(JvmBuiltInsPackageFragmentProvider.DOT_BUILTINS_METADATA_FILE_EXTENSION)) {
val builtins = BuiltInDefinitionFile.read(fileContent.content, fileContent.file.parent)
(builtins as? BuiltInDefinitionFile)?.packageFqName
} else null
}
}
object KotlinStdlibIndex : KotlinFileIndexBase<KotlinStdlibIndex>(KotlinStdlibIndex::class.java) {
private const val LIBRARY_NAME_MANIFEST_ATTRIBUTE = "Implementation-Title"
private const val STDLIB_TAG_MANIFEST_ATTRIBUTE = "Kotlin-Runtime-Component"
val KOTLIN_STDLIB_NAME = FqName("kotlin-stdlib")
val KOTLIN_STDLIB_COMMON_NAME = FqName("kotlin-stdlib-common")
val STANDARD_LIBRARY_DEPENDENCY_NAMES = setOf(
KOTLIN_STDLIB_COMMON_NAME,
)
override fun getIndexer() = INDEXER
override fun getInputFilter() = FileBasedIndex.InputFilter { file -> file.fileType is ManifestFileType }
override fun getVersion() = VERSION
private val VERSION = 1
// TODO: refactor [KotlinFileIndexBase] and get rid of FqName here, it's never a proper fully qualified name, just a String wrapper
private val INDEXER = indexer { fileContent ->
if (fileContent.fileType is ManifestFileType) {
val manifest = Manifest(ByteArrayInputStream(fileContent.content))
val attributes = manifest.mainAttributes
attributes.getValue(STDLIB_TAG_MANIFEST_ATTRIBUTE) ?: return@indexer null
val libraryName = attributes.getValue(LIBRARY_NAME_MANIFEST_ATTRIBUTE) ?: return@indexer null
FqName(libraryName)
} else null
}
}
object KlibMetaFileIndex : KotlinFileIndexBase<KlibMetaFileIndex>(KlibMetaFileIndex::class.java) {
override fun getIndexer() = INDEXER