mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-12-11 13:42:04 +00:00
Merge remote-tracking branch
'origin/GP-3204_ghidra007_fixCreateEnumsFromSelectionDupNameIssue--SQUASHED' (Closes #5036)
This commit is contained in:
commit
667773c9f3
@ -854,7 +854,11 @@
|
|||||||
or you will be prompted to enter a unique name. The resulting enum will contain a
|
or you will be prompted to enter a unique name. The resulting enum will contain a
|
||||||
combination of all names and values from the selected enums. NOTE: If more than one of
|
combination of all names and values from the selected enums. NOTE: If more than one of
|
||||||
the same value is contained in the enums, they will all be added to the new enum.
|
the same value is contained in the enums, they will all be added to the new enum.
|
||||||
However, only the first one entered will be applied when this enum is used.</P>
|
However, only the first one entered will be applied when this enum is used. If more than one
|
||||||
|
entry with the same name is contained in the selected enums, any extras with the same value will
|
||||||
|
be ignored and any with different value will be given a new name consisting of original name appended
|
||||||
|
with as many underscores needed to make it unique. A comment will be added so users know which ones
|
||||||
|
had names modified to allow the addition of the entry.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3><A name="DeleteDataType"></A>Deleting a Data Type</H3>
|
<H3><A name="DeleteDataType"></A>Deleting a Data Type</H3>
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.actions;
|
package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
@ -107,9 +110,8 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
|||||||
|
|
||||||
DataType dt = myDataTypeManager.getDataType(category.getCategoryPath(), newName);
|
DataType dt = myDataTypeManager.getDataType(category.getCategoryPath(), newName);
|
||||||
while (dt != null) {
|
while (dt != null) {
|
||||||
InputDialog dupInputDialog =
|
InputDialog dupInputDialog = new InputDialog("Duplicate ENUM Name",
|
||||||
new InputDialog("Duplicate ENUM Name",
|
"Please enter a unique name for the new ENUM: ");
|
||||||
"Please enter a unique name for the new ENUM: ");
|
|
||||||
tool = plugin.getTool();
|
tool = plugin.getTool();
|
||||||
tool.showDialog(dupInputDialog);
|
tool.showDialog(dupInputDialog);
|
||||||
|
|
||||||
@ -119,7 +121,7 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
|||||||
newName = dupInputDialog.getValue();
|
newName = dupInputDialog.getValue();
|
||||||
dt = myDataTypeManager.getDataType(category.getCategoryPath(), newName);
|
dt = myDataTypeManager.getDataType(category.getCategoryPath(), newName);
|
||||||
}
|
}
|
||||||
createNewEnum(category, enumArray, newName);
|
createMergedEnum(category, enumArray, newName);
|
||||||
|
|
||||||
// select new node in tree. Must use invoke later to give the tree a chance to add the
|
// select new node in tree. Must use invoke later to give the tree a chance to add the
|
||||||
// the new node to the tree.
|
// the new node to the tree.
|
||||||
@ -130,8 +132,8 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
GTreeNode rootNode = gTree.getViewRoot();
|
GTreeNode rootNode = gTree.getViewRoot();
|
||||||
gTree.setSelectedNodeByNamePath(new String[] { rootNode.getName(), parentNodeName,
|
gTree.setSelectedNodeByNamePath(
|
||||||
newNodeName });
|
new String[] { rootNode.getName(), parentNodeName, newNodeName });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -168,34 +170,109 @@ public class CreateEnumFromSelectionAction extends DockingAction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createNewEnum(Category category, Enum[] enumArray, String newName) {
|
private void createMergedEnum(Category category, Enum[] enumsToMerge, String mergedEnumName) {
|
||||||
|
|
||||||
// figure out size of the new enum using the max size of the selected enums
|
int maxEnumSize = computeNewEnumSize(enumsToMerge);
|
||||||
|
|
||||||
|
SourceArchive sourceArchive = category.getDataTypeManager().getLocalSourceArchive();
|
||||||
|
Enum mergedEnum = new EnumDataType(category.getCategoryPath(), mergedEnumName, maxEnumSize,
|
||||||
|
category.getDataTypeManager());
|
||||||
|
|
||||||
|
mergedEnum.setSourceArchive(sourceArchive);
|
||||||
|
|
||||||
|
for (Enum element : enumsToMerge) {
|
||||||
|
mergeEnum(mergedEnum, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEnumDataType(category, mergedEnum);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEnumDataType(Category category, Enum mergedEnum) {
|
||||||
|
int id = category.getDataTypeManager().startTransaction("Create New Enum Data Type");
|
||||||
|
category.getDataTypeManager()
|
||||||
|
.addDataType(mergedEnum, DataTypeConflictHandler.REPLACE_HANDLER);
|
||||||
|
category.getDataTypeManager().endTransaction(id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeEnum(Enum mergedEnum, Enum enumToMerge) {
|
||||||
|
|
||||||
|
for (String name : enumToMerge.getNames()) {
|
||||||
|
|
||||||
|
long valueToAdd = enumToMerge.getValue(name);
|
||||||
|
String comment = "";
|
||||||
|
|
||||||
|
if (isDuplicateEntry(mergedEnum, enumToMerge, name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isConflictingEntry(mergedEnum, enumToMerge, name)) {
|
||||||
|
name = createDeconflictedName(mergedEnum, name);
|
||||||
|
|
||||||
|
comment = "NOTE: Duplicate name with different value";
|
||||||
|
Msg.debug(this,
|
||||||
|
"Merged Enum " + mergedEnum.getName() +
|
||||||
|
" has at least one duplicate named entry with different value than " +
|
||||||
|
"original. Underscore(s) have been appended to name allow addition.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedEnum.add(name, valueToAdd, comment);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createDeconflictedName(Enum enumm, String name) {
|
||||||
|
|
||||||
|
List<String> existingNames = Arrays.asList(enumm.getNames());
|
||||||
|
while (existingNames.contains(name)) {
|
||||||
|
name = name + "_";
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDuplicateEntry(Enum mergedEnum, Enum enumToMerge, String name) {
|
||||||
|
|
||||||
|
List<String> existingNames = Arrays.asList(mergedEnum.getNames());
|
||||||
|
|
||||||
|
if (!existingNames.contains(name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long valueToAdd = enumToMerge.getValue(name);
|
||||||
|
long existingValue = mergedEnum.getValue(name);
|
||||||
|
if (valueToAdd == existingValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConflictingEntry(Enum mergedEnum, Enum enumToMerge, String name) {
|
||||||
|
|
||||||
|
List<String> existingNames = Arrays.asList(mergedEnum.getNames());
|
||||||
|
|
||||||
|
if (!existingNames.contains(name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long valueToAdd = enumToMerge.getValue(name);
|
||||||
|
long existingValue = mergedEnum.getValue(name);
|
||||||
|
if (valueToAdd == existingValue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out size in bytes of the new enum using the max size of the selected enums
|
||||||
|
private int computeNewEnumSize(Enum[] enumArray) {
|
||||||
int maxEnumSize = 1;
|
int maxEnumSize = 1;
|
||||||
for (Enum element : enumArray) {
|
for (Enum element : enumArray) {
|
||||||
if (maxEnumSize < element.getLength()) {
|
if (maxEnumSize < element.getLength()) {
|
||||||
maxEnumSize = element.getLength();
|
maxEnumSize = element.getLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SourceArchive sourceArchive = category.getDataTypeManager().getLocalSourceArchive();
|
return maxEnumSize;
|
||||||
Enum dataType =
|
|
||||||
new EnumDataType(category.getCategoryPath(), newName, maxEnumSize,
|
|
||||||
category.getDataTypeManager());
|
|
||||||
|
|
||||||
for (Enum element : enumArray) {
|
|
||||||
String[] names = element.getNames();
|
|
||||||
for (String name : names) {
|
|
||||||
dataType.add(name, element.getValue(name));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataType.setSourceArchive(sourceArchive);
|
|
||||||
int id = category.getDataTypeManager().startTransaction("Create New Enum Data Type");
|
|
||||||
category.getDataTypeManager()
|
|
||||||
.addDataType(dataType, DataTypeConflictHandler.REPLACE_HANDLER);
|
|
||||||
category.getDataTypeManager().endTransaction(id, true);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,8 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateEnumFromSelection() throws Exception {
|
public void testCreateEnumFromSelection() throws Exception {
|
||||||
|
|
||||||
|
//NOTE: This test tests basic create enum from selection
|
||||||
|
|
||||||
// make two test enums in the program name folder
|
// make two test enums in the program name folder
|
||||||
|
|
||||||
@ -196,6 +198,9 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateEnumFromSelectionDupe() throws Exception {
|
public void testCreateEnumFromSelectionDupe() throws Exception {
|
||||||
|
|
||||||
|
//NOTE: This test tests basic create enum from selection and
|
||||||
|
// tries to make second new enum with duplicate name and verifies that it won't
|
||||||
|
|
||||||
// make two test enums in the program name folder
|
// make two test enums in the program name folder
|
||||||
|
|
||||||
@ -281,6 +286,115 @@ public class CreateEnumFromSelectionTest extends AbstractGhidraHeadedIntegration
|
|||||||
assertNotNull(newEnumNode);
|
assertNotNull(newEnumNode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateEnumFromSelectionDupeEntryNameOrValue() throws Exception {
|
||||||
|
|
||||||
|
//NOTE: This test tests handing of duplicate entry names and values
|
||||||
|
// duplicate value diff name - add both
|
||||||
|
// duplicate name and value - just add one entry with that combo
|
||||||
|
// duplicate name diff value - add second name with _ appended and a comment to indicate change
|
||||||
|
|
||||||
|
// make two test enums in the program name folder
|
||||||
|
|
||||||
|
Category category = programNode.getCategory();
|
||||||
|
DataTypeManager dataTypeManager = category.getDataTypeManager();
|
||||||
|
|
||||||
|
int id = dataTypeManager.startTransaction("new enum 1");
|
||||||
|
Enum enumm = new EnumDataType("Colors", 1);
|
||||||
|
enumm.add("Red", 0);
|
||||||
|
enumm.add("Green", 0x10);
|
||||||
|
enumm.add("Blue", 0x20);
|
||||||
|
|
||||||
|
category.addDataType(enumm, null);
|
||||||
|
dataTypeManager.endTransaction(id, true);
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
int id2 = dataTypeManager.startTransaction("new enum 2");
|
||||||
|
Enum enumm2 = new EnumDataType("MoreColors", 1);
|
||||||
|
enumm2.add("Red", 0); // add dup name same value
|
||||||
|
enumm2.add("Green", 0x5); // add dup name diff value
|
||||||
|
enumm2.add("Black", 0x10); // add dup value diff name
|
||||||
|
enumm2.add("Purple", 0x30);
|
||||||
|
enumm2.add("White", 0x40);
|
||||||
|
enumm2.add("Yellow", 0x50);
|
||||||
|
|
||||||
|
category.addDataType(enumm2, null);
|
||||||
|
dataTypeManager.endTransaction(id2, true);
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
program.flushEvents();
|
||||||
|
waitForPostedSwingRunnables();
|
||||||
|
|
||||||
|
DataTypeNode testEnumNode1 = (DataTypeNode) programNode.getChild("Colors");
|
||||||
|
assertNotNull(testEnumNode1);
|
||||||
|
|
||||||
|
DataTypeNode testEnumNode2 = (DataTypeNode) programNode.getChild("MoreColors");
|
||||||
|
assertNotNull(testEnumNode2);
|
||||||
|
|
||||||
|
expandNode(programNode);
|
||||||
|
selectNodes(testEnumNode1, testEnumNode2);
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
final DockingActionIf action = getAction(plugin, "Enum from Selection");
|
||||||
|
assertNotNull(action);
|
||||||
|
assertTrue(action.isEnabledForContext(provider.getActionContext(null)));
|
||||||
|
assertTrue(action.isAddToPopup(provider.getActionContext(null)));
|
||||||
|
|
||||||
|
executeOnSwingWithoutBlocking(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
DataTypeTestUtils.performAction(action, tree);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Window window = waitForWindow("Name new ENUM");
|
||||||
|
assertNotNull(window);
|
||||||
|
|
||||||
|
final JTextField tf = findComponent(window, JTextField.class);
|
||||||
|
assertNotNull(tf);
|
||||||
|
|
||||||
|
tf.setText("myNewEnum");
|
||||||
|
pressButtonByText(window, "OK");
|
||||||
|
assertTrue(!window.isShowing());
|
||||||
|
waitForPostedSwingRunnables();
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
DataTypeNode newEnumNode = (DataTypeNode) programNode.getChild("myNewEnum");
|
||||||
|
waitForTree();
|
||||||
|
|
||||||
|
assertNotNull(newEnumNode);
|
||||||
|
|
||||||
|
Enum newEnum = (Enum) newEnumNode.getDataType();
|
||||||
|
long values[] = newEnum.getValues();
|
||||||
|
String names[] = newEnum.getNames();
|
||||||
|
|
||||||
|
assertEquals(values.length, 7);
|
||||||
|
assertEquals(names.length, 8);
|
||||||
|
|
||||||
|
|
||||||
|
assertEquals(newEnum.getName(0x00L), "Red");
|
||||||
|
assertEquals(newEnum.getName(0x5L), "Green_");
|
||||||
|
assertEquals(newEnum.getName(0x10L), "Black"); // single query will return first alphabetically
|
||||||
|
assertEquals(newEnum.getName(0x20L), "Blue");
|
||||||
|
assertEquals(newEnum.getName(0x30L), "Purple");
|
||||||
|
assertEquals(newEnum.getName(0x40L), "White");
|
||||||
|
assertEquals(newEnum.getName(0x50L), "Yellow");
|
||||||
|
|
||||||
|
String[] namesfor10 = newEnum.getNames(0x10);
|
||||||
|
assertEquals(namesfor10.length, 2);
|
||||||
|
assertEquals(namesfor10[0], "Black");
|
||||||
|
assertEquals(namesfor10[1], "Green");
|
||||||
|
|
||||||
|
assertEquals(newEnum.getValue("Red"), 0x00L);
|
||||||
|
assertEquals(newEnum.getValue("Green_"), 0x5L);
|
||||||
|
assertEquals(newEnum.getValue("Green"), 0x10L);
|
||||||
|
assertEquals(newEnum.getValue("Black"), 0x10L);
|
||||||
|
assertEquals(newEnum.getValue("Blue"), 0x20L);
|
||||||
|
assertEquals(newEnum.getValue("Purple"), 0x30L);
|
||||||
|
assertEquals(newEnum.getValue("White"), 0x40L);
|
||||||
|
assertEquals(newEnum.getValue("Yellow"), 0x50L);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDontCreateEnumFromSingleSelection() throws Exception {
|
public void testDontCreateEnumFromSingleSelection() throws Exception {
|
||||||
|
Loading…
Reference in New Issue
Block a user