Caching of partial body resolve
This commit is contained in:
@@ -46,41 +46,26 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
public abstract class ElementResolver protected constructor(
|
||||
public val resolveSession: ResolveSession
|
||||
) {
|
||||
|
||||
public open fun getElementAdditionalResolve(jetElement: JetElement): BindingContext {
|
||||
return performElementAdditionalResolve(jetElement, jetElement, BodyResolveMode.FULL)
|
||||
public open fun getElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext {
|
||||
return performElementAdditionalResolve(resolveElement, resolveElement, bodyResolveMode)
|
||||
}
|
||||
|
||||
public open fun hasElementAdditionalResolveCached(jetElement: JetElement): Boolean = false
|
||||
|
||||
protected open fun probablyNothingCallableNames(): ProbablyNothingCallableNames
|
||||
= throw UnsupportedOperationException("Cannot use partial body resolve with no Nothing-functions index");
|
||||
|
||||
public fun resolveToElement(jetElement: JetElement, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): BindingContext {
|
||||
@suppress("NAME_SHADOWING")
|
||||
var jetElement = jetElement
|
||||
public fun resolveToElement(element: JetElement, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): BindingContext {
|
||||
var contextElement = element
|
||||
|
||||
val elementOfAdditionalResolve = findElementOfAdditionalResolve(jetElement)
|
||||
val elementOfAdditionalResolve = findElementOfAdditionalResolve(contextElement)
|
||||
|
||||
if (elementOfAdditionalResolve != null) {
|
||||
if (elementOfAdditionalResolve !is JetParameter) {
|
||||
if (bodyResolveMode != BodyResolveMode.FULL && !hasElementAdditionalResolveCached(elementOfAdditionalResolve)) {
|
||||
return performElementAdditionalResolve(elementOfAdditionalResolve, jetElement, bodyResolveMode)
|
||||
}
|
||||
|
||||
return getElementAdditionalResolve(elementOfAdditionalResolve)
|
||||
}
|
||||
|
||||
val klass = elementOfAdditionalResolve.getParentOfType<JetClass>(true)
|
||||
if (klass != null && elementOfAdditionalResolve.getParent() == klass.getPrimaryConstructorParameterList()) {
|
||||
return getElementAdditionalResolve(klass)
|
||||
}
|
||||
|
||||
// Parameters for function literal could be met inside other parameters. We can't make resolveToDescriptors for internal elements.
|
||||
jetElement = elementOfAdditionalResolve
|
||||
if (elementOfAdditionalResolve is JetParameter) {
|
||||
contextElement = elementOfAdditionalResolve
|
||||
}
|
||||
else if (elementOfAdditionalResolve != null) {
|
||||
return getElementAdditionalResolve(elementOfAdditionalResolve, contextElement, bodyResolveMode)
|
||||
}
|
||||
|
||||
val declaration = jetElement.getParentOfType<JetDeclaration>(false)
|
||||
val declaration = contextElement.getParentOfType<JetDeclaration>(false)
|
||||
if (declaration != null && declaration !is JetClassInitializer) {
|
||||
// Activate descriptor resolution
|
||||
resolveSession.resolveToDescriptor(declaration)
|
||||
@@ -89,8 +74,8 @@ public abstract class ElementResolver protected constructor(
|
||||
return resolveSession.getBindingContext()
|
||||
}
|
||||
|
||||
private fun findElementOfAdditionalResolve(element: JetElement): JetElement? {
|
||||
var elementOfAdditionalResolve = JetPsiUtil.getTopmostParentOfTypes(
|
||||
protected fun findElementOfAdditionalResolve(element: JetElement): JetElement? {
|
||||
val elementOfAdditionalResolve = JetPsiUtil.getTopmostParentOfTypes(
|
||||
element,
|
||||
javaClass<JetNamedFunction>(),
|
||||
javaClass<JetClassInitializer>(),
|
||||
@@ -104,13 +89,22 @@ public abstract class ElementResolver protected constructor(
|
||||
javaClass<JetTypeParameter>(),
|
||||
javaClass<JetTypeConstraint>(),
|
||||
javaClass<JetPackageDirective>(),
|
||||
javaClass<JetCodeFragment>()) as JetElement?
|
||||
javaClass<JetCodeFragment>()) as JetElement? ?: return null
|
||||
|
||||
if (elementOfAdditionalResolve is JetPackageDirective) {
|
||||
return element
|
||||
when (elementOfAdditionalResolve) {
|
||||
is JetPackageDirective -> return element
|
||||
|
||||
is JetParameter -> {
|
||||
val klass = elementOfAdditionalResolve.getParentOfType<JetClass>(strict = true)
|
||||
if (klass != null && elementOfAdditionalResolve.getParent() == klass.getPrimaryConstructorParameterList()) {
|
||||
return klass
|
||||
}
|
||||
|
||||
return elementOfAdditionalResolve
|
||||
}
|
||||
|
||||
else -> return elementOfAdditionalResolve
|
||||
}
|
||||
|
||||
return elementOfAdditionalResolve
|
||||
}
|
||||
|
||||
protected fun performElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext {
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.JetNodeTypes
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.StatementFilter
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.swap
|
||||
import org.jetbrains.kotlin.util.isProbablyNothing
|
||||
@@ -538,6 +539,10 @@ class PartialBodyResolveFilter(
|
||||
}
|
||||
|
||||
companion object {
|
||||
public fun findResolveElement(element: JetElement, declaration: JetDeclaration): JetExpression? {
|
||||
return element.parentsWithSelf.takeWhile { it != declaration }.firstOrNull { it.isStatement() } as JetExpression?
|
||||
}
|
||||
|
||||
private fun JetElement.blocks(): Collection<JetBlockExpression> {
|
||||
val result = ArrayList<JetBlockExpression>(1)
|
||||
this.accept(object : JetVisitorVoid() {
|
||||
|
||||
@@ -24,15 +24,10 @@ import com.intellij.psi.util.PsiModificationTracker
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingFunctionShortNameIndex
|
||||
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex
|
||||
import org.jetbrains.kotlin.psi.JetElement
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetNamedFunction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BodyResolveCache
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.lazy.ElementResolver
|
||||
import org.jetbrains.kotlin.resolve.lazy.ProbablyNothingCallableNames
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||
import org.jetbrains.kotlin.resolve.lazy.*
|
||||
import org.jetbrains.kotlin.storage.MemoizedFunctionToNotNull
|
||||
|
||||
public class ResolveElementCache(resolveSession: ResolveSession, private val project: Project) : ElementResolver(resolveSession), BodyResolveCache {
|
||||
@@ -41,20 +36,48 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
|
||||
object : CachedValueProvider<MemoizedFunctionToNotNull<JetElement, BindingContext>> {
|
||||
override fun compute(): CachedValueProvider.Result<MemoizedFunctionToNotNull<JetElement, BindingContext>> {
|
||||
val manager = resolveSession.getStorageManager()
|
||||
val elementsCacheFunction = manager.createSoftlyRetainedMemoizedFunction<JetElement, BindingContext> { element ->
|
||||
val cacheFunction = manager.createSoftlyRetainedMemoizedFunction<JetElement, BindingContext> { element ->
|
||||
performElementAdditionalResolve(element, element, BodyResolveMode.FULL)
|
||||
}
|
||||
return CachedValueProvider.Result.create(elementsCacheFunction,
|
||||
return CachedValueProvider.Result.create(cacheFunction,
|
||||
PsiModificationTracker.MODIFICATION_COUNT,
|
||||
resolveSession.getExceptionTracker())
|
||||
}
|
||||
},
|
||||
false)
|
||||
|
||||
override fun getElementAdditionalResolve(jetElement: JetElement)
|
||||
= additionalResolveCache.getValue().invoke(jetElement)
|
||||
private val partialBodyResolveCache: CachedValue<MemoizedFunctionToNotNull<JetExpression, BindingContext>> = CachedValuesManager.getManager(project).createCachedValue(
|
||||
object : CachedValueProvider<MemoizedFunctionToNotNull<JetExpression, BindingContext>> {
|
||||
override fun compute(): CachedValueProvider.Result<MemoizedFunctionToNotNull<JetExpression, BindingContext>> {
|
||||
val manager = resolveSession.getStorageManager()
|
||||
val cacheFunction = manager.createSoftlyRetainedMemoizedFunction<JetExpression, BindingContext> { expression ->
|
||||
val resolveElement = findElementOfAdditionalResolve(expression)!!
|
||||
performElementAdditionalResolve(resolveElement, expression, BodyResolveMode.PARTIAL)
|
||||
}
|
||||
return CachedValueProvider.Result.create(cacheFunction,
|
||||
PsiModificationTracker.MODIFICATION_COUNT,
|
||||
resolveSession.getExceptionTracker())
|
||||
}
|
||||
},
|
||||
false)
|
||||
|
||||
override fun hasElementAdditionalResolveCached(jetElement: JetElement)
|
||||
override fun getElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext {
|
||||
if (bodyResolveMode != BodyResolveMode.FULL && !hasElementAdditionalResolveCached(resolveElement) && resolveElement is JetDeclaration) {
|
||||
if (bodyResolveMode == BodyResolveMode.PARTIAL) {
|
||||
val partialResolveElement = PartialBodyResolveFilter.findResolveElement(contextElement, resolveElement)
|
||||
if (partialResolveElement != null) {
|
||||
return partialBodyResolveCache.getValue().invoke(partialResolveElement)
|
||||
}
|
||||
}
|
||||
else {
|
||||
return performElementAdditionalResolve(resolveElement, contextElement, bodyResolveMode)
|
||||
}
|
||||
}
|
||||
|
||||
return additionalResolveCache.getValue().invoke(resolveElement)
|
||||
}
|
||||
|
||||
private fun hasElementAdditionalResolveCached(jetElement: JetElement)
|
||||
= additionalResolveCache.hasUpToDateValue() && additionalResolveCache.getValue().isComputed(jetElement)
|
||||
|
||||
override fun createAdditionalCheckerProvider(jetFile: JetFile, module: ModuleDescriptor)
|
||||
@@ -71,5 +94,5 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
|
||||
}
|
||||
|
||||
override fun resolveFunctionBody(function: JetNamedFunction)
|
||||
= getElementAdditionalResolve(function)
|
||||
= getElementAdditionalResolve(function, function, BodyResolveMode.FULL)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea
|
||||
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.JetLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
public class ResolveElementCacheTest : JetLightCodeInsightFixtureTestCase() {
|
||||
override fun getProjectDescriptor() = JetLightProjectDescriptor.INSTANCE
|
||||
|
||||
private val FILE_TEXT =
|
||||
"""
|
||||
class C {
|
||||
fun a() {
|
||||
b(1, 2)
|
||||
c()
|
||||
}
|
||||
|
||||
fun b() {
|
||||
}
|
||||
|
||||
fun c() {
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
private data class Data(
|
||||
val file: JetFile,
|
||||
val members: List<JetDeclaration>,
|
||||
val statements: List<JetExpression>,
|
||||
val factory: JetPsiFactory
|
||||
)
|
||||
|
||||
private fun doTest(handler: Data.() -> Unit) {
|
||||
val file = myFixture.configureByText("Test.kt", FILE_TEXT) as JetFile
|
||||
val klass = file.getDeclarations().single() as JetClass
|
||||
val members = klass.getDeclarations()
|
||||
val function = members.first() as JetNamedFunction
|
||||
val statements = (function.getBodyExpression() as JetBlockExpression).getStatements()
|
||||
myFixture.getProject().executeWriteCommand("") {
|
||||
Data(file, members, statements, JetPsiFactory(getProject())).handler()
|
||||
}
|
||||
}
|
||||
|
||||
public fun testResolveCaching() {
|
||||
doTest {
|
||||
val statement1 = statements[0]
|
||||
val statement2 = statements[1]
|
||||
val bindingContext1 = statement1.analyze(BodyResolveMode.FULL)
|
||||
val bindingContext2 = statement2.analyze(BodyResolveMode.FULL)
|
||||
assert(bindingContext1 === bindingContext2)
|
||||
|
||||
val bindingContext3 = statement1.analyze(BodyResolveMode.FULL)
|
||||
assert(bindingContext3 === bindingContext1)
|
||||
|
||||
file.add(factory.createFunction("fun foo(){}"))
|
||||
|
||||
val bindingContext4 = statement1.analyze(BodyResolveMode.FULL)
|
||||
assert(bindingContext4 !== bindingContext1)
|
||||
|
||||
statement1.getParent().addAfter(factory.createExpression("x()"), statement1)
|
||||
|
||||
val bindingContext5 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||
assert(bindingContext5 !== bindingContext4)
|
||||
}
|
||||
}
|
||||
|
||||
public fun testPartialResolveUsesFullResolveCached() {
|
||||
doTest {
|
||||
val statement1 = statements[0]
|
||||
val statement2 = statements[1]
|
||||
val bindingContext1 = statement1.analyze(BodyResolveMode.FULL)
|
||||
|
||||
val bindingContext2 = statement2.analyze(BodyResolveMode.PARTIAL)
|
||||
assert(bindingContext2 === bindingContext1)
|
||||
|
||||
val bindingContext3 = statement2.analyze(BodyResolveMode.PARTIAL_FOR_COMPLETION)
|
||||
assert(bindingContext3 === bindingContext1)
|
||||
}
|
||||
}
|
||||
|
||||
public fun testPartialResolveCaching() {
|
||||
doTest {
|
||||
val statement1 = statements[0]
|
||||
val statement2 = statements[1]
|
||||
val bindingContext1 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||
val bindingContext2 = statement2.analyze(BodyResolveMode.PARTIAL)
|
||||
assert(bindingContext1 !== bindingContext2)
|
||||
|
||||
val bindingContext3 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||
val bindingContext4 = statement2.analyze(BodyResolveMode.PARTIAL)
|
||||
assert(bindingContext3 === bindingContext1)
|
||||
assert(bindingContext4 === bindingContext2)
|
||||
|
||||
file.add(factory.createFunction("fun foo(){}"))
|
||||
|
||||
val bindingContext5 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||
assert(bindingContext5 !== bindingContext1)
|
||||
|
||||
statement1.getParent().addAfter(factory.createExpression("x()"), statement1)
|
||||
|
||||
val bindingContext6 = statement1.analyze(BodyResolveMode.PARTIAL)
|
||||
assert(bindingContext6 !== bindingContext5)
|
||||
}
|
||||
}
|
||||
|
||||
public fun testPartialResolveCachedForWholeStatement() {
|
||||
doTest {
|
||||
val statement = statements[0] as JetCallExpression
|
||||
val argument1 = statement.getValueArguments()[0]
|
||||
val argument2 = statement.getValueArguments()[1]
|
||||
val bindingContext1 = argument1.analyze(BodyResolveMode.PARTIAL)
|
||||
val bindingContext2 = argument2.analyze(BodyResolveMode.PARTIAL)
|
||||
assert(bindingContext1 === bindingContext2)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user