clear references to dom problem holders on a PSI change (needed for IDEA-225622 to avoid leaks)

instead of user data with a complicated CachedValue-based uptodateness check, keep holders in a single map

GitOrigin-RevId: 381ceeb72ccdea854ae433764b97a1cefc5d1662
This commit is contained in:
peter
2019-11-18 19:07:46 +00:00
committed by intellij-monorepo-bot
parent 57756544af
commit 1b600a5f5f
6 changed files with 38 additions and 47 deletions
@@ -11,19 +11,16 @@ import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Key;
import com.intellij.profile.ProfileChangeAdapter;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.impl.source.xml.XmlFileImpl;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.EventDispatcher;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomUtil;
@@ -32,12 +29,11 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManager {
public static final Object LOCK = new Object();
static final Object LOCK = new Object();
private static final Key<DomElementsProblemsHolderImpl> DOM_PROBLEM_HOLDER_KEY = Key.create("DomProblemHolder");
private static final Key<CachedValue<Boolean>> CACHED_VALUE_KEY = Key.create("DomProblemHolderCachedValue");
private final EventDispatcher<DomHighlightingListener> myDispatcher = EventDispatcher.create(DomHighlightingListener.class);
private static final DomElementsProblemsHolder EMPTY_PROBLEMS_HOLDER = new DomElementsProblemsHolder() {
@@ -75,11 +71,11 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag
}
};
private final Project myProject;
private final Map<XmlTag, DomElementsProblemsHolderImpl> myHolders = ContainerUtil.createWeakMap();
public DomElementAnnotationsManagerImpl(@NotNull Project project) {
myProject = project;
project.getMessageBus().connect().subscribe(ProfileChangeAdapter.TOPIC, new ProfileChangeAdapter() {
MessageBusConnection connection = project.getMessageBus().connect();
connection.subscribe(ProfileChangeAdapter.TOPIC, new ProfileChangeAdapter() {
@Override
public void profileActivated(InspectionProfile oldProfile, @Nullable InspectionProfile profile) {
dropAnnotationsCache();
@@ -90,11 +86,14 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag
dropAnnotationsCache();
}
});
connection.subscribe(PsiModificationTracker.TOPIC, this::dropAnnotationsCache);
}
@Override
public void dropAnnotationsCache() {
incModificationCount();
synchronized (LOCK) {
myHolders.clear();
}
}
public final List<DomElementProblemDescriptor> appendProblems(@NotNull DomFileElement element, @NotNull DomElementAnnotationHolder annotationHolder, Class<? extends DomElementsInspection> inspectionClass) {
@@ -107,40 +106,37 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag
return Collections.unmodifiableList(holderImpl);
}
@NotNull
private DomElementsProblemsHolderImpl _getOrCreateProblemsHolder(final DomFileElement element) {
DomElementsProblemsHolderImpl holder;
final DomElement rootElement = element.getRootElement();
final XmlTag rootTag = rootElement.getXmlTag();
XmlTag rootTag = element.getRootElement().getXmlTag();
if (rootTag == null) return new DomElementsProblemsHolderImpl(element);
holder = rootTag.getUserData(DOM_PROBLEM_HOLDER_KEY);
if (isHolderOutdated(element.getFile()) || holder == null) {
holder = new DomElementsProblemsHolderImpl(element);
rootTag.putUserData(DOM_PROBLEM_HOLDER_KEY, holder);
final CachedValue<Boolean> cachedValue = CachedValuesManager.getManager(myProject).createCachedValue(
() -> new CachedValueProvider.Result<>(Boolean.FALSE, element, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT,
this, ProjectRootManager.getInstance(myProject)), false);
cachedValue.getValue();
element.getFile().putUserData(CACHED_VALUE_KEY, cachedValue);
}
return holder;
return myHolders.computeIfAbsent(rootTag, __ -> new DomElementsProblemsHolderImpl(element));
}
public static boolean isHolderUpToDate(DomElement element) {
public boolean isHolderUpToDate(DomElement element) {
return !isHolderOutdated(DomUtil.getFile(element));
}
public void outdateProblemHolder(DomElement element) {
XmlTag rootTag = getRootTagIfParsed(DomUtil.getFile(element));
synchronized (LOCK) {
return !isHolderOutdated(DomUtil.getFile(element));
if (rootTag != null) {
myHolders.remove(rootTag);
}
}
}
public static void outdateProblemHolder(final DomElement element) {
private boolean isHolderOutdated(XmlFile file) {
synchronized (LOCK) {
DomUtil.getFile(element).putUserData(CACHED_VALUE_KEY, null);
XmlTag rootTag = getRootTagIfParsed(file);
return rootTag == null || !myHolders.containsKey(rootTag);
}
}
private static boolean isHolderOutdated(final XmlFile file) {
final CachedValue<Boolean> cachedValue = file.getUserData(CACHED_VALUE_KEY);
return cachedValue == null || !cachedValue.hasUpToDateValue();
@Nullable
private static XmlTag getRootTagIfParsed(@NotNull XmlFile file) {
return ((XmlFileImpl)file).isContentsLoaded() ? file.getRootTag() : null;
}
@Override
@@ -152,7 +148,7 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag
synchronized (LOCK) {
final XmlTag tag = fileElement.getRootElement().getXmlTag();
if (tag != null) {
final DomElementsProblemsHolder readyHolder = tag.getUserData(DOM_PROBLEM_HOLDER_KEY);
DomElementsProblemsHolder readyHolder = myHolders.get(tag);
if (readyHolder != null) {
return readyHolder;
}
@@ -48,7 +48,7 @@ public class DefaultDomAnnotator implements Annotator {
public <T extends DomElement> void runInspection(@Nullable final DomElementsInspection<T> inspection, final DomFileElement<T> fileElement, List<? super Annotation> toFill) {
if (inspection == null) return;
DomElementAnnotationsManagerImpl annotationsManager = getAnnotationsManager(fileElement);
if (DomElementAnnotationsManagerImpl.isHolderUpToDate(fileElement) && annotationsManager.getProblemHolder(fileElement).isInspectionCompleted(inspection)) return;
if (annotationsManager.isHolderUpToDate(fileElement) && annotationsManager.getProblemHolder(fileElement).isInspectionCompleted(inspection)) return;
DomElementAnnotationHolderImpl annotationHolder = new DomElementAnnotationHolderImpl(true, fileElement);
inspection.checkFileElement(fileElement, annotationHolder);
@@ -24,6 +24,7 @@ import com.intellij.util.containers.ClassMap;
import com.intellij.util.ui.JBUI;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomUtil;
import com.intellij.util.xml.highlighting.DomElementAnnotationsManager;
import com.intellij.util.xml.highlighting.DomElementAnnotationsManagerImpl;
import com.intellij.util.xml.highlighting.DomElementsErrorPanel;
import org.jetbrains.annotations.NotNull;
@@ -104,7 +105,7 @@ public class DomUIFactoryImpl extends DomUIFactory {
isProcessingChange = true;
try {
for (final DomElement element : elements) {
DomElementAnnotationsManagerImpl.outdateProblemHolder(element);
((DomElementAnnotationsManagerImpl)DomElementAnnotationsManager.getInstance(element.getManager().getProject())).outdateProblemHolder(element);
}
CommittableUtil.updateHighlighting(panel);
}
@@ -21,7 +21,6 @@ import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SimpleModificationTracker;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomFileElement;
import org.jetbrains.annotations.NotNull;
@@ -29,7 +28,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.EventListener;
import java.util.List;
public abstract class DomElementAnnotationsManager extends SimpleModificationTracker {
public abstract class DomElementAnnotationsManager {
public static DomElementAnnotationsManager getInstance(Project project) {
return ServiceManager.getService(project, DomElementAnnotationsManager.class);
@@ -157,12 +157,11 @@ public class DomHighlightingLiteTest extends DomTestCase {
public void testHolderRecreationAfterChange() {
myAnnotationsManager.appendProblems(myElement, createHolder(), MyDomElementsInspection.class);
assertTrue(DomElementAnnotationsManagerImpl.isHolderUpToDate(myElement));
assertTrue(myAnnotationsManager.isHolderUpToDate(myElement));
final DomElementsProblemsHolder holder = myAnnotationsManager.getProblemHolder(myElement);
myElement.incModificationCount();
assertFalse(DomElementAnnotationsManagerImpl.isHolderUpToDate(myElement));
assertSame(holder, myAnnotationsManager.getProblemHolder(myElement));
getPsiManager().dropPsiCaches();
assertFalse(myAnnotationsManager.isHolderUpToDate(myElement));
myAnnotationsManager.appendProblems(myElement, createHolder(), MyDomElementsInspection.class);
assertNotSame(holder, assertNotEmptyHolder(myAnnotationsManager.getProblemHolder(myElement)));
@@ -34,7 +34,6 @@ import java.lang.reflect.Type;
* @author peter
*/
public class MockDomFileElement extends UserDataHolderBase implements DomFileElement<DomElement> {
private long myModCount = 0;
private DomFileDescription<DomElement> myFileDescription;
public void setFileDescription(final DomFileDescription<DomElement> fileDescription) {
@@ -230,10 +229,7 @@ public class MockDomFileElement extends UserDataHolderBase implements DomFileEle
@Override
public long getModificationCount() {
return myModCount;
return 0;
}
public void incModificationCount() {
myModCount++;
}
}