mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 13:26:07 +08:00
Merge branch 'GP-6606_dragonmacher_PR-9054_fcrwr_org-node-bug'
(Closes #9054)
This commit is contained in:
+2
-2
@@ -47,8 +47,8 @@ import ghidra.util.bean.opteditor.OptionsVetoException;
|
|||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class SymbolTreePlugin extends Plugin implements SymbolTreeService {
|
public class SymbolTreePlugin extends Plugin implements SymbolTreeService {
|
||||||
|
|
||||||
private static final String OPTIONS_CATEGORY = "Symbol Tree";
|
public static final String OPTIONS_CATEGORY = "Symbol Tree";
|
||||||
private static final String OPTION_NAME_GROUP_THRESHOLD = "Group Threshold";
|
public static final String OPTION_NAME_GROUP_THRESHOLD = "Group Threshold";
|
||||||
|
|
||||||
private SymbolTreeProvider connectedProvider;
|
private SymbolTreeProvider connectedProvider;
|
||||||
private List<SymbolTreeProvider> disconnectedProviders = new ArrayList<>();
|
private List<SymbolTreeProvider> disconnectedProviders = new ArrayList<>();
|
||||||
|
|||||||
+3
@@ -263,6 +263,9 @@ public abstract class SymbolTreeNode extends GTreeSlowLoadingNode {
|
|||||||
if (symbolNode.getSymbol() == searchSymbol) {
|
if (symbolNode.getSymbol() == searchSymbol) {
|
||||||
return symbolNode;
|
return symbolNode;
|
||||||
}
|
}
|
||||||
|
else if (symbolNode instanceof OrganizationNode) {
|
||||||
|
return findNode(symbolNode, key, loadChildren, monitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+146
-2
@@ -36,9 +36,9 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
|||||||
import ghidra.app.plugin.core.marker.MarkerManagerPlugin;
|
import ghidra.app.plugin.core.marker.MarkerManagerPlugin;
|
||||||
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
||||||
import ghidra.app.plugin.core.symboltree.actions.NavigateOnIncomingAction;
|
import ghidra.app.plugin.core.symboltree.actions.NavigateOnIncomingAction;
|
||||||
import ghidra.app.plugin.core.symboltree.nodes.SymbolCategoryNode;
|
import ghidra.app.plugin.core.symboltree.nodes.*;
|
||||||
import ghidra.app.plugin.core.symboltree.nodes.SymbolNode;
|
|
||||||
import ghidra.app.util.viewer.field.*;
|
import ghidra.app.util.viewer.field.*;
|
||||||
|
import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.address.AddressFactory;
|
||||||
@@ -666,10 +666,150 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
assertEquals("MyAnotherLocal", (((SymbolNode) node).getSymbol()).getName());
|
assertEquals("MyAnotherLocal", (((SymbolNode) node).getSymbol()).getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrgNode_Namespaces() throws Exception {
|
||||||
|
|
||||||
|
// set org node threshold to a low value
|
||||||
|
ToolOptions options = tool.getOptions(SymbolTreePlugin.OPTIONS_CATEGORY);
|
||||||
|
int newThreshold = 4;
|
||||||
|
options.setInt(SymbolTreePlugin.OPTION_NAME_GROUP_THRESHOLD, newThreshold);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create enough nodes to trigger an org node.
|
||||||
|
|
||||||
|
Namespaces
|
||||||
|
NsGroup1
|
||||||
|
NsGroup10
|
||||||
|
NsGroup100
|
||||||
|
NsGroup2
|
||||||
|
NsGroup3
|
||||||
|
*/
|
||||||
|
createOrgNamespaces(newThreshold);
|
||||||
|
|
||||||
|
// create a new namespace node that would appear under an org node
|
||||||
|
NamespaceSymbolNode parentNode = openNamespaceNodes("NsGroup1::NsGroup10::NsGroup100");
|
||||||
|
Namespace parentNs = parentNode.getNamespace();
|
||||||
|
createNamespace(parentNs, "NewNamespace");
|
||||||
|
|
||||||
|
// verify node is in the tree
|
||||||
|
assertNamespaceNodes("NsGroup1::NsGroup10::NsGroup100::NewNamespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrgNode_Classes() throws Exception {
|
||||||
|
|
||||||
|
// set org node threshold to a low value
|
||||||
|
ToolOptions options = tool.getOptions(SymbolTreePlugin.OPTIONS_CATEGORY);
|
||||||
|
int newThreshold = 4;
|
||||||
|
options.setInt(SymbolTreePlugin.OPTION_NAME_GROUP_THRESHOLD, newThreshold);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create enough nodes to trigger an org node.
|
||||||
|
|
||||||
|
Classes
|
||||||
|
ClassGroup1
|
||||||
|
ClassGroup10
|
||||||
|
ClassGroup100
|
||||||
|
ClassGroup2
|
||||||
|
ClassGroup3
|
||||||
|
*/
|
||||||
|
createOrgClasses(newThreshold);
|
||||||
|
|
||||||
|
// create a new namespace node that would appear under an org node
|
||||||
|
ClassSymbolNode parentNode =
|
||||||
|
openClassNodes("ClassGroup1::ClassGroup10::ClassGroup100");
|
||||||
|
Namespace parentNs = parentNode.getNamespace();
|
||||||
|
createClass(parentNs, "NewClass");
|
||||||
|
|
||||||
|
// verify node is in the tree
|
||||||
|
assertClassNodes("ClassGroup1::ClassGroup10::ClassGroup100::NewClass");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//=================================================================================================
|
//=================================================================================================
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//=================================================================================================
|
//=================================================================================================
|
||||||
|
|
||||||
|
private NamespaceCategoryNode getNamespacesNode() {
|
||||||
|
GTreeNode root = tree.getViewRoot();
|
||||||
|
return (NamespaceCategoryNode) root.getChild("Namespaces");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassCategoryNode getClassesNode() {
|
||||||
|
GTreeNode root = tree.getViewRoot();
|
||||||
|
return (ClassCategoryNode) root.getChild("Classes");
|
||||||
|
}
|
||||||
|
|
||||||
|
private NamespaceSymbolNode openNamespaceNodes(String path) {
|
||||||
|
|
||||||
|
GTreeNode parent = getNamespacesNode();
|
||||||
|
String[] parts = path.split("::");
|
||||||
|
for (String name : parts) {
|
||||||
|
GTreeNode child = parent.getChild(name);
|
||||||
|
String message =
|
||||||
|
"Child '%s' not found in parent '%s' \n\tfor path '%s'"
|
||||||
|
.formatted(name, parent, path);
|
||||||
|
assertNotNull(message, child);
|
||||||
|
tree.expandPath(child);
|
||||||
|
parent = child;
|
||||||
|
}
|
||||||
|
waitForTree(tree);
|
||||||
|
|
||||||
|
return (NamespaceSymbolNode) parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassSymbolNode openClassNodes(String path) {
|
||||||
|
|
||||||
|
GTreeNode parent = getClassesNode();
|
||||||
|
String[] parts = path.split("::");
|
||||||
|
for (String name : parts) {
|
||||||
|
GTreeNode child = parent.getChild(name);
|
||||||
|
String message =
|
||||||
|
"Child '%s' not found in parent '%s' \n\tfor path '%s'"
|
||||||
|
.formatted(name, parent, path);
|
||||||
|
assertNotNull(message, child);
|
||||||
|
tree.expandPath(child);
|
||||||
|
parent = child;
|
||||||
|
}
|
||||||
|
waitForTree(tree);
|
||||||
|
|
||||||
|
return (ClassSymbolNode) parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createOrgNamespaces(int threshold) throws Exception {
|
||||||
|
int groupCount = 3;
|
||||||
|
for (int i = 0; i < groupCount; i++) {
|
||||||
|
// groups need to share a common prefix
|
||||||
|
String groupName = "Group" + (i + 1); // 1-based for readability
|
||||||
|
int childCount = (threshold + 1);
|
||||||
|
for (int j = 0; j < childCount; j++) {
|
||||||
|
|
||||||
|
String subGroupName = groupName + j;
|
||||||
|
for (int k = 0; k < childCount; k++) {
|
||||||
|
|
||||||
|
createNamespace("Ns" + subGroupName + k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createOrgClasses(int threshold) throws Exception {
|
||||||
|
int groupCount = 3;
|
||||||
|
for (int i = 0; i < groupCount; i++) {
|
||||||
|
// groups need to share a common prefix
|
||||||
|
String groupName = "Group" + (i + 1); // 1-based for readability
|
||||||
|
int childCount = (threshold + 1);
|
||||||
|
for (int j = 0; j < childCount; j++) {
|
||||||
|
|
||||||
|
String subGroupName = groupName + j;
|
||||||
|
for (int k = 0; k < childCount; k++) {
|
||||||
|
|
||||||
|
createClass("Class" + subGroupName + k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void expandClasses() {
|
private void expandClasses() {
|
||||||
GTreeNode node = rootNode.getChild("Classes");
|
GTreeNode node = rootNode.getChild("Classes");
|
||||||
tree.expandTree(node);
|
tree.expandTree(node);
|
||||||
@@ -740,6 +880,10 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
return cmd.getNamespace();
|
return cmd.getNamespace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createClass(String name) throws Exception {
|
||||||
|
createClass(program.getGlobalNamespace(), name);
|
||||||
|
}
|
||||||
|
|
||||||
private GhidraClass createClass(Namespace parent, String name) throws Exception {
|
private GhidraClass createClass(Namespace parent, String name) throws Exception {
|
||||||
GhidraClass c = tx(program, () -> {
|
GhidraClass c = tx(program, () -> {
|
||||||
SymbolTable symbolTable = program.getSymbolTable();
|
SymbolTable symbolTable = program.getSymbolTable();
|
||||||
|
|||||||
Reference in New Issue
Block a user