[caches] IJPL-160878 CachedValue has unnecessary memory overhead

GitOrigin-RevId: 37c8cc57fa5eeb468aad7c51b35e8f5d3c1d8f57
This commit is contained in:
Yuriy Artamonov
2024-08-23 14:28:03 +02:00
committed by intellij-monorepo-bot
parent 8b7819ec3f
commit f2e65d6529
9 changed files with 312 additions and 107 deletions

View File

@@ -2420,6 +2420,12 @@ a:com.intellij.psi.impl.AbstractModificationTracker
- pa:isInsideCodeBlock(com.intellij.psi.PsiElement):Z - pa:isInsideCodeBlock(com.intellij.psi.PsiElement):Z
- p:processOutOfCodeBlockModification(com.intellij.psi.impl.PsiTreeChangeEventImpl):V - p:processOutOfCodeBlockModification(com.intellij.psi.impl.PsiTreeChangeEventImpl):V
- treeChanged(com.intellij.psi.impl.PsiTreeChangeEventImpl):V - treeChanged(com.intellij.psi.impl.PsiTreeChangeEventImpl):V
a:com.intellij.psi.impl.AbstractPsiCachedValue
- com.intellij.psi.impl.PsiCachedValue
- com.intellij.psi.util.CachedValue
- pf:doCompute(java.lang.Object):com.intellij.psi.util.CachedValueProvider$Result
- getValue():java.lang.Object
- f:getValueProvider():com.intellij.psi.util.CachedValueProvider
com.intellij.psi.impl.AnyPsiChangeListener com.intellij.psi.impl.AnyPsiChangeListener
- afterPsiChanged(Z):V - afterPsiChanged(Z):V
- beforePsiChanged(Z):V - beforePsiChanged(Z):V
@@ -2568,23 +2574,30 @@ c:com.intellij.psi.impl.PsiCachedValueImpl
- com.intellij.psi.impl.PsiCachedValue - com.intellij.psi.impl.PsiCachedValue
- com.intellij.psi.util.CachedValue - com.intellij.psi.util.CachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider):V - <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider):V
- p:doCompute(java.lang.Object):com.intellij.psi.util.CachedValueProvider$Result - pf:doCompute(java.lang.Object):com.intellij.psi.util.CachedValueProvider$Result
- p:getRawData():com.intellij.util.CachedValueBase$Data - p:getRawData():com.intellij.util.CachedValueBase$Data
- getValue():java.lang.Object - getValue():java.lang.Object
- getValueProvider():com.intellij.psi.util.CachedValueProvider - f:getValueProvider():com.intellij.psi.util.CachedValueProvider
- p:isTrackValue():Z
- p:setData(com.intellij.util.CachedValueBase$Data):V - p:setData(com.intellij.util.CachedValueBase$Data):V
f:com.intellij.psi.impl.PsiCachedValueImpl$Direct f:com.intellij.psi.impl.PsiCachedValueImpl$Direct
- com.intellij.psi.impl.PsiCachedValue - com.intellij.psi.impl.AbstractPsiCachedValue
- com.intellij.psi.util.CachedValue - com.intellij.psi.util.CachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider):V - <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider):V
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider,Z):V
- getValue():java.lang.Object - getValue():java.lang.Object
- getValueProvider():com.intellij.psi.util.CachedValueProvider f:com.intellij.psi.impl.PsiCachedValueImpl$DirectTracked
f:com.intellij.psi.impl.PsiCachedValueImpl$Soft - com.intellij.psi.impl.AbstractPsiCachedValue
- com.intellij.psi.impl.PsiCachedValueImpl - com.intellij.psi.util.CachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider):V
- getValue():java.lang.Object
f:com.intellij.psi.impl.PsiCachedValueImpl$Soft
- com.intellij.psi.impl.AbstractPsiCachedValue
- com.intellij.psi.util.CachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider):V
f:com.intellij.psi.impl.PsiCachedValueImpl$SoftTracked
- com.intellij.psi.impl.AbstractPsiCachedValue
- com.intellij.psi.util.CachedValue - com.intellij.psi.util.CachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider):V - <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider):V
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.CachedValueProvider,Z):V
a:com.intellij.psi.impl.PsiDocumentManagerBase a:com.intellij.psi.impl.PsiDocumentManagerBase
- com.intellij.psi.PsiDocumentManager - com.intellij.psi.PsiDocumentManager
- com.intellij.openapi.Disposable - com.intellij.openapi.Disposable
@@ -2796,11 +2809,15 @@ a:com.intellij.psi.impl.PsiParameterizedCachedValue
f:com.intellij.psi.impl.PsiParameterizedCachedValue$Direct f:com.intellij.psi.impl.PsiParameterizedCachedValue$Direct
- com.intellij.psi.impl.PsiParameterizedCachedValue - com.intellij.psi.impl.PsiParameterizedCachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.ParameterizedCachedValueProvider):V - <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.ParameterizedCachedValueProvider):V
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.ParameterizedCachedValueProvider,Z):V f:com.intellij.psi.impl.PsiParameterizedCachedValue$DirectTracked
- com.intellij.psi.impl.PsiParameterizedCachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.ParameterizedCachedValueProvider):V
f:com.intellij.psi.impl.PsiParameterizedCachedValue$Soft f:com.intellij.psi.impl.PsiParameterizedCachedValue$Soft
- com.intellij.psi.impl.PsiParameterizedCachedValue - com.intellij.psi.impl.PsiParameterizedCachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.ParameterizedCachedValueProvider):V - <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.ParameterizedCachedValueProvider):V
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.ParameterizedCachedValueProvider,Z):V f:com.intellij.psi.impl.PsiParameterizedCachedValue$SoftTracked
- com.intellij.psi.impl.PsiParameterizedCachedValue
- <init>(com.intellij.psi.PsiManager,com.intellij.psi.util.ParameterizedCachedValueProvider):V
f:com.intellij.psi.impl.PsiParserFacadeImpl f:com.intellij.psi.impl.PsiParserFacadeImpl
- com.intellij.psi.PsiParserFacade - com.intellij.psi.PsiParserFacade
- <init>(com.intellij.openapi.project.Project):V - <init>(com.intellij.openapi.project.Project):V
@@ -4370,7 +4387,7 @@ com.intellij.refactoring.rename.FragmentaryPsiReference
- a:isFragmentOnlyRename():Z - a:isFragmentOnlyRename():Z
- a:isReadOnlyFragment():Z - a:isReadOnlyFragment():Z
a:com.intellij.util.CachedValueBase a:com.intellij.util.CachedValueBase
- p:<init>(Z):V - <init>():V
- clear():V - clear():V
- pa:doCompute(java.lang.Object):com.intellij.psi.util.CachedValueProvider$Result - pa:doCompute(java.lang.Object):com.intellij.psi.util.CachedValueProvider$Result
- p:getIdempotenceFailureContext():java.lang.String - p:getIdempotenceFailureContext():java.lang.String
@@ -4382,15 +4399,16 @@ a:com.intellij.util.CachedValueBase
- hasUpToDateValue():Z - hasUpToDateValue():Z
- p:isDependencyOutOfDate(java.lang.Object,J):Z - p:isDependencyOutOfDate(java.lang.Object,J):Z
- a:isFromMyProject(com.intellij.openapi.project.Project):Z - a:isFromMyProject(com.intellij.openapi.project.Project):Z
- pa:isTrackValue():Z
- p:isUpToDate(com.intellij.util.CachedValueBase$Data):Z - p:isUpToDate(com.intellij.util.CachedValueBase$Data):Z
- p:normalizeDependencies(java.lang.Object,java.lang.Object[]):java.lang.Object[] - p:normalizeDependencies(java.lang.Object,java.lang.Object[]):java.lang.Object[]
- pa:setData(com.intellij.util.CachedValueBase$Data):V - pa:setData(com.intellij.util.CachedValueBase$Data):V
- setValue(com.intellij.psi.util.CachedValueProvider$Result):java.lang.Object - setValue(com.intellij.psi.util.CachedValueProvider$Result):java.lang.Object
f:com.intellij.util.CachedValueBase$Data Fa:com.intellij.util.CachedValueBase$Data
- com.intellij.openapi.util.Getter - com.intellij.openapi.util.Getter
- get():java.lang.Object - get():java.lang.Object
- getDependencies():java.lang.Object[] - a:getDependencies():java.lang.Object[]
- getTimeStamps():J[] - a:getTimeStamps():J[]
- getValue():java.lang.Object - getValue():java.lang.Object
c:com.intellij.util.CachedValueImpl c:com.intellij.util.CachedValueImpl
- com.intellij.util.CachedValueBase - com.intellij.util.CachedValueBase
@@ -4401,6 +4419,7 @@ c:com.intellij.util.CachedValueImpl
- getValue():java.lang.Object - getValue():java.lang.Object
- getValueProvider():com.intellij.psi.util.CachedValueProvider - getValueProvider():com.intellij.psi.util.CachedValueProvider
- isFromMyProject(com.intellij.openapi.project.Project):Z - isFromMyProject(com.intellij.openapi.project.Project):Z
- p:isTrackValue():Z
- p:setData(com.intellij.util.CachedValueBase$Data):V - p:setData(com.intellij.util.CachedValueBase$Data):V
f:com.intellij.util.CachedValuesManagerImpl f:com.intellij.util.CachedValuesManagerImpl
- com.intellij.psi.util.CachedValuesManager - com.intellij.psi.util.CachedValuesManager

