diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt index 5522d5d8f79..e8373e30e03 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/TopDownAnalyzerFacadeForJVM.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.context.MutableModuleContext import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ModuleCapability import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider @@ -143,13 +144,14 @@ object TopDownAnalyzerFacadeForJVM { klibList: List = emptyList(), implicitsResolutionFilter: ImplicitsExtensionsResolutionFilter? = null, explicitModuleDependencyList: List = emptyList(), - explicitModuleFriendsList: List = emptyList() + explicitModuleFriendsList: List = emptyList(), + moduleCapabilities: Map, Any?> = emptyMap() ): ComponentProvider { val jvmTarget = configuration.get(JVMConfigurationKeys.JVM_TARGET, JvmTarget.DEFAULT) val languageVersionSettings = configuration.languageVersionSettings val jvmPlatform = JvmPlatforms.jvmPlatformByTargetVersion(jvmTarget) - val moduleContext = createModuleContext(project, configuration, jvmPlatform) + val moduleContext = createModuleContext(project, configuration, jvmPlatform, moduleCapabilities) val storageManager = moduleContext.storageManager val module = moduleContext.module @@ -318,11 +320,17 @@ object TopDownAnalyzerFacadeForJVM { } } - private fun createModuleContext(project: Project, configuration: CompilerConfiguration, platform: TargetPlatform?): MutableModuleContext { + private fun createModuleContext( + project: Project, + configuration: CompilerConfiguration, + platform: TargetPlatform?, + capabilities: Map, Any?> = emptyMap() + ): MutableModuleContext { val projectContext = ProjectContext(project, "TopDownAnalyzer for JVM") val builtIns = JvmBuiltIns(projectContext.storageManager, JvmBuiltIns.Kind.FROM_DEPENDENCIES) return ContextForNewModule( - projectContext, Name.special("<${configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME)}>"), builtIns, platform + projectContext, Name.special("<${configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME)}>"), + builtIns, platform, capabilities ).apply { builtIns.builtInsModule = module } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/context/context.kt b/compiler/frontend/src/org/jetbrains/kotlin/context/context.kt index a1cf143871a..adaa86ff9a0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/context/context.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/context/context.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ModuleCapability import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl @@ -103,8 +104,9 @@ fun ContextForNewModule( projectContext: ProjectContext, moduleName: Name, builtIns: KotlinBuiltIns, - platform: TargetPlatform? + platform: TargetPlatform?, + capabilities: Map, Any?> = emptyMap(), ): MutableModuleContext { - val module = ModuleDescriptorImpl(moduleName, projectContext.storageManager, builtIns, platform) + val module = ModuleDescriptorImpl(moduleName, projectContext.storageManager, builtIns, platform, capabilities) return MutableModuleContextImpl(module, projectContext) } diff --git a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java index c2ac39cb9cb..46b0254d03f 100644 --- a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java +++ b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/KotlinJavaPsiFacade.java @@ -83,6 +83,7 @@ public class KotlinJavaPsiFacade implements Disposable { } private volatile PackageCache packageCache; + private volatile NotFoundPackagesCachingStrategy notFoundPackagesCachingStrategy = NotFoundPackagesCachingStrategy.Default.INSTANCE; private final Project project; private final LightModifierList emptyModifierList; @@ -157,6 +158,10 @@ public class KotlinJavaPsiFacade implements Disposable { } } + public void setNotFoundPackagesCachingStrategy(NotFoundPackagesCachingStrategy notFoundPackagesCachingStrategy) { + this.notFoundPackagesCachingStrategy = notFoundPackagesCachingStrategy; + } + public LightModifierList getEmptyModifierList() { return emptyModifierList; } @@ -314,6 +319,8 @@ public class KotlinJavaPsiFacade implements Disposable { } boolean isALibrarySearchScope = isALibrarySearchScope(searchScope); + NotFoundPackagesCachingStrategy.CacheType notFoundCacheType = + notFoundPackagesCachingStrategy.chooseStrategy(isALibrarySearchScope, qualifiedName); { // store found package in a long term cache if package is found in library search scope @@ -353,24 +360,24 @@ public class KotlinJavaPsiFacade implements Disposable { } } - cache.hasPackageInAllScopeCache.put(qualifiedName, found); + if (found || notFoundCacheType != NotFoundPackagesCachingStrategy.CacheType.NO_CACHING) + cache.hasPackageInAllScopeCache.put(qualifiedName, found); } } - // qualifiedName could be like a proper package name, e.g `org.jetbrains.kotlin` - // but it could be as well part of typed text like `fooba` - // - // all those temporary names and those don't even look like a package name should be stored in a short term cache - // while names those are potentially proper package name could be stored for a long time - // (till PROJECT_ROOTS or specific VFS changes) - boolean packageLikeQName = qualifiedName.indexOf('.') > 0; - - ConcurrentMap, PsiPackage> notFoundPackageInScopeCache = - // store NULL_PACKAGE (attribute that package not found) in a long term cache if: - // - library search scope - // - qualifiedName looks like package (has `.` in its name) - isALibrarySearchScope && packageLikeQName ? - cache.packageInLibScopeCache : cache.packageInScopeCache; + ConcurrentMap, PsiPackage> notFoundPackageInScopeCache; + switch (notFoundCacheType) { + case LIB_SCOPE: + notFoundPackageInScopeCache = cache.packageInLibScopeCache; + break; + case SCOPE: + notFoundPackageInScopeCache = cache.packageInScopeCache; + break; + case NO_CACHING: + return null; + default: + throw new IllegalStateException("Impossible enum value: " + notFoundCacheType.toString()); + } return unwrap(ConcurrencyUtil.cacheOrGet(notFoundPackageInScopeCache, key, NULL_PACKAGE)); } diff --git a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/NotFoundPackagesCachingStrategy.kt b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/NotFoundPackagesCachingStrategy.kt new file mode 100644 index 00000000000..35cb63e499f --- /dev/null +++ b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/resolve/jvm/NotFoundPackagesCachingStrategy.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2021 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.resolve.jvm + +interface NotFoundPackagesCachingStrategy { + + fun chooseStrategy(isLibrarySearchScope: Boolean, qualifiedName: String): CacheType + + enum class CacheType { + LIB_SCOPE, SCOPE, NO_CACHING + } + + object Default : NotFoundPackagesCachingStrategy { + override fun chooseStrategy(isLibrarySearchScope: Boolean, qualifiedName: String): CacheType { + // qualifiedName could be like a proper package name, e.g `org.jetbrains.kotlin` + // but it could be as well part of typed text like `fooba` + // + // all those temporary names and those don't even look like a package name should be stored in a short term cache + // while names those are potentially proper package name could be stored for a long time + // (till PROJECT_ROOTS or specific VFS changes) + val packageLikeQName = qualifiedName.indexOf('.') > 0 + + return if (isLibrarySearchScope && packageLikeQName) CacheType.LIB_SCOPE + else CacheType.SCOPE + } + } +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/LazyPackageViewDescriptorImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/LazyPackageViewDescriptorImpl.kt index c3069310378..8732dec07fe 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/LazyPackageViewDescriptorImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/LazyPackageViewDescriptorImpl.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.getValue -class LazyPackageViewDescriptorImpl( +open class LazyPackageViewDescriptorImpl( override val module: ModuleDescriptorImpl, override val fqName: FqName, storageManager: StorageManager diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt index 07ce4f8e12a..660fc39bb45 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt @@ -38,6 +38,7 @@ class ModuleDescriptorImpl @JvmOverloads constructor( override val stableName: Name? = null, ) : DeclarationDescriptorImpl(Annotations.EMPTY, moduleName), ModuleDescriptor { private val capabilities: Map, Any?> + private val packageViewDescriptorFactory: PackageViewDescriptorFactory init { if (!moduleName.isSpecial) { @@ -46,6 +47,7 @@ class ModuleDescriptorImpl @JvmOverloads constructor( this.capabilities = capabilities.toMutableMap() @OptIn(TypeRefinement::class) this.capabilities[REFINER_CAPABILITY] = Ref(null) + packageViewDescriptorFactory = getCapability(PackageViewDescriptorFactory.CAPABILITY) ?: PackageViewDescriptorFactory.Default } private var dependencies: ModuleDependencies? = null @@ -63,8 +65,8 @@ class ModuleDescriptorImpl @JvmOverloads constructor( } } - private val packages = storageManager.createMemoizedFunction { fqName: FqName -> - LazyPackageViewDescriptorImpl(this, fqName, storageManager) + private val packages = storageManager.createMemoizedFunction { fqName: FqName -> + packageViewDescriptorFactory.compute(this, fqName, storageManager) } @Deprecated("This method is not going to be supported. Please do not use it") diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PackageViewDescriptorFactory.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PackageViewDescriptorFactory.kt new file mode 100644 index 00000000000..cc4262e1520 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/PackageViewDescriptorFactory.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2021 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.descriptors.impl + +import org.jetbrains.kotlin.descriptors.ModuleCapability +import org.jetbrains.kotlin.descriptors.PackageViewDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.storage.StorageManager + +interface PackageViewDescriptorFactory { + fun compute( + module: ModuleDescriptorImpl, + fqName: FqName, + storageManager: StorageManager + ): PackageViewDescriptor + + object Default: PackageViewDescriptorFactory { + override fun compute(module: ModuleDescriptorImpl, fqName: FqName, storageManager: StorageManager): PackageViewDescriptor { + return LazyPackageViewDescriptorImpl(module, fqName, storageManager) + } + } + + companion object { + val CAPABILITY = ModuleCapability("PackageViewDescriptorFactory") + } +} diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmReplCompilerBase.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmReplCompilerBase.kt index 8755b58c65c..c3c48164c73 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmReplCompilerBase.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/KJvmReplCompilerBase.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.calls.tower.ImplicitsExtensionsResolutionFilter +import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.scripting.compiler.plugin.repl.* import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider import org.jetbrains.kotlin.scripting.resolve.skipExtensionsResolutionForImplicits @@ -333,8 +334,10 @@ class ReplCompilationState( override val baseScriptCompilationConfiguration: ScriptCompilationConfiguration get() = context.baseScriptCompilationConfiguration override val environment: KotlinCoreEnvironment get() = context.environment override val analyzerEngine: AnalyzerT by lazy { - // ReplCodeAnalyzer1(context.environment) - analyzerInit(context, implicitsResolutionFilter) + val analyzer = analyzerInit(context, implicitsResolutionFilter) + val psiFacade = KotlinJavaPsiFacade.getInstance(environment.project) + psiFacade.setNotFoundPackagesCachingStrategy(ReplNotFoundPackagesCachingStrategy) + analyzer } private val manglerAndSymbolTable by lazy { diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ReplNotFoundPackagesCachingStrategy.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ReplNotFoundPackagesCachingStrategy.kt new file mode 100644 index 00000000000..ca779d17305 --- /dev/null +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ReplNotFoundPackagesCachingStrategy.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2021 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.scripting.compiler.plugin.impl + +import org.jetbrains.kotlin.resolve.jvm.NotFoundPackagesCachingStrategy + +object ReplNotFoundPackagesCachingStrategy : NotFoundPackagesCachingStrategy { + override fun chooseStrategy(isLibrarySearchScope: Boolean, qualifiedName: String): NotFoundPackagesCachingStrategy.CacheType { + return NotFoundPackagesCachingStrategy.CacheType.NO_CACHING + } +} diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ReplPackageViewDescriptorFactory.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ReplPackageViewDescriptorFactory.kt new file mode 100644 index 00000000000..be0906fe953 --- /dev/null +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/ReplPackageViewDescriptorFactory.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2021 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.scripting.compiler.plugin.impl + +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.PackageViewDescriptor +import org.jetbrains.kotlin.descriptors.impl.LazyPackageViewDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PackageViewDescriptorFactory +import org.jetbrains.kotlin.descriptors.packageFragments +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.storage.StorageManager + +object ReplPackageViewDescriptorFactory : PackageViewDescriptorFactory { + override fun compute(module: ModuleDescriptorImpl, fqName: FqName, storageManager: StorageManager): PackageViewDescriptor { + return ReplPackageViewDescriptor(module, fqName, storageManager) + } +} + +class ReplPackageViewDescriptor( + module: ModuleDescriptorImpl, + fqName: FqName, + storageManager: StorageManager +) : LazyPackageViewDescriptorImpl(module, fqName, storageManager) { + private var cachedFragments: List? = null + + override val fragments: List + get() { + cachedFragments?.let { return it } + val calculatedFragments = module.packageFragmentProvider.packageFragments(fqName) + if (calculatedFragments.isNotEmpty()) cachedFragments = calculatedFragments + return calculatedFragments + } +} diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/ReplCodeAnalyzer.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/ReplCodeAnalyzer.kt index e43dfec00de..dcbd2e6bb90 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/ReplCodeAnalyzer.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/repl/ReplCodeAnalyzer.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.container.ComponentProvider import org.jetbrains.kotlin.container.get import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PackageViewDescriptorFactory import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtFile @@ -33,6 +34,7 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.* import org.jetbrains.kotlin.resolve.scopes.ImportingScope import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes +import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ReplPackageViewDescriptorFactory import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities import kotlin.script.experimental.api.SourceCode import kotlin.script.experimental.jvm.util.CompiledHistoryItem @@ -65,7 +67,8 @@ open class ReplCodeAnalyzerBase( environment.configuration, environment::createPackagePartProvider, { _, _ -> ScriptMutableDeclarationProviderFactory() }, - implicitsResolutionFilter = implicitsResolutionFilter + implicitsResolutionFilter = implicitsResolutionFilter, + moduleCapabilities = mapOf(PackageViewDescriptorFactory.CAPABILITY to ReplPackageViewDescriptorFactory) ) this.module = container.get() diff --git a/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplTest.kt b/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplTest.kt index 8d0fee6bec5..7a85e1bc325 100644 --- a/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplTest.kt +++ b/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplTest.kt @@ -296,6 +296,12 @@ class JvmIdeServicesTest : TestCase() { val (exitCode, outputJarPath) = compileFile("stringTo.kt", outputJarName) assertEquals(ExitCode.OK, exitCode) + assertCompileFails( + repl, """ + import example.dependency.* + """.trimIndent() + ) + assertEvalUnit( repl, """ @file:DependsOn("$outputJarPath") @@ -412,6 +418,17 @@ private fun JvmTestRepl.compileAndEval(codeLine: SourceCode): Pair