[SLC] invalidate local class members cache on any file changes

We should track the containing file modifications to avoid PIEAE by
changes in members

^KTIJ-26661 Fixed
This commit is contained in:
Dmitrii Gridin
2023-08-15 20:04:02 +02:00
committed by Space Team
parent 765a8bdd7c
commit e05cb49671
6 changed files with 59 additions and 25 deletions
@@ -44,7 +44,7 @@ open class KtLightClassForDecompiledDeclaration(
ClassInnerStuffCache( ClassInnerStuffCache(
/* aClass = */ this, /* aClass = */ this,
/* generateEnumMethods = */ true, /* generateEnumMethods = */ true,
/* modificationTracker = */ project.createAllLibrariesModificationTracker(), /* modificationTrackers = */ listOf(project.createAllLibrariesModificationTracker()),
) )
} }
@@ -50,27 +50,27 @@ public final class ClassInnerStuffCache {
public static final String NOT_NULL_ANNOTATION_QUALIFIER = "@" + NotNull.class.getName(); public static final String NOT_NULL_ANNOTATION_QUALIFIER = "@" + NotNull.class.getName();
private final @NotNull KtExtensibleLightClass myClass; private final @NotNull KtExtensibleLightClass myClass;
private final @NotNull ModificationTracker myModificationTracker; private final @NotNull List<ModificationTracker> myModificationTrackers;
private final @NotNull Ref<Pair<Long, Interner<PsiMember>>> myInterner = Ref.create(); private final @NotNull Ref<Pair<Long, Interner<PsiMember>>> myInterner = Ref.create();
private final boolean myGenerateEnumMethods; private final boolean myGenerateEnumMethods;
public ClassInnerStuffCache( public ClassInnerStuffCache(
@NotNull KtExtensibleLightClass aClass, @NotNull KtExtensibleLightClass aClass,
boolean generateEnumMethods, boolean generateEnumMethods,
@NotNull ModificationTracker modificationTracker @NotNull List<ModificationTracker> modificationTrackers
) { ) {
myGenerateEnumMethods = generateEnumMethods; myGenerateEnumMethods = generateEnumMethods;
myClass = aClass; myClass = aClass;
myModificationTracker = modificationTracker; myModificationTrackers = modificationTrackers;
} }
@NotNull @NotNull
public PsiMethod[] getConstructors() { public PsiMethod[] getConstructors() {
return copy(CachedValuesManager.getCachedValue( return copy(CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
PsiImplUtil.getConstructors(myClass), PsiImplUtil.getConstructors(myClass),
myModificationTracker myModificationTrackers
) )
)); ));
} }
@@ -79,9 +79,9 @@ public final class ClassInnerStuffCache {
public PsiField[] getFields() { public PsiField[] getFields() {
return copy(CachedValuesManager.getCachedValue( return copy(CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
calcFields(), calcFields(),
myModificationTracker myModificationTrackers
) )
)); ));
} }
@@ -90,9 +90,9 @@ public final class ClassInnerStuffCache {
public PsiMethod[] getMethods() { public PsiMethod[] getMethods() {
return copy(CachedValuesManager.getCachedValue( return copy(CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
calcMethods(), calcMethods(),
myModificationTracker myModificationTrackers
) )
)); ));
} }
@@ -101,9 +101,9 @@ public final class ClassInnerStuffCache {
public PsiClass[] getInnerClasses() { public PsiClass[] getInnerClasses() {
return copy(CachedValuesManager.getCachedValue( return copy(CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
calcInnerClasses(), calcInnerClasses(),
myModificationTracker myModificationTrackers
) )
)); ));
} }
@@ -116,9 +116,9 @@ public final class ClassInnerStuffCache {
else { else {
return CachedValuesManager.getCachedValue( return CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
getFieldsMap(), getFieldsMap(),
myModificationTracker myModificationTrackers
) )
).get(name); ).get(name);
} }
@@ -132,9 +132,9 @@ public final class ClassInnerStuffCache {
else { else {
return copy(notNull(CachedValuesManager.getCachedValue( return copy(notNull(CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
getMethodsMap(), getMethodsMap(),
myModificationTracker myModificationTrackers
) )
).get(name), PsiMethod.EMPTY_ARRAY)); ).get(name), PsiMethod.EMPTY_ARRAY));
} }
@@ -148,9 +148,9 @@ public final class ClassInnerStuffCache {
else { else {
return CachedValuesManager.getCachedValue( return CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
getInnerClassesMap(), getInnerClassesMap(),
myModificationTracker myModificationTrackers
) )
).get(name); ).get(name);
} }
@@ -160,9 +160,9 @@ public final class ClassInnerStuffCache {
private PsiMethod getValuesMethod() { private PsiMethod getValuesMethod() {
return isEnum() ? internMember(CachedValuesManager.getCachedValue( return isEnum() ? internMember(CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
makeValuesMethod(myClass), makeValuesMethod(myClass),
myModificationTracker myModificationTrackers
) )
)) : null; )) : null;
} }
@@ -171,9 +171,9 @@ public final class ClassInnerStuffCache {
private PsiMethod getValueOfMethod() { private PsiMethod getValueOfMethod() {
return isEnum() ? internMember(CachedValuesManager.getCachedValue( return isEnum() ? internMember(CachedValuesManager.getCachedValue(
myClass, myClass,
() -> CachedValueProvider.Result.createSingleDependency( () -> CachedValueProvider.Result.create(
makeValueOfMethod(myClass), makeValueOfMethod(myClass),
myModificationTracker myModificationTrackers
) )
)) : null; )) : null;
} }
@@ -201,7 +201,11 @@ public final class ClassInnerStuffCache {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <T extends PsiMember> T internMember(T m) { private <T extends PsiMember> T internMember(T m) {
if (m == null) return null; if (m == null) return null;
long modCount = myModificationTracker.getModificationCount(); long modCount = 0;
for (ModificationTracker tracker : myModificationTrackers) {
modCount += tracker.getModificationCount();
}
synchronized (myInterner) { synchronized (myInterner) {
Pair<Long, Interner<PsiMember>> pair = myInterner.get(); Pair<Long, Interner<PsiMember>> pair = myInterner.get();
if (pair == null || pair.first.longValue() != modCount) { if (pair == null || pair.first.longValue() != modCount) {
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.light.classes.symbol.classes
import com.intellij.navigation.ItemPresentation import com.intellij.navigation.ItemPresentation
import com.intellij.navigation.ItemPresentationProviders import com.intellij.navigation.ItemPresentationProviders
import com.intellij.openapi.util.ModificationTracker
import com.intellij.openapi.util.Pair import com.intellij.openapi.util.Pair
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.* import com.intellij.psi.*
@@ -37,10 +38,14 @@ abstract class SymbolLightClassBase protected constructor(val ktModule: KtModule
ClassInnerStuffCache( ClassInnerStuffCache(
/* aClass = */ this, /* aClass = */ this,
/* generateEnumMethods = */ false, /* generateEnumMethods = */ false,
/* modificationTracker = */ project.createProjectWideOutOfBlockModificationTracker(), /* modificationTrackers = */ modificationTrackerForClassInnerStuff(),
) )
} }
protected open fun modificationTrackerForClassInnerStuff(): List<ModificationTracker> {
return listOf(project.createProjectWideOutOfBlockModificationTracker())
}
override fun getFields(): Array<PsiField> = myInnersCache.fields override fun getFields(): Array<PsiField> = myInnersCache.fields
override fun getMethods(): Array<PsiMethod> = myInnersCache.methods override fun getMethods(): Array<PsiMethod> = myInnersCache.methods
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.light.classes.symbol.classes package org.jetbrains.kotlin.light.classes.symbol.classes
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.InheritanceImplUtil import com.intellij.psi.impl.InheritanceImplUtil
import com.intellij.psi.impl.PsiClassImplUtil import com.intellij.psi.impl.PsiClassImplUtil
@@ -56,6 +57,10 @@ abstract class SymbolLightClassForClassLike<SType : KtClassOrObjectSymbol> prote
manager = manager, manager = manager,
) )
override fun modificationTrackerForClassInnerStuff(): List<ModificationTracker> {
return classOrObjectDeclaration?.modificationTrackerForClassInnerStuff() ?: super.modificationTrackerForClassInnerStuff()
}
override val kotlinOrigin: KtClassOrObject? get() = classOrObjectDeclaration override val kotlinOrigin: KtClassOrObject? get() = classOrObjectDeclaration
internal inline fun <T> withClassOrObjectSymbol(crossinline action: KtAnalysisSession.(SType) -> T): T = internal inline fun <T> withClassOrObjectSymbol(crossinline action: KtAnalysisSession.(SType) -> T): T =
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.light.classes.symbol.classes package org.jetbrains.kotlin.light.classes.symbol.classes
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import com.intellij.psi.PsiModifier import com.intellij.psi.PsiModifier
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.api.types.KtTypeMappingMode import org.jetbrains.kotlin.analysis.api.types.KtTypeMappingMode
import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker
import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance
import org.jetbrains.kotlin.analysis.utils.printer.parentOfType import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
@@ -68,6 +70,16 @@ internal fun createLightClassNoCache(ktClassOrObject: KtClassOrObject, ktModule:
else -> SymbolLightClassForClassOrObject(ktClassOrObject, ktModule) else -> SymbolLightClassForClassOrObject(ktClassOrObject, ktModule)
} }
internal fun KtClassOrObject.modificationTrackerForClassInnerStuff(): List<ModificationTracker> {
val outOfBlockTracker = project.createProjectWideOutOfBlockModificationTracker()
return if (isLocal) {
val file = containingKtFile
listOf(outOfBlockTracker, ModificationTracker { file.modificationStamp })
} else {
listOf(outOfBlockTracker)
}
}
context(KtAnalysisSession) context(KtAnalysisSession)
internal fun createLightClassNoCache( internal fun createLightClassNoCache(
ktClassOrObjectSymbol: KtNamedClassOrObjectSymbol, ktClassOrObjectSymbol: KtNamedClassOrObjectSymbol,
@@ -32,8 +32,10 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.light.classes.symbol.annotations.* import org.jetbrains.kotlin.light.classes.symbol.annotations.*
import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassBase import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassBase
import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassForClassLike
import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassForInterface import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassForInterface
import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassForInterfaceDefaultImpls import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassForInterfaceDefaultImpls
import org.jetbrains.kotlin.light.classes.symbol.classes.modificationTrackerForClassInnerStuff
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
@@ -324,5 +326,11 @@ internal inline fun <reified T> Collection<T>.toArrayIfNotEmptyOrDefault(default
internal inline fun <R : PsiElement, T> R.cachedValue( internal inline fun <R : PsiElement, T> R.cachedValue(
crossinline computer: () -> T, crossinline computer: () -> T,
): T = CachedValuesManager.getCachedValue(this) { ): T = CachedValuesManager.getCachedValue(this) {
CachedValueProvider.Result.createSingleDependency(computer(), project.createProjectWideOutOfBlockModificationTracker()) val value = computer()
val specialClassTrackers = (this as? SymbolLightClassForClassLike<*>)?.classOrObjectDeclaration?.modificationTrackerForClassInnerStuff()
if (specialClassTrackers != null) {
CachedValueProvider.Result.create(value, specialClassTrackers)
} else {
CachedValueProvider.Result.createSingleDependency(value, project.createProjectWideOutOfBlockModificationTracker())
}
} }