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

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

View file

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

View file

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

View file

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

View file

@ -33,10 +33,6 @@ public class DataTypeWriterTest extends AbstractGTest {
private StringWriter writer;
private DataTypeWriter dtWriter;
public DataTypeWriterTest() {
super();
}
@Before
public void setUp() throws Exception {
@ -105,8 +101,16 @@ public class DataTypeWriterTest extends AbstractGTest {
enumm.add("E", 4);
dtWriter.write(enumm, TaskMonitor.DUMMY);
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);
}
@ -120,8 +124,84 @@ public class DataTypeWriterTest extends AbstractGTest {
enumm.add("E", 254);
dtWriter.write(enumm, TaskMonitor.DUMMY);
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);
}

View file

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