mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-04-25 17:25:17 +02:00
Merge branch 'GP-6606_dragonmacher_PR-9054_fcrwr_org-node-bug'
(Closes #9054)
This commit is contained in:
@@ -47,8 +47,8 @@ import ghidra.util.bean.opteditor.OptionsVetoException;
|
||||
//@formatter:on
|
||||
public class SymbolTreePlugin extends Plugin implements SymbolTreeService {
|
||||
|
||||
private static final String OPTIONS_CATEGORY = "Symbol Tree";
|
||||
private static final String OPTION_NAME_GROUP_THRESHOLD = "Group Threshold";
|
||||
public static final String OPTIONS_CATEGORY = "Symbol Tree";
|
||||
public static final String OPTION_NAME_GROUP_THRESHOLD = "Group Threshold";
|
||||
|
||||
private SymbolTreeProvider connectedProvider;
|
||||
private List<SymbolTreeProvider> disconnectedProviders = new ArrayList<>();
|
||||
|
||||
@@ -263,6 +263,9 @@ public abstract class SymbolTreeNode extends GTreeSlowLoadingNode {
|
||||
if (symbolNode.getSymbol() == searchSymbol) {
|
||||
return symbolNode;
|
||||
}
|
||||
else if (symbolNode instanceof OrganizationNode) {
|
||||
return findNode(symbolNode, key, loadChildren, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -36,9 +36,9 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.marker.MarkerManagerPlugin;
|
||||
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
||||
import ghidra.app.plugin.core.symboltree.actions.NavigateOnIncomingAction;
|
||||
import ghidra.app.plugin.core.symboltree.nodes.SymbolCategoryNode;
|
||||
import ghidra.app.plugin.core.symboltree.nodes.SymbolNode;
|
||||
import ghidra.app.plugin.core.symboltree.nodes.*;
|
||||
import ghidra.app.util.viewer.field.*;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
@@ -666,10 +666,150 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
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 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() {
|
||||
GTreeNode node = rootNode.getChild("Classes");
|
||||
tree.expandTree(node);
|
||||
@@ -740,6 +880,10 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
return cmd.getNamespace();
|
||||
}
|
||||
|
||||
private void createClass(String name) throws Exception {
|
||||
createClass(program.getGlobalNamespace(), name);
|
||||
}
|
||||
|
||||
private GhidraClass createClass(Namespace parent, String name) throws Exception {
|
||||
GhidraClass c = tx(program, () -> {
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
|
||||
Reference in New Issue
Block a user