diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm index 361f6f127d..75944bace4 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm @@ -25,17 +25,24 @@

When you make any change to the enum, the diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png index 3dfef311d2..4eceff2d80 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java index f2bbdd6e9b..30a42453f5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java @@ -246,6 +246,30 @@ class DataTypePanel extends JPanel { insertLength(comp); } + private class EnumEntry implements Comparable { + + private final String name; + private final long value; + private final String comment; + + EnumEntry(String name, long value, String comment) { + this.name = name; + this.value = value; + this.comment = comment; + + } + + @Override + public int compareTo(EnumEntry o) { + int c = Long.compare(value, o.value); + if (c == 0) { + c = name.compareTo(o.name); + } + return c; + } + + } + private void formatEnumText(Enum enuum) { formatSourceArchive(enuum); formatPath(enuum); @@ -254,28 +278,41 @@ class DataTypePanel extends JPanel { StringBuffer sb = new StringBuffer(); - String[] names = enuum.getNames(); - int maxLength = 0; - for (String name : names) { - if (name.length() > maxLength) { - maxLength = name.length(); - } - } - long[] values = enuum.getValues(); - Arrays.sort(values); + int maxNameLength = 0; + int maxValueLength = 0; - for (int i = 0; i < values.length; i++) { - String name = enuum.getName(values[i]); - name = pad(name, maxLength); - sb.append(" " + name + " = 0x" + Long.toHexString(values[i]) + " "); - if (i < values.length - 1) { - sb.append("\n"); - } + String[] names = enuum.getNames(); + EnumEntry[] entries = new EnumEntry[names.length]; + for (int i = 0; i < names.length; i++) { + String name = names[i]; + EnumEntry entry = new EnumEntry(name, enuum.getValue(name), enuum.getComment(name)); + entries[i] = entry; + maxNameLength = Math.max(maxNameLength, name.length()); + String valStr = Long.toHexString(entry.value); + maxValueLength = Math.max(maxValueLength, valStr.length()); } - sb.append("\n }\n"); + Arrays.sort(entries); + + for (EnumEntry entry : entries) { + renderEnumEntry(entry, maxNameLength, maxValueLength); + } + sb.append("}\n"); insertString(sb.toString(), contentAttrSet); } + private void renderEnumEntry(EnumEntry entry, int maxNameLength, int maxValueLength) { + String name = entry.name; + name = pad(name, maxNameLength); + String valStr = Long.toHexString(entry.value); + valStr = pad(valStr, maxValueLength); + insertString(" " + name, fieldNameAttrSet); + insertString(" = 0x" + valStr, contentAttrSet); + if (entry.comment != null) { + insertString(" " + entry.comment, commentAttrSet); + } + insertString("\n", contentAttrSet); + } + private void formatTypeDefText(TypeDef td) { formatSourceArchive(td); formatPath(td); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEntry.java index cff8937317..955a41cc63 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEntry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEntry.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +15,8 @@ */ package ghidra.app.plugin.core.datamgr.editor; +import generic.json.Json; + public class EnumEntry { private String name; private long value; @@ -50,4 +51,9 @@ public class EnumEntry { public void setComment(String newComment) { this.comment = newComment; } + + @Override + public String toString() { + return Json.toString(this); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumTableModel.java index ce352784a3..3fc52763c6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumTableModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumTableModel.java @@ -266,8 +266,8 @@ class EnumTableModel extends AbstractSortedTableModel { } private long findNextValue(int afterRow) { - if (enumEntryList.size() == 0) { - return new Long(0); + if (enumEntryList.isEmpty()) { + return 0; } if (afterRow < 0 || afterRow >= enumEntryList.size()) { afterRow = 0; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ArchiveConverterPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ArchiveConverterPlugin.java deleted file mode 100644 index 312acc404b..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ArchiveConverterPlugin.java +++ /dev/null @@ -1,698 +0,0 @@ -/* ### - * 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. - */ -/* - * Created on Aug 3, 2005 - */ -package ghidra.app.plugin.prototype.dataArchiveUtilities; - -import java.awt.Component; -import java.io.*; -import java.util.*; - -import docking.ActionContext; -import docking.action.DockingAction; -import docking.action.MenuData; -import docking.tool.ToolConstants; -import docking.widgets.filechooser.GhidraFileChooser; -import ghidra.MiscellaneousPluginPackage; -import ghidra.app.context.ListingActionContext; -import ghidra.app.plugin.PluginCategoryNames; -import ghidra.app.plugin.ProgramPlugin; -import ghidra.app.util.xml.DataTypesXmlMgr; -import ghidra.framework.plugintool.PluginInfo; -import ghidra.framework.plugintool.PluginTool; -import ghidra.framework.plugintool.util.*; -import ghidra.framework.preferences.Preferences; -import ghidra.program.model.data.*; -import ghidra.program.model.data.Enum; -import ghidra.program.model.listing.Program; -import ghidra.util.Msg; -import ghidra.util.filechooser.ExtensionFileFilter; -import ghidra.util.task.*; - -//@formatter:off -@PluginInfo( - status = PluginStatus.STABLE, - packageName = MiscellaneousPluginPackage.NAME, - category = PluginCategoryNames.CODE_VIEWER, - shortDescription = "Convert data archives", - description = "This plugin allows the user to convert GSL-generated archives back and forth to Ghidra data archives." -) -//@formatter:on -public class ArchiveConverterPlugin extends ProgramPlugin { - - private static final String IMPORT_EXPORT_GROUP = "Import/Export"; - final static String CONVERT_DATA = "Convert GSL- to data-archive"; - final static String GSL_ARCHIVE_DIR = "GSL Archive Directory"; - final static String GDT_ARCHIVE_DIR = "GDT Archive Directory"; - final static String ALIGNMENT_TAG = "added for alignment"; - - final static String WRITE_GSL = "Write data-archive in GSL Format"; - - private GhidraFileChooser fileChooser; - private TypedefDataType dataUI; - private Hashtable dataTypes = new Hashtable<>(); - - /** - * @param id - * @param plugintool - * @param consumeLocationChange - * @param consumeSelectionChange - */ - public ArchiveConverterPlugin(PluginTool plugintool) { - super(plugintool, false, false); - createActions(); - } - - public Program getProgram() { - return currentProgram; - } - - @Override - public void dispose() { - super.dispose(); - } - - /** - * - */ - private void createActions() { - DockingAction parseAction = new DockingAction(CONVERT_DATA, getName()) { - @Override - public void actionPerformed(ActionContext context) { - File gslArchive = - chooseFile(tool.getToolFrame(), "Select GSL archive", GSL_ARCHIVE_DIR, "gsl"); - new TaskLauncher(new GSLParserTask(tool, gslArchive), tool.getToolFrame()); - } - - @Override - public boolean isEnabledForContext(ActionContext context) { - return context.getContextObject() instanceof ListingActionContext; - } - }; - String[] menuPath = { ToolConstants.MENU_FILE, "Parse GSL Archive..." }; - parseAction.setMenuBarData(new MenuData(menuPath, IMPORT_EXPORT_GROUP)); - parseAction.setEnabled(true); - tool.addAction(parseAction); - - DockingAction writeGslAction = new DockingAction(WRITE_GSL, getName()) { - @Override - public void actionPerformed(ActionContext context) { - File gslInfile = chooseFile(tool.getToolFrame(), "Select input file", - GDT_ARCHIVE_DIR, FileDataTypeManager.SUFFIX); - File gslOutfile = - chooseFile(tool.getToolFrame(), "Select output file", GSL_ARCHIVE_DIR, "gsl"); - new TaskLauncher(new GSLWriterTask(tool, gslInfile, gslOutfile), - tool.getToolFrame()); - } - - @Override - public boolean isEnabledForContext(ActionContext context) { - return context.getContextObject() instanceof ListingActionContext; - } - }; - String[] menuPath2 = { ToolConstants.MENU_FILE, "Write GSL Archive..." }; - writeGslAction.setMenuBarData(new MenuData(menuPath2, IMPORT_EXPORT_GROUP)); - writeGslAction.setEnabled(true); - tool.addAction(writeGslAction); - } - - private class GSLParserTask extends Task { - PluginTool myTool; - File gslArchive; - - public GSLParserTask(PluginTool tool, File gslArchive) { - super("GSL Archive Parser", true, true, false); - this.gslArchive = gslArchive; - this.myTool = tool; - } - - @Override - public void run(TaskMonitor monitor) { - int lineCount = 0; - try { - if (gslArchive != null) { - String gslName = gslArchive.getName(); - String gslNameWithPath = gslArchive.getAbsolutePath(); - monitor.setMessage("Parsing " + gslName); - FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive( - new File(gslNameWithPath + FileDataTypeManager.SUFFIX)); - int id = dtMgr.startTransaction("process archive"); - try { - addPrimitives(dtMgr); - - BufferedReader reader = new BufferedReader(new FileReader(gslArchive)); - while (reader.readLine() != null) { - lineCount++; - } - reader.close(); - monitor.initialize(lineCount); - - reader = new BufferedReader(new FileReader(gslArchive)); - String line; - lineCount = 0; - DataType dt = null; - while (((line = reader.readLine()) != null) && !monitor.isCancelled()) { - try { - dt = parseLine(dtMgr, line); - if (dt != null) { - addDataType(dtMgr, dataTypes, dt); - } - if (lineCount % 100 == 0) { - monitor.setProgress(lineCount); - } - lineCount++; - } - catch (Exception e) { - Msg.error(this, - "Error in " + name + " at line " + lineCount + " of " + - gslArchive.getName() + - "...possibly an attempt to redefine a Ghidra primitive", - e); - } - } - reader.close(); - - } - finally { - dtMgr.endTransaction(id, true); - } - - monitor.setMessage("Checking for parser errors"); - searchForErrors(dtMgr); - - monitor.setMessage("Writing XML file"); - try { - DataTypesXmlMgr.writeAsXMLForDebug(dtMgr, gslNameWithPath); - } - catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - - try { - dtMgr.save(); - } - catch (Exception e) { - Msg.showError(this, myTool.getToolFrame(), "GSL Archive Parser", - gslNameWithPath + ".gdt already exists - not overwritten", e); - } - dtMgr.close(); - } - } - catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - return; - } - } - - private class GSLWriterTask extends Task { - File gslInfile; - File gslOutfile; - - public GSLWriterTask(PluginTool tool, File gslInfile, File gslOutfile) { - super("GSL Archive Writer", true, false, false); - this.gslInfile = gslInfile; - this.gslOutfile = gslOutfile; - } - - @Override - public void run(TaskMonitor monitor) { - if (gslInfile != null) { - DataTypeManager dtMgr = null; - try { - dtMgr = FileDataTypeManager.openFileArchive(gslInfile, false); - } - catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - if (dtMgr != null) { - try { - monitor.setMessage("Writing " + gslOutfile.getName()); - writeAsGsl(gslOutfile, dtMgr, monitor); - } - finally { - dtMgr.close(); - } - } - } - return; - } - } - - private DataType parseLine(FileDataTypeManager dtMgr, String line) { - DataType dt = null; - int index; - // "$$" doesn't get parsed correctly, so replace it with "$ $" - while ((index = line.indexOf("$$")) >= 0) { - line = line.substring(0, index + 1) + "noname" + line.substring(index + 1); - } - while ((index = line.indexOf("//")) >= 0) { - line = line.substring(0, index) + line.substring(index + 1); - } - - StringTokenizer tokenizer = new StringTokenizer(line, "$"); - String fieldId = tokenizer.nextToken(); - String complexName = tokenizer.nextToken(); - - ComplexName cName = new ComplexName(complexName); - String myName = cName.getName(); - - if ((fieldId.compareToIgnoreCase("STRUCT") == 0) || - (fieldId.compareToIgnoreCase("UNION") == 0)) { - tokenizer.nextToken(); // size - tokenizer.nextToken(); // alignment - if (fieldId.compareToIgnoreCase("STRUCT") == 0) { - dt = new StructureDataType(cName.getCategoryPath(), myName, 0); - } - else { - dt = new UnionDataType(cName.getCategoryPath(), myName); - } - addDataType(dtMgr, dataTypes, dt); - - int lastOffset = dt.getLength(); - DataType member; - while (tokenizer.hasMoreElements()) { - String fieldName = tokenizer.nextToken(); - String fieldType = tokenizer.nextToken(); - String fieldOffset = tokenizer.nextToken(); - String fieldSize = tokenizer.nextToken(); - String fieldAlign = tokenizer.nextToken(); - - int align = valueOf(fieldAlign); - int fSize = valueOf(fieldSize); - int fOff = valueOf(fieldOffset); - ComplexName fType = new ComplexName(fieldType); - member = fType.getDataType(dtMgr, dataTypes); - - try { - // If we don't know what this is, make something up - if ((member == null) || (member.getLength() < 0)) { - member = genUIData(dtMgr, fType, fOff - dt.getLength()); - fSize = member.getLength(); - } - // Zero length fields are OK if they're the last field (again we fake the size) - if (member.getLength() == 0) { - if (!tokenizer.hasMoreElements()) { - fType.count = 1; - member = fType.getDataType(dtMgr, dataTypes); - fSize = member.getLength(); - } - } - - if ((dt instanceof Structure) && ((fOff < dt.getLength()) || - ((fOff >= dt.getLength()) && (fSize < member.getLength())))) { - if (fOff >= dt.getLength()) { - lastOffset = dt.getLength(); - ((StructureDataType) dt).add(member, member.getLength(), - "_bit_fields_" + dt.getLength(), ""); - } - DataTypeComponent dtc = ((StructureDataType) dt).getComponentAt(lastOffset); - String comment = dtc.getComment(); - comment += " " + fieldName + "(" + fieldSize + ")"; - dtc.setComment(comment); - if (fSize < member.getLength()) { - Msg.debug(this, "Dropping bitfield=[" + fieldName + "] type=[" + - member.getName() + "] in " + myName); - } - else { - Msg.debug(this, "Dropping element=[" + fieldName + "] type=[" + - member.getName() + "] in " + myName); - } - } - else { - // HACKALERT: Big ol' hack here... (GSL appears to be lying to us) - if (align > 4) { - align = 4; - } - int mod = (align == 0) ? 0 : dt.getLength() % align; - DataType modDt = null; - if (mod != 0) { - modDt = new ArrayDataType(new ByteDataType(), align - mod, 1); - } - if (member.getLength() > 0) { - lastOffset = dt.getLength(); - if (modDt != null) { - ((CompositeDataTypeImpl) dt).add(modDt, modDt.getLength(), - "_fill_" + dt.getLength(), ALIGNMENT_TAG); - } - ((CompositeDataTypeImpl) dt).add(member, member.getLength(), fieldName, - ""); - } - else { - Msg.debug(this, "Dropping mid-structure zero length element=[" + - fieldName + "] type=[" + member.getName() + "] in " + myName); - } - } - } - catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - } - } - else if (fieldId.compareToIgnoreCase("FUNCTION") == 0) { - dt = new FunctionDefinitionDataType(cName.getCategoryPath(), cName.getName()); - String retVal = tokenizer.nextToken(); - ComplexName rvName = new ComplexName(retVal); - DataType rvDt = rvName.getDataType(dtMgr, dataTypes); - if (rvDt == null) { - rvDt = genUIData(dtMgr, rvName, 4); - } - ((FunctionDefinitionDataType) dt).setReturnType(rvDt); - ArrayList parameters = new ArrayList<>(); - index = 0; - while (tokenizer.hasMoreElements()) { - String fieldName = tokenizer.nextToken(); - String fieldType = tokenizer.nextToken(); - ComplexName fType = new ComplexName(fieldType); - DataType parameter = fType.getDataType(dtMgr, dataTypes); - if (parameter == null) { - parameter = genUIData(dtMgr, fType, 4); - } - parameters.add(new ParameterDefinitionImpl(fieldName, parameter, "")); - index++; - } - if (index > 0) { - ParameterDefinition[] parms = new ParameterDefinition[parameters.size()]; - parameters.toArray(parms); - ((FunctionDefinitionDataType) dt).setArguments(parms); - } - } - else if (fieldId.compareToIgnoreCase("TYPEDEF") == 0) { - String newName = tokenizer.nextToken(); - try { - DataType baseType = cName.getDataType(dtMgr, dataTypes); - if (baseType == null) { - baseType = genUIData(dtMgr, cName, 4); - } - ComplexName cNewName = new ComplexName(newName); - dt = new TypedefDataType(cNewName.getCategoryPath(), newName, baseType); - } - catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - } - else if (fieldId.compareToIgnoreCase("ENUM") == 0) { - ArrayList fNames = new ArrayList<>(); - ArrayList fValues = new ArrayList<>(); - ArrayList fComments = new ArrayList<>(); - while (tokenizer.hasMoreElements()) { - fNames.add(tokenizer.nextToken()); - fValues.add(tokenizer.nextToken()); - fComments.add(tokenizer.nextToken()); - } - dt = new EnumDataType(cName.getCategoryPath(), cName.getName(), fNames.size(), null); - for (int i = 0; i < fNames.size(); i++) { - ((EnumDataType) dt).add(fNames.get(i), new Long(fValues.get(i)).longValue(), fComments.get(i)); - } - } - else if (fieldId.compareToIgnoreCase("SYMBOL") == 0) { - - } - else { - Msg.warn(this, "What is this? " + fieldId); - } - return dt; - } - - private void searchForErrors(FileDataTypeManager dtMgr) { - try { - Iterator dts = dtMgr.getAllDataTypes(); - while (dts.hasNext()) { - DataType dti = dts.next(); - if (dti.getDisplayName().indexOf("%") >= 0) { - Msg.warn(this, "Misprocessed data type: " + dti.getDisplayName()); - } - if (dti instanceof TypeDef) { - DataType base = ((TypeDef) dti).getBaseDataType(); - if (base instanceof Pointer) { - base = ((Pointer) base).getDataType(); - if (base.isEquivalent(dataUI)) { - Msg.warn(this, "Data type (" + dti.getDisplayName() + ") not found."); - } - } - } - } - } - catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - } - - public DataType genUIData(FileDataTypeManager dtMgr, ComplexName cName, int len) { - TypedefDataType dt = new TypedefDataType(cName.getCategoryPath(), cName.getName(), - new ArrayDataType(new ByteDataType(), len, 1)); - addDataType(dtMgr, dataTypes, dt); - return dt; - } - - int valueOf(String intString) { - return (new Integer(intString)).intValue() / 8; - } - - private void addPrimitives(FileDataTypeManager dtMgr) { - addDataType(dtMgr, dataTypes, new TypedefDataType("char", new ByteDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("signed char", new ByteDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("unsigned char", new ByteDataType())); - - addDataType(dtMgr, dataTypes, new TypedefDataType("short", new WordDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("short int", new WordDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("short signed int", new WordDataType())); - addDataType(dtMgr, dataTypes, - new TypedefDataType("short unsigned int", new WordDataType())); - - addDataType(dtMgr, dataTypes, new TypedefDataType("int", new DWordDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("long", new DWordDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("long int", new DWordDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("signed int", new DWordDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("unsigned int", new DWordDataType())); - addDataType(dtMgr, dataTypes, new TypedefDataType("long signed int", new DWordDataType())); - addDataType(dtMgr, dataTypes, - new TypedefDataType("long unsigned int", new DWordDataType())); - - addDataType(dtMgr, dataTypes, new TypedefDataType("long long int", new QWordDataType())); - addDataType(dtMgr, dataTypes, - new TypedefDataType("long long signed int", new QWordDataType())); - addDataType(dtMgr, dataTypes, - new TypedefDataType("long long unsigned int", new QWordDataType())); - - addDataType(dtMgr, dataTypes, new TypedefDataType("void", new VoidDataType())); - dataUI = new TypedefDataType("U/I", new DWordDataType()); - addDataType(dtMgr, dataTypes, dataUI); - } - - private void addDataType(FileDataTypeManager dtMgr, Hashtable myDataTypes, - DataType dt) { - DataType type = dtMgr.addDataType(dt, DataTypeConflictHandler.REPLACE_HANDLER); - myDataTypes.put(type.getCategoryPath() + type.getName(), type); - } - - private File chooseFile(Component parent, String title, String propertyName, - final String fileType) { - if (fileChooser == null) { - fileChooser = new GhidraFileChooser(parent); - } - fileChooser.setFileFilter(new ExtensionFileFilter(fileType, - fileType.toUpperCase() + " files (." + fileType + ")")); - fileChooser.setTitle(title); - - // start the browsing in the user's preferred directory - // - File directory = - new File(Preferences.getProperty(propertyName, System.getProperty("user.home"), true)); - fileChooser.setCurrentDirectory(directory); - fileChooser.setSelectedFile(directory); - - File file = fileChooser.getSelectedFile(); - if (file != null) { - // record where we last exported a file from to the user's preferences - Preferences.setProperty(propertyName, file.getAbsolutePath()); - } - - return file; - } - - final static String NO_NAMESPACE = "/"; - final int SIZE_X = 8; - final int ALIGNMENT = 32; - - private void writeAsGsl(File file, DataTypeManager dtMgr, TaskMonitor monitor) { - String out = ""; - int count = 0; - try { - FileOutputStream stream = new FileOutputStream(file); - Iterator it = dtMgr.getAllDataTypes(); - while (it.hasNext()) { - DataType dt = it.next(); - out = ""; - if (dt instanceof Composite) { - out = writeComposite(stream, dt); - } - else if (dt instanceof TypeDef) { - out = writeTypeDef(stream, (TypeDef) dt); - } - else if (dt instanceof FunctionDefinition) { - out = writeFunctionDefinition(stream, (FunctionDefinition) dt); - } - else if (dt instanceof Enum) { - out = writeEnum(stream, (Enum) dt); - } - else { - Msg.debug(this, "Something went wrong while printing GSL output..."); - } - if (out != "") { - stream.write(out.getBytes()); - } - if (count % 100 == 0) { - monitor.setProgress(count); - } - count++; - } - stream.close(); - } - catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } - } - - private String writeComposite(FileOutputStream stream, DataType dt) { - String out; - DataTypeComponent[] components; - - out = (dt instanceof Structure) ? "STRUCT$" : "UNION$"; - components = ((Composite) dt).getComponents(); - out += new NameComplex(dt).getGSLName() + "$"; - out += SIZE_X * dt.getLength() + "$"; - out += ALIGNMENT; - for (DataTypeComponent component : components) { - String comment = component.getComment(); - if ((comment != null) && (comment.compareTo(ALIGNMENT_TAG) != 0)) { - NameComplex cComp = new NameComplex(component.getDataType()); - out += "$"; - out += component.getFieldName() + "$"; - out += cComp.getGSLName() + "$"; - out += SIZE_X * component.getOffset() + "$"; - out += SIZE_X * component.getLength() + "$"; - if (component.getDataType() instanceof Composite) { - out += ALIGNMENT; - } - else { - out += SIZE_X * cComp.getBaseTypeSize(); - } - } - } - out += "\n"; - return out; - } - - private String writeTypeDef(FileOutputStream stream, TypeDef def) { - DataType dt = def.getDataType(); - - String out = "TYPEDEF$"; - out += new NameComplex(dt).getGSLName() + "$"; - out += new NameComplex(def).getGSLName() + "\n"; - return out; - } - - private String writeEnum(FileOutputStream stream, Enum enuum) { - String out = "ENUM$"; - String[] names = enuum.getNames(); - long[] values = enuum.getValues(); - - out += new NameComplex(enuum).getGSLName(); - for (int i = 0; i < names.length; i++) { - out += "$"; - out += names[i] + "$"; - out += values[i]; - } - out += "\n"; - return out; - } - - private String writeFunctionDefinition(FileOutputStream stream, FunctionDefinition fn) { - String out = "FUNCTION$"; - ParameterDefinition[] parameters = fn.getArguments(); - DataType ret = fn.getReturnType(); - - out += new NameComplex(fn).getGSLName() + "$"; - out += new NameComplex(ret).getGSLName(); - for (ParameterDefinition parameter : parameters) { - NameComplex cParm = new NameComplex(parameter.getDataType()); - out += "$"; - String pName = parameter.getName(); - out += ((pName.compareToIgnoreCase(" ") == 0) ? "" : pName); - out += "$"; - out += cParm.getGSLName(); - } - out += "\n"; - return out; - } - - public class NameComplex { - - DataType dt; - String myName; - String dtNamespace; - int baseTypeSize; - - public NameComplex(DataType dt) { - this.dt = dt; - dtNamespace = dt.getCategoryPath().getPath(); - - DataType base = dt; - boolean shouldDescend = false; - myName = ""; - if (dt instanceof Pointer) { - myName = "Pointer%"; - base = ((Pointer) dt).getDataType(); - shouldDescend = true; - } - else if (dt instanceof Array) { - Array array = (Array) dt; - myName = - "Array%" + array.getNumElements() + "%" + (SIZE_X * array.getLength() + "%"); - base = array.getDataType(); - shouldDescend = true; - } - - if (shouldDescend) { - NameComplex cName = new NameComplex(base); - myName += cName.getGSLName(); - if (dt instanceof Pointer) { - baseTypeSize = dt.getLength(); - } - else { - baseTypeSize = cName.getBaseTypeSize(); - } - } - else { - if (dtNamespace.compareToIgnoreCase(NO_NAMESPACE) != 0) { - myName += dtNamespace + ":"; - } - myName += dt.getName(); - baseTypeSize = dt.getLength(); - } - } - - public String getGSLName() { - return myName; - } - - public int getBaseTypeSize() { - return baseTypeSize; - } - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ComplexName.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ComplexName.java deleted file mode 100644 index f5228e06b1..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ComplexName.java +++ /dev/null @@ -1,141 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * 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. - */ -/* - * Created on Aug 9, 2005 - * - * TODO To change the template for this generated file go to - * Window - Preferences - Java - Code Style - Code Templates - */ -package ghidra.app.plugin.prototype.dataArchiveUtilities; - -import ghidra.program.model.data.*; -import ghidra.util.Msg; - -import java.util.Hashtable; - -public class ComplexName { - boolean isPointer; - boolean isArray; - boolean usesNamespace; - String complexName; - String namespace; - String name; - int count = 1; - - public ComplexName(String complexName) { - this.complexName = complexName; - if (complexName.startsWith("Pointer%")) { - isPointer = true; - complexName = complexName.substring(complexName.indexOf("%")+1); - } - if (complexName.startsWith("Array%")) { - isArray = true; - complexName = complexName.substring(complexName.indexOf("%")+1); - String countStr = complexName.substring(0, complexName.indexOf("%")); - if (countStr.compareToIgnoreCase("") != 0) { - count = new Integer(countStr).intValue(); - } else { - count = 1; - } - complexName = complexName.substring(complexName.indexOf("%")+1); - complexName = complexName.substring(complexName.indexOf("%")+1); - } - usesNamespace = (complexName.indexOf(":") >= 0); - if (usesNamespace) { - namespace = complexName.substring(complexName.indexOf("/"), complexName.indexOf(":")); - name = complexName.substring(0, complexName.indexOf("/"))+ - complexName.substring(complexName.indexOf(":")+1, complexName.length()); - } else { - namespace = ""; - name = complexName; - } - } - public boolean isArray() { - return isArray; - } - public boolean isPointer() { - return isPointer; - } - public String getName() { - return name; - } - public String getNamespace() { - return namespace; - } - public boolean usesNamespace() { - return usesNamespace; - } - public CategoryPath getCategoryPath() { - return (usesNamespace ? new CategoryPath(namespace) : CategoryPath.ROOT); - } - - public DataType getDataType(FileDataTypeManager dtMgr, Hashtable dataTypes) { - return getDataType(dtMgr, dataTypes, true); - } - public DataType getDataType(DataTypeManager dtMgr, Hashtable dataTypes, boolean generateUI) { - DataType dt; - if (name.indexOf("%") >= 0) { - ComplexName cName = new ComplexName(name); - dt = cName.getDataType(dtMgr, dataTypes, generateUI); - name = cName.getName(); - } else { - if (dataTypes == null) { - dt = dtMgr.getDataType(getCategoryPath(), getName()); - } else { - dt = dataTypes.get(getCategoryPath()+getName()); - } - //if (dt == null) { - // dt = dtMgr.getDataType(getCategoryPath(), getName()); - //} - } - if (dt == null) { - if (generateUI) { - // Add placeholders for the archive entries that are missing - dt = genUIData(dtMgr, dataTypes, new ComplexName(complexName), 4); - } else { - Msg.warn(this, "Data type ("+name+") not found."); - return dt; - } - } - if (isPointer) { - dt = new Pointer32DataType(dt); - } - if (isArray) { - if (dt.getLength() >= 0) { - dt = new ArrayDataType(dt, count, dt.getLength()); - } else { - Msg.error(this, "Error in array length ("+dt.getLength()+") for "+dt.getName()); - return null; - } - } - return dt; - } - - public DataType genUIData(DataTypeManager dtMgr, Hashtable dataTypes, ComplexName cName, int len) { - TypedefDataType dt = new TypedefDataType(cName.getCategoryPath(), cName.getName(), - new ArrayDataType(new ByteDataType(), len, 1)); - addDataType(dtMgr, dataTypes, dt); - return dt; - } - - private void addDataType(DataTypeManager dtMgr, Hashtable dataTypes, DataType dt) { - DataType type = dtMgr.addDataType(dt, DataTypeConflictHandler.REPLACE_HANDLER); - dataTypes.put(type.getCategoryPath()+type.getName(), type); - } - - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EnumDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EnumDataTypeHTMLRepresentation.java index fd7b837050..18920b2d2d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EnumDataTypeHTMLRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EnumDataTypeHTMLRepresentation.java @@ -40,8 +40,7 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation { // private constructor for making diff copies private EnumDataTypeHTMLRepresentation(Enum enumDataType, List headerLines, - TextLine displayName, - List bodyContent, TextLine footerLine) { + TextLine displayName, List bodyContent, TextLine footerLine) { this.enumDataType = enumDataType; this.headerContent = headerLines; this.displayName = displayName; @@ -113,7 +112,13 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation { int length = hexString.length(); hexString = hexString.substring(length - (n * 2)); } - list.add(new TextLine(name + " = 0x" + hexString)); + + String comment = enumDataType.getComment(name); + if (trim && comment != null) { + comment = StringUtilities.trim(comment, ToolTipUtils.LINE_LENGTH); + } + + list.add(new TextLine(name + " = 0x" + hexString + " " + comment)); } return list; @@ -147,7 +152,7 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation { displayNameText = HTMLUtilities.friendlyEncodeHTML(displayNameText); displayNameText = wrapStringInColor(displayNameText, displayName.getTextColor()); //@formatter:off - append(fullHtml, truncatedHtml, lineCount, TT_OPEN, + append(fullHtml, truncatedHtml, lineCount, TT_OPEN, displayNameText, TT_CLOSE, HTML_SPACE, @@ -192,8 +197,8 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation { return fullHtml.toString(); } - private static void append(StringBuilder fullHtml, StringBuilder truncatedHtml, - int lineCount, String... content) { + private static void append(StringBuilder fullHtml, StringBuilder truncatedHtml, int lineCount, + String... content) { for (String string : content) { fullHtml.append(string); @@ -247,8 +252,7 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation { diffDisplayName, bodyDiff.getLeftLines(), footerLine), new EnumDataTypeHTMLRepresentation(enumRepresentation.enumDataType, headerDiff.getRightLines(), otherDiffDisplayName, bodyDiff.getRightLines(), - enumRepresentation.footerLine) - }; + enumRepresentation.footerLine) }; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/HTMLDataTypeRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/HTMLDataTypeRepresentation.java index fc5a1bce7f..e5368f5797 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/HTMLDataTypeRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/HTMLDataTypeRepresentation.java @@ -70,7 +70,7 @@ public abstract class HTMLDataTypeRepresentation { // Note 1: Indentation tags (note: we switched from

tags because the Java rendering engine // does not keep the color of the div's parent tags. The

tag seems to work). // Note 2: Switch back to

from

, since the

tag gets broken by the tag - // used by composite types. If not inheriting the color becomes an issue, then we will need + // used by composite types. If not inheriting the color becomes an issue, then we will need // to find another solution for indentation. protected static final String INDENT_OPEN = "
"; protected static final String INDENT_CLOSE = "
"; @@ -87,7 +87,7 @@ public abstract class HTMLDataTypeRepresentation { protected final static Color DIFF_COLOR = ValidatableLine.INVALID_COLOR; private static String createSpace(int numberOfSpaces) { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); for (int i = 0; i < numberOfSpaces; i++) { buffer.append(HTML_SPACE); } @@ -134,7 +134,7 @@ public abstract class HTMLDataTypeRepresentation { * Returns the plain-text value of the data type's description. *

* If there were html tags in the string, they are escaped. - * + * * @param dataType the type to get the description / comment for * @return plain-text string, w/html escaped */ @@ -167,7 +167,7 @@ public abstract class HTMLDataTypeRepresentation { /** * Formats a multi-line plain-text comment string into a HTML string where the text has been * wrapped at MAX_LINE_LENGTH. - * + * * @param string plain-text string * @return list of html strings */ @@ -241,9 +241,9 @@ public abstract class HTMLDataTypeRepresentation { /** * Formats a multi-line plain-text comment as a list of HTML marked-up lines. - * + * * @param comment multi-line plain-text string - * @param maxLines max number of formatted lines to return + * @param maxLines max number of formatted lines to return * @return list of html marked-up {@link TextLine}s */ protected static List createCommentLines(String comment, int maxLines) { @@ -325,9 +325,9 @@ public abstract class HTMLDataTypeRepresentation { } /** - * Returns an HTML string for this data representation object. The HTML returned will be + * Returns an HTML string for this data representation object. The HTML returned will be * truncated if it is too long. To get the full HTML, call {@link #getFullHTMLString()}. - * + * * @return the html * @see #getFullHTMLString() */ @@ -337,7 +337,7 @@ public abstract class HTMLDataTypeRepresentation { /** * Returns an HTML string for this data representation object - * + * * @return the html * @see #getHTMLString() */ @@ -345,16 +345,16 @@ public abstract class HTMLDataTypeRepresentation { return HTML_OPEN + originalHTMLData + HTML_CLOSE; } - /** - * This is like {@link #getHTMLString()}, but does not put HTML tags around the data + /** + * This is like {@link #getHTMLString()}, but does not put HTML tags around the data * @return the content */ public String getHTMLContentString() { return originalHTMLData; // default to full text; subclasses can override } - /** - * This is like {@link #getHTMLString()}, but does not put HTML tags around the data + /** + * This is like {@link #getHTMLString()}, but does not put HTML tags around the data * @return the content */ public String getFullHTMLContentString() { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java index ea3f654c3d..ecaad2678d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java @@ -1115,12 +1115,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { @Test public void testEditEnum() throws Exception { - // edit DLL_Table in latest; edit DLL_Table in private - // only DLL_Table should be in conflict; not the ones where it is used. + mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) { boolean commit = false; @@ -1140,9 +1136,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { boolean commit = false; @@ -1180,7 +1173,164 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { assertNotNull(dt); Enum enumm = (Enum) dt; assertEquals(0x10, enumm.getValue("Pink")); + } + @Test + public void testEditEnumComments_NoConflict_CommentAddedInLatest() throws Exception { + + mtf.initialize("notepad", new ProgramModifierListener() { + + @Override + public void modifyLatest(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + Category c = dtm.getCategory(new CategoryPath("/MISC")); + DataType dt = c.getDataType("FavoriteColors"); + try { + Enum enumm = (Enum) dt; + String valueName = "Pink"; + long value = enumm.getValue(valueName); + enumm.remove(valueName); + enumm.add(valueName, value, "This is the latest comment on server"); + commit = true; + } + finally { + program.endTransaction(transactionID, commit); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + // no change + } + + }); + executeMerge(DataTypeMergeManager.OPTION_MY); + DataTypeManager dtm = resultProgram.getDataTypeManager(); + + Category c = dtm.getCategory(new CategoryPath("/MISC")); + DataType dt = c.getDataType("FavoriteColors"); + assertNotNull(dt); + Enum enumm = (Enum) dt; + assertEquals(0x3, enumm.getValue("Pink")); + assertEquals("This is the latest comment on server", enumm.getComment("Pink")); + } + + @Test + public void testEditEnumComments_Conflict_TakeMyChanges() throws Exception { + + mtf.initialize("notepad", new ProgramModifierListener() { + + @Override + public void modifyLatest(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + Category c = dtm.getCategory(new CategoryPath("/MISC")); + DataType dt = c.getDataType("FavoriteColors"); + try { + Enum enumm = (Enum) dt; + String valueName = "Pink"; + long value = enumm.getValue(valueName); + enumm.remove(valueName); + enumm.add(valueName, value, "This is the latest comment on server"); + commit = true; + } + finally { + program.endTransaction(transactionID, commit); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + Category c = dtm.getCategory(new CategoryPath("/MISC")); + DataType dt = c.getDataType("FavoriteColors"); + + try { + Enum enumm = (Enum) dt; + String valueName = "Pink"; + long value = enumm.getValue(valueName); + enumm.remove(valueName); + enumm.add(valueName, value, "This my local updated comment"); + commit = true; + } + finally { + program.endTransaction(transactionID, commit); + } + } + + }); + executeMerge(DataTypeMergeManager.OPTION_MY); + DataTypeManager dtm = resultProgram.getDataTypeManager(); + + Category c = dtm.getCategory(new CategoryPath("/MISC")); + DataType dt = c.getDataType("FavoriteColors"); + assertNotNull(dt); + Enum enumm = (Enum) dt; + assertEquals(0x3, enumm.getValue("Pink")); + assertEquals("This my local updated comment", enumm.getComment("Pink")); + } + + @Test + public void testEditEnumComments_Conflict_TakeLatestChanges() throws Exception { + + mtf.initialize("notepad", new ProgramModifierListener() { + + @Override + public void modifyLatest(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + Category c = dtm.getCategory(new CategoryPath("/MISC")); + DataType dt = c.getDataType("FavoriteColors"); + try { + Enum enumm = (Enum) dt; + String valueName = "Pink"; + long value = enumm.getValue(valueName); + enumm.remove(valueName); + enumm.add(valueName, value, "This is the latest comment on server"); + commit = true; + } + finally { + program.endTransaction(transactionID, commit); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + Category c = dtm.getCategory(new CategoryPath("/MISC")); + DataType dt = c.getDataType("FavoriteColors"); + + try { + Enum enumm = (Enum) dt; + String valueName = "Pink"; + long value = enumm.getValue(valueName); + enumm.remove(valueName); + enumm.add(valueName, value, "This my local updated comment"); + commit = true; + } + finally { + program.endTransaction(transactionID, commit); + } + } + + }); + executeMerge(DataTypeMergeManager.OPTION_LATEST); + DataTypeManager dtm = resultProgram.getDataTypeManager(); + + Category c = dtm.getCategory(new CategoryPath("/MISC")); + DataType dt = c.getDataType("FavoriteColors"); + assertNotNull(dt); + Enum enumm = (Enum) dt; + assertEquals(0x3, enumm.getValue("Pink")); + assertEquals("This is the latest comment on server", enumm.getComment("Pink")); } @Test @@ -1355,9 +1505,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = program.getDataTypeManager(); int transactionID = program.startTransaction("test"); Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); try { fd.setReturnType(bar); @@ -1376,9 +1525,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { boolean commit = false; DataTypeManager dtm = program.getDataTypeManager(); Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); ParameterDefinition[] vars = fd.getArguments(); int transactionID = program.startTransaction("test"); @@ -1423,9 +1571,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = program.getDataTypeManager(); int transactionID = program.startTransaction("test"); Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); try { fd.setReturnType(bar); @@ -1446,9 +1593,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { boolean commit = false; DataTypeManager dtm = program.getDataTypeManager(); Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); ParameterDefinition[] vars = fd.getArguments(); int transactionID = program.startTransaction("test"); @@ -1491,9 +1637,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { boolean commit = false; DataTypeManager dtm = program.getDataTypeManager(); int transactionID = program.startTransaction("test"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); try { fd.setVarArgs(true); @@ -1514,9 +1659,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { boolean commit = false; DataTypeManager dtm = program.getDataTypeManager(); Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); ParameterDefinition[] vars = fd.getArguments(); int transactionID = program.startTransaction("test"); @@ -1560,9 +1704,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { boolean commit = false; DataTypeManager dtm = program.getDataTypeManager(); int transactionID = program.startTransaction("test"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); try { fd.setVarArgs(true); @@ -1583,9 +1726,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { boolean commit = false; DataTypeManager dtm = program.getDataTypeManager(); Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); ParameterDefinition[] vars = fd.getArguments(); int transactionID = program.startTransaction("test"); @@ -1633,9 +1775,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = program.getDataTypeManager(); int transactionID = program.startTransaction("test"); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); try { fd.setReturnType(VoidDataType.dataType); @@ -1655,9 +1796,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest { boolean commit = false; DataTypeManager dtm = program.getDataTypeManager(); - FunctionDefinition fd = - (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"), - "MyFunctionDef"); + FunctionDefinition fd = (FunctionDefinition) dtm + .getDataType(new CategoryPath("/MISC"), "MyFunctionDef"); ParameterDefinition[] vars = fd.getArguments(); int transactionID = program.startTransaction("test"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java index 2a2dbce884..aee235f5e8 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java @@ -77,8 +77,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { public void testEnumFields() throws Exception { Category c = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); Enum enumm = createEnum(c, "TestEnum", 1); edit(enumm); @@ -132,8 +131,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { public void testEnumSize1() throws Exception { Category category = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); Enum enumm = createEnum(category, "TestEnum", 1); edit(enumm); @@ -184,8 +182,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { // test entering too large a value Category category = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); Enum enumm = createEnum(category, "TestEnum", 1); edit(enumm); @@ -228,8 +225,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { public void testEnumSize4BadInput() throws Exception { Category category = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); Enum enumm = createEnum(category, "MyTestEnum", 4); edit(enumm); @@ -280,8 +276,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { public void testBadInputForValue() throws Exception { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); Enum enumm = createEnum(cat, "TestEnum", 1); edit(enumm); @@ -310,8 +305,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0); enumm.add("Green", 1); @@ -375,8 +369,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { public void testValueForNewEntry() throws Exception { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0x10); enumm.add("Green", 0x20); @@ -656,7 +649,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { EnumEditorPanel panel = findEditorPanel(tool.getToolFrame()); JTable table = panel.getTable(); - // + // // First, let's try forward then backward // int startRow = 1; @@ -665,12 +658,18 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { Component c = getEditorComponent(editor); triggerActionKey(c, 0, KeyEvent.VK_TAB); + editor = assertEditingCell(table, startRow, startCol + 1); + c = getEditorComponent(editor); + triggerActionKey(c, 0, KeyEvent.VK_TAB); + editor = assertEditingCell(table, startRow, startCol + 2); + c = getEditorComponent(editor); + + triggerActionKey(c, InputEvent.SHIFT_DOWN_MASK, KeyEvent.VK_TAB); editor = assertEditingCell(table, startRow, startCol + 1); c = getEditorComponent(editor); triggerActionKey(c, InputEvent.SHIFT_DOWN_MASK, KeyEvent.VK_TAB); - editor = assertEditingCell(table, startRow, startCol); c = getEditorComponent(editor); @@ -678,7 +677,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { // Now, let's try going around the world and back // int lastRow = 2; - int lastCol = 1; + int lastCol = 2; editor = startEditTableCell(table, lastRow, lastCol); c = getEditorComponent(editor); @@ -703,7 +702,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { JTable table = getEditTable(); - // + // // First, let's try up and down // int startRow = 0; @@ -739,7 +738,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { @Test public void testNewEnumFromAction() throws Exception { // - // This test works differently that the others in that it uses the same path as the + // This test works differently that the others in that it uses the same path as the // GUI action to start the editing process. // DataTypeManager dtm = program.getListing().getDataTypeManager(); @@ -764,8 +763,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { Category category = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); Enum enumm = createEnum(category, "EnumX", 2); int transactionID = program.startTransaction("Test"); @@ -835,8 +833,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { Category category = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); Enum enumm = createEnum(category, "EnumX", 2); int transactionID = program.startTransaction("Test"); @@ -1049,8 +1046,8 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { try { Category category = dtm.getCategory(enummDt.getCategoryPath()); Category parentCategory = category.getParent(); - assertTrue("Did not remove category", parentCategory.removeCategory(category.getName(), - TaskMonitor.DUMMY)); + assertTrue("Did not remove category", + parentCategory.removeCategory(category.getName(), TaskMonitor.DUMMY)); } finally { program.endTransaction(txID, true); @@ -1126,8 +1123,8 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { private TableCellEditor assertEditingCell(final JTable table, final int row, final int col) { Pair rowCol = getEditingCell(table); - assertEquals(row, (int) rowCol.first); - assertEquals(col, (int) rowCol.second); + assertEquals("Not editing expected row", row, (int) rowCol.first); + assertEquals("Not editing expected column", col, (int) rowCol.second); return runSwing(() -> table.getCellEditor()); } @@ -1144,8 +1141,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { private Enum createRedGreenBlueEnum() { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0); enumm.add("Green", 1); @@ -1235,7 +1231,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { addEnumValue(); waitForSwing(); final int row = model.getRowCount() - 1; - // change entry + // change entry table.addRowSelectionInterval(row, row); Rectangle rect = table.getCellRect(row, NAME_COL, true); clickMouse(table, 1, rect.x, rect.y, 2, 0); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor2Test.java index dd1a1ba06f..e2564e0db9 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor2Test.java @@ -41,10 +41,6 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { private TestEnv env; private DataTypeManagerPlugin plugin; - public EnumEditor2Test() { - super(); - } - @Before public void setUp() throws Exception { @@ -70,8 +66,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0); enumm.add("Green", 0x10); @@ -107,8 +102,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { public void testSortColumns() throws Exception { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0); enumm.add("Green", 0x10); @@ -151,8 +145,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { public void testSortOrder() throws Exception { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0); enumm.add("Green", 0x10); @@ -197,8 +190,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { public void testInsertRowByName() throws Exception { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0); enumm.add("Green", 0x10); @@ -324,8 +316,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { waitForSwing(); } - final ComponentProvider provider = - waitForComponentProvider(EnumEditorProvider.class); + final ComponentProvider provider = waitForComponentProvider(EnumEditorProvider.class); assertNotNull(provider); SwingUtilities.invokeLater(() -> provider.closeComponent()); waitForSwing(); @@ -360,8 +351,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { waitForSwing(); } - final ComponentProvider provider = - waitForComponentProvider(EnumEditorProvider.class); + final ComponentProvider provider = waitForComponentProvider(EnumEditorProvider.class); assertNotNull(provider); SwingUtilities.invokeLater(() -> provider.closeComponent()); waitForSwing(); @@ -390,8 +380,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { waitForSwing(); } - final ComponentProvider provider = - waitForComponentProvider(EnumEditorProvider.class); + final ComponentProvider provider = waitForComponentProvider(EnumEditorProvider.class); assertNotNull(provider); SwingUtilities.invokeLater(() -> provider.closeComponent()); waitForSwing(); @@ -455,8 +444,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0); enumm.add("Green", 0x10); @@ -760,7 +748,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { }); waitForSwing(); final int newRow = model.getRowCount() - 1; - // change entry + // change entry runSwing(() -> table.addRowSelectionInterval(newRow, newRow)); Rectangle rect = table.getCellRect(newRow, EnumTableModel.NAME_COL, true); clickMouse(table, 1, rect.x, rect.y, 2, 0); @@ -828,8 +816,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest { private Enum editSampleEnum() { Category cat = program.getListing() .getDataTypeManager() - .getCategory( - new CategoryPath(CategoryPath.ROOT, "Category1")); + .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1")); final Enum enumm = new EnumDataType("Colors", 1); enumm.add("Red", 0); enumm.add("Green", 0x10); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java index d2eb415322..fb5402e47f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java @@ -4,9 +4,9 @@ * 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. @@ -19,6 +19,8 @@ import java.io.IOException; import java.math.BigInteger; import java.util.*; +import org.apache.commons.lang3.StringUtils; + import db.DBRecord; import db.Field; import ghidra.docking.settings.Settings; @@ -33,7 +35,6 @@ import ghidra.util.UniversalID; /** * Database implementation for the enumerated data type. - * */ class EnumDB extends DataTypeDB implements Enum { @@ -42,6 +43,7 @@ class EnumDB extends DataTypeDB implements Enum { private EnumDBAdapter adapter; private EnumValueDBAdapter valueAdapter; + private Map nameMap; // name to value private Map> valueMap; // value to names private Map commentMap; // name to comment @@ -88,21 +90,22 @@ class EnumDB extends DataTypeDB implements Enum { commentMap = new HashMap<>(); Field[] ids = valueAdapter.getValueIdsInEnum(key); - for (Field id : ids) { DBRecord rec = valueAdapter.getRecord(id.getLongValue()); String valueName = rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL); long value = rec.getLongValue(EnumValueDBAdapter.ENUMVAL_VALUE_COL); - String valueNameComment = rec.getString(EnumValueDBAdapter.ENUMVAL_COMMENT_COL); - addToCache(valueName, value, valueNameComment); + String comment = rec.getString(EnumValueDBAdapter.ENUMVAL_COMMENT_COL); + addToCache(valueName, value, comment); } } - private void addToCache(String valueName, long value, String valueNameComment) { + private void addToCache(String valueName, long value, String comment) { nameMap.put(valueName, value); List list = valueMap.computeIfAbsent(value, v -> new ArrayList<>()); list.add(valueName); - commentMap.put(valueName, valueNameComment); + if (!StringUtils.isBlank(comment)) { + commentMap.put(valueName, comment); + } } private boolean removeFromCache(String valueName) { @@ -121,10 +124,7 @@ class EnumDB extends DataTypeDB implements Enum { if (list.isEmpty()) { valueMap.remove(value); } - String comment = commentMap.remove(valueName); - if (comment == null) { - return false; - } + commentMap.remove(valueName); return true; } @@ -168,7 +168,7 @@ class EnumDB extends DataTypeDB implements Enum { } @Override - public String getComment(String valueName) throws NoSuchElementException { + public String getComment(String valueName) { lock.acquire(); try { checkIsValid(); @@ -214,19 +214,6 @@ class EnumDB extends DataTypeDB implements Enum { } } - @Override - public String[] getComments() { - lock.acquire(); - try { - checkIsValid(); - initializeIfNeeded(); - return commentMap.keySet().toArray(new String[commentMap.size()]); - } - finally { - lock.release(); - } - } - @Override public int getCount() { lock.acquire(); @@ -242,12 +229,11 @@ class EnumDB extends DataTypeDB implements Enum { @Override public void add(String valueName, long value) { - String valueNameComment = ""; - add(valueName, value, valueNameComment); + add(valueName, value, null); } @Override - public void add(String valueName, long value, String valueNameComment) { + public void add(String valueName, long value, String comment) { lock.acquire(); try { checkDeleted(); @@ -256,10 +242,15 @@ class EnumDB extends DataTypeDB implements Enum { if (nameMap.containsKey(valueName)) { throw new IllegalArgumentException(valueName + " already exists in this enum"); } + + if (StringUtils.isBlank(comment)) { + comment = null; // use null values in the db to save space + } + bitGroups = null; - valueAdapter.createRecord(key, valueName, value, valueNameComment); + valueAdapter.createRecord(key, valueName, value, comment); adapter.updateRecord(record, true); - addToCache(valueName, value, valueNameComment); + addToCache(valueName, value, comment); dataMgr.dataTypeChanged(this, false); } catch (IOException e) { @@ -294,10 +285,9 @@ class EnumDB extends DataTypeDB implements Enum { if (!removeFromCache(valueName)) { return; } + bitGroups = null; - Field[] ids = valueAdapter.getValueIdsInEnum(key); - for (Field id : ids) { DBRecord rec = valueAdapter.getRecord(id.getLongValue()); if (valueName.equals(rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL))) { @@ -321,6 +311,7 @@ class EnumDB extends DataTypeDB implements Enum { if (!(dataType instanceof Enum)) { throw new IllegalArgumentException(); } + Enum enumm = (Enum) dataType; lock.acquire(); try { @@ -338,19 +329,21 @@ class EnumDB extends DataTypeDB implements Enum { int oldLength = getLength(); int newLength = enumm.getLength(); - if (oldLength != newLength) { record.setByteValue(EnumDBAdapter.ENUM_SIZE_COL, (byte) newLength); adapter.updateRecord(record, true); } String[] names = enumm.getNames(); - for (String name2 : names) { - long value = enumm.getValue(name2); - String comment = enumm.getComment(name2); - valueAdapter.createRecord(key, name2, value, comment); + for (String valueName : names) { + long value = enumm.getValue(valueName); + String comment = enumm.getComment(valueName); + if (StringUtils.isBlank(comment)) { + comment = null; // use null values in the db to save space + } + valueAdapter.createRecord(key, valueName, value, comment); adapter.updateRecord(record, true); - addToCache(name2, value, comment); + addToCache(valueName, value, comment); } if (oldLength != newLength) { @@ -525,7 +518,7 @@ class EnumDB extends DataTypeDB implements Enum { return "0"; } List list = getBitGroups(); - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (BitGroup bitGroup : list) { long subValue = bitGroup.getMask() & value; if (subValue != 0) { @@ -576,21 +569,39 @@ class EnumDB extends DataTypeDB implements Enum { getLength() != enumm.getLength() || getCount() != enumm.getCount()) { return false; } + + if (!isEachValueEquivalent(enumm)) { + return false; + } + return true; + } + + private boolean isEachValueEquivalent(Enum enumm) { String[] names = getNames(); String[] otherNames = enumm.getNames(); try { for (int i = 0; i < names.length; i++) { + if (!names[i].equals(otherNames[i])) { + return false; + } + long value = getValue(names[i]); long otherValue = enumm.getValue(names[i]); - if (!names[i].equals(otherNames[i]) || value != otherValue) { + if (value != otherValue) { + return false; + } + + String comment = getComment(names[i]); + String otherComment = enumm.getComment(names[i]); + if (!comment.equals(otherComment)) { return false; } } + return true; } catch (NoSuchElementException e) { return false; // named element not found } - return true; } @Override @@ -598,6 +609,7 @@ class EnumDB extends DataTypeDB implements Enum { try { nameMap = null; valueMap = null; + commentMap = null; bitGroups = null; DBRecord rec = adapter.getRecord(key); if (rec != null) { @@ -731,5 +743,4 @@ class EnumDB extends DataTypeDB implements Enum { lock.release(); } } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java index 863e8c2552..bb0adfeba2 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java @@ -4,9 +4,9 @@ * 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. @@ -30,15 +30,16 @@ import ghidra.util.task.TaskMonitor; /** * Adapter to access the Enumeration data type values tables. */ -abstract class EnumValueDBAdapter { +abstract class EnumValueDBAdapter implements RecordTranslator { static final String ENUM_VALUE_TABLE_NAME = "Enumeration Values"; - static final Schema ENUM_VALUE_SCHEMA = EnumValueDBAdapterV0.V0_ENUM_VALUE_SCHEMA; + static final Schema ENUM_VALUE_SCHEMA = EnumValueDBAdapterV1.SCHEMA; + // Enum Value Columns - static final int ENUMVAL_NAME_COL = EnumValueDBAdapterV0.V0_ENUMVAL_NAME_COL; - static final int ENUMVAL_VALUE_COL = EnumValueDBAdapterV0.V0_ENUMVAL_VALUE_COL; - static final int ENUMVAL_ID_COL = EnumValueDBAdapterV0.V0_ENUMVAL_ID_COL; - static final int ENUMVAL_COMMENT_COL = EnumValueDBAdapterV0.V0_ENUMVAL_COMMENT_COL; + static final int ENUMVAL_NAME_COL = 0; + static final int ENUMVAL_VALUE_COL = 1; + static final int ENUMVAL_ID_COL = 2; + static final int ENUMVAL_COMMENT_COL = 3; /** * Gets an adapter for working with the enumeration data type values database table. The adapter is based @@ -53,16 +54,16 @@ abstract class EnumValueDBAdapter { static EnumValueDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) throws VersionException, IOException { if (openMode == DBConstants.CREATE) { - return new EnumValueDBAdapterV0(handle, true); + return new EnumValueDBAdapterV1(handle, true); } try { - return new EnumValueDBAdapterV0(handle, false); + return new EnumValueDBAdapterV1(handle, false); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { throw e; } - EnumValueDBAdapter adapter = new EnumValueDBAdapterNoTable(handle); + EnumValueDBAdapter adapter = findReadOnlyAdapter(handle); if (openMode == DBConstants.UPGRADE) { adapter = upgrade(handle, adapter); } @@ -70,8 +71,18 @@ abstract class EnumValueDBAdapter { } } + static EnumValueDBAdapter findReadOnlyAdapter(DBHandle handle) { + try { + return new EnumValueDBAdapterV0(handle); + } + catch (VersionException e) { + return new EnumValueDBAdapterNoTable(handle); + } + } + /** - * Upgrades the Enumeration Data Type Values table from the oldAdapter's version to the current version. + * Upgrades the Enumeration Data Type Values table from the oldAdapter's version to the current + * version. * @param handle handle to the database whose table is to be upgraded to a newer version. * @param oldAdapter the adapter for the existing table to be upgraded. * @return the adapter for the new upgraded version of the table. @@ -81,7 +92,30 @@ abstract class EnumValueDBAdapter { */ static EnumValueDBAdapter upgrade(DBHandle handle, EnumValueDBAdapter oldAdapter) throws VersionException, IOException { - return new EnumValueDBAdapterV0(handle, true); + + DBHandle tmpHandle = new DBHandle(); + long id = tmpHandle.startTransaction(); + EnumValueDBAdapter tmpAdapter = null; + try { + tmpAdapter = new EnumValueDBAdapterV1(tmpHandle, true); + RecordIterator it = oldAdapter.getRecords(); + while (it.hasNext()) { + DBRecord rec = it.next(); + tmpAdapter.updateRecord(rec); + } + oldAdapter.deleteTable(handle); + EnumValueDBAdapter newAdapter = new EnumValueDBAdapterV1(handle, true); + it = tmpAdapter.getRecords(); + while (it.hasNext()) { + DBRecord rec = it.next(); + newAdapter.updateRecord(rec); + } + return newAdapter; + } + finally { + tmpHandle.endTransaction(id, true); + tmpHandle.close(); + } } /** @@ -90,7 +124,7 @@ abstract class EnumValueDBAdapter { * @param name value name * @param value numeric value * @param comment the field comment - * @throws IOException if IO error occurs + * @throws IOException if there was a problem accessing the database */ abstract void createRecord(long enumID, String name, long value, String comment) throws IOException; @@ -99,10 +133,26 @@ abstract class EnumValueDBAdapter { * Get enum value record which corresponds to specified value record ID * @param valueID value record ID * @return value record or null - * @throws IOException if IO error occurs + * @throws IOException if there was a problem accessing the database */ abstract DBRecord getRecord(long valueID) throws IOException; + /** + * Returns an iterator over the value records inside of this Enum + * @return the iterator + * @throws IOException if there was a problem accessing the database + */ + abstract RecordIterator getRecords() throws IOException; + + /** + * Deletes the table; used when upgrading + * @param handle the handle used to delete the table + * @throws IOException if there was a problem accessing the database + */ + void deleteTable(DBHandle handle) throws IOException { + handle.deleteTable(ENUM_VALUE_TABLE_NAME); + } + /** * Remove the record for the given enum Value ID. * @param valueID ID of the value record to delete @@ -113,7 +163,7 @@ abstract class EnumValueDBAdapter { /** * Updates the enum data type values table with the provided record. * @param record the new record - * @throws IOException if the database can't be accessed. + * @throws IOException if there was a problem accessing the database */ abstract void updateRecord(DBRecord record) throws IOException; @@ -121,8 +171,7 @@ abstract class EnumValueDBAdapter { * Get enum value record IDs which correspond to specified enum datatype ID * @param enumID enum datatype ID * @return enum value record IDs as LongField values within Field array - * @throws IOException if IO error occurs + * @throws IOException if there was a problem accessing the database */ abstract Field[] getValueIdsInEnum(long enumID) throws IOException; - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java index ef709967a3..a07ff31f4e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java @@ -4,9 +4,9 @@ * 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. @@ -17,7 +17,10 @@ package ghidra.program.database.data; import java.io.IOException; +import javax.help.UnsupportedOperationException; + import db.*; +import ghidra.program.database.util.EmptyRecordIterator; /** * Adapter needed for a read-only version of data type manager that is not going @@ -59,4 +62,18 @@ class EnumValueDBAdapterNoTable extends EnumValueDBAdapter { return Field.EMPTY_ARRAY; } + @Override + RecordIterator getRecords() throws IOException { + return new EmptyRecordIterator(); + } + + @Override + protected void deleteTable(DBHandle handle) throws IOException { + // do nothing + } + + @Override + public DBRecord translateRecord(DBRecord rec) { + throw new UnsupportedOperationException(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java index e23903e5a8..87dfa5fd83 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java @@ -4,9 +4,9 @@ * 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. @@ -18,92 +18,84 @@ package ghidra.program.database.data; import java.io.IOException; import db.*; -import ghidra.program.model.lang.ConstantPool.Record; import ghidra.util.exception.VersionException; /** * Version 0 implementation for the enumeration tables adapter. - * */ class EnumValueDBAdapterV0 extends EnumValueDBAdapter { static final int VERSION = 0; - // Enum Value Columns - static final int V0_ENUMVAL_NAME_COL = 0; - static final int V0_ENUMVAL_VALUE_COL = 1; - static final int V0_ENUMVAL_ID_COL = 2; - static final int V0_ENUMVAL_COMMENT_COL = 3; +// Keep for reference +// static final Schema SCHEMA = new Schema(0, "Enum Value ID", +// new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, +// new String[] { "Name", "Value", "Enum ID" }); - static final Schema V0_ENUM_VALUE_SCHEMA = new Schema(0, "Enum Value ID", - new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, - new String[] { "Name", "Value", "Enum ID" }); - -// static final Schema V1_ENUM_VALUE_SCHEMA = new Schema(0, "Enum Value ID", -// new Class[] { StringField.class, LongField.class, LongField.class, StringField.class }, -// new String[] { "Name", "Value", "Enum ID", "Comment" }); - - private Table valueTable; + private Table table; /** * Gets a version 0 adapter for the Enumeration Data Type Values database table. * @param handle handle to the database containing the table. - * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. - * @throws IOException if IO error occurs */ - public EnumValueDBAdapterV0(DBHandle handle, boolean create) - throws VersionException, IOException { + public EnumValueDBAdapterV0(DBHandle handle) throws VersionException { - if (create) { - valueTable = handle.createTable(ENUM_VALUE_TABLE_NAME, V0_ENUM_VALUE_SCHEMA, - new int[] { V0_ENUMVAL_ID_COL }); + table = handle.getTable(ENUM_VALUE_TABLE_NAME); + if (table == null) { + throw new VersionException("Missing Table: " + ENUM_VALUE_TABLE_NAME); } - else { - valueTable = handle.getTable(ENUM_VALUE_TABLE_NAME); - if (valueTable == null) { - throw new VersionException("Missing Table: " + ENUM_VALUE_TABLE_NAME); - } - int version = valueTable.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME + - " but got " + valueTable.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); - } + + int version = table.getSchema().getVersion(); + if (version != VERSION) { + String msg = "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME + + " but got " + table.getSchema().getVersion(); + throw new VersionException(msg, VersionException.NEWER_VERSION, false); } } @Override public void createRecord(long enumID, String name, long value, String comment) throws IOException { - Record record = V0_ENUM_VALUE_SCHEMA.createRecord(valueTable.getKey()); - record.setLongValue(V0_ENUMVAL_ID_COL, enumID); - record.setString(V0_ENUMVAL_NAME_COL, name); - record.setLongValue(V0_ENUMVAL_VALUE_COL, value); - record.setString(V0_ENUMVAL_COMMENT_COL, comment); - valueTable.putRecord(record); + throw new UnsupportedOperationException("Cannot update Version 0"); } @Override public DBRecord getRecord(long valueID) throws IOException { - return valueTable.getRecord(valueID); + return translateRecord(table.getRecord(valueID)); } @Override public void removeRecord(long valueID) throws IOException { - valueTable.deleteRecord(valueID); + throw new UnsupportedOperationException("Cannot remove Version 0"); } @Override public void updateRecord(DBRecord record) throws IOException { - valueTable.putRecord(record); + throw new UnsupportedOperationException("Cannot update Version 0"); } @Override public Field[] getValueIdsInEnum(long enumID) throws IOException { - return valueTable.findRecords(new LongField(enumID), V0_ENUMVAL_ID_COL); + return table.findRecords(new LongField(enumID), ENUMVAL_ID_COL); + } + + @Override + public DBRecord translateRecord(DBRecord oldRec) { + if (oldRec == null) { + return null; + } + + DBRecord record = EnumValueDBAdapter.ENUM_VALUE_SCHEMA.createRecord(oldRec.getKey()); + record.setLongValue(ENUMVAL_ID_COL, oldRec.getLongValue(ENUMVAL_ID_COL)); + record.setString(ENUMVAL_NAME_COL, oldRec.getString(ENUMVAL_NAME_COL)); + record.setLongValue(ENUMVAL_VALUE_COL, oldRec.getLongValue(ENUMVAL_VALUE_COL)); + record.setString(ENUMVAL_COMMENT_COL, null); + return record; + } + + @Override + RecordIterator getRecords() throws IOException { + return new TranslatedRecordIterator(table.iterator(), this); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java new file mode 100644 index 0000000000..1b3b922767 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java @@ -0,0 +1,107 @@ +/* ### + * 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.program.database.data; + +import java.io.IOException; + +import db.*; +import ghidra.util.exception.VersionException; + +/** + * Version 1 implementation for the enumeration tables adapter. + */ +class EnumValueDBAdapterV1 extends EnumValueDBAdapter { + + static final int VERSION = 1; + + static final Schema SCHEMA = new Schema(VERSION, "Enum Value ID", + new Class[] { StringField.class, LongField.class, LongField.class, StringField.class }, + new String[] { "Name", "Value", "Enum ID", "Comment" }); + + private Table table; + + /** + * Gets a version 1 adapter for the Enumeration Data Type Values database table. + * @param handle handle to the database containing the table. + * @param create true if this constructor should create the table. + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. + * @throws IOException if IO error occurs + */ + public EnumValueDBAdapterV1(DBHandle handle, boolean create) + throws VersionException, IOException { + + if (create) { + table = handle.createTable(ENUM_VALUE_TABLE_NAME, SCHEMA, new int[] { ENUMVAL_ID_COL }); + } + else { + table = handle.getTable(ENUM_VALUE_TABLE_NAME); + if (table == null) { + throw new VersionException(true); + } + int version = table.getSchema().getVersion(); + if (version != VERSION) { + String msg = "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME + + " but got " + table.getSchema().getVersion(); + if (version < VERSION) { + throw new VersionException(msg, VersionException.OLDER_VERSION, true); + } + throw new VersionException(msg, VersionException.NEWER_VERSION, false); + } + } + } + + @Override + public void createRecord(long enumID, String name, long value, String comment) + throws IOException { + DBRecord record = SCHEMA.createRecord(table.getKey()); + record.setLongValue(ENUMVAL_ID_COL, enumID); + record.setString(ENUMVAL_NAME_COL, name); + record.setLongValue(ENUMVAL_VALUE_COL, value); + record.setString(ENUMVAL_COMMENT_COL, comment); + table.putRecord(record); + } + + @Override + public DBRecord getRecord(long valueID) throws IOException { + return table.getRecord(valueID); + } + + @Override + public void removeRecord(long valueID) throws IOException { + table.deleteRecord(valueID); + } + + @Override + public void updateRecord(DBRecord record) throws IOException { + table.putRecord(record); + } + + @Override + public Field[] getValueIdsInEnum(long enumID) throws IOException { + return table.findRecords(new LongField(enumID), ENUMVAL_ID_COL); + } + + @Override + RecordIterator getRecords() throws IOException { + return table.iterator(); + } + + @Override + public DBRecord translateRecord(DBRecord r) { + return r; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java index 255256c520..2d9499cb1d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java @@ -24,9 +24,9 @@ import ghidra.util.task.TaskMonitor; /** * Adapter to access the Pointer database table for Pointer data types. - * + * */ -abstract class PointerDBAdapter { +abstract class PointerDBAdapter implements RecordTranslator { static final String POINTER_TABLE_NAME = "Pointers"; static final Schema SCHEMA = new Schema(PointerDBAdapterV2.VERSION, "Pointer ID", @@ -96,7 +96,7 @@ abstract class PointerDBAdapter { } /** - * + * */ abstract void deleteTable(DBHandle handle) throws IOException; @@ -105,6 +105,7 @@ abstract class PointerDBAdapter { * @param dataTypeID data type ID of the date type being pointed to * @param categoryID the category ID of the datatype * @param length pointer size in bytes + * @return the record * @throws IOException if there was a problem accessing the database */ abstract DBRecord createRecord(long dataTypeID, long categoryID, int length) throws IOException; @@ -117,6 +118,11 @@ abstract class PointerDBAdapter { */ abstract DBRecord getRecord(long pointerID) throws IOException; + /** + * An iterator over the records of this adapter + * @return the iterator + * @throws IOException if there was a problem accessing the database + */ abstract RecordIterator getRecords() throws IOException; /** @@ -135,51 +141,11 @@ abstract class PointerDBAdapter { abstract void updateRecord(DBRecord record) throws IOException; /** - * Gets all the pointer data types that are contained in the category that + * Gets all the pointer data types that are contained in the category that * have the indicated ID. * @param categoryID the category whose pointer data types are wanted. * @return an array of IDs for the pointer data types in the category. * @throws IOException if the database can't be accessed. */ abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException; - - DBRecord translateRecord(DBRecord rec) { - return rec; - } - - class TranslatedRecordIterator implements RecordIterator { - private RecordIterator it; - - TranslatedRecordIterator(RecordIterator it) { - this.it = it; - } - - @Override - public boolean delete() throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasNext() throws IOException { - return it.hasNext(); - } - - @Override - public boolean hasPrevious() throws IOException { - return it.hasPrevious(); - } - - @Override - public DBRecord next() throws IOException { - DBRecord rec = it.next(); - return translateRecord(rec); - } - - @Override - public DBRecord previous() throws IOException { - DBRecord rec = it.previous(); - return translateRecord(rec); - } - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java index 24f7f0c00f..d14661ff47 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java @@ -22,14 +22,15 @@ import ghidra.util.exception.VersionException; /** * Version 0 implementation for the accessing the pointer database table. - * + * */ class PointerDBAdapterV0 extends PointerDBAdapter { final static int VERSION = 0; - static final int OLD_PTR_DTD_COL = 0; - static final int OLD_PTR_SIZE_COL = 1; + static final int V0_PTR_DTD_COL_ = 0; + static final int V0_PTR_SIZE_COL = 1; +// Keep for reference // static final Schema SCHEMA = new Schema(VERSION, "Pointer ID", // new Class[] {LongField.class, IntField.class}, // new String[] {"Data Type ID", "Size"}); @@ -60,7 +61,7 @@ class PointerDBAdapterV0 extends PointerDBAdapter { @Override RecordIterator getRecords() throws IOException { - return new TranslatedRecordIterator(table.iterator()); + return new TranslatedRecordIterator(table.iterator(), this); } @Override @@ -79,13 +80,14 @@ class PointerDBAdapterV0 extends PointerDBAdapter { } @Override - DBRecord translateRecord(DBRecord oldRec) { + public DBRecord translateRecord(DBRecord oldRec) { if (oldRec == null) { return null; } DBRecord rec = PointerDBAdapter.SCHEMA.createRecord(oldRec.getKey()); - rec.setLongValue(PTR_DT_ID_COL, oldRec.getLongValue(OLD_PTR_DTD_COL)); + rec.setLongValue(PTR_DT_ID_COL, oldRec.getLongValue(V0_PTR_DTD_COL_)); rec.setLongValue(PTR_CATEGORY_COL, 0); + rec.setByteValue(PTR_LENGTH_COL, (byte) -1); return rec; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java index d6537c86b1..63e8976f81 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java @@ -26,11 +26,13 @@ import ghidra.util.exception.VersionException; class PointerDBAdapterV1 extends PointerDBAdapter { final static int VERSION = 1; - static final int OLD_PTR_DT_ID_COL = 0; - static final int OLD_PTR_CATEGORY_COL = 1; - static final Schema V1_SCHEMA = - new Schema(VERSION, "Pointer ID", new Field[] { LongField.INSTANCE, LongField.INSTANCE }, - new String[] { "Data Type ID", "Category ID" }); + static final int V1_PTR_DT_ID_COL = 0; + static final int V1_PTR_CATEGORY_COL = 1; + +// Keep for reference +// static final Schema V1_SCHEMA = +// new Schema(VERSION, "Pointer ID", new Field[] { LongField.INSTANCE, LongField.INSTANCE }, +// new String[] { "Data Type ID", "Category ID" }); private Table table; @@ -49,13 +51,13 @@ class PointerDBAdapterV1 extends PointerDBAdapter { } @Override - DBRecord translateRecord(DBRecord oldRec) { + public DBRecord translateRecord(DBRecord oldRec) { if (oldRec == null) { return null; } DBRecord rec = PointerDBAdapter.SCHEMA.createRecord(oldRec.getKey()); - rec.setLongValue(PTR_DT_ID_COL, oldRec.getLongValue(OLD_PTR_DT_ID_COL)); - rec.setLongValue(PTR_CATEGORY_COL, oldRec.getLongValue(OLD_PTR_CATEGORY_COL)); + rec.setLongValue(PTR_DT_ID_COL, oldRec.getLongValue(V1_PTR_DT_ID_COL)); + rec.setLongValue(PTR_CATEGORY_COL, oldRec.getLongValue(V1_PTR_CATEGORY_COL)); rec.setByteValue(PTR_LENGTH_COL, (byte) -1); return rec; } @@ -72,7 +74,7 @@ class PointerDBAdapterV1 extends PointerDBAdapter { @Override RecordIterator getRecords() throws IOException { - return new TranslatedRecordIterator(table.iterator()); + return new TranslatedRecordIterator(table.iterator(), this); } @Override @@ -87,7 +89,7 @@ class PointerDBAdapterV1 extends PointerDBAdapter { @Override Field[] getRecordIdsInCategory(long categoryID) throws IOException { - return table.findRecords(new LongField(categoryID), OLD_PTR_CATEGORY_COL); + return table.findRecords(new LongField(categoryID), V1_PTR_CATEGORY_COL); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java index ded3e665d4..3890b5e22a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java @@ -86,7 +86,10 @@ class PointerDBAdapterV2 extends PointerDBAdapter { @Override void deleteTable(DBHandle handle) throws IOException { handle.deleteTable(POINTER_TABLE_NAME); - } + @Override + public DBRecord translateRecord(DBRecord rec) { + return rec; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapter.java index 01f95d6868..a402775711 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapter.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,15 @@ */ package ghidra.program.database.map; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressFactory; -import ghidra.util.exception.VersionException; -import ghidra.util.task.TaskMonitor; - import java.io.IOException; import java.util.List; import db.DBConstants; import db.DBHandle; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressFactory; +import ghidra.util.exception.VersionException; +import ghidra.util.task.TaskMonitor; /** * Database adapter for address map diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java index 299df2c259..bc6cf210fc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java @@ -24,9 +24,9 @@ public interface Enum extends DataType { /** * Get the value for the given name. - * @param name name of the entry - * @return the value - * @throws NoSuchElementException if the name does not exist in this Enum + * @param name name of the entry. + * @return the value. + * @throws NoSuchElementException if the name does not exist in this Enum. */ public long getValue(String name) throws NoSuchElementException; @@ -39,11 +39,11 @@ public interface Enum extends DataType { /** * Get the comment for the given name. - * @param name name of the entry - * @return the comment - * @throws NoSuchElementException if the name does not exist in this Enum + * @param name name of the entry. + * @return the comment or the empty string if the name does not exist in this enum or if no + * comment is set. */ - public String getComment(String name) throws NoSuchElementException; + public String getComment(String name); /** * Get the values of the enum entries. @@ -52,17 +52,15 @@ public interface Enum extends DataType { public long[] getValues(); /** - * Get the names of the enum entries. + * Get the names of the enum entries. The returned names are sorted using String's natural + * sort order. + * @return the names of the enum entries. */ public String[] getNames(); /** - * Get the comments of the enum entries. - */ - public String[] getComments(); - - /** - * Get the number of entries in this Enum. + * Get the number of entries in this Enum. + * @return the number of entries in this Enum. */ public int getCount(); @@ -82,14 +80,14 @@ public interface Enum extends DataType { public void add(String name, long value, String comment); /** - * Remove the enum entry with the given name. + * Remove the enum entry with the given name. * @param name name of entry to remove. */ public void remove(String name); /** * Set the description for this Enum. - * @param description + * @param description the description */ @Override public void setDescription(String description); @@ -98,6 +96,7 @@ public interface Enum extends DataType { * Get enum representation of the big-endian value. * @param bigInt BigInteger value with the appropriate sign * @param settings integer format settings (PADDING, FORMAT, etc.) + * @param bitLength the bit length * @return formatted integer string */ public String getRepresentation(BigInteger bigInt, Settings settings, int bitLength); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java index 81be3e7d7f..796883dd02 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java @@ -4,9 +4,9 @@ * 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. @@ -18,6 +18,8 @@ package ghidra.program.model.data; import java.math.BigInteger; import java.util.*; +import org.apache.commons.lang3.StringUtils; + import ghidra.docking.settings.Settings; import ghidra.docking.settings.SettingsDefinition; import ghidra.program.database.data.DataTypeUtilities; @@ -95,12 +97,12 @@ public class EnumDataType extends GenericDataType implements Enum { } @Override - public String getComment(String valueName) throws NoSuchElementException { - String valueNameComment = commentMap.get(valueName); - if (valueNameComment == null) { - valueNameComment = ""; + public String getComment(String valueName) { + String comment = commentMap.get(valueName); + if (comment == null) { + comment = ""; } - return valueNameComment; + return comment; } @Override @@ -117,11 +119,6 @@ public class EnumDataType extends GenericDataType implements Enum { return names; } - @Override - public String[] getComments() { - return commentMap.values().toArray(new String[commentMap.size()]); - } - @Override public int getCount() { return nameMap.size(); @@ -129,28 +126,25 @@ public class EnumDataType extends GenericDataType implements Enum { @Override public void add(String valueName, long value) { - String valueNameComment = ""; - add(valueName, value, valueNameComment); + add(valueName, value, null); } @Override - public void add(String valueName, long value, String valueNameComment) { + public void add(String valueName, long value, String comment) { bitGroups = null; checkValue(value); if (nameMap.containsKey(valueName)) { throw new IllegalArgumentException(valueName + " already exists in this enum"); } + nameMap.put(valueName, value); - List list = valueMap.get(value); - if (list == null) { - list = new ArrayList<>(); - valueMap.put(value, list); - } + List list = valueMap.computeIfAbsent(value, v -> new ArrayList<>()); list.add(valueName); - if (valueNameComment == null) { - valueNameComment = ""; + + if (!StringUtils.isBlank(comment)) { + commentMap.put(valueName, comment); } - commentMap.put(valueName, valueNameComment); + } private void checkValue(long value) { @@ -180,21 +174,24 @@ public class EnumDataType extends GenericDataType implements Enum { public void remove(String valueName) { bitGroups = null; Long value = nameMap.get(valueName); - if (value != null) { - nameMap.remove(valueName); - List list = valueMap.get(value); - Iterator iter = list.iterator(); - while (iter.hasNext()) { - if (valueName.equals(iter.next())) { - iter.remove(); - break; - } - } - if (list.isEmpty()) { - valueMap.remove(value); - } - commentMap.remove(valueName); + if (value == null) { + return; } + + nameMap.remove(valueName); + List list = valueMap.get(value); + Iterator iter = list.iterator(); + while (iter.hasNext()) { + if (valueName.equals(iter.next())) { + iter.remove(); + break; + } + } + if (list.isEmpty()) { + valueMap.remove(value); + } + + commentMap.remove(valueName); } @Override @@ -231,11 +228,11 @@ public class EnumDataType extends GenericDataType implements Enum { public void setLength(int length) { String[] names = getNames(); - for (String enumName : names) { - long value = getValue(enumName); + for (String valueName : names) { + long value = getValue(valueName); if (isTooBig(length, value)) { throw new IllegalArgumentException("Setting the length of this Enum to a size " + - "that cannot contain the current value for \"" + enumName + "\" of " + + "that cannot contain the current value for \"" + valueName + "\" of " + Long.toHexString(value)); } } @@ -327,7 +324,7 @@ public class EnumDataType extends GenericDataType implements Enum { return "0"; } List list = getBitGroups(); - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (BitGroup bitGroup : list) { long subValue = bitGroup.getMask() & value; if (subValue != 0) { @@ -372,30 +369,45 @@ public class EnumDataType extends GenericDataType implements Enum { if (dt == null || !(dt instanceof Enum)) { return false; } - Enum enumm = (Enum) dt; + Enum enumm = (Enum) dt; if (!DataTypeUtilities.equalsIgnoreConflict(name, enumm.getName()) || length != enumm.getLength() || getCount() != enumm.getCount()) { return false; } + + if (!isEachValueEquivalent(enumm)) { + return false; + } + return true; + } + + private boolean isEachValueEquivalent(Enum enumm) { String[] names = getNames(); String[] otherNames = enumm.getNames(); try { for (int i = 0; i < names.length; i++) { + if (!names[i].equals(otherNames[i])) { + return false; + } + long value = getValue(names[i]); long otherValue = enumm.getValue(names[i]); + if (value != otherValue) { + return false; + } + String comment = getComment(names[i]); String otherComment = enumm.getComment(names[i]); - if (!names[i].equals(otherNames[i]) || value != otherValue || - !comment.equals(otherComment)) { + if (!comment.equals(otherComment)) { return false; } } + return true; } catch (NoSuchElementException e) { return false; // named element not found } - return true; } @Override @@ -410,14 +422,14 @@ public class EnumDataType extends GenericDataType implements Enum { commentMap = new HashMap<>(); setLength(enumm.getLength()); String[] names = enumm.getNames(); - for (String name2 : names) { - add(name2, enumm.getValue(name2), enumm.getComment(name2)); + for (String valueName : names) { + add(valueName, enumm.getValue(valueName), enumm.getComment(valueName)); } stateChanged(null); } @Override public String getDefaultLabelPrefix() { - return name == null ? null : name.toUpperCase(); + return name; } } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java index 0f1b09d39c..8b384508c5 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java @@ -66,7 +66,7 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { dataTypeWindows.add(dataTypeDialog); captureComponents(dataTypeWindows); - closeAllWindowsAndFrames(); + closeAllWindows(); } private DropDownSelectionTextField showTypeChooserDialog() throws Exception { @@ -189,7 +189,7 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { waitForSwing(); captureDialog(); - closeAllWindowsAndFrames(); + closeAllWindows(); } @Test