View File

@@ -18,15 +18,11 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Avdeev
*/
public abstract class PsiCachedValue<T> extends CachedValueBase<T> { public abstract class PsiCachedValue<T> extends CachedValueBase<T> {
private static final Key<?> PSI_MOD_COUNT_OPTIMIZATION = Key.create("PSI_MOD_COUNT_OPTIMIZATION"); private static final Key<?> PSI_MOD_COUNT_OPTIMIZATION = Key.create("PSI_MOD_COUNT_OPTIMIZATION");
private final PsiManager myManager; private final PsiManager myManager;
PsiCachedValue(@NotNull PsiManager manager, boolean trackValue) { PsiCachedValue(@NotNull PsiManager manager) {
super(trackValue);
myManager = manager; myManager = manager;
} }
@@ -61,7 +57,8 @@ public abstract class PsiCachedValue<T> extends CachedValueBase<T> {
return false; return false;
} }
// injected files are physical but can sometimes (look at you, completion) // injected files are physical but can sometimes (look at you, completion)
// be inexplicably injected into non-physical element, in which case PSI_MODIFICATION_COUNT doesn't change and thus can't be relied upon // be inexplicably injected into a non-physical element,
// in this case PSI_MODIFICATION_COUNT doesn't change and thus can't be relied upon
InjectedLanguageManager manager = InjectedLanguageManager.getInstance(myManager.getProject()); InjectedLanguageManager manager = InjectedLanguageManager.getInstance(myManager.getProject());
PsiFile topLevelFile = manager.getTopLevelFile(dependency); PsiFile topLevelFile = manager.getTopLevelFile(dependency);
return topLevelFile != null && topLevelFile.isPhysical(); return topLevelFile != null && topLevelFile.isPhysical();

View File

@@ -6,19 +6,35 @@ import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValueProvider
import java.lang.ref.SoftReference import java.lang.ref.SoftReference
sealed class AbstractPsiCachedValue<T>(
manager: PsiManager,
private val myProvider: CachedValueProvider<T>
) : PsiCachedValue<T>(manager), CachedValue<T> {
override fun getValue(): T? {
return getValueWithLock<Any>(null)
}
final override fun getValueProvider(): CachedValueProvider<T> = myProvider
final override fun <P> doCompute(param: P): CachedValueProvider.Result<T>? {
return myProvider.compute()
}
}
open class PsiCachedValueImpl<T> open class PsiCachedValueImpl<T>
@Deprecated(message = "Use PsiCachedValueImpl.Soft") @Deprecated(message = "Use PsiCachedValueImpl.Soft")
internal constructor( internal constructor(
manager: PsiManager, manager: PsiManager,
private val myProvider: CachedValueProvider<T>, private val myProvider: CachedValueProvider<T>,
trackValue: Boolean private val trackValue: Boolean
) : PsiCachedValue<T>(manager, trackValue), CachedValue<T> { ) : PsiCachedValue<T>(manager), CachedValue<T> {
@Deprecated(message = "Use PsiCachedValueImpl.Soft")
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@Deprecated(message = "Use PsiCachedValueImpl.Soft")
constructor(manager: PsiManager, provider: CachedValueProvider<T>) : this(manager, provider, false) constructor(manager: PsiManager, provider: CachedValueProvider<T>) : this(manager, provider, false)
override fun isTrackValue(): Boolean = trackValue
@Volatile @Volatile
private var data: SoftReference<Data<T>>? = null private var data: SoftReference<Data<T>>? = null
@@ -34,49 +50,71 @@ internal constructor(
return getValueWithLock<Any>(null) return getValueWithLock<Any>(null)
} }
override fun getValueProvider(): CachedValueProvider<T> = myProvider final override fun getValueProvider(): CachedValueProvider<T> = myProvider
override fun <P> doCompute(param: P): CachedValueProvider.Result<T>? { final override fun <P> doCompute(param: P): CachedValueProvider.Result<T>? {
return myProvider.compute() return myProvider.compute()
} }
@Suppress("DEPRECATION")
class Soft<T>( class Soft<T>(
manager: PsiManager, manager: PsiManager,
myProvider: CachedValueProvider<T>, myProvider: CachedValueProvider<T>,
trackValue: Boolean ) : AbstractPsiCachedValue<T>(manager, myProvider), CachedValue<T> {
) : PsiCachedValueImpl<T>(manager, myProvider, trackValue), CachedValue<T> { @Volatile
private var data: SoftReference<Data<T>>? = null
constructor(manager: PsiManager, provider: CachedValueProvider<T>) : this(manager, provider, false) override fun isTrackValue(): Boolean = false
override fun getRawData(): Data<T>? {
return com.intellij.reference.SoftReference.dereference(data)
}
override fun setData(data: Data<T>?) {
this.data = data?.let { SoftReference(data) }
}
override fun toString(): String { override fun toString(): String {
return "PsiCachedValue.Soft()" return "PsiCachedValue.Soft()"
} }
} }
class SoftTracked<T>(
manager: PsiManager,
myProvider: CachedValueProvider<T>,
) : AbstractPsiCachedValue<T>(manager, myProvider), CachedValue<T> {
@Volatile
private var data: SoftReference<Data<T>>? = null
override fun isTrackValue(): Boolean = true
override fun getRawData(): Data<T>? {
return com.intellij.reference.SoftReference.dereference(data)
}
override fun setData(data: Data<T>?) {
this.data = data?.let { SoftReference(data) }
}
override fun toString(): String {
return "PsiCachedValue.SoftTracked()"
}
}
class Direct<T>( class Direct<T>(
manager: PsiManager, manager: PsiManager,
private val myProvider: CachedValueProvider<T>, myProvider: CachedValueProvider<T>,
trackValue: Boolean ) : AbstractPsiCachedValue<T>(manager, myProvider), CachedValue<T> {
) : PsiCachedValue<T>(manager, trackValue), CachedValue<T> {
constructor(manager: PsiManager, provider: CachedValueProvider<T>) : this(manager, provider, false)
@Volatile @Volatile
private var data: Data<T>? = null private var data: Data<T>? = null
override fun isTrackValue(): Boolean = false
override fun getRawData(): Data<T>? = data override fun getRawData(): Data<T>? = data
override fun setData(data: Data<T>?) { override fun setData(data: Data<T>?) {
this.data = data this.data = data
} }
override fun getValueProvider(): CachedValueProvider<T> = myProvider
override fun <P> doCompute(param: P): CachedValueProvider.Result<T>? {
return myProvider.compute()
}
override fun getValue(): T? { override fun getValue(): T? {
return getValueWithLock<Any>(null) return getValueWithLock<Any>(null)
} }
@@ -85,4 +123,28 @@ internal constructor(
return "PsiCachedValue.Direct()" return "PsiCachedValue.Direct()"
} }
} }
class DirectTracked<T>(
manager: PsiManager,
myProvider: CachedValueProvider<T>,
) : AbstractPsiCachedValue<T>(manager, myProvider), CachedValue<T> {
@Volatile
private var data: Data<T>? = null
override fun isTrackValue(): Boolean = true
override fun getRawData(): Data<T>? = data
override fun setData(data: Data<T>?) {
this.data = data
}
override fun getValue(): T? {
return getValueWithLock<Any>(null)
}
override fun toString(): String {
return "PsiCachedValue.DirectTracked()"
}
}
} }

