mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge branch 'GP-225_dragonmacher_PR-2301_astrelsky_ConvertToClassAction' into Ghidra_9.2
This commit is contained in:
commit
a3a550e89a
6 changed files with 183 additions and 53 deletions
|
@ -184,6 +184,13 @@
|
|||
<P>Shows all locations that reference the given symbol.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Convert_to_Class"></A>Convert Namespace to Class</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>You can convert a <I>Namespace</I> to a <I>Class</I>.
|
||||
Right mouse click on a namespace and choose the <B>Convert To Class</B> option.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Create_Class"></A>Create a Class</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
|
@ -206,8 +206,15 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
DockingAction setExternalProgramAction = new SetExternalProgramAction(plugin, this);
|
||||
DockingAction createExternalLocationAction = new CreateExternalLocationAction(plugin);
|
||||
DockingAction editExternalLocationAction = new EditExternalLocationAction(plugin);
|
||||
DockingAction createClassAction = new CreateClassAction(plugin);
|
||||
DockingAction createNamespaceAction = new CreateNamespaceAction(plugin);
|
||||
|
||||
String createGroup = "0Create";
|
||||
int createGroupIndex = 0;
|
||||
DockingAction createNamespaceAction = new CreateNamespaceAction(plugin, createGroup,
|
||||
Integer.toString(createGroupIndex++));
|
||||
DockingAction createClassAction = new CreateClassAction(plugin, createGroup,
|
||||
Integer.toString(createGroupIndex++));
|
||||
DockingAction convertToClassAction = new ConvertToClassAction(plugin, createGroup,
|
||||
Integer.toString(createGroupIndex++));
|
||||
|
||||
DockingAction renameAction = new RenameAction(plugin);
|
||||
DockingAction cutAction = new CutAction(plugin, this);
|
||||
|
@ -231,6 +238,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
|||
tool.addLocalAction(this, editExternalLocationAction);
|
||||
tool.addLocalAction(this, createClassAction);
|
||||
tool.addLocalAction(this, createNamespaceAction);
|
||||
tool.addLocalAction(this, convertToClassAction);
|
||||
tool.addLocalAction(this, renameAction);
|
||||
tool.addLocalAction(this, cutAction);
|
||||
tool.addLocalAction(this, pasteAction);
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.symboltree.actions;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.symboltree.*;
|
||||
import ghidra.app.plugin.core.symboltree.nodes.SymbolNode;
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
/**
|
||||
* Symbol tree action for converting a namespace to a class
|
||||
*/
|
||||
public class ConvertToClassAction extends SymbolTreeContextAction {
|
||||
|
||||
private static final String NAME = "Convert to Class";
|
||||
|
||||
public ConvertToClassAction(SymbolTreePlugin plugin, String group, String subGroup) {
|
||||
super(NAME, plugin.getName());
|
||||
MenuData menuData = new MenuData(new String[] { NAME }, group);
|
||||
menuData.setMenuSubGroup(subGroup);
|
||||
setPopupMenuData(menuData);
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(SymbolTreeActionContext context) {
|
||||
TreePath[] selectionPaths = context.getSelectedSymbolTreePaths();
|
||||
if (selectionPaths.length != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object object = selectionPaths[0].getLastPathComponent();
|
||||
if (object instanceof SymbolNode) {
|
||||
SymbolNode symbolNode = (SymbolNode) object;
|
||||
Symbol symbol = symbolNode.getSymbol();
|
||||
return symbol.getSymbolType() == SymbolType.NAMESPACE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(SymbolTreeActionContext context) {
|
||||
TreePath[] selectionPaths = context.getSelectedSymbolTreePaths();
|
||||
|
||||
Program program = context.getProgram();
|
||||
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
|
||||
|
||||
SymbolGTree tree = context.getSymbolTree();
|
||||
GTreeNode root = tree.getViewRoot();
|
||||
GTreeNode classesNode = root.getChild(SymbolCategory.CLASS_CATEGORY.getName());
|
||||
|
||||
Symbol symbol = ((SymbolNode) node).getSymbol();
|
||||
Namespace namespace = (Namespace) symbol.getObject();
|
||||
if (namespace != null) {
|
||||
String name = namespace.getName();
|
||||
convertToClass(program, namespace);
|
||||
program.flushEvents();
|
||||
context.getSymbolTree().startEditing(classesNode, name);
|
||||
}
|
||||
}
|
||||
|
||||
private static void convertToClass(Program program, Namespace ns) {
|
||||
int id = program.startTransaction(NAME);
|
||||
boolean success = false;
|
||||
try {
|
||||
NamespaceUtils.convertNamespaceToClass(ns);
|
||||
success = true;
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// This is thrown when the provided namespace is a function
|
||||
// It was checked in isEnabledForContext and thus cannot occur
|
||||
throw new AssertException(e);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(id, success);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -33,9 +33,11 @@ import ghidra.util.exception.InvalidInputException;
|
|||
|
||||
public class CreateClassAction extends SymbolTreeContextAction {
|
||||
|
||||
public CreateClassAction(SymbolTreePlugin plugin) {
|
||||
public CreateClassAction(SymbolTreePlugin plugin, String group, String subGroup) {
|
||||
super("Create Class", plugin.getName());
|
||||
setPopupMenuData(new MenuData(new String[] { "Create Class" }, "0Create"));
|
||||
MenuData menuData = new MenuData(new String[] { "Create Class" }, group);
|
||||
menuData.setMenuSubGroup(subGroup);
|
||||
setPopupMenuData(menuData);
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
|
@ -54,7 +56,10 @@ public class CreateClassAction extends SymbolTreeContextAction {
|
|||
protected boolean isEnabledForContext(SymbolTreeActionContext context) {
|
||||
|
||||
TreePath[] selectionPaths = context.getSelectedSymbolTreePaths();
|
||||
if (selectionPaths.length == 1) {
|
||||
if (selectionPaths.length != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object object = selectionPaths[0].getLastPathComponent();
|
||||
if (object instanceof ClassCategoryNode) {
|
||||
return true;
|
||||
|
@ -71,7 +76,6 @@ public class CreateClassAction extends SymbolTreeContextAction {
|
|||
}
|
||||
return (symbolType == SymbolType.CLASS || symbolType == SymbolType.LIBRARY);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,9 +32,11 @@ import ghidra.util.exception.InvalidInputException;
|
|||
|
||||
public class CreateNamespaceAction extends SymbolTreeContextAction {
|
||||
|
||||
public CreateNamespaceAction(SymbolTreePlugin plugin) {
|
||||
public CreateNamespaceAction(SymbolTreePlugin plugin, String group, String subGroup) {
|
||||
super("Create Namespace", plugin.getName());
|
||||
setPopupMenuData(new MenuData(new String[] { "Create Namespace" }, "0Create"));
|
||||
MenuData menuData = new MenuData(new String[] { "Create Namespace" }, group);
|
||||
menuData.setMenuSubGroup(subGroup);
|
||||
setPopupMenuData(menuData);
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
private DockingActionIf selectionAction;
|
||||
private DockingActionIf createNamespaceAction;
|
||||
private DockingActionIf createClassAction;
|
||||
private DockingActionIf convertToClassAction;
|
||||
private ToggleDockingAction goToToggleAction;
|
||||
private SymbolTreeTestUtils util;
|
||||
private SymbolGTree tree;
|
||||
|
@ -225,7 +226,7 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
for (int i = 0; i < count - 1; i++) {
|
||||
GTreeNode n = ghidraNode.getChild(i);
|
||||
Symbol s = ((SymbolNode) n).getSymbol();
|
||||
assertTrue(!s.getName().equals("AnotherLocal"));
|
||||
assertFalse(s.getName().equals("AnotherLocal"));
|
||||
}
|
||||
|
||||
// test undo/redo
|
||||
|
@ -254,7 +255,7 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
for (int i = 0; i < count - 1; i++) {
|
||||
GTreeNode n = ghidraNode.getChild(i);
|
||||
Symbol s = ((SymbolNode) n).getSymbol();
|
||||
assertTrue(!s.getName().equals("AnotherLocal"));
|
||||
assertNotEquals("AnotherLocal", s.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,11 +296,21 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
}
|
||||
|
||||
private void performTreeAction(DockingActionIf action, ActionContext context) {
|
||||
assertTrue(action.isEnabledForContext(context));
|
||||
performAction(action, context, true);
|
||||
program.flushEvents();
|
||||
util.waitForTree();
|
||||
@Test
|
||||
public void testConvertNamespaceToClass() throws Exception {
|
||||
String classNodeName = "MyClass";
|
||||
GTreeNode nsNode = rootNode.getChild(SymbolCategory.NAMESPACE_CATEGORY.getName());
|
||||
GTreeNode classNode = util.createObject(
|
||||
nsNode, classNodeName, createNamespaceAction);
|
||||
|
||||
util.selectNode(classNode);
|
||||
ActionContext context = util.getSymbolTreeContext();
|
||||
performTreeAction(convertToClassAction, context);
|
||||
|
||||
GTreeNode classRootNode = rootNode.getChild(SymbolCategory.CLASS_CATEGORY.getName());
|
||||
classNode = classRootNode.getChild(classNodeName);
|
||||
assertNotNull(classNode);
|
||||
waitForCondition(tree::isEditing);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -313,13 +324,13 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
util.selectNode(lNode);
|
||||
|
||||
ActionContext context = util.getSymbolTreeContext();
|
||||
assertTrue(!renameAction.isEnabledForContext(context));
|
||||
assertTrue(!cutAction.isEnabledForContext(context));
|
||||
assertTrue(!pasteAction.isEnabledForContext(context));
|
||||
assertTrue(!deleteAction.isEnabledForContext(context));
|
||||
assertTrue(!selectionAction.isEnabledForContext(context));
|
||||
assertTrue(!createNamespaceAction.isEnabledForContext(context));
|
||||
assertTrue(!createClassAction.isEnabledForContext(context));
|
||||
assertFalse(renameAction.isEnabledForContext(context));
|
||||
assertFalse(cutAction.isEnabledForContext(context));
|
||||
assertFalse(pasteAction.isEnabledForContext(context));
|
||||
assertFalse(deleteAction.isEnabledForContext(context));
|
||||
assertFalse(selectionAction.isEnabledForContext(context));
|
||||
assertFalse(createNamespaceAction.isEnabledForContext(context));
|
||||
assertFalse(createClassAction.isEnabledForContext(context));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -329,16 +340,11 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
util.expandNode(lNode);
|
||||
|
||||
// add a label
|
||||
tx(program, () -> {
|
||||
SymbolTable symTable = program.getSymbolTable();
|
||||
int transactionID = program.startTransaction("test");
|
||||
try {
|
||||
symTable.createLabel(util.addr(0x010048a1L), "abcdefg", SourceType.USER_DEFINED);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(transactionID, true);
|
||||
}
|
||||
});
|
||||
|
||||
program.flushEvents();
|
||||
util.waitForTree();
|
||||
|
||||
lNode = rootNode.getChild(3);
|
||||
|
@ -355,18 +361,13 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
// verify that the tree updates
|
||||
|
||||
Function f = program.getFunctionManager().getFunctionAt(util.addr(0x01002cf5L));
|
||||
|
||||
Symbol s = getUniqueSymbol(program, "AnotherLocal", f);
|
||||
assertNotNull(s);
|
||||
int transactionID = program.startTransaction("test");
|
||||
try {
|
||||
|
||||
tx(program, () -> {
|
||||
s.setName("MyAnotherLocal", SourceType.USER_DEFINED);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(transactionID, true);
|
||||
}
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
});
|
||||
|
||||
util.waitForTree();
|
||||
|
||||
GTreeNode fNode = getFunctionsNode();
|
||||
|
@ -421,11 +422,20 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertNotNull(createClassAction);
|
||||
createNamespaceAction = getAction(plugin, "Create Namespace");
|
||||
assertNotNull(createNamespaceAction);
|
||||
convertToClassAction = getAction(plugin, "Convert To Class");
|
||||
assertNotNull(convertToClassAction);
|
||||
|
||||
goToToggleAction = (ToggleDockingAction) getAction(plugin, "Navigation");
|
||||
assertNotNull(goToToggleAction);
|
||||
}
|
||||
|
||||
private void performTreeAction(DockingActionIf action, ActionContext context) {
|
||||
assertTrue(action.isEnabledForContext(context));
|
||||
performAction(action, context, true);
|
||||
program.flushEvents();
|
||||
util.waitForTree();
|
||||
}
|
||||
|
||||
private void clickOnNode(GTreeNode node) throws Exception {
|
||||
JTree jTree = (JTree) AbstractGenericTest.getInstanceField("tree", tree);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue