More advanced partial body resolve caching

This commit is contained in:
Valentin Kipyatkov
2015-07-13 16:50:48 +03:00
parent 53e751be1f
commit e612787833
4 changed files with 59 additions and 33 deletions
@@ -47,7 +47,7 @@ public abstract class ElementResolver protected constructor(
public val resolveSession: ResolveSession public val resolveSession: ResolveSession
) { ) {
public open fun getElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext { public open fun getElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext {
return performElementAdditionalResolve(resolveElement, resolveElement, bodyResolveMode) return performElementAdditionalResolve(resolveElement, resolveElement, bodyResolveMode).first
} }
protected open fun probablyNothingCallableNames(): ProbablyNothingCallableNames protected open fun probablyNothingCallableNames(): ProbablyNothingCallableNames
@@ -74,7 +74,7 @@ public abstract class ElementResolver protected constructor(
return resolveSession.getBindingContext() return resolveSession.getBindingContext()
} }
protected fun findElementOfAdditionalResolve(element: JetElement): JetElement? { private fun findElementOfAdditionalResolve(element: JetElement): JetElement? {
val elementOfAdditionalResolve = JetPsiUtil.getTopmostParentOfTypes( val elementOfAdditionalResolve = JetPsiUtil.getTopmostParentOfTypes(
element, element,
javaClass<JetNamedFunction>(), javaClass<JetNamedFunction>(),
@@ -107,7 +107,7 @@ public abstract class ElementResolver protected constructor(
} }
} }
protected fun performElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext { protected fun performElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): Pair<BindingContext, StatementFilter> {
val file = resolveElement.getContainingJetFile() val file = resolveElement.getContainingJetFile()
val statementFilter = if (bodyResolveMode != BodyResolveMode.FULL && resolveElement is JetDeclaration) val statementFilter = if (bodyResolveMode != BodyResolveMode.FULL && resolveElement is JetDeclaration)
@@ -158,7 +158,7 @@ public abstract class ElementResolver protected constructor(
JetFlowInformationProvider(resolveElement, controlFlowTrace).checkDeclaration() JetFlowInformationProvider(resolveElement, controlFlowTrace).checkDeclaration()
controlFlowTrace.addOwnDataTo(trace, null, false) controlFlowTrace.addOwnDataTo(trace, null, false)
return trace.getBindingContext() return Pair(trace.getBindingContext(), statementFilter)
} }
private fun packageRefAdditionalResolve(resolveSession: ResolveSession, jetElement: JetElement): BindingTrace { private fun packageRefAdditionalResolve(resolveSession: ResolveSession, jetElement: JetElement): BindingTrace {
@@ -447,8 +447,9 @@ public abstract class ElementResolver protected constructor(
return null return null
} }
protected abstract fun createAdditionalCheckerProvider(jetFile: JetFile, module: ModuleDescriptor): AdditionalCheckerProvider protected abstract fun createAdditionalCheckerProvider(file: JetFile, module: ModuleDescriptor): AdditionalCheckerProvider
protected abstract fun getDynamicTypesSettings(jetFile: JetFile): DynamicTypesSettings
protected abstract fun getDynamicTypesSettings(file: JetFile): DynamicTypesSettings
private class BodyResolveContextForLazy( private class BodyResolveContextForLazy(
private val topDownAnalysisMode: TopDownAnalysisMode, private val topDownAnalysisMode: TopDownAnalysisMode,
@@ -46,7 +46,10 @@ class PartialBodyResolveFilter(
private val nothingFunctionNames = HashSet(probablyNothingCallableNames.functionNames()) private val nothingFunctionNames = HashSet(probablyNothingCallableNames.functionNames())
private val nothingVariableNames = HashSet(probablyNothingCallableNames.propertyNames()) private val nothingVariableNames = HashSet(probablyNothingCallableNames.propertyNames())
override val filter: ((JetExpression) -> Boolean)? = { statementMarks.statementMark(it) != MarkLevel.SKIP } override val filter: ((JetExpression) -> Boolean)? = { statementMarks.statementMark(it) != MarkLevel.NONE }
val allStatementsToResolve: Collection<JetExpression>
get() = statementMarks.allMarkedStatements()
init { init {
assert(declaration.isAncestor(elementToResolve)) assert(declaration.isAncestor(elementToResolve))
@@ -532,14 +535,14 @@ class PartialBodyResolveFilter(
} }
private enum class MarkLevel { private enum class MarkLevel {
SKIP, NONE,
TAKE, TAKE,
NEED_REFERENCE_RESOLVE, NEED_REFERENCE_RESOLVE,
NEED_COMPLETION NEED_COMPLETION
} }
companion object { companion object {
public fun findResolveElement(element: JetElement, declaration: JetDeclaration): JetExpression? { public fun findStatementToResolve(element: JetElement, declaration: JetDeclaration): JetExpression? {
return element.parentsWithSelf.takeWhile { it != declaration }.firstOrNull { it.isStatement() } as JetExpression? return element.parentsWithSelf.takeWhile { it != declaration }.firstOrNull { it.isStatement() } as JetExpression?
} }
@@ -628,7 +631,7 @@ class PartialBodyResolveFilter(
statementMarks[statement] = level statementMarks[statement] = level
val block = statement.getParent() as JetBlockExpression val block = statement.getParent() as JetBlockExpression
val currentBlockLevel = blockLevels[block] ?: MarkLevel.SKIP val currentBlockLevel = blockLevels[block] ?: MarkLevel.NONE
if (currentBlockLevel < level) { if (currentBlockLevel < level) {
blockLevels[block] = level blockLevels[block] = level
} }
@@ -636,10 +639,13 @@ class PartialBodyResolveFilter(
} }
fun statementMark(statement: JetExpression): MarkLevel fun statementMark(statement: JetExpression): MarkLevel
= statementMarks[statement] ?: MarkLevel.SKIP = statementMarks[statement] ?: MarkLevel.NONE
fun allMarkedStatements(): Collection<JetExpression>
= statementMarks.keySet()
fun lastMarkedStatement(block: JetBlockExpression, minLevel: MarkLevel): JetExpression? { fun lastMarkedStatement(block: JetBlockExpression, minLevel: MarkLevel): JetExpression? {
val level = blockLevels[block] ?: MarkLevel.SKIP val level = blockLevels[block] ?: MarkLevel.NONE
if (level < minLevel) return null // optimization if (level < minLevel) return null // optimization
return block.getLastChild().siblings(forward = false) return block.getLastChild().siblings(forward = false)
.filterIsInstance<JetExpression>() .filterIsInstance<JetExpression>()
@@ -21,6 +21,7 @@ import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingFunctionShortNameIndex import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingFunctionShortNameIndex
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex
@@ -37,7 +38,7 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
override fun compute(): CachedValueProvider.Result<MemoizedFunctionToNotNull<JetElement, BindingContext>> { override fun compute(): CachedValueProvider.Result<MemoizedFunctionToNotNull<JetElement, BindingContext>> {
val manager = resolveSession.getStorageManager() val manager = resolveSession.getStorageManager()
val cacheFunction = manager.createSoftlyRetainedMemoizedFunction<JetElement, BindingContext> { element -> val cacheFunction = manager.createSoftlyRetainedMemoizedFunction<JetElement, BindingContext> { element ->
performElementAdditionalResolve(element, element, BodyResolveMode.FULL) performElementAdditionalResolve(element, element, BodyResolveMode.FULL).first
} }
return CachedValueProvider.Result.create(cacheFunction, return CachedValueProvider.Result.create(cacheFunction,
PsiModificationTracker.MODIFICATION_COUNT, PsiModificationTracker.MODIFICATION_COUNT,
@@ -46,15 +47,10 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
}, },
false) false)
private val partialBodyResolveCache: CachedValue<MemoizedFunctionToNotNull<JetExpression, BindingContext>> = CachedValuesManager.getManager(project).createCachedValue( private val partialBodyResolveCache: CachedValue<MutableMap<JetExpression, BindingContext>> = CachedValuesManager.getManager(project).createCachedValue(
object : CachedValueProvider<MemoizedFunctionToNotNull<JetExpression, BindingContext>> { object : CachedValueProvider<MutableMap<JetExpression, BindingContext>> {
override fun compute(): CachedValueProvider.Result<MemoizedFunctionToNotNull<JetExpression, BindingContext>> { override fun compute(): CachedValueProvider.Result<MutableMap<JetExpression, BindingContext>> {
val manager = resolveSession.getStorageManager() return CachedValueProvider.Result.create(ContainerUtil.createConcurrentSoftValueMap<JetExpression, BindingContext>(),
val cacheFunction = manager.createSoftlyRetainedMemoizedFunction<JetExpression, BindingContext> { expression ->
val resolveElement = findElementOfAdditionalResolve(expression)!!
performElementAdditionalResolve(resolveElement, expression, BodyResolveMode.PARTIAL)
}
return CachedValueProvider.Result.create(cacheFunction,
PsiModificationTracker.MODIFICATION_COUNT, PsiModificationTracker.MODIFICATION_COUNT,
resolveSession.getExceptionTracker()) resolveSession.getExceptionTracker())
} }
@@ -64,27 +60,38 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
override fun getElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext { override fun getElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext {
if (bodyResolveMode != BodyResolveMode.FULL && !hasElementAdditionalResolveCached(resolveElement) && resolveElement is JetDeclaration) { if (bodyResolveMode != BodyResolveMode.FULL && !hasElementAdditionalResolveCached(resolveElement) && resolveElement is JetDeclaration) {
if (bodyResolveMode == BodyResolveMode.PARTIAL) { if (bodyResolveMode == BodyResolveMode.PARTIAL) {
val partialResolveElement = PartialBodyResolveFilter.findResolveElement(contextElement, resolveElement) val statementToResolve = PartialBodyResolveFilter.findStatementToResolve(contextElement, resolveElement)
if (partialResolveElement != null) { if (statementToResolve != null) {
return partialBodyResolveCache.getValue().invoke(partialResolveElement) val map = partialBodyResolveCache.getValue()
map[statementToResolve]?.let { return it }
val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, statementToResolve, BodyResolveMode.PARTIAL)
for (statement in (statementFilter as PartialBodyResolveFilter).allStatementsToResolve) {
if (!map.containsKey(statement)) {
map[statement] = bindingContext
}
}
return bindingContext
} }
} }
else { else {
return performElementAdditionalResolve(resolveElement, contextElement, bodyResolveMode) return performElementAdditionalResolve(resolveElement, contextElement, bodyResolveMode).first
} }
} }
return additionalResolveCache.getValue().invoke(resolveElement) return additionalResolveCache.getValue().invoke(resolveElement)
} }
private fun hasElementAdditionalResolveCached(jetElement: JetElement) private fun hasElementAdditionalResolveCached(element: JetElement)
= additionalResolveCache.hasUpToDateValue() && additionalResolveCache.getValue().isComputed(jetElement) = additionalResolveCache.hasUpToDateValue() && additionalResolveCache.getValue().isComputed(element)
override fun createAdditionalCheckerProvider(jetFile: JetFile, module: ModuleDescriptor) override fun createAdditionalCheckerProvider(file: JetFile, module: ModuleDescriptor)
= TargetPlatformDetector.getPlatform(jetFile).createAdditionalCheckerProvider(module) = TargetPlatformDetector.getPlatform(file).createAdditionalCheckerProvider(module)
override fun getDynamicTypesSettings(jetFile: JetFile) override fun getDynamicTypesSettings(file: JetFile)
= TargetPlatformDetector.getPlatform(jetFile).getDynamicTypesSettings() = TargetPlatformDetector.getPlatform(file).getDynamicTypesSettings()
override fun probablyNothingCallableNames(): ProbablyNothingCallableNames { override fun probablyNothingCallableNames(): ProbablyNothingCallableNames {
return object : ProbablyNothingCallableNames { return object : ProbablyNothingCallableNames {
@@ -31,7 +31,8 @@ public class ResolveElementCacheTest : JetLightCodeInsightFixtureTestCase() {
class C { class C {
fun a() { fun a() {
b(1, 2) b(1, 2)
c() val x = c()
d(x)
} }
fun b() { fun b() {
@@ -132,4 +133,15 @@ class C {
assert(bindingContext1 === bindingContext2) assert(bindingContext1 === bindingContext2)
} }
} }
public fun testPartialResolveCachedForAllStatementsResolved() {
doTest {
val bindingContext1 = statements[2].analyze(BodyResolveMode.PARTIAL) // resolve 'd(x)'
val bindingContext2 = (statements[1] as JetVariableDeclaration).getInitializer()!!.analyze(BodyResolveMode.PARTIAL) // resolve initializer in 'val x = c()' - it required for resolved 'd(x)' and should be already resolved
assert(bindingContext1 === bindingContext2)
val bindingContext3 = statements[0].analyze(BodyResolveMode.PARTIAL)
assert(bindingContext3 !== bindingContext1)
}
}
} }