View File

@@ -29,7 +29,11 @@ public final class PsiCachedValuesFactory implements CachedValuesFactory {
@Override @Override
public @NotNull <T> CachedValue<T> createCachedValue(@NotNull CachedValueProvider<T> provider, boolean trackValue) { public @NotNull <T> CachedValue<T> createCachedValue(@NotNull CachedValueProvider<T> provider, boolean trackValue) {
return new PsiCachedValueImpl.Soft<>(myManager, provider, trackValue); if (trackValue) {
return new PsiCachedValueImpl.SoftTracked<>(myManager, provider);
}
return new PsiCachedValueImpl.Soft<>(myManager, provider);
} }
@Override @Override
@@ -40,7 +44,10 @@ public final class PsiCachedValuesFactory implements CachedValuesFactory {
&& userDataHolder instanceof PsiElement && userDataHolder instanceof PsiElement
&& !(userDataHolder instanceof StubBasedPsiElement) // StubBasedPsiElement cache may outlive the loaded content of a file && !(userDataHolder instanceof StubBasedPsiElement) // StubBasedPsiElement cache may outlive the loaded content of a file
&& !(userDataHolder instanceof PsiFile)) { && !(userDataHolder instanceof PsiFile)) {
return new PsiCachedValueImpl.Direct<>(myManager, provider, trackValue);
return trackValue ?
new PsiCachedValueImpl.DirectTracked<>(myManager, provider) :
new PsiCachedValueImpl.Direct<>(myManager, provider);
} }
return createCachedValue(provider, trackValue); return createCachedValue(provider, trackValue);
@@ -49,7 +56,9 @@ public final class PsiCachedValuesFactory implements CachedValuesFactory {
@Override @Override
public @NotNull <T, P> ParameterizedCachedValue<T, P> createParameterizedCachedValue(@NotNull ParameterizedCachedValueProvider<T, P> provider, public @NotNull <T, P> ParameterizedCachedValue<T, P> createParameterizedCachedValue(@NotNull ParameterizedCachedValueProvider<T, P> provider,
boolean trackValue) { boolean trackValue) {
return new PsiParameterizedCachedValue.Soft<>(myManager, provider, trackValue); return trackValue ?
new PsiParameterizedCachedValue.SoftTracked<>(myManager, provider) :
new PsiParameterizedCachedValue.Soft<>(myManager, provider);
} }
@Override @Override
@@ -60,7 +69,9 @@ public final class PsiCachedValuesFactory implements CachedValuesFactory {
&& userDataHolder instanceof PsiElement && userDataHolder instanceof PsiElement
&& !(userDataHolder instanceof StubBasedPsiElement) // StubBasedPsiElement cache may outlive the loaded content of a file && !(userDataHolder instanceof StubBasedPsiElement) // StubBasedPsiElement cache may outlive the loaded content of a file
&& !(userDataHolder instanceof PsiFile)) { && !(userDataHolder instanceof PsiFile)) {
return new PsiParameterizedCachedValue.Direct<>(myManager, provider, trackValue); return trackValue ?
new PsiParameterizedCachedValue.DirectTracked<>(myManager, provider) :
new PsiParameterizedCachedValue.Direct<>(myManager, provider);
} }
return createParameterizedCachedValue(provider, trackValue); return createParameterizedCachedValue(provider, trackValue);

