From e7fcf5f24a4724c730f8ef461ceeba0f96bc913f Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:15:57 -0500 Subject: [PATCH] GP-4134: Less abreasive refreshing of the Model tree. --- .../core/debug/gui/model/ObjectTreeModel.java | 80 +++++++++++++++++-- .../debug/gui/model/ObjectsTreePanel.java | 3 +- .../gui/model/DebuggerModelProviderTest.java | 2 +- 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTreeModel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTreeModel.java index 6cd7205879..15e45f51e9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTreeModel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectTreeModel.java @@ -30,6 +30,7 @@ import ghidra.trace.model.*; import ghidra.trace.model.Trace.TraceObjectChangeType; import ghidra.trace.model.target.*; import ghidra.util.HTMLUtilities; +import ghidra.util.LockHold; import ghidra.util.datastruct.WeakValueHashMap; import utilities.util.IDKeyed; @@ -51,7 +52,7 @@ public class ObjectTreeModel implements DisplaysModified { } public void domainObjectRestored(DomainObjectChangeRecord rec) { - reload(); + reloadSameTrace(); } protected boolean isEventValue(TraceObjectValue value) { @@ -167,7 +168,7 @@ public class ObjectTreeModel implements DisplaysModified { } AbstractNode node = byValue.computeIfAbsent(new IDKeyed<>(value), k -> createNode(value)); - node.unloadChildren(); + //node.unloadChildren(); //AbstractNode node = createNode(value); if (value.isCanonical()) { byObject.put(new IDKeyed<>(value.getChild()), node); @@ -204,6 +205,18 @@ public class ObjectTreeModel implements DisplaysModified { addNode(i, node); } + @Override + public void dispose() { + /** + * Our nodes are re-usable. They're cached so that as an item comes and goes, its + * corresponding node can also come and go without being re-instantiated each time. + * Furthermore, it's like to have all the same children as before, too. For now, we'll + * just ignore dispose. If there's too many unexpected behaviors resulting from this, + * then perhaps we should just have dispose also remove itself from the node cache. + */ + // DO NOTHING + } + @Override public int compareTo(GTreeNode node) { return TargetObjectKeyComparator.CHILD.compare(this.getName(), node.getName()); @@ -211,7 +224,7 @@ public class ObjectTreeModel implements DisplaysModified { @Override public String getName() { - return getValue().getEntryKey() + "\n" + getValue().getMinSnap(); + return getValue().getEntryKey() + "@" + getValue().getMinSnap(); } @Override @@ -260,6 +273,50 @@ public class ObjectTreeModel implements DisplaysModified { protected boolean isModified() { return isValueModified(getValue()); } + + protected synchronized void reloadChildrenNow() { + if (!isLoaded()) { + return; + } + // Use a merge to effect the minimal changes to set the children + var current = List.copyOf(children()); + var generated = generateChildren(); + // NB. The two lists ought to be sorted already. + int ic = 0; + int ig = 0; + int diff = 0; + while (ic < current.size() && ig < generated.size()) { + GTreeNode nc = current.get(ic); + GTreeNode ng = generated.get(ig); + int comp = nc.compareTo(ng); + if (comp == 0) { + ic++; + ig++; + } + else if (comp < 0) { + removeNode(nc); + diff--; + ic++; + } + else { // comp > 0 + addNode(ic + diff, ng); + diff++; + ig++; + } + } + while (ic < current.size()) { + GTreeNode nc = current.get(ic); + removeNode(nc); + // diff--; // Not really needed + ic++; + } + while (ig < generated.size()) { + GTreeNode ng = generated.get(ig); + addNode(ic + diff, ng); + diff++; + ig++; + } + } } class RootNode extends AbstractNode { @@ -670,6 +727,15 @@ public class ObjectTreeModel implements DisplaysModified { root.unloadChildren(); } + protected void reloadSameTrace() { + try (LockHold hold = trace == null ? null : trace.lockRead()) { + for (AbstractNode node : List.copyOf(nodeCache.byObject.values())) { + node.reloadChildrenNow(); + } + root.reloadChildrenNow(); + } + } + public void setTrace(Trace trace) { if (this.trace == trace) { return; @@ -761,7 +827,7 @@ public class ObjectTreeModel implements DisplaysModified { } protected void spanChanged() { - reload(); + reloadSameTrace(); } public void setSpan(Lifespan span) { @@ -777,7 +843,7 @@ public class ObjectTreeModel implements DisplaysModified { } protected void showHiddenChanged() { - reload(); + reloadSameTrace(); } public void setShowHidden(boolean showHidden) { @@ -793,7 +859,7 @@ public class ObjectTreeModel implements DisplaysModified { } protected void showPrimitivesChanged() { - reload(); + reloadSameTrace(); } public void setShowPrimitives(boolean showPrimitives) { @@ -809,7 +875,7 @@ public class ObjectTreeModel implements DisplaysModified { } protected void showMethodsChanged() { - reload(); + reloadSameTrace(); } public void setShowMethods(boolean showMethods) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java index 59ddc1cdcf..a4d82dd1a6 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectsTreePanel.java @@ -153,7 +153,7 @@ public class ObjectsTreePanel extends JPanel { if (limitToSnap) { treeModel.setSpan(Lifespan.at(current.getSnap())); } - tree.filterChanged(); + //tree.filterChanged(); // Repaint for bold current path is already going to happen } } @@ -298,7 +298,6 @@ public class ObjectsTreePanel extends JPanel { tree.expandPath(parentPath); } } - public void setSelectedObject(TraceObject object) { if (object == null) { diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java index 1832d3a588..3bdc1a709a 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProviderTest.java @@ -410,7 +410,7 @@ public class DebuggerModelProviderTest extends AbstractGhidraHeadedDebuggerTest GTree tree = modelProvider.objectsTreePanel.tree; GTreeNode node = waitForPass(() -> { GTreeNode n = Unique.assertOne(tree.getSelectedNodes()); - assertEquals("Processes\n0", n.getName()); + assertEquals("Processes@0", n.getName()); return n; }); clickTreeNode(tree, node, MouseEvent.BUTTON1);