提交 14d38622 编写于 作者: I Ilya Muradyan

[REPL] Fix unresolved imports caching

Before this fix, if some imports were not resolved during compilation,
this result had been saved in caches, and this import couldn't been
resolved during following compilations even if it was added to the
module dependencies. This commit adds special handling of resolution
caches for the REPL compiler.
上级 ae5fefce
......@@ -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<KotlinLibrary> = emptyList(),
implicitsResolutionFilter: ImplicitsExtensionsResolutionFilter? = null,
explicitModuleDependencyList: List<ModuleDescriptorImpl> = emptyList(),
explicitModuleFriendsList: List<ModuleDescriptorImpl> = emptyList()
explicitModuleFriendsList: List<ModuleDescriptorImpl> = emptyList(),
moduleCapabilities: Map<ModuleCapability<*>, 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<ModuleCapability<*>, 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
}
......
......@@ -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<ModuleCapability<*>, Any?> = emptyMap(),
): MutableModuleContext {
val module = ModuleDescriptorImpl(moduleName, projectContext.storageManager, builtIns, platform)
val module = ModuleDescriptorImpl(moduleName, projectContext.storageManager, builtIns, platform, capabilities)
return MutableModuleContextImpl(module, projectContext)
}
......@@ -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<Pair<String, GlobalSearchScope>, 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<Pair<String, GlobalSearchScope>, 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));
}
......
/*
* 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
}
}
}
......@@ -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
......
......@@ -38,6 +38,7 @@ class ModuleDescriptorImpl @JvmOverloads constructor(
override val stableName: Name? = null,
) : DeclarationDescriptorImpl(Annotations.EMPTY, moduleName), ModuleDescriptor {
private val capabilities: Map<ModuleCapability<*>, 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, PackageViewDescriptor> { 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")
......
/*
* 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>("PackageViewDescriptorFactory")
}
}
......@@ -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<AnalyzerT : ReplCodeAnalyzerBase>(
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 {
......
/*
* 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
}
}
/*
* 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<PackageFragmentDescriptor>? = null
override val fragments: List<PackageFragmentDescriptor>
get() {
cachedFragments?.let { return it }
val calculatedFragments = module.packageFragmentProvider.packageFragments(fqName)
if (calculatedFragments.isNotEmpty()) cachedFragments = calculatedFragments
return calculatedFragments
}
}
......@@ -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()
......
......@@ -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<ResultWithDia
return compRes to evalRes?.valueOrNull().get()
}
private fun assertCompileFails(
repl: JvmTestRepl,
@Suppress("SameParameterValue")
line: String
) {
val compiledSnippet =
checkCompile(repl, line)
TestCase.assertNull(compiledSnippet)
}
private fun assertEvalUnit(
repl: JvmTestRepl,
@Suppress("SameParameterValue")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册