View File

@@ -7,11 +7,10 @@ import com.intellij.psi.util.ParameterizedCachedValue
import com.intellij.psi.util.ParameterizedCachedValueProvider import com.intellij.psi.util.ParameterizedCachedValueProvider
import java.lang.ref.SoftReference import java.lang.ref.SoftReference
abstract class PsiParameterizedCachedValue<T, P> internal constructor( sealed class PsiParameterizedCachedValue<T, P> protected constructor(
manager: PsiManager, manager: PsiManager,
private val myProvider: ParameterizedCachedValueProvider<T, P>, private val myProvider: ParameterizedCachedValueProvider<T, P>,
trackValue: Boolean ) : PsiCachedValue<T>(manager), ParameterizedCachedValue<T, P> {
) : PsiCachedValue<T>(manager, trackValue), ParameterizedCachedValue<T, P> {
override fun getValue(param: P): T? { override fun getValue(param: P): T? {
return getValueWithLock(param) return getValueWithLock(param)
@@ -26,14 +25,12 @@ abstract class PsiParameterizedCachedValue<T, P> internal constructor(
class Soft<T, P>( class Soft<T, P>(
manager: PsiManager, manager: PsiManager,
myProvider: ParameterizedCachedValueProvider<T, P>, myProvider: ParameterizedCachedValueProvider<T, P>,
trackValue: Boolean ) : PsiParameterizedCachedValue<T, P>(manager, myProvider) {
) : PsiParameterizedCachedValue<T, P>(manager, myProvider, trackValue) {
constructor(manager: PsiManager, provider: ParameterizedCachedValueProvider<T, P>) : this(manager, provider, false)
@Volatile @Volatile
private var data: SoftReference<Data<T>>? = null private var data: SoftReference<Data<T>>? = null
override fun isTrackValue(): Boolean = false
override fun getRawData(): Data<T>? { override fun getRawData(): Data<T>? {
return com.intellij.reference.SoftReference.dereference(data) return com.intellij.reference.SoftReference.dereference(data)
} }
@@ -47,17 +44,37 @@ abstract class PsiParameterizedCachedValue<T, P> internal constructor(
} }
} }
class SoftTracked<T, P>(
manager: PsiManager,
myProvider: ParameterizedCachedValueProvider<T, P>,
) : PsiParameterizedCachedValue<T, P>(manager, myProvider) {
@Volatile
private var data: SoftReference<Data<T>>? = null
override fun isTrackValue(): Boolean = true
override fun getRawData(): Data<T>? {
return com.intellij.reference.SoftReference.dereference(data)
}
override fun setData(data: Data<T>?) {
this.data = data?.let { SoftReference(data) }
}
override fun toString(): String {
return "PsiParameterizedCachedValue.SoftTracked()"
}
}
class Direct<T, P>( class Direct<T, P>(
manager: PsiManager, manager: PsiManager,
myProvider: ParameterizedCachedValueProvider<T, P>, myProvider: ParameterizedCachedValueProvider<T, P>,
trackValue: Boolean ) : PsiParameterizedCachedValue<T, P>(manager, myProvider) {
) : PsiParameterizedCachedValue<T, P>(manager, myProvider, trackValue) {
constructor(manager: PsiManager, provider: ParameterizedCachedValueProvider<T, P>) : this(manager, provider, false)
@Volatile @Volatile
private var data: Data<T>? = null private var data: Data<T>? = null
override fun isTrackValue(): Boolean = false
override fun getRawData(): Data<T>? = data override fun getRawData(): Data<T>? = data
override fun setData(data: Data<T>?) { override fun setData(data: Data<T>?) {
@@ -68,4 +85,24 @@ abstract class PsiParameterizedCachedValue<T, P> internal constructor(
return "PsiParameterizedCachedValue.Direct()" return "PsiParameterizedCachedValue.Direct()"
} }
} }
class DirectTracked<T, P>(
manager: PsiManager,
myProvider: ParameterizedCachedValueProvider<T, P>,
) : PsiParameterizedCachedValue<T, P>(manager, myProvider) {
@Volatile
private var data: Data<T>? = null
override fun isTrackValue(): Boolean = true
override fun getRawData(): Data<T>? = data
override fun setData(data: Data<T>?) {
this.data = data
}
override fun toString(): String {
return "PsiParameterizedCachedValue.DirectTracked()"
}
}
} }

View File

@@ -10,21 +10,15 @@ import com.intellij.psi.util.CachedValueProfiler;
import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.PsiModificationTracker; import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.containers.NotNullList; import com.intellij.util.containers.NotNullList;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.util.List; import java.util.List;
/**
* @author Dmitry Avdeev
*/
public abstract class CachedValueBase<T> { public abstract class CachedValueBase<T> {
private final boolean myTrackValue; protected abstract boolean isTrackValue();
protected CachedValueBase(boolean trackValue) {
myTrackValue = trackValue;
}
protected abstract @Nullable Data<T> getRawData(); protected abstract @Nullable Data<T> getRawData();
@@ -44,7 +38,7 @@ public abstract class CachedValueBase<T> {
tracker = null; tracker = null;
} }
if (result == null) { if (result == null) {
return new Data<>(null, ArrayUtilRt.EMPTY_OBJECT_ARRAY, ArrayUtil.EMPTY_LONG_ARRAY, null); return new DefaultData<>(null, ArrayUtilRt.EMPTY_OBJECT_ARRAY, ArrayUtil.EMPTY_LONG_ARRAY);
} }
T value = result.getValue(); T value = result.getValue();
Object[] inferredDependencies = normalizeDependencies(value, result.getDependencyItems()); Object[] inferredDependencies = normalizeDependencies(value, result.getDependencyItems());
@@ -52,7 +46,17 @@ public abstract class CachedValueBase<T> {
for (int i = 0; i < inferredDependencies.length; i++) { for (int i = 0; i < inferredDependencies.length; i++) {
inferredTimeStamps[i] = getTimeStamp(inferredDependencies[i]); inferredTimeStamps[i] = getTimeStamp(inferredDependencies[i]);
} }
return new Data<>(value, inferredDependencies, inferredTimeStamps, tracker);
if (tracker != null) {
return new TrackedData<>(value, inferredDependencies, inferredTimeStamps, tracker);
}
// more lightweight storage for simple caches dependent on PSI
if (inferredDependencies.length == 1 && inferredDependencies[0] == PsiModificationTracker.MODIFICATION_COUNT) {
return new PsiDependentData<>(value, inferredTimeStamps[0]);
}
return new DefaultData<>(value, inferredDependencies, inferredTimeStamps);
} }
private synchronized @Nullable Data<T> cacheOrGetData(@Nullable Data<T> expected, @Nullable Data<T> updatedValue) { private synchronized @Nullable Data<T> cacheOrGetData(@Nullable Data<T> expected, @Nullable Data<T> updatedValue) {
@@ -70,9 +74,9 @@ public abstract class CachedValueBase<T> {
} }
protected Object @NotNull [] normalizeDependencies(@Nullable T value, Object @NotNull [] dependencyItems) { protected Object @NotNull [] normalizeDependencies(@Nullable T value, Object @NotNull [] dependencyItems) {
List<Object> flattened = new NotNullList<>(dependencyItems.length+1); List<Object> flattened = new NotNullList<>(dependencyItems.length + 1);
collectDependencies(dependencyItems, flattened); collectDependencies(dependencyItems, flattened);
if (myTrackValue && value != null) { if (isTrackValue() && value != null) {
if (value instanceof Object[]) { if (value instanceof Object[]) {
collectDependencies((Object[])value, flattened); collectDependencies((Object[])value, flattened);
} }
@@ -100,16 +104,24 @@ public abstract class CachedValueBase<T> {
if (isUpToDate(data)) { if (isUpToDate(data)) {
return true; return true;
} }
if (data.trackingInfo != null) { if (data instanceof TrackedData) {
data.trackingInfo.onValueInvalidated(); CachedValueProfiler.ValueTracker trackingInfo = ((TrackedData<T>)data).trackingInfo;
if (trackingInfo != null) {
trackingInfo.onValueInvalidated();
}
} }
return false; return false;
} }
protected boolean isUpToDate(@NotNull Data<T> data) { protected boolean isUpToDate(@NotNull Data<T> data) {
for (int i = 0; i < data.myDependencies.length; i++) { if (data instanceof CachedValueBase.PsiDependentData) {
Object dependency = data.myDependencies[i]; // do not create an unnecessary long[] array
if (isDependencyOutOfDate(dependency, data.myTimeStamps[i])) return false; return !isDependencyOutOfDate(PSI_MODIFICATION_DEPENDENCIES[0], ((PsiDependentData<T>)data).getTimeStamp());
}
for (int i = 0; i < data.getDependencies().length; i++) {
Object dependency = data.getDependencies()[i];
if (isDependencyOutOfDate(dependency, data.getTimeStamps()[i])) return false;
} }
return true; return true;
@@ -142,14 +154,14 @@ public abstract class CachedValueBase<T> {
if (dependency instanceof ModificationTracker) { if (dependency instanceof ModificationTracker) {
return ((ModificationTracker)dependency).getModificationCount(); return ((ModificationTracker)dependency).getModificationCount();
} }
else if (dependency instanceof Reference){ else if (dependency instanceof Reference) {
Object original = ((Reference<?>)dependency).get(); Object original = ((Reference<?>)dependency).get();
if(original == null) return -1; if (original == null) return -1;
return getTimeStamp(original); return getTimeStamp(original);
} }
else if (dependency instanceof Ref) { else if (dependency instanceof Ref) {
Object original = ((Ref<?>)dependency).get(); Object original = ((Ref<?>)dependency).get();
if(original == null) return -1; if (original == null) return -1;
return getTimeStamp(original); return getTimeStamp(original);
} }
else if (dependency instanceof Document) { else if (dependency instanceof Document) {
@@ -175,36 +187,48 @@ public abstract class CachedValueBase<T> {
public abstract @NotNull Object getValueProvider(); public abstract @NotNull Object getValueProvider();
private static final Object[] PSI_MODIFICATION_DEPENDENCIES = new Object[] { PsiModificationTracker.MODIFICATION_COUNT }; private static final Object[] PSI_MODIFICATION_DEPENDENCIES = new Object[]{PsiModificationTracker.MODIFICATION_COUNT};
public static final class Data<T> implements Getter<T> { @ApiStatus.NonExtendable
public static abstract class Data<T> implements Getter<T> {
private final T myValue; private final T myValue;
private final Object @NotNull [] myDependencies;
private final long @NotNull [] myTimeStamps;
final @Nullable CachedValueProfiler.ValueTracker trackingInfo;
Data(T value, protected Data(T value) {
Object @NotNull [] dependencies,
long @NotNull [] timeStamps,
@Nullable CachedValueProfiler.ValueTracker trackingInfo) {
myValue = value; myValue = value;
if (dependencies.length == 1 && dependencies[0] == PsiModificationTracker.MODIFICATION_COUNT) {
// there is no sense in storing hundreds of new arrays of [PsiModificationTracker.MODIFICATION_COUNT]
myDependencies = PSI_MODIFICATION_DEPENDENCIES;
}
else {
myDependencies = dependencies;
}
myTimeStamps = timeStamps;
this.trackingInfo = trackingInfo;
} }
public abstract Object @NotNull [] getDependencies();
public abstract long @NotNull [] getTimeStamps();
@Override
public T get() {
return getValue();
}
public T getValue() {
return myValue;
}
}
@ApiStatus.Internal
protected static class DefaultData<T> extends Data<T> implements Getter<T> {
private final Object @NotNull [] myDependencies;
private final long @NotNull [] myTimeStamps;
protected DefaultData(T value, Object @NotNull [] dependencies, long @NotNull [] timeStamps) {
super(value);
myDependencies = dependencies;
myTimeStamps = timeStamps;
}
@Override
public Object @NotNull [] getDependencies() { public Object @NotNull [] getDependencies() {
return myDependencies; return myDependencies;
} }
@Override
public long @NotNull [] getTimeStamps() { public long @NotNull [] getTimeStamps() {
return myTimeStamps; return myTimeStamps;
} }
@@ -213,12 +237,51 @@ public abstract class CachedValueBase<T> {
public T get() { public T get() {
return getValue(); return getValue();
} }
}
// only depends on PsiModificationTracker.MODIFICATION_COUNT, less memory to hold value needed
private static final class PsiDependentData<T> extends Data<T> {
private final long myPsiTimeStamp;
PsiDependentData(T value, long psiTimeStamp) {
super(value);
myPsiTimeStamp = psiTimeStamp;
}
private long getTimeStamp() {
return myPsiTimeStamp;
}
@Override
public Object @NotNull [] getDependencies() {
return PSI_MODIFICATION_DEPENDENCIES;
}
@Override
public long @NotNull [] getTimeStamps() {
return new long[] {myPsiTimeStamp};
}
}
// used only with cached value profiler enabled,
// trackingInfo is not added to Data to avoid additional memory overhead in production
private static final class TrackedData<T> extends DefaultData<T> {
final @Nullable CachedValueProfiler.ValueTracker trackingInfo;
TrackedData(T value,
Object @NotNull [] dependencies,
long @NotNull [] timeStamps,
@Nullable CachedValueProfiler.ValueTracker trackingInfo) {
super(value, dependencies, timeStamps);
this.trackingInfo = trackingInfo;
}
@Override
public T getValue() { public T getValue() {
if (trackingInfo != null) { if (trackingInfo != null) {
trackingInfo.onValueUsed(); trackingInfo.onValueUsed();
} }
return myValue; return super.getValue();
} }
} }
@@ -243,12 +306,16 @@ public abstract class CachedValueBase<T> {
Data<T> alreadyComputed = getRawData(); Data<T> alreadyComputed = getRawData();
boolean reuse = alreadyComputed != null && checkUpToDate(alreadyComputed); boolean reuse = alreadyComputed != null && checkUpToDate(alreadyComputed);
if (reuse) { if (reuse) {
IdempotenceChecker.checkEquivalence(alreadyComputed, data, getValueProvider().getClass(), calcData, this::getIdempotenceFailureContext); IdempotenceChecker.checkEquivalence(alreadyComputed, data, getValueProvider().getClass(), calcData,
this::getIdempotenceFailureContext);
} }
Data<T> toReturn = cacheOrGetData(alreadyComputed, reuse ? null : data); Data<T> toReturn = cacheOrGetData(alreadyComputed, reuse ? null : data);
if (toReturn != null) { if (toReturn != null) {
if (data != toReturn && data.trackingInfo != null) { if (data != toReturn && data instanceof TrackedData) {
data.trackingInfo.onValueRejected(); CachedValueProfiler.ValueTracker trackingInfo = ((TrackedData<T>)data).trackingInfo;
if (trackingInfo != null) {
trackingInfo.onValueRejected();
}
} }
return toReturn.getValue(); return toReturn.getValue();
} }

View File

@@ -13,6 +13,7 @@ import static com.intellij.reference.SoftReference.dereference;
public class CachedValueImpl<T> extends CachedValueBase<T> implements CachedValue<T> { public class CachedValueImpl<T> extends CachedValueBase<T> implements CachedValue<T> {
private final CachedValueProvider<T> myProvider; private final CachedValueProvider<T> myProvider;
private final boolean myTrackValue;
private volatile SoftReference<Data<T>> myData; private volatile SoftReference<Data<T>> myData;
public CachedValueImpl(@NotNull CachedValueProvider<T> provider) { public CachedValueImpl(@NotNull CachedValueProvider<T> provider) {
@@ -20,8 +21,13 @@ public class CachedValueImpl<T> extends CachedValueBase<T> implements CachedValu
} }
CachedValueImpl(@NotNull CachedValueProvider<T> provider, boolean trackValue) { CachedValueImpl(@NotNull CachedValueProvider<T> provider, boolean trackValue) {
super(trackValue);
myProvider = provider; myProvider = provider;
myTrackValue = trackValue;
}
@Override
protected boolean isTrackValue() {
return myTrackValue;
} }
@Override @Override

View File

@@ -17,15 +17,21 @@ public final class ParameterizedCachedValueImpl<T,P> extends CachedValueBase<T>
private final @NotNull Project myProject; private final @NotNull Project myProject;
private final ParameterizedCachedValueProvider<T,P> myProvider; private final ParameterizedCachedValueProvider<T,P> myProvider;
private volatile SoftReference<Data<T>> myData; private volatile SoftReference<Data<T>> myData;
private final boolean myTrackValue;
ParameterizedCachedValueImpl(@NotNull Project project, ParameterizedCachedValueImpl(@NotNull Project project,
@NotNull ParameterizedCachedValueProvider<T, P> provider, @NotNull ParameterizedCachedValueProvider<T, P> provider,
boolean trackValue) { boolean trackValue) {
super(trackValue); myTrackValue = trackValue;
myProject = project; myProject = project;
myProvider = provider; myProvider = provider;
} }
@Override
protected boolean isTrackValue() {
return myTrackValue;
}
@Override @Override
protected @Nullable Data<T> getRawData() { protected @Nullable Data<T> getRawData() {
return dereference(myData); return dereference(myData);

View File

@@ -29,6 +29,7 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager; import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.AbstractPsiCachedValue;
import com.intellij.psi.impl.FakePsiElement; import com.intellij.psi.impl.FakePsiElement;
import com.intellij.psi.impl.PsiCachedValueImpl; import com.intellij.psi.impl.PsiCachedValueImpl;
import com.intellij.psi.impl.source.html.dtd.HtmlSymbolDeclaration; import com.intellij.psi.impl.source.html.dtd.HtmlSymbolDeclaration;
@@ -70,8 +71,7 @@ public class RngElementDescriptor implements XmlElementDescriptor {
private final DElementPattern myElementPattern; private final DElementPattern myElementPattern;
protected final RngNsDescriptor myNsDescriptor; protected final RngNsDescriptor myNsDescriptor;
private final PsiCachedValueImpl<PsiElement> myCachedElement; private final AbstractPsiCachedValue<PsiElement> myCachedElement;
RngElementDescriptor(RngNsDescriptor nsDescriptor, DElementPattern pattern) { RngElementDescriptor(RngNsDescriptor nsDescriptor, DElementPattern pattern) {
myNsDescriptor = nsDescriptor; myNsDescriptor = nsDescriptor;