Merge remote-tracking branch

'origin/GP-1374-dragonmacher-sort-exported-enum-values--SQUASHED'
(Closes #1664)
This commit is contained in:
Ryan Kurtz 2021-10-13 11:31:35 -04:00
commit 353a85e4fd
8 changed files with 225 additions and 108 deletions

View file

@ -30,7 +30,6 @@ import ghidra.util.Msg;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
import ghidra.util.xml.*; import ghidra.util.xml.*;
import ghidra.xml.*; import ghidra.xml.*;
@ -245,7 +244,7 @@ public class DataTypesXmlMgr {
private boolean processEnum(XmlTreeNode root) { private boolean processEnum(XmlTreeNode root) {
XmlElement element = root.getStartElement(); XmlElement element = root.getStartElement();
String name = element.getAttribute("NAME"); String name = element.getAttribute("NAME");
String comment = getRegularComment(root); String enuumComment = getRegularComment(root);
CategoryPath cp = getCategoryPath(element); CategoryPath cp = getCategoryPath(element);
int size = XmlUtilities.parseInt(element.getAttribute("SIZE"), defaultEnumSize); int size = XmlUtilities.parseInt(element.getAttribute("SIZE"), defaultEnumSize);
@ -257,9 +256,10 @@ public class DataTypesXmlMgr {
XmlElement childElement = node.getStartElement(); XmlElement childElement = node.getStartElement();
String entryName = childElement.getAttribute("NAME"); String entryName = childElement.getAttribute("NAME");
long entryValue = XmlUtilities.parseLong(childElement.getAttribute("VALUE")); long entryValue = XmlUtilities.parseLong(childElement.getAttribute("VALUE"));
enuum.add(entryName, entryValue); String comment = childElement.getAttribute("COMMENT");
enuum.add(entryName, entryValue, comment);
} }
enuum.setDescription(comment); enuum.setDescription(enuumComment);
dataManager.addDataType(enuum, null); dataManager.addDataType(enuum, null);
return true; return true;
} }
@ -288,6 +288,7 @@ public class DataTypesXmlMgr {
td.setCategoryPath(cp); td.setCategoryPath(cp);
} }
catch (DuplicateNameException e) { catch (DuplicateNameException e) {
log.appendMsg("Unable to place typedef '" + name + "' in category '" + cp + "'");
} }
dataManager.addDataType(td, null); dataManager.addDataType(td, null);
@ -546,11 +547,11 @@ public class DataTypesXmlMgr {
writeRegularComment(writer, enuum.getDescription()); writeRegularComment(writer, enuum.getDescription());
String[] names = enuum.getNames(); String[] names = enuum.getNames();
Arrays.sort(names);
for (String name : names) { for (String name : names) {
attrs = new XmlAttributes(); attrs = new XmlAttributes();
attrs.addAttribute("NAME", name); attrs.addAttribute("NAME", name);
attrs.addAttribute("VALUE", enuum.getValue(name), true); attrs.addAttribute("VALUE", enuum.getValue(name), true);
attrs.addAttribute("COMMENT", enuum.getComment(name));
writer.startElement("ENUM_ENTRY", attrs); writer.startElement("ENUM_ENTRY", attrs);
writer.endElement("ENUM_ENTRY"); writer.endElement("ENUM_ENTRY");
} }
@ -666,8 +667,8 @@ public class DataTypesXmlMgr {
/** /**
* Output data types in XML format for debugging purposes. * Output data types in XML format for debugging purposes.
* NOTE: There is no support for reading the XML produced by this * NOTE: There is no support for reading the XML produced by this method.
* method. * @param dataManager the data type manager
* @param outputFilename name of the output file * @param outputFilename name of the output file
* @throws IOException if there was a problem writing to the file * @throws IOException if there was a problem writing to the file
*/ */
@ -683,9 +684,10 @@ public class DataTypesXmlMgr {
MessageLog log = new MessageLog(); MessageLog log = new MessageLog();
DataTypesXmlMgr mgr = new DataTypesXmlMgr(dataManager, log); DataTypesXmlMgr mgr = new DataTypesXmlMgr(dataManager, log);
try { try {
mgr.write(writer, TaskMonitorAdapter.DUMMY_MONITOR); mgr.write(writer, TaskMonitor.DUMMY);
} }
catch (CancelledException e) { catch (CancelledException e) {
// can't happen with dummy monitor
} }
writer.close(); writer.close();

View file

@ -29,7 +29,7 @@ import ghidra.program.model.data.Enum;
import ghidra.program.model.data.EnumDataType; import ghidra.program.model.data.EnumDataType;
import ghidra.program.util.ChangeManager; import ghidra.program.util.ChangeManager;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.task.TaskMonitorAdapter; import ghidra.util.task.TaskMonitor;
/** /**
* Tests for Enum data types. * Tests for Enum data types.
@ -40,10 +40,6 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
private DataTypeManagerDB dataMgr; private DataTypeManagerDB dataMgr;
private int transactionID; private int transactionID;
public EnumTest() {
super();
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
program = createDefaultProgram("Test", ProgramBuilder._TOY, this); program = createDefaultProgram("Test", ProgramBuilder._TOY, this);
@ -223,7 +219,7 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
Enum enummDT = (Enum) dataMgr.resolve(enumm, null); Enum enummDT = (Enum) dataMgr.resolve(enumm, null);
assertNotNull(enummDT); assertNotNull(enummDT);
c.remove(enummDT, TaskMonitorAdapter.DUMMY_MONITOR); c.remove(enummDT, TaskMonitor.DUMMY);
assertNull(c.getDataType("Color")); assertNull(c.getDataType("Color"));
assertTrue(enummDT.isDeleted()); assertTrue(enummDT.isDeleted());
@ -294,6 +290,7 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
Assert.fail("Should have gotten no such element exception!"); Assert.fail("Should have gotten no such element exception!");
} }
catch (NoSuchElementException e) { catch (NoSuchElementException e) {
// expected
} }
} }
@ -335,27 +332,56 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
myEnum.add("Green", 15); myEnum.add("Green", 15);
myEnum.add("Blue", 20); myEnum.add("Blue", 20);
assertTrue(enummDT.isEquivalent(myEnum)); assertTrue(enummDT.isEquivalent(myEnum));
}
@Test
public void testNameSort() {
Enum myEnum = new EnumDataType("Color", 1);
myEnum.add("Red", 1);
myEnum.add("Green", 5);
myEnum.add("Blue", 10);
String[] names = myEnum.getNames();
assertEquals("Red", names[0]);
assertEquals("Green", names[1]);
assertEquals("Blue", names[2]);
myEnum = new EnumDataType("Color", 1);
myEnum.add("Red", 20);
myEnum.add("Green", 1);
myEnum.add("Blue", 3);
names = myEnum.getNames();
assertEquals("Green", names[0]);
assertEquals("Blue", names[1]);
assertEquals("Red", names[2]);
// multiple names per value, requires sub-sorting
myEnum = new EnumDataType("Color", 1);
myEnum.add("Red", 20);
myEnum.add("Pink", 20);
myEnum.add("Salmon", 20);
myEnum.add("Green", 1);
myEnum.add("AnotherGreen", 1);
myEnum.add("Blue", 3);
names = myEnum.getNames();
assertEquals("AnotherGreen", names[0]);
assertEquals("Green", names[1]);
assertEquals("Blue", names[2]);
assertEquals("Pink", names[3]);
assertEquals("Red", names[4]);
assertEquals("Salmon", names[5]);
} }
private void waitForListenerCount(DomainObjListener listener, int count) { private void waitForListenerCount(DomainObjListener listener, int count) {
int cnt = 0; waitForCondition(() -> listener.getCount() == count);
try {
while (cnt++ < 10 && listener.getCount() != count) {
Thread.sleep(100);
}
Thread.sleep(300);
}
catch (InterruptedException e) {
}
} }
private class DomainObjListener implements DomainObjectListener { private class DomainObjListener implements DomainObjectListener {
private int count; private int count;
/* (non-Javadoc)
* @see ghidra.framework.model.DomainObjectListener#domainObjectChanged(ghidra.framework.model.DomainObjectChangedEvent)
*/
@Override @Override
public void domainObjectChanged(DomainObjectChangedEvent ev) { public void domainObjectChanged(DomainObjectChangedEvent ev) {
for (int i = 0; i < ev.numRecords(); i++) { for (int i = 0; i < ev.numRecords(); i++) {

View file

@ -45,7 +45,7 @@ class EnumDB extends DataTypeDB implements Enum {
private EnumValueDBAdapter valueAdapter; private EnumValueDBAdapter valueAdapter;
private Map<String, Long> nameMap; // name to value private Map<String, Long> nameMap; // name to value
private Map<Long, List<String>> valueMap; // value to names private TreeMap<Long, List<String>> valueMap; // value to names
private Map<String, String> commentMap; // name to comment private Map<String, String> commentMap; // name to comment
private List<BitGroup> bitGroups; private List<BitGroup> bitGroups;
@ -86,7 +86,7 @@ class EnumDB extends DataTypeDB implements Enum {
private void initialize() throws IOException { private void initialize() throws IOException {
bitGroups = null; bitGroups = null;
nameMap = new HashMap<>(); nameMap = new HashMap<>();
valueMap = new HashMap<>(); valueMap = new TreeMap<>();
commentMap = new HashMap<>(); commentMap = new HashMap<>();
Field[] ids = valueAdapter.getValueIdsInEnum(key); Field[] ids = valueAdapter.getValueIdsInEnum(key);
@ -190,9 +190,7 @@ class EnumDB extends DataTypeDB implements Enum {
try { try {
checkIsValid(); checkIsValid();
initializeIfNeeded(); initializeIfNeeded();
long[] values = valueMap.keySet().stream().mapToLong(Long::longValue).toArray(); return valueMap.keySet().stream().mapToLong(Long::longValue).toArray();
Arrays.sort(values);
return values;
} }
finally { finally {
lock.release(); lock.release();
@ -205,9 +203,15 @@ class EnumDB extends DataTypeDB implements Enum {
try { try {
checkIsValid(); checkIsValid();
initializeIfNeeded(); initializeIfNeeded();
String[] names = nameMap.keySet().toArray(new String[nameMap.size()]);
Arrays.sort(names); // names are first sorted by int value, then sub-sorted by name value
return names; List<String> names = new ArrayList<>();
Collection<List<String>> values = valueMap.values();
for (List<String> list : values) {
Collections.sort(list);
names.addAll(list);
}
return names.toArray(new String[0]);
} }
finally { finally {
lock.release(); lock.release();
@ -319,7 +323,7 @@ class EnumDB extends DataTypeDB implements Enum {
bitGroups = null; bitGroups = null;
nameMap = new HashMap<>(); nameMap = new HashMap<>();
valueMap = new HashMap<>(); valueMap = new TreeMap<>();
commentMap = new HashMap<>(); commentMap = new HashMap<>();
Field[] ids = valueAdapter.getValueIdsInEnum(key); Field[] ids = valueAdapter.getValueIdsInEnum(key);

View file

@ -19,14 +19,15 @@ import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.*; import java.util.*;
import org.apache.commons.lang3.StringUtils;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
/** /**
* A class used to convert data types into ANSI-C. * A class used to convert data types into ANSI-C.
* *
* The ANSI-C code should compile on most platforms. * The ANSI-C code should compile on most platforms.
*/ */
public class DataTypeWriter { public class DataTypeWriter {
@ -53,25 +54,23 @@ public class DataTypeWriter {
private boolean cppStyleComments = false; private boolean cppStyleComments = false;
/** /**
* Constructs a new instance of this class using the * Constructs a new instance of this class using the given writer. The default annotation
* given writer. The default annotation handler is used. * handler is used.
* @param dtm data-type manager corresponding to target program or null * @param dtm data-type manager corresponding to target program or null for default
* for default
* @param writer the writer to use when writing data types * @param writer the writer to use when writing data types
* @throws IOException * @throws IOException if there is an exception writing the output
*/ */
public DataTypeWriter(DataTypeManager dtm, Writer writer) throws IOException { public DataTypeWriter(DataTypeManager dtm, Writer writer) throws IOException {
this(dtm, writer, new DefaultAnnotationHandler()); this(dtm, writer, new DefaultAnnotationHandler());
} }
/** /**
* Constructs a new instance of this class using the * Constructs a new instance of this class using the given writer. The default annotation
* given writer. The default annotation handler is used. * handler is used.
* @param dtm data-type manager corresponding to target program or null * @param dtm data-type manager corresponding to target program or null for default
* for default
* @param writer the writer to use when writing data types * @param writer the writer to use when writing data types
* @param cppStyleComments whether to use C++ style comments * @param cppStyleComments whether to use C++ style comments
* @throws IOException * @throws IOException if there is an exception writing the output
*/ */
public DataTypeWriter(DataTypeManager dtm, Writer writer, boolean cppStyleComments) public DataTypeWriter(DataTypeManager dtm, Writer writer, boolean cppStyleComments)
throws IOException { throws IOException {
@ -79,13 +78,11 @@ public class DataTypeWriter {
} }
/** /**
* Constructs a new instance of this class using the * Constructs a new instance of this class using the given writer and annotation handler
* given writer and annotation handler * @param dtm data-type manager corresponding to target program or null for default
* @param dtm data-type manager corresponding to target program or null
* for default
* @param writer the writer to use when writing data types * @param writer the writer to use when writing data types
* @param annotator the annotation handler to use to annotate the data types * @param annotator the annotation handler to use to annotate the data types
* @throws IOException * @throws IOException if there is an exception writing the output
*/ */
public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator) public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator)
throws IOException { throws IOException {
@ -93,14 +90,12 @@ public class DataTypeWriter {
} }
/** /**
* Constructs a new instance of this class using the * Constructs a new instance of this class using the given writer and annotation handler
* given writer and annotation handler * @param dtm data-type manager corresponding to target program or null for default
* @param dtm data-type manager corresponding to target program or null
* for default
* @param writer the writer to use when writing data types * @param writer the writer to use when writing data types
* @param annotator the annotation handler to use to annotate the data types * @param annotator the annotation handler to use to annotate the data types
* @param cppStyleComments whether to use C++ style comments * @param cppStyleComments whether to use C++ style comments
* @throws IOException * @throws IOException if there is an exception writing the output
*/ */
public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator, public DataTypeWriter(DataTypeManager dtm, Writer writer, AnnotationHandler annotator,
boolean cppStyleComments) throws IOException { boolean cppStyleComments) throws IOException {
@ -130,11 +125,11 @@ public class DataTypeWriter {
} }
/** /**
* Converts all data types in the data type manager into ANSI-C code. * Converts all data types in the data type manager into ANSI-C code.
* @param dataTypeManager the manager containing the data types to write * @param dataTypeManager the manager containing the data types to write
* @param monitor the task monitor * @param monitor the task monitor
* @throws IOException if an I/O error occurs when writing the data types to the specified writer * @throws IOException if there is an exception writing the output
* @throws CancelledException * @throws CancelledException if the action is cancelled by the user
*/ */
public void write(DataTypeManager dataTypeManager, TaskMonitor monitor) public void write(DataTypeManager dataTypeManager, TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -142,11 +137,11 @@ public class DataTypeWriter {
} }
/** /**
* Converts all data types in the category into ANSI-C code. * Converts all data types in the category into ANSI-C code.
* @param category the category containing the datatypes to write * @param category the category containing the datatypes to write
* @param monitor the task monitor * @param monitor the task monitor
* @throws IOException if an I/O error occurs when writing the data types to the specified writer * @throws IOException if there is an exception writing the output
* @throws CancelledException * @throws CancelledException if the action is cancelled by the user
*/ */
public void write(Category category, TaskMonitor monitor) public void write(Category category, TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -163,11 +158,11 @@ public class DataTypeWriter {
} }
/** /**
* Converts all data types in the array into ANSI-C code. * Converts all data types in the array into ANSI-C code.
* @param dataTypes the data types to write * @param dataTypes the data types to write
* @param monitor the task monitor * @param monitor the task monitor
* @throws IOException if an I/O error occurs when writing the data types to the specified writer * @throws IOException if there is an exception writing the output
* @throws CancelledException * @throws CancelledException if the action is cancelled by the user
*/ */
public void write(DataType[] dataTypes, TaskMonitor monitor) public void write(DataType[] dataTypes, TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -181,11 +176,11 @@ public class DataTypeWriter {
} }
/** /**
* Converts all data types in the list into ANSI-C code. * Converts all data types in the list into ANSI-C code.
* @param dataTypes the data types to write * @param dataTypes the data types to write
* @param monitor the task monitor * @param monitor the task monitor
* @throws IOException if an I/O error occurs when writing the data types to the specified writer * @throws IOException if there is an exception writing the output
* @throws CancelledException * @throws CancelledException if the action is cancelled by the user
*/ */
public void write(List<DataType> dataTypes, TaskMonitor monitor) public void write(List<DataType> dataTypes, TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -222,8 +217,8 @@ public class DataTypeWriter {
/** /**
* Writes the data type as ANSI-C using the underlying writer. * Writes the data type as ANSI-C using the underlying writer.
* @param dt the data type to write as ANSI-C * @param dt the data type to write as ANSI-C
* @param monitor * @param monitor the task monitor
* @throws IOException * @throws IOException if there is an exception writing the output
*/ */
private void doWrite(DataType dt, TaskMonitor monitor, boolean throwExceptionOnInvalidType) private void doWrite(DataType dt, TaskMonitor monitor, boolean throwExceptionOnInvalidType)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -456,7 +451,7 @@ public class DataTypeWriter {
String compositeType = composite instanceof Structure ? "struct" : "union"; String compositeType = composite instanceof Structure ? "struct" : "union";
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
sb.append(compositeType + " " + composite.getDisplayName() + " {"); sb.append(compositeType + " " + composite.getDisplayName() + " {");
String descrip = composite.getDescription(); String descrip = composite.getDescription();
@ -478,7 +473,7 @@ public class DataTypeWriter {
writer.write(EOL); writer.write(EOL);
} }
private void writeComponent(DataTypeComponent component, Composite composite, StringBuffer sb, private void writeComponent(DataTypeComponent component, Composite composite, StringBuilder sb,
TaskMonitor monitor) throws IOException, CancelledException { TaskMonitor monitor) throws IOException, CancelledException {
sb.append(" "); sb.append(" ");
sb.append(annotator.getPrefix(composite, component)); sb.append(annotator.getPrefix(composite, component));
@ -504,14 +499,13 @@ public class DataTypeWriter {
} }
private String getTypeDeclaration(String name, DataType dataType, int instanceLength, private String getTypeDeclaration(String name, DataType dataType, int instanceLength,
boolean writeEnabled, TaskMonitor monitor) boolean writeEnabled, TaskMonitor monitor) throws IOException, CancelledException {
throws IOException, CancelledException {
if (name == null) { if (name == null) {
name = ""; name = "";
} }
StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder();
String componentString = null; String componentString = null;
if (dataType instanceof Dynamic) { if (dataType instanceof Dynamic) {
componentString = getDynamicComponentString((Dynamic) dataType, name, instanceLength); componentString = getDynamicComponentString((Dynamic) dataType, name, instanceLength);
@ -581,11 +575,12 @@ public class DataTypeWriter {
} }
writer.write("typedef enum " + enumName + " " + "{"); writer.write("typedef enum " + enumName + " " + "{");
String descrip = enumm.getDescription(); String description = enumm.getDescription();
if (descrip != null && descrip.length() != 0) { if (description != null && description.length() != 0) {
writer.write(" " + comment(descrip)); writer.write(" " + comment(description));
} }
writer.write(EOL); writer.write(EOL);
String[] names = enumm.getNames(); String[] names = enumm.getNames();
for (int j = 0; j < names.length; j++) { for (int j = 0; j < names.length; j++) {
writer.write(" "); writer.write(" ");
@ -593,7 +588,14 @@ public class DataTypeWriter {
writer.write(names[j]); writer.write(names[j]);
writer.write("="); writer.write("=");
writer.write(Long.toString(enumm.getValue(names[j]))); writer.write(Long.toString(enumm.getValue(names[j])));
String comment = enumm.getComment(names[j]);
if (!StringUtils.isBlank(comment)) {
writer.write(" " + comment(comment));
}
writer.write(annotator.getSuffix(enumm, names[j])); writer.write(annotator.getSuffix(enumm, names[j]));
if (j < names.length - 1) { if (j < names.length - 1) {
writer.write(","); writer.write(",");
} }
@ -606,7 +608,7 @@ public class DataTypeWriter {
/** /**
* Typedef Format: typedef <TYPE_DEF_NAME> <BASE_TYPE_NAME> * Typedef Format: typedef <TYPE_DEF_NAME> <BASE_TYPE_NAME>
* @throws CancelledException * @throws CancelledException if the action is cancelled by the user
*/ */
private void writeTypeDef(TypeDef typeDef, TaskMonitor monitor) private void writeTypeDef(TypeDef typeDef, TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
@ -704,16 +706,14 @@ public class DataTypeWriter {
} }
/** /**
* Write all built-in data types declarations into ANSI-C code. * Write all built-in data types declarations into ANSI-C code. Those types whose name matches
* Those types whose name matches the corresponding primitive C-type. * the corresponding primitive C-type. are not included.
* are not included. * @throws IOException if there is an exception writing the output
* @throws IOException if an I/O error occurs when writing the data types to the specified writer
* @throws CancelledException
*/ */
private void writeBuiltInDeclarations(DataTypeManager manager) throws IOException { private void writeBuiltInDeclarations(DataTypeManager manager) throws IOException {
try { try {
write(DataType.DEFAULT, TaskMonitorAdapter.DUMMY_MONITOR); write(DataType.DEFAULT, TaskMonitor.DUMMY);
SourceArchive builtInArchive = SourceArchive builtInArchive =
manager.getSourceArchive(DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID); manager.getSourceArchive(DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID);
@ -726,7 +726,7 @@ public class DataTypeWriter {
(dt instanceof Dynamic)) { (dt instanceof Dynamic)) {
continue; continue;
} }
write(dt, TaskMonitorAdapter.DUMMY_MONITOR); write(dt, TaskMonitor.DUMMY);
} }
} }
catch (CancelledException e) { catch (CancelledException e) {
@ -872,7 +872,7 @@ public class DataTypeWriter {
buf.append(ghidra.program.model.listing.FunctionSignature.VAR_ARGS_DISPLAY_STRING); buf.append(ghidra.program.model.listing.FunctionSignature.VAR_ARGS_DISPLAY_STRING);
} }
if ((n == 0) && (!hasVarArgs)) { // If no parameters if ((n == 0) && (!hasVarArgs)) { // If no parameters
buf.append(DataType.VOID.getName()); // Print "void" keyword buf.append(VoidDataType.dataType.getName()); // Print "void" keyword
} }
buf.append(")"); buf.append(")");
return buf.toString(); return buf.toString();

View file

@ -52,8 +52,8 @@ public interface Enum extends DataType {
public long[] getValues(); public long[] getValues();
/** /**
* Get the names of the enum entries. The returned names are sorted using String's natural * Get the names of the enum entries. The returned names are first sorted by the enum int
* sort order. * value, then sub-sorted by name value where there are multiple name values per int value.
* @return the names of the enum entries. * @return the names of the enum entries.
*/ */
public String[] getNames(); public String[] getNames();

View file

@ -34,7 +34,7 @@ public class EnumDataType extends GenericDataType implements Enum {
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF }; new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private Map<String, Long> nameMap; // name to value private Map<String, Long> nameMap; // name to value
private Map<Long, List<String>> valueMap; // value to names private TreeMap<Long, List<String>> valueMap; // value to names
private Map<String, String> commentMap; // name to comment private Map<String, String> commentMap; // name to comment
private int length; private int length;
private String description; private String description;
@ -54,7 +54,7 @@ public class EnumDataType extends GenericDataType implements Enum {
throw new IllegalArgumentException("unsupported enum length: " + length); throw new IllegalArgumentException("unsupported enum length: " + length);
} }
nameMap = new HashMap<>(); nameMap = new HashMap<>();
valueMap = new HashMap<>(); valueMap = new TreeMap<>();
commentMap = new HashMap<>(); commentMap = new HashMap<>();
this.length = length; this.length = length;
} }
@ -68,7 +68,7 @@ public class EnumDataType extends GenericDataType implements Enum {
throw new IllegalArgumentException("unsupported enum length: " + length); throw new IllegalArgumentException("unsupported enum length: " + length);
} }
nameMap = new HashMap<>(); nameMap = new HashMap<>();
valueMap = new HashMap<>(); valueMap = new TreeMap<>();
commentMap = new HashMap<>(); commentMap = new HashMap<>();
this.length = length; this.length = length;
} }
@ -108,15 +108,19 @@ public class EnumDataType extends GenericDataType implements Enum {
@Override @Override
public long[] getValues() { public long[] getValues() {
long[] values = valueMap.keySet().stream().mapToLong(Long::longValue).toArray(); long[] values = valueMap.keySet().stream().mapToLong(Long::longValue).toArray();
Arrays.sort(values);
return values; return values;
} }
@Override @Override
public String[] getNames() { public String[] getNames() {
String[] names = nameMap.keySet().toArray(new String[nameMap.size()]); // names are first sorted by int value, then sub-sorted by name value
Arrays.sort(names); List<String> names = new ArrayList<>();
return names; Collection<List<String>> values = valueMap.values();
for (List<String> list : values) {
Collections.sort(list);
names.addAll(list);
}
return names.toArray(new String[0]);
} }
@Override @Override
@ -417,7 +421,7 @@ public class EnumDataType extends GenericDataType implements Enum {
} }
Enum enumm = (Enum) dataType; Enum enumm = (Enum) dataType;
nameMap = new HashMap<>(); nameMap = new HashMap<>();
valueMap = new HashMap<>(); valueMap = new TreeMap<>();
commentMap = new HashMap<>(); commentMap = new HashMap<>();
setLength(enumm.getLength()); setLength(enumm.getLength());
String[] names = enumm.getNames(); String[] names = enumm.getNames();

View file

@ -33,10 +33,6 @@ public class DataTypeWriterTest extends AbstractGTest {
private StringWriter writer; private StringWriter writer;
private DataTypeWriter dtWriter; private DataTypeWriter dtWriter;
public DataTypeWriterTest() {
super();
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@ -105,8 +101,16 @@ public class DataTypeWriterTest extends AbstractGTest {
enumm.add("E", 4); enumm.add("E", 4);
dtWriter.write(enumm, TaskMonitor.DUMMY); dtWriter.write(enumm, TaskMonitor.DUMMY);
String actual = writer.getBuffer().toString(); String actual = writer.getBuffer().toString();
String expected = "typedef enum myEnum {" + EOL + " A=0," + EOL + " B=1," + EOL +
" C=2," + EOL + " D=3," + EOL + " E=4" + EOL + "} myEnum;" + EOL + EOL; //@formatter:off
String expected = "typedef enum myEnum {" + EOL +
" A=0," + EOL +
" B=1," + EOL +
" C=2," + EOL +
" D=3," + EOL +
" E=4" + EOL +
"} myEnum;" + EOL + EOL;
//@formatter:on
assertEquals(expected, actual); assertEquals(expected, actual);
} }
@ -120,8 +124,84 @@ public class DataTypeWriterTest extends AbstractGTest {
enumm.add("E", 254); enumm.add("E", 254);
dtWriter.write(enumm, TaskMonitor.DUMMY); dtWriter.write(enumm, TaskMonitor.DUMMY);
String actual = writer.getBuffer().toString(); String actual = writer.getBuffer().toString();
String expected = "typedef enum myEnum {" + EOL + " A=4," + EOL + " B=8," + EOL +
" C=16," + EOL + " D=32," + EOL + " E=254" + EOL + "} myEnum;" + EOL + EOL; //@formatter:off
String expected = "typedef enum myEnum {" + EOL +
" A=4," + EOL +
" B=8," + EOL +
" C=16," + EOL +
" D=32," + EOL +
" E=254" + EOL +
"} myEnum;" + EOL + EOL;
//@formatter:on
assertEquals(expected, actual);
}
@Test
public void testEnum_WithComments() throws IOException, CancelledException {
Enum enumm = new EnumDataType("myEnum", 1);
enumm.add("A", 0);
enumm.add("B", 1, "B Comment");
enumm.add("C", 2);
enumm.add("D", 3, "D Comment");
enumm.add("E", 4);
dtWriter.write(enumm, TaskMonitor.DUMMY);
String actual = writer.getBuffer().toString();
//@formatter:off
String expected = "typedef enum myEnum {" + EOL +
" A=0," + EOL +
" B=1 /* B Comment */," + EOL +
" C=2," + EOL +
" D=3 /* D Comment */," + EOL +
" E=4" + EOL +
"} myEnum;" + EOL + EOL;
//@formatter:on
assertEquals(expected, actual);
}
@Test
public void testEnum_MultipleNamesPerValue() throws IOException, CancelledException {
Enum enumm = new EnumDataType("myEnum", 1);
enumm.add("A", 4);
enumm.add("Two", 8);
enumm.add("One", 8);
enumm.add("End", 32);
dtWriter.write(enumm, TaskMonitor.DUMMY);
String actual = writer.getBuffer().toString();
//@formatter:off
String expected = "typedef enum myEnum {" + EOL +
" A=4," + EOL +
" One=8," + EOL +
" Two=8," + EOL +
" End=32" + EOL +
"} myEnum;" + EOL + EOL;
//@formatter:on
assertEquals(expected, actual);
}
@Test
public void testEnum_NamesOrderDifferenentThanValueOrder()
throws IOException, CancelledException {
Enum enumm = new EnumDataType("myEnum", 1);
enumm.add("E", 4);
enumm.add("C", 8);
enumm.add("D", 16);
enumm.add("B", 32);
enumm.add("A", 254);
dtWriter.write(enumm, TaskMonitor.DUMMY);
String actual = writer.getBuffer().toString();
//@formatter:off
String expected = "typedef enum myEnum {" + EOL +
" E=4," + EOL +
" C=8," + EOL +
" D=16," + EOL +
" B=32," + EOL +
" A=254" + EOL +
"} myEnum;" + EOL + EOL;
//@formatter:on
assertEquals(expected, actual); assertEquals(expected, actual);
} }

View file

@ -22,7 +22,7 @@ import java.util.NoSuchElementException;
import org.junit.*; import org.junit.*;
import generic.test.AbstractGTest; import generic.test.AbstractGTest;
import ghidra.util.task.TaskMonitorAdapter; import ghidra.util.task.TaskMonitor;
/** /**
* Tests for Enum data types. * Tests for Enum data types.
@ -166,7 +166,7 @@ public class EnumTest extends AbstractGTest {
Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER); Enum enummDT = (Enum) c.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER);
assertNotNull(enummDT); assertNotNull(enummDT);
c.remove(enummDT, TaskMonitorAdapter.DUMMY_MONITOR); c.remove(enummDT, TaskMonitor.DUMMY);
assertNull(c.getDataType("Color")); assertNull(c.getDataType("Color"));
assertTrue(enummDT.isDeleted()); assertTrue(enummDT.isDeleted());
@ -235,6 +235,7 @@ public class EnumTest extends AbstractGTest {
Assert.fail("Should have gotten no such element exception!"); Assert.fail("Should have gotten no such element exception!");
} }
catch (NoSuchElementException e) { catch (NoSuchElementException e) {
// expected
} }
} }