Merge ResolutionFacadeImpl and KotlinResolveCache, rewrite JavaResolveExtension

This commit is contained in:
Pavel V. Talanov
2015-08-05 12:34:20 +03:00
parent 94431bc3fa
commit d09f4a4dbb
17 changed files with 194 additions and 283 deletions
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.idea.analysis
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.getService
import org.jetbrains.kotlin.idea.caches.resolve.frontendService
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
@@ -36,7 +36,7 @@ public fun JetExpression.computeTypeInfoInContext(
dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY,
expectedType: JetType = TypeUtils.NO_EXPECTED_TYPE
): JetTypeInfo {
return getResolutionFacade().getService<ExpressionTypingServices>(this).getTypeInfo(scope, this, expectedType, dataFlowInfo, trace)
return getResolutionFacade().frontendService<ExpressionTypingServices>(this).getTypeInfo(scope, this, expectedType, dataFlowInfo, trace)
}
jvmOverloads
@@ -16,41 +16,24 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.load.java.structure.impl.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.scopes.JetScope
public object JavaResolveExtension : CacheExtension<(PsiElement) -> Pair<JavaDescriptorResolver, BindingContext>> {
override val platform: TargetPlatform = JvmPlatform
override fun getData(resolverProvider: ModuleResolverProvider): (PsiElement) -> Pair<JavaDescriptorResolver, BindingContext> {
return {
val componentProvider = resolverProvider.resolverByModule(it.getModuleInfo()).componentProvider
Pair(componentProvider.get<JavaDescriptorResolver>(), componentProvider.get<BindingTrace>().getBindingContext())
}
}
public fun getResolver(project: Project, element: PsiElement): JavaDescriptorResolver =
KotlinCacheService.getInstance(project)[this](element).first
public fun getContext(project: Project, element: PsiElement): BindingContext =
KotlinCacheService.getInstance(project)[this](element).second
private fun PsiElement.getJavaDescriptorResolver(): JavaDescriptorResolver {
@suppress("DEPRECATED_SYMBOL_WITH_MESSAGE")
return KotlinCacheService.getInstance(getProject()).getGlobalFacade(JvmPlatform).frontendService<JavaDescriptorResolver>(this)
}
fun PsiMethod.getJavaMethodDescriptor(): FunctionDescriptor? {
val method = getOriginalElement() as? PsiMethod ?: return null
val resolver = JavaResolveExtension.getResolver(method.getProject(), method)
val resolver = method.getJavaDescriptorResolver()
return when {
method.isConstructor() -> resolver.resolveConstructor(JavaConstructorImpl(method))
else -> resolver.resolveMethod(JavaMethodImpl(method))
@@ -58,11 +41,11 @@ fun PsiMethod.getJavaMethodDescriptor(): FunctionDescriptor? {
}
fun PsiClass.getJavaClassDescriptor(): ClassDescriptor? {
return JavaResolveExtension.getResolver(getProject(), this).resolveClass(JavaClassImpl(this))
return getJavaDescriptorResolver().resolveClass(JavaClassImpl(this))
}
fun PsiField.getJavaFieldDescriptor(): PropertyDescriptor? {
return JavaResolveExtension.getResolver(getProject(), this).resolveField(JavaFieldImpl(this))
return getJavaDescriptorResolver().resolveField(JavaFieldImpl(this))
}
fun PsiMember.getJavaMemberDescriptor(): DeclarationDescriptor? {
@@ -24,21 +24,17 @@ import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.analyzer.EmptyResolverForProject
import org.jetbrains.kotlin.idea.project.AnalyzerFacadeProvider
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.psi.JetCodeFragment
import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.utils.keysToMap
import kotlin.platform.platformStatic
@@ -50,37 +46,43 @@ public class KotlinCacheService(val project: Project) {
}
public fun getResolutionFacade(elements: List<JetElement>): ResolutionFacade {
return ResolutionFacadeImpl(
project,
getCacheToAnalyzeFiles(elements.map { it.getContainingJetFile() })
)
return getFacadeToAnalyzeFiles(elements.map { it.getContainingJetFile() })
}
private val globalCachesPerPlatform = listOf(JvmPlatform, JsPlatform).keysToMap { platform -> GlobalCache(platform) }
private val globalFacadesPerPlatform = listOf(JvmPlatform, JsPlatform).keysToMap { platform -> GlobalFacade(platform) }
private inner class GlobalCache(platform: TargetPlatform) {
val librariesCache = KotlinResolveCache(project) {
private inner class GlobalFacade(platform: TargetPlatform) {
val facadeForLibraries = ResolutionFacadeImpl(project) {
globalResolveSessionProvider(
project, platform,
project,
platform,
logProcessCanceled = true,
moduleFilter = { it.isLibraryClasses() },
dependencies = listOf(
LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project)))
ProjectRootModificationTracker.getInstance(project)
)
)
}
val modulesCache = KotlinResolveCache(project) {
globalResolveSessionProvider(project, platform,
reuseDataFromCache = librariesCache,
moduleFilter = { !it.isLibraryClasses() },
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT))
val facadeForModules = ResolutionFacadeImpl(project) {
globalResolveSessionProvider(
project,
platform,
reuseDataFrom = facadeForLibraries,
moduleFilter = { !it.isLibraryClasses() },
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT))
}
}
private fun getGlobalCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.modulesCache
private fun getGlobalLibrariesCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.librariesCache
deprecated("Use JetElement.getResolutionFacade(), please avoid introducing new usages")
public fun getGlobalFacade(platform: TargetPlatform): ResolutionFacade = globalFacade(platform)
private fun createCacheForSyntheticFiles(files: Set<JetFile>): KotlinResolveCache {
private fun globalFacade(platform: TargetPlatform) = globalFacadesPerPlatform[platform]!!.facadeForModules
private fun librariesFacade(platform: TargetPlatform) = globalFacadesPerPlatform[platform]!!.facadeForLibraries
private fun createFacadeForSyntheticFiles(files: Set<JetFile>): ResolutionFacadeImpl {
// we assume that all files come from the same module
val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single()
val syntheticFileModule = files.map { it.getModuleInfo() }.toSet().single()
@@ -91,11 +93,12 @@ public class KotlinCacheService(val project: Project) {
return when {
syntheticFileModule is ModuleSourceInfo -> {
val dependentModules = syntheticFileModule.getDependentModules()
KotlinResolveCache(project) {
ResolutionFacadeImpl(project) {
globalResolveSessionProvider(
project, targetPlatform,
project,
targetPlatform,
syntheticFiles = files,
reuseDataFromCache = getGlobalCache(targetPlatform),
reuseDataFrom = globalFacade(targetPlatform),
moduleFilter = { it in dependentModules },
dependencies = dependenciesForSyntheticFileCache
)
@@ -103,11 +106,12 @@ public class KotlinCacheService(val project: Project) {
}
syntheticFileModule is LibrarySourceInfo || syntheticFileModule is NotUnderContentRootModuleInfo -> {
KotlinResolveCache(project) {
ResolutionFacadeImpl(project) {
globalResolveSessionProvider(
project, targetPlatform,
project,
targetPlatform,
syntheticFiles = files,
reuseDataFromCache = getGlobalLibrariesCache(targetPlatform),
reuseDataFrom = librariesFacade(targetPlatform),
moduleFilter = { it == syntheticFileModule },
dependencies = dependenciesForSyntheticFileCache
)
@@ -119,9 +123,10 @@ public class KotlinCacheService(val project: Project) {
// currently the only known scenario is when we cannot determine that file is a library source
// (file under both classes and sources root)
LOG.warn("Creating cache with synthetic files ($files) in classes of library $syntheticFileModule")
KotlinResolveCache(project) {
ResolutionFacadeImpl(project) {
globalResolveSessionProvider(
project, targetPlatform,
project,
targetPlatform,
syntheticFiles = files,
moduleFilter = { true },
dependencies = dependenciesForSyntheticFileCache
@@ -136,14 +141,14 @@ public class KotlinCacheService(val project: Project) {
private val syntheticFileCachesLock = Any()
private val slruCacheProvider = CachedValueProvider {
CachedValueProvider.Result(object : SLRUCache<Set<JetFile>, KotlinResolveCache>(2, 3) {
override fun createValue(files: Set<JetFile>): KotlinResolveCache {
return createCacheForSyntheticFiles(files)
CachedValueProvider.Result(object : SLRUCache<Set<JetFile>, ResolutionFacadeImpl>(2, 3) {
override fun createValue(files: Set<JetFile>): ResolutionFacadeImpl {
return createFacadeForSyntheticFiles(files)
}
}, LibraryModificationTracker.getInstance(project), ProjectRootModificationTracker.getInstance(project))
}
private fun getCacheForSyntheticFiles(files: Set<JetFile>): KotlinResolveCache {
private fun getFacadeForSyntheticFiles(files: Set<JetFile>): ResolutionFacadeImpl {
return synchronized(syntheticFileCachesLock) {
//NOTE: computations inside createCacheForSyntheticFiles depend on project root structure
// so we additionally drop the whole slru cache on change
@@ -153,13 +158,13 @@ public class KotlinCacheService(val project: Project) {
public fun getLazyResolveSession(element: JetElement): ResolveSession {
val file = element.getContainingJetFile()
return getCacheToAnalyzeFiles(listOf(file)).getLazyResolveSession(file)
return getFacadeToAnalyzeFiles(listOf(file)).getLazyResolveSession(file)
}
private fun getCacheToAnalyzeFiles(files: Collection<JetFile>): KotlinResolveCache {
private fun getFacadeToAnalyzeFiles(files: Collection<JetFile>): ResolutionFacadeImpl {
val syntheticFiles = findSyntheticFiles(files)
return if (syntheticFiles.isNotEmpty()) {
getCacheForSyntheticFiles(syntheticFiles)
getFacadeForSyntheticFiles(syntheticFiles)
}
else {
val firstFile = files.firstOrNull()
@@ -167,7 +172,7 @@ public class KotlinCacheService(val project: Project) {
TargetPlatformDetector.getPlatform(firstFile)
else
TargetPlatformDetector.getDefaultPlatform()
getGlobalCache(targetPlatform)
globalFacade(targetPlatform)
}
}
@@ -183,10 +188,6 @@ public class KotlinCacheService(val project: Project) {
?: throw AssertionError("Analyzing kotlin code fragment of type $javaClass with java context of type ${contextElement.javaClass}")
return if (contextFile is JetCodeFragment) contextFile.getContextFile() else contextFile
}
public fun <T> get(extension: CacheExtension<T>): T {
return getGlobalCache(extension.platform)[extension]
}
}
private fun globalResolveSessionProvider(
@@ -194,23 +195,21 @@ private fun globalResolveSessionProvider(
platform: TargetPlatform,
dependencies: Collection<Any>,
moduleFilter: (IdeaModuleInfo) -> Boolean,
reuseDataFromCache: KotlinResolveCache? = null,
reuseDataFrom: ResolutionFacadeImpl? = null,
syntheticFiles: Collection<JetFile> = listOf(),
logProcessCanceled: Boolean = false
): CachedValueProvider.Result<ModuleResolverProvider> {
val analyzerFacade = AnalyzerFacadeProvider.getAnalyzerFacade(platform)
val delegateResolverProvider = reuseDataFromCache?.moduleResolverProvider ?: EmptyModuleResolverProvider
val globalContext = (delegateResolverProvider as? ModuleResolverProviderImpl)
?.globalContext
val delegateResolverProvider = reuseDataFrom?.moduleResolverProvider
val delegateResolverForProject = delegateResolverProvider?.resolverForProject ?: EmptyResolverForProject()
val globalContext = (delegateResolverProvider as? ModuleResolverProviderImpl)?.globalContext
?.withCompositeExceptionTrackerUnderSameLock()
?: GlobalContext(logProcessCanceled)
val moduleResolverProvider = createModuleResolverProvider(
project, globalContext, analyzerFacade, syntheticFiles, delegateResolverProvider, moduleFilter
project, globalContext,
AnalyzerFacadeProvider.getAnalyzerFacade(platform),
syntheticFiles, delegateResolverForProject, moduleFilter
)
val allDependencies = dependencies + listOf(moduleResolverProvider.exceptionTracker)
return CachedValueProvider.Result.create(moduleResolverProvider, allDependencies)
}
@@ -49,79 +49,6 @@ public interface CacheExtension<T> {
public fun getData(resolverProvider: ModuleResolverProvider): T
}
private class KotlinResolveCache(
val project: Project,
computeModuleResolverProvider: () -> CachedValueProvider.Result<ModuleResolverProvider>
) {
private val resolverCache = SynchronizedCachedValue(project, computeModuleResolverProvider, trackValue = false)
val moduleResolverProvider: ModuleResolverProvider
get() = resolverCache.getValue()
public fun getLazyResolveSession(element: JetElement): ResolveSession {
return moduleResolverProvider.resolverByModule(element.getModuleInfo()).componentProvider.get<ResolveSession>()
}
public fun <T> getService(element: JetElement, serviceClass: Class<T>): T {
//TODO_R:
return moduleResolverProvider.resolverForProject.resolverForModule(element.getModuleInfo()).componentProvider.resolve(serviceClass)?.getValue() as T
}
public fun <T> getService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
//TODO_R:
return moduleResolverProvider.resolverForProject.resolverForModuleDescriptor(moduleDescriptor).componentProvider.resolve(serviceClass)?.getValue() as T
}
public fun <T> getIdeService(element: JetElement, serviceClass: Class<T>): T {
//TODO_R:
return moduleResolverProvider.resolverForProject.resolverForModule(element.getModuleInfo()).componentProvider.create(serviceClass)
}
public fun <T> getIdeService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
//TODO_R:
return moduleResolverProvider.resolverForProject.resolverForModuleDescriptor(moduleDescriptor).componentProvider.create(serviceClass)
}
public fun getLazyResolveSession(moduleDescriptor: ModuleDescriptor): ResolveSession {
return moduleResolverProvider.resolveSessionForBodiesByDescriptor(moduleDescriptor)
}
public fun <T> get(extension: CacheExtension<T>): T {
return extension.getData(moduleResolverProvider)
}
private val analysisResults = CachedValuesManager.getManager(project).createCachedValue(
{
val resolverProvider = moduleResolverProvider
val results = object : SLRUCache<JetFile, PerFileAnalysisCache>(2, 3) {
override fun createValue(file: JetFile?): PerFileAnalysisCache {
return PerFileAnalysisCache(file!!, resolverProvider.resolverByModule(file.getModuleInfo()).componentProvider)
}
}
CachedValueProvider.Result(results, PsiModificationTracker.MODIFICATION_COUNT, resolverProvider.exceptionTracker)
}, false)
fun getAnalysisResultsForElements(elements: Collection<JetElement>): AnalysisResult {
assert(elements.isNotEmpty(), "elements collection should not be empty")
val slruCache = synchronized(analysisResults) {
analysisResults.getValue()!!
}
val results = elements.map {
val perFileCache = synchronized(slruCache) {
slruCache[it.getContainingJetFile()]
}
perFileCache.getAnalysisResults(it)
}
val withError = results.firstOrNull { it.isError() }
val bindingContext = CompositeBindingContext.create(results.map { it.bindingContext })
return if (withError != null)
AnalysisResult.error(bindingContext, withError.error)
else
//TODO: (module refactoring) several elements are passed here in debugger
AnalysisResult.success(bindingContext, getLazyResolveSession(elements.first()).getModuleDescriptor())
}
}
private class PerFileAnalysisCache(val file: JetFile, val componentProvider: ComponentProvider) {
private val cache = HashMap<PsiElement, AnalysisResult>()
@@ -21,17 +21,16 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.JdkOrderEntry
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.analyzer.AnalyzerFacade
import org.jetbrains.kotlin.analyzer.ModuleContent
import org.jetbrains.kotlin.analyzer.ResolverForProject
import org.jetbrains.kotlin.context.GlobalContextImpl
import org.jetbrains.kotlin.context.withProject
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.storage.ExceptionTracker
fun createModuleResolverProvider(
@@ -39,7 +38,7 @@ fun createModuleResolverProvider(
globalContext: GlobalContextImpl,
analyzerFacade: AnalyzerFacade<JvmPlatformParameters>,
syntheticFiles: Collection<JetFile>,
delegateProvider: ModuleResolverProvider,
delegateResolver: ResolverForProject<IdeaModuleInfo>,
moduleFilter: (IdeaModuleInfo) -> Boolean
): ModuleResolverProvider {
@@ -64,7 +63,7 @@ fun createModuleResolverProvider(
val resolverForProject = analyzerFacade.setupResolverForProject(
globalContext.withProject(project), modulesToCreateResolversFor, modulesContent,
jvmPlatformParameters, IdeaEnvironment, delegateProvider.resolverForProject
jvmPlatformParameters, IdeaEnvironment, delegateResolver
)
return resolverForProject
}
@@ -73,8 +72,7 @@ fun createModuleResolverProvider(
return ModuleResolverProviderImpl(
resolverForProject,
globalContext,
delegateProvider
globalContext
)
}
@@ -105,36 +103,12 @@ private fun collectAllModuleInfosFromIdeaModel(project: Project): List<IdeaModul
interface ModuleResolverProvider {
val exceptionTracker: ExceptionTracker
fun resolverByModule(module: IdeaModuleInfo): ResolverForModule = resolverForProject.resolverForModule(module)
fun resolveSessionForBodiesByModule(module: IdeaModuleInfo): ResolveSession {
return resolveSessionForBodiesByDescriptor(resolverForProject.descriptorForModule(module))
}
fun resolveSessionForBodiesByDescriptor(descriptor: ModuleDescriptor): ResolveSession
val resolverForProject: ResolverForProject<IdeaModuleInfo>
}
object EmptyModuleResolverProvider: ModuleResolverProvider {
override val exceptionTracker: ExceptionTracker
get() = throw IllegalStateException("Should not be called")
override val resolverForProject: ResolverForProject<IdeaModuleInfo> = EmptyResolverForProject()
override fun resolveSessionForBodiesByDescriptor(descriptor: ModuleDescriptor): ResolveSession {
throw IllegalStateException("Trying to obtain resolve session for $descriptor not present in this resolver")
}
}
class ModuleResolverProviderImpl(
override val resolverForProject: ResolverForProject<IdeaModuleInfo>,
val globalContext: GlobalContextImpl,
val delegateProvider: ModuleResolverProvider = EmptyModuleResolverProvider
): ModuleResolverProvider {
val globalContext: GlobalContextImpl
) : ModuleResolverProvider {
override val exceptionTracker: ExceptionTracker = globalContext.exceptionTracker
override fun resolveSessionForBodiesByDescriptor(descriptor: ModuleDescriptor): ResolveSession {
return resolverForProject.resolverForModuleDescriptor(descriptor).componentProvider.get<ResolveSession>()
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -44,11 +45,11 @@ public interface ResolutionFacade {
public fun <T> get(extension: CacheExtension<T>): T
public fun <T> getFrontendService(element: JetElement, serviceClass: Class<T>): T
public fun <T> getFrontendService(element: PsiElement, serviceClass: Class<T>): T
public fun <T> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T
public fun <T> getIdeService(element: JetElement, serviceClass: Class<T>): T
public fun <T> getIdeService(element: PsiElement, serviceClass: Class<T>): T
public fun <T> getIdeService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T
@@ -65,13 +66,13 @@ public interface ResolutionFacade {
}
}
public inline fun <reified T> ResolutionFacade.getService(element: JetElement): T
public inline fun <reified T> ResolutionFacade.frontendService(element: PsiElement): T
= this.getFrontendService(element, javaClass<T>())
public inline fun <reified T> ResolutionFacade.getService(moduleDescriptor: ModuleDescriptor): T
public inline fun <reified T> ResolutionFacade.frontendService(moduleDescriptor: ModuleDescriptor): T
= this.getFrontendService(moduleDescriptor, javaClass<T>())
public inline fun <reified T> ResolutionFacade.ideService(element: JetElement): T
public inline fun <reified T> ResolutionFacade.ideService(element: PsiElement): T
= this.getIdeService(element, javaClass<T>())
public inline fun <reified T> ResolutionFacade.ideService(moduleDescriptor: ModuleDescriptor): T
@@ -17,7 +17,14 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.container.getService
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.project.ResolveElementCache
@@ -26,59 +33,101 @@ import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.scopes.JetScope
private class ResolutionFacadeImpl(private val project: Project, private val cache: KotlinResolveCache) : ResolutionFacade {
override fun <T> getIdeService(element: JetElement, serviceClass: Class<T>): T {
return cache.getIdeService(element, serviceClass)
private class ResolutionFacadeImpl(
private val project: Project,
computeModuleResolverProvider: () -> CachedValueProvider.Result<ModuleResolverProvider>
) : ResolutionFacade {
override fun <T> get(extension: CacheExtension<T>): T = error("TODO: support")
private val resolverCache = SynchronizedCachedValue(project, computeModuleResolverProvider, trackValue = false)
val moduleResolverProvider: ModuleResolverProvider
get() = resolverCache.getValue()
public fun getLazyResolveSession(element: JetElement): ResolveSession {
return resolverForModuleInfo(element.getModuleInfo()).componentProvider.get<ResolveSession>()
}
private fun resolverForModuleInfo(moduleInfo: IdeaModuleInfo) = moduleResolverProvider.resolverForProject.resolverForModule(moduleInfo)
private fun resolverForDescriptor(moduleDescriptor: ModuleDescriptor) = moduleResolverProvider.resolverForProject.resolverForModuleDescriptor(moduleDescriptor)
override fun <T> getFrontendService(element: PsiElement, serviceClass: Class<T>): T {
return resolverForModuleInfo(element.getModuleInfo()).componentProvider.getService(serviceClass)
}
override fun <T> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
return resolverForDescriptor(moduleDescriptor).componentProvider.getService(serviceClass)
}
override fun <T> getIdeService(element: PsiElement, serviceClass: Class<T>): T {
return resolverForModuleInfo(element.getModuleInfo()).componentProvider.create(serviceClass)
}
override fun <T> getIdeService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
return cache.getIdeService(moduleDescriptor, serviceClass)
return resolverForDescriptor(moduleDescriptor).componentProvider.create(serviceClass)
}
override fun analyze(element: JetElement, bodyResolveMode: BodyResolveMode): BindingContext {
val resolveElementCache = cache.getService(element, javaClass<ResolveElementCache>())
val resolveElementCache = getFrontendService(element, javaClass<ResolveElementCache>())
return resolveElementCache.resolveToElement(element, bodyResolveMode)
}
override fun findModuleDescriptor(element: JetElement): ModuleDescriptor {
return cache.getLazyResolveSession(element).getModuleDescriptor()
return moduleResolverProvider.resolverForProject.descriptorForModule(element.getModuleInfo())
}
override fun resolveToDescriptor(declaration: JetDeclaration): DeclarationDescriptor {
return cache.getLazyResolveSession(declaration).resolveToDescriptor(declaration)
return getLazyResolveSession(declaration).resolveToDescriptor(declaration)
}
override fun analyzeFullyAndGetResult(elements: Collection<JetElement>): AnalysisResult {
return cache.getAnalysisResultsForElements(elements)
return getAnalysisResultsForElements(elements)
}
override fun getFileTopLevelScope(file: JetFile): JetScope {
return cache.getLazyResolveSession(file).getFileScopeProvider().getFileScope(file)
}
override fun <T> getFrontendService(element: JetElement, serviceClass: Class<T>): T {
return cache.getService(element, serviceClass)
}
override fun <T> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
return cache.getService(moduleDescriptor, serviceClass)
return getLazyResolveSession(file).getFileScopeProvider().getFileScope(file)
}
override fun resolveImportReference(moduleDescriptor: ModuleDescriptor, fqName: FqName): Collection<DeclarationDescriptor> {
val importDirective = JetPsiFactory(project).createImportDirective(ImportPath(fqName, false))
val qualifiedExpressionResolver = this.getService<QualifiedExpressionResolver>(moduleDescriptor)
val qualifiedExpressionResolver = this.frontendService<QualifiedExpressionResolver>(moduleDescriptor)
return qualifiedExpressionResolver.processImportReference(
importDirective, moduleDescriptor, BindingTraceContext(), QualifiedExpressionResolver.LookupMode.EVERYTHING).getAllDescriptors()
}
override fun <T> get(extension: CacheExtension<T>): T {
return cache[extension]
private val analysisResults = CachedValuesManager.getManager(project).createCachedValue(
{
val resolverProvider = moduleResolverProvider
val results = object : SLRUCache<JetFile, PerFileAnalysisCache>(2, 3) {
override fun createValue(file: JetFile?): PerFileAnalysisCache {
return PerFileAnalysisCache(file!!, resolverProvider.resolverForProject.resolverForModule(file.getModuleInfo()).componentProvider)
}
}
CachedValueProvider.Result(results, PsiModificationTracker.MODIFICATION_COUNT, resolverProvider.exceptionTracker)
}, false)
fun getAnalysisResultsForElements(elements: Collection<JetElement>): AnalysisResult {
assert(elements.isNotEmpty(), "elements collection should not be empty")
val slruCache = synchronized(analysisResults) {
analysisResults.getValue()!!
}
val results = elements.map {
val perFileCache = synchronized(slruCache) {
slruCache[it.getContainingJetFile()]
}
perFileCache.getAnalysisResults(it)
}
val withError = results.firstOrNull { it.isError() }
val bindingContext = CompositeBindingContext.create(results.map { it.bindingContext })
return if (withError != null)
AnalysisResult.error(bindingContext, withError.error)
else
//TODO: (module refactoring) several elements are passed here in debugger
AnalysisResult.success(bindingContext, getLazyResolveSession(elements.first()).getModuleDescriptor())
}
}