mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
[caches] IJPL-160878 CachedValue has unnecessary memory overhead
GitOrigin-RevId: 37c8cc57fa5eeb468aad7c51b35e8f5d3c1d8f57
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8b7819ec3f
commit
f2e65d6529
@@ -2420,6 +2420,12 @@ a:com.intellij.psi.impl.AbstractModificationTracker
|
||||
- pa:isInsideCodeBlock(com.intellij.psi.PsiElement):Z
|
||||
- p:processOutOfCodeBlockModification(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
|
||||
- afterPsiChanged(Z):V
|
||||
- beforePsiChanged(Z):V
|
||||
@@ -2568,23 +2574,30 @@ c:com.intellij.psi.impl.PsiCachedValueImpl
|
||||
- com.intellij.psi.impl.PsiCachedValue
|
||||
- com.intellij.psi.util.CachedValue
|
||||
- <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
|
||||
- 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
|
||||
f:com.intellij.psi.impl.PsiCachedValueImpl$Direct
|
||||
- com.intellij.psi.impl.PsiCachedValue
|
||||
- com.intellij.psi.impl.AbstractPsiCachedValue
|
||||
- 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,Z):V
|
||||
- getValue():java.lang.Object
|
||||
- getValueProvider():com.intellij.psi.util.CachedValueProvider
|
||||
f:com.intellij.psi.impl.PsiCachedValueImpl$Soft
|
||||
- com.intellij.psi.impl.PsiCachedValueImpl
|
||||
f:com.intellij.psi.impl.PsiCachedValueImpl$DirectTracked
|
||||
- com.intellij.psi.impl.AbstractPsiCachedValue
|
||||
- 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
|
||||
- <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
|
||||
- com.intellij.psi.PsiDocumentManager
|
||||
- com.intellij.openapi.Disposable
|
||||
@@ -2796,11 +2809,15 @@ a:com.intellij.psi.impl.PsiParameterizedCachedValue
|
||||
f:com.intellij.psi.impl.PsiParameterizedCachedValue$Direct
|
||||
- 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,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
|
||||
- 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,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
|
||||
- com.intellij.psi.PsiParserFacade
|
||||
- <init>(com.intellij.openapi.project.Project):V
|
||||
@@ -4370,7 +4387,7 @@ com.intellij.refactoring.rename.FragmentaryPsiReference
|
||||
- a:isFragmentOnlyRename():Z
|
||||
- a:isReadOnlyFragment():Z
|
||||
a:com.intellij.util.CachedValueBase
|
||||
- p:<init>(Z):V
|
||||
- <init>():V
|
||||
- clear():V
|
||||
- pa:doCompute(java.lang.Object):com.intellij.psi.util.CachedValueProvider$Result
|
||||
- p:getIdempotenceFailureContext():java.lang.String
|
||||
@@ -4382,15 +4399,16 @@ a:com.intellij.util.CachedValueBase
|
||||
- hasUpToDateValue():Z
|
||||
- p:isDependencyOutOfDate(java.lang.Object,J):Z
|
||||
- a:isFromMyProject(com.intellij.openapi.project.Project):Z
|
||||
- pa:isTrackValue():Z
|
||||
- p:isUpToDate(com.intellij.util.CachedValueBase$Data):Z
|
||||
- p:normalizeDependencies(java.lang.Object,java.lang.Object[]):java.lang.Object[]
|
||||
- pa:setData(com.intellij.util.CachedValueBase$Data):V
|
||||
- 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
|
||||
- get():java.lang.Object
|
||||
- getDependencies():java.lang.Object[]
|
||||
- getTimeStamps():J[]
|
||||
- a:getDependencies():java.lang.Object[]
|
||||
- a:getTimeStamps():J[]
|
||||
- getValue():java.lang.Object
|
||||
c:com.intellij.util.CachedValueImpl
|
||||
- com.intellij.util.CachedValueBase
|
||||
@@ -4401,6 +4419,7 @@ c:com.intellij.util.CachedValueImpl
|
||||
- getValue():java.lang.Object
|
||||
- getValueProvider():com.intellij.psi.util.CachedValueProvider
|
||||
- isFromMyProject(com.intellij.openapi.project.Project):Z
|
||||
- p:isTrackValue():Z
|
||||
- p:setData(com.intellij.util.CachedValueBase$Data):V
|
||||
f:com.intellij.util.CachedValuesManagerImpl
|
||||
- com.intellij.psi.util.CachedValuesManager
|
||||
|
||||
@@ -18,15 +18,11 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Avdeev
|
||||
*/
|
||||
public abstract class PsiCachedValue<T> extends CachedValueBase<T> {
|
||||
private static final Key<?> PSI_MOD_COUNT_OPTIMIZATION = Key.create("PSI_MOD_COUNT_OPTIMIZATION");
|
||||
private final PsiManager myManager;
|
||||
|
||||
PsiCachedValue(@NotNull PsiManager manager, boolean trackValue) {
|
||||
super(trackValue);
|
||||
PsiCachedValue(@NotNull PsiManager manager) {
|
||||
myManager = manager;
|
||||
}
|
||||
|
||||
@@ -61,7 +57,8 @@ public abstract class PsiCachedValue<T> extends CachedValueBase<T> {
|
||||
return false;
|
||||
}
|
||||
// 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());
|
||||
PsiFile topLevelFile = manager.getTopLevelFile(dependency);
|
||||
return topLevelFile != null && topLevelFile.isPhysical();
|
||||
|
||||
@@ -6,19 +6,35 @@ import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
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>
|
||||
|
||||
@Deprecated(message = "Use PsiCachedValueImpl.Soft")
|
||||
internal constructor(
|
||||
manager: PsiManager,
|
||||
private val myProvider: CachedValueProvider<T>,
|
||||
trackValue: Boolean
|
||||
) : PsiCachedValue<T>(manager, trackValue), CachedValue<T> {
|
||||
|
||||
@Deprecated(message = "Use PsiCachedValueImpl.Soft")
|
||||
private val trackValue: Boolean
|
||||
) : PsiCachedValue<T>(manager), CachedValue<T> {
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated(message = "Use PsiCachedValueImpl.Soft")
|
||||
constructor(manager: PsiManager, provider: CachedValueProvider<T>) : this(manager, provider, false)
|
||||
|
||||
override fun isTrackValue(): Boolean = trackValue
|
||||
|
||||
@Volatile
|
||||
private var data: SoftReference<Data<T>>? = null
|
||||
|
||||
@@ -34,49 +50,71 @@ internal constructor(
|
||||
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()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class Soft<T>(
|
||||
manager: PsiManager,
|
||||
myProvider: CachedValueProvider<T>,
|
||||
trackValue: Boolean
|
||||
) : PsiCachedValueImpl<T>(manager, myProvider, trackValue), CachedValue<T> {
|
||||
) : AbstractPsiCachedValue<T>(manager, myProvider), 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 {
|
||||
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>(
|
||||
manager: PsiManager,
|
||||
private val myProvider: CachedValueProvider<T>,
|
||||
trackValue: Boolean
|
||||
) : PsiCachedValue<T>(manager, trackValue), CachedValue<T> {
|
||||
|
||||
constructor(manager: PsiManager, provider: CachedValueProvider<T>) : this(manager, provider, false)
|
||||
|
||||
myProvider: CachedValueProvider<T>,
|
||||
) : AbstractPsiCachedValue<T>(manager, myProvider), CachedValue<T> {
|
||||
@Volatile
|
||||
private var data: Data<T>? = null
|
||||
|
||||
override fun isTrackValue(): Boolean = false
|
||||
|
||||
override fun getRawData(): Data<T>? = data
|
||||
|
||||
override fun setData(data: Data<T>?) {
|
||||
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? {
|
||||
return getValueWithLock<Any>(null)
|
||||
}
|
||||
@@ -85,4 +123,28 @@ internal constructor(
|
||||
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()"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,11 @@ public final class PsiCachedValuesFactory implements CachedValuesFactory {
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -40,7 +44,10 @@ public final class PsiCachedValuesFactory implements CachedValuesFactory {
|
||||
&& userDataHolder instanceof PsiElement
|
||||
&& !(userDataHolder instanceof StubBasedPsiElement) // StubBasedPsiElement cache may outlive the loaded content of a file
|
||||
&& !(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);
|
||||
@@ -49,7 +56,9 @@ public final class PsiCachedValuesFactory implements CachedValuesFactory {
|
||||
@Override
|
||||
public @NotNull <T, P> ParameterizedCachedValue<T, P> createParameterizedCachedValue(@NotNull ParameterizedCachedValueProvider<T, P> provider,
|
||||
boolean trackValue) {
|
||||
return new PsiParameterizedCachedValue.Soft<>(myManager, provider, trackValue);
|
||||
return trackValue ?
|
||||
new PsiParameterizedCachedValue.SoftTracked<>(myManager, provider) :
|
||||
new PsiParameterizedCachedValue.Soft<>(myManager, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,7 +69,9 @@ public final class PsiCachedValuesFactory implements CachedValuesFactory {
|
||||
&& userDataHolder instanceof PsiElement
|
||||
&& !(userDataHolder instanceof StubBasedPsiElement) // StubBasedPsiElement cache may outlive the loaded content of a file
|
||||
&& !(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);
|
||||
|
||||
@@ -7,11 +7,10 @@ import com.intellij.psi.util.ParameterizedCachedValue
|
||||
import com.intellij.psi.util.ParameterizedCachedValueProvider
|
||||
import java.lang.ref.SoftReference
|
||||
|
||||
abstract class PsiParameterizedCachedValue<T, P> internal constructor(
|
||||
sealed class PsiParameterizedCachedValue<T, P> protected constructor(
|
||||
manager: PsiManager,
|
||||
private val myProvider: ParameterizedCachedValueProvider<T, P>,
|
||||
trackValue: Boolean
|
||||
) : PsiCachedValue<T>(manager, trackValue), ParameterizedCachedValue<T, P> {
|
||||
) : PsiCachedValue<T>(manager), ParameterizedCachedValue<T, P> {
|
||||
|
||||
override fun getValue(param: P): T? {
|
||||
return getValueWithLock(param)
|
||||
@@ -26,14 +25,12 @@ abstract class PsiParameterizedCachedValue<T, P> internal constructor(
|
||||
class Soft<T, P>(
|
||||
manager: PsiManager,
|
||||
myProvider: ParameterizedCachedValueProvider<T, P>,
|
||||
trackValue: Boolean
|
||||
) : PsiParameterizedCachedValue<T, P>(manager, myProvider, trackValue) {
|
||||
|
||||
constructor(manager: PsiManager, provider: ParameterizedCachedValueProvider<T, P>) : this(manager, provider, false)
|
||||
|
||||
) : PsiParameterizedCachedValue<T, P>(manager, myProvider) {
|
||||
@Volatile
|
||||
private var data: SoftReference<Data<T>>? = null
|
||||
|
||||
override fun isTrackValue(): Boolean = false
|
||||
|
||||
override fun getRawData(): Data<T>? {
|
||||
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>(
|
||||
manager: PsiManager,
|
||||
myProvider: ParameterizedCachedValueProvider<T, P>,
|
||||
trackValue: Boolean
|
||||
) : PsiParameterizedCachedValue<T, P>(manager, myProvider, trackValue) {
|
||||
|
||||
constructor(manager: PsiManager, provider: ParameterizedCachedValueProvider<T, P>) : this(manager, provider, false)
|
||||
|
||||
) : PsiParameterizedCachedValue<T, P>(manager, myProvider) {
|
||||
@Volatile
|
||||
private var data: Data<T>? = null
|
||||
|
||||
override fun isTrackValue(): Boolean = false
|
||||
|
||||
override fun getRawData(): Data<T>? = data
|
||||
|
||||
override fun setData(data: Data<T>?) {
|
||||
@@ -68,4 +85,24 @@ abstract class PsiParameterizedCachedValue<T, P> internal constructor(
|
||||
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()"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,21 +10,15 @@ import com.intellij.psi.util.CachedValueProfiler;
|
||||
import com.intellij.psi.util.CachedValueProvider;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.util.containers.NotNullList;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dmitry Avdeev
|
||||
*/
|
||||
public abstract class CachedValueBase<T> {
|
||||
private final boolean myTrackValue;
|
||||
|
||||
protected CachedValueBase(boolean trackValue) {
|
||||
myTrackValue = trackValue;
|
||||
}
|
||||
protected abstract boolean isTrackValue();
|
||||
|
||||
protected abstract @Nullable Data<T> getRawData();
|
||||
|
||||
@@ -44,7 +38,7 @@ public abstract class CachedValueBase<T> {
|
||||
tracker = 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();
|
||||
Object[] inferredDependencies = normalizeDependencies(value, result.getDependencyItems());
|
||||
@@ -52,7 +46,17 @@ public abstract class CachedValueBase<T> {
|
||||
for (int i = 0; i < inferredDependencies.length; 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) {
|
||||
@@ -70,9 +74,9 @@ public abstract class CachedValueBase<T> {
|
||||
}
|
||||
|
||||
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);
|
||||
if (myTrackValue && value != null) {
|
||||
if (isTrackValue() && value != null) {
|
||||
if (value instanceof Object[]) {
|
||||
collectDependencies((Object[])value, flattened);
|
||||
}
|
||||
@@ -100,16 +104,24 @@ public abstract class CachedValueBase<T> {
|
||||
if (isUpToDate(data)) {
|
||||
return true;
|
||||
}
|
||||
if (data.trackingInfo != null) {
|
||||
data.trackingInfo.onValueInvalidated();
|
||||
if (data instanceof TrackedData) {
|
||||
CachedValueProfiler.ValueTracker trackingInfo = ((TrackedData<T>)data).trackingInfo;
|
||||
if (trackingInfo != null) {
|
||||
trackingInfo.onValueInvalidated();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isUpToDate(@NotNull Data<T> data) {
|
||||
for (int i = 0; i < data.myDependencies.length; i++) {
|
||||
Object dependency = data.myDependencies[i];
|
||||
if (isDependencyOutOfDate(dependency, data.myTimeStamps[i])) return false;
|
||||
if (data instanceof CachedValueBase.PsiDependentData) {
|
||||
// do not create an unnecessary long[] array
|
||||
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;
|
||||
@@ -142,14 +154,14 @@ public abstract class CachedValueBase<T> {
|
||||
if (dependency instanceof ModificationTracker) {
|
||||
return ((ModificationTracker)dependency).getModificationCount();
|
||||
}
|
||||
else if (dependency instanceof Reference){
|
||||
else if (dependency instanceof Reference) {
|
||||
Object original = ((Reference<?>)dependency).get();
|
||||
if(original == null) return -1;
|
||||
if (original == null) return -1;
|
||||
return getTimeStamp(original);
|
||||
}
|
||||
else if (dependency instanceof Ref) {
|
||||
Object original = ((Ref<?>)dependency).get();
|
||||
if(original == null) return -1;
|
||||
if (original == null) return -1;
|
||||
return getTimeStamp(original);
|
||||
}
|
||||
else if (dependency instanceof Document) {
|
||||
@@ -175,36 +187,48 @@ public abstract class CachedValueBase<T> {
|
||||
|
||||
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;
|
||||
|
||||
protected Data(T value) {
|
||||
myValue = value;
|
||||
}
|
||||
|
||||
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;
|
||||
final @Nullable CachedValueProfiler.ValueTracker trackingInfo;
|
||||
|
||||
Data(T value,
|
||||
Object @NotNull [] dependencies,
|
||||
long @NotNull [] timeStamps,
|
||||
@Nullable CachedValueProfiler.ValueTracker trackingInfo) {
|
||||
myValue = value;
|
||||
protected DefaultData(T value, Object @NotNull [] dependencies, long @NotNull [] timeStamps) {
|
||||
super(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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object @NotNull [] getDependencies() {
|
||||
return myDependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long @NotNull [] getTimeStamps() {
|
||||
return myTimeStamps;
|
||||
}
|
||||
@@ -213,12 +237,51 @@ public abstract class CachedValueBase<T> {
|
||||
public T get() {
|
||||
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() {
|
||||
if (trackingInfo != null) {
|
||||
trackingInfo.onValueUsed();
|
||||
}
|
||||
return myValue;
|
||||
return super.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,12 +306,16 @@ public abstract class CachedValueBase<T> {
|
||||
Data<T> alreadyComputed = getRawData();
|
||||
boolean reuse = alreadyComputed != null && checkUpToDate(alreadyComputed);
|
||||
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);
|
||||
if (toReturn != null) {
|
||||
if (data != toReturn && data.trackingInfo != null) {
|
||||
data.trackingInfo.onValueRejected();
|
||||
if (data != toReturn && data instanceof TrackedData) {
|
||||
CachedValueProfiler.ValueTracker trackingInfo = ((TrackedData<T>)data).trackingInfo;
|
||||
if (trackingInfo != null) {
|
||||
trackingInfo.onValueRejected();
|
||||
}
|
||||
}
|
||||
return toReturn.getValue();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import static com.intellij.reference.SoftReference.dereference;
|
||||
|
||||
public class CachedValueImpl<T> extends CachedValueBase<T> implements CachedValue<T> {
|
||||
private final CachedValueProvider<T> myProvider;
|
||||
private final boolean myTrackValue;
|
||||
private volatile SoftReference<Data<T>> myData;
|
||||
|
||||
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) {
|
||||
super(trackValue);
|
||||
myProvider = provider;
|
||||
myTrackValue = trackValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTrackValue() {
|
||||
return myTrackValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,15 +17,21 @@ public final class ParameterizedCachedValueImpl<T,P> extends CachedValueBase<T>
|
||||
private final @NotNull Project myProject;
|
||||
private final ParameterizedCachedValueProvider<T,P> myProvider;
|
||||
private volatile SoftReference<Data<T>> myData;
|
||||
private final boolean myTrackValue;
|
||||
|
||||
ParameterizedCachedValueImpl(@NotNull Project project,
|
||||
@NotNull ParameterizedCachedValueProvider<T, P> provider,
|
||||
boolean trackValue) {
|
||||
super(trackValue);
|
||||
myTrackValue = trackValue;
|
||||
myProject = project;
|
||||
myProvider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isTrackValue() {
|
||||
return myTrackValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Data<T> getRawData() {
|
||||
return dereference(myData);
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.AbstractPsiCachedValue;
|
||||
import com.intellij.psi.impl.FakePsiElement;
|
||||
import com.intellij.psi.impl.PsiCachedValueImpl;
|
||||
import com.intellij.psi.impl.source.html.dtd.HtmlSymbolDeclaration;
|
||||
@@ -70,8 +71,7 @@ public class RngElementDescriptor implements XmlElementDescriptor {
|
||||
|
||||
private final DElementPattern myElementPattern;
|
||||
protected final RngNsDescriptor myNsDescriptor;
|
||||
private final PsiCachedValueImpl<PsiElement> myCachedElement;
|
||||
|
||||
private final AbstractPsiCachedValue<PsiElement> myCachedElement;
|
||||
|
||||
RngElementDescriptor(RngNsDescriptor nsDescriptor, DElementPattern pattern) {
|
||||
myNsDescriptor = nsDescriptor;
|
||||
|
||||
Reference in New Issue
Block a user