GP-1403 Improved support for auto-named typedefs. Updated create

typedef action from pointer to use auto-naming.  Replaced old
ImageBaseOffsetDataType 32/64-bit BuiltIn types with new pointer-typedef
based implementations. Improved settings modification
restrictions.  Resolved various bugs.
This commit is contained in:
ghidra1
2022-02-15 10:16:08 -05:00
parent ec5b6aada7
commit 8f0589a6d8
103 changed files with 2226 additions and 1156 deletions
@@ -20,6 +20,7 @@ import java.util.Collection;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.TypeDefSettingsDefinition;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.SourceType;
import ghidra.trace.database.data.DBTraceDataSettingsOperations;
@@ -68,6 +69,15 @@ public interface DBTraceDataAdapter extends DBTraceCodeUnitAdapter, DataAdapterM
DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent);
@Override
default boolean isChangeAllowed(SettingsDefinition settingsDefinition) {
if (settingsDefinition instanceof TypeDefSettingsDefinition) {
return false;
}
// assume instance setting allowed if default setting allowed
return getDefaultSettings().isChangeAllowed(settingsDefinition);
}
@Override
default void setLong(String name, long value) {
try (LockHold hold = getTrace().lockWrite()) {
+1
View File
@@ -65,6 +65,7 @@ dependencies {
testImplementation project(path: ':Generic', configuration: 'testArtifacts')
testImplementation project(path: ':Project', configuration: 'testArtifacts')
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
testImplementation project(path: ':DB', configuration: 'testArtifacts')
javacc 'net.java.dev.javacc:javacc:5.0'
}
@@ -800,7 +800,16 @@
<P>A Typedef created with a Pointer base type will allow additional Settings to be made
which can influence how such a pointer should be interpretted
(see <A href="#Pointer_Typedef_Settings">Pointer-Typedef Settings</A>).</P>
(see <A href="#Pointer_Typedef_Settings">Pointer-Typedef Settings</A>).
If no name is assigned to a new Pointer-Typedef is will be treated as an "auto-typedef"
where a dynamic name will be assigned based upon the underlying pointer and assigned
typedef attribute settings. Examples:
<ul>
<li><i>char * __((space(ram)))</i></li>
<li><i>int * __((offset(0x8)))</i></li>
<li><i>pointer __((image-base-relative))</i></li>
</ul>
</P>
<H4>Creating a Pointer</H4>
<P>To create a <B>pointer</B>, you can click <I><B>New<IMG src="../../shared/arrow.gif"
@@ -1076,18 +1085,18 @@
<P>The following Pointer-Typedef settings are currently supported:</P>
<UL>
<LI><B>Address Space</B> (case-sensitive string) - Allows a specific address space to be associated with a pointer.
If an unknown name is used it will be silently ignored. </LI>
If an unknown name is used it will be silently ignored. Auto name attribute format: __((space(<i>name</i>))</LI>
<LI><B>Component Offset</B> (signed value) - Allows a base-relative offset to be specified. When applied
to memory an Offset Reference will be generated. I addition, type analysis may use the offset to identify
a component relative to the pointer's base-datatype (e.g., structure).</LI>
a component relative to the pointer's base-datatype (e.g., structure). Auto name attribute format: __((offset(<i>signed_value</i>))</LI>
<LI><B>Offset Mask</B> (64-bit mask) - Allows a bit-mask to be applied to a stored value when computing the
absolute memory offset. This bit-mask will be applied prior to any applied bit-shift.</LI>
absolute memory offset. This bit-mask will be applied prior to any applied bit-shift. Auto name attribute format: __((mask(<i>hex_mask</i>))</LI>
<LI><B>Offset Shift</B> (-64..0..64) - Allows a bit-shift (left=negative, right=positive) to be applied to a
stored value when computing the absolute memory offset.</LI>
<LI><B>Pointer Type</B> (<I>default, IBO, relative, file-offset</I>) - allows the overall interpretation of a
stored value when computing the absolute memory offset. Auto name attribute format: __((shift(<i>bitshift_amount</i>))</LI>
<LI><B>Pointer Type</B> (<I>default, image-base-relative, relative, file-offset</I>) - allows the overall interpretation of a
pointer to be specified. The <I>relative</I> pointer type has limited applicabaility and is only
intended to be applied to pointers stored in memory since their storage location is used in computing
the absolute address that the pointer refers to. (IBO: Image Base Offset Relative).</LI>
the absolute address that the pointer refers to.</LI>
</UL>
<P><IMG src="../../shared/note.png" alt="" border="0">All Typedef Settings must be established on
@@ -665,7 +665,6 @@ public class DataTypeMergeManager implements MergeResolver {
* in RESULT; false if the data type did not have to be added
*/
private boolean dataTypeRenamedOrMoved(long id) {
DataType newDt = null;
switch (conflictOption) {
@@ -700,7 +699,7 @@ public class DataTypeMergeManager implements MergeResolver {
DataType resultDt = dtms[RESULT].getDataType(id);
DataType newDt = null;
if (resultDt != null) {
setDataTypeName(resultDt, dt.getName());
setDataTypeName(resultDt, dt);
setCategoryPath(resultDt, dt.getCategoryPath());
}
else {
@@ -1645,8 +1644,12 @@ public class DataTypeMergeManager implements MergeResolver {
return;
}
}
String name = category.getName();
String newName = name;
String newName = category.getName();
String baseName = newName;
int index = newName.indexOf(DataType.CONFLICT_SUFFIX);
if (index > 0) {
baseName = newName.substring(0, index);
}
int oneUpNumber = 0;
while (true) {
try {
@@ -1656,7 +1659,7 @@ public class DataTypeMergeManager implements MergeResolver {
return;
}
++oneUpNumber;
newName = name + DataType.CONFLICT_SUFFIX + oneUpNumber;
newName = baseName + DataType.CONFLICT_SUFFIX + oneUpNumber;
}
catch (DuplicateNameException e) {
throw new AssertException("Got DuplicateNameException");
@@ -1673,6 +1676,11 @@ public class DataTypeMergeManager implements MergeResolver {
return;
}
String name = newName;
String baseName = newName;
int index = newName.indexOf(DataType.CONFLICT_SUFFIX);
if (index > 0) {
baseName = newName.substring(0, index);
}
int oneUpNumber = 0;
while (true) {
try {
@@ -1681,7 +1689,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
catch (DuplicateNameException e) {
++oneUpNumber;
name = newName + DataType.CONFLICT_SUFFIX + oneUpNumber;
name = baseName + DataType.CONFLICT_SUFFIX + oneUpNumber;
}
catch (InvalidNameException e) {
throw new AssertException("Got InvalidNameException: " + e);
@@ -1689,20 +1697,31 @@ public class DataTypeMergeManager implements MergeResolver {
}
}
private void setDataTypeName(DataType dt, String newName) {
private void setDataTypeName(DataType dt, DataType dtToCopy) {
if (isAutoNamedTypedef(dtToCopy)) {
if (dt instanceof TypeDef) {
((TypeDef) dt).enableAutoNaming();
return;
}
}
String newName = dtToCopy.getName();
if (dt.getName().equals(newName)) {
return;
}
String name = newName;
String baseName = newName;
int index = newName.indexOf(DataType.CONFLICT_SUFFIX);
if (index > 0) {
baseName = newName.substring(0, index);
}
int oneUpNumber = 0;
while (true) {
try {
dt.setName(name);
dt.setName(newName);
return;
}
catch (DuplicateNameException e) {
++oneUpNumber;
name = newName + DataType.CONFLICT_SUFFIX + oneUpNumber;
newName = baseName + DataType.CONFLICT_SUFFIX + oneUpNumber;
}
catch (InvalidNameException e) {
throw new AssertException("Got InvalidNameException: " + e);
@@ -1710,6 +1729,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
}
private boolean categoryWasMoved(long id, DataTypeManager dtm1, DataTypeManager dtm2) {
Category cat1 = dtm1.getCategory(id);
Category cat2 = dtm2.getCategory(id);
@@ -1765,10 +1785,24 @@ public class DataTypeMergeManager implements MergeResolver {
return dataTypeWasRenamed(id, dtms[ORIGINAL], dtm);
}
private boolean isAutoNamedTypedef(DataType dt) {
if (dt instanceof TypeDef) {
TypeDef td = (TypeDef) dt;
return td.isAutoNamed();
}
return false;
}
private boolean dataTypeWasRenamed(long id, DataTypeManager dtm1, DataTypeManager dtm2) {
DataType dt1 = dtm1.getDataType(id);
DataType dt2 = dtm2.getDataType(id);
if (dt1 != null && dt2 != null) {
if (isAutoNamedTypedef(dt1)) {
return isAutoNamedTypedef(dt2);
}
else if (isAutoNamedTypedef(dt2)) {
return false;
}
String name1 = dt1.getName();
String name2 = dt2.getName();
return !name1.equals(name2);
@@ -2695,7 +2729,7 @@ public class DataTypeMergeManager implements MergeResolver {
DataType dt = dtms[RESULT].getDataType(id);
if (dataTypeWasRenamed(id, dtms[MY])) {
if (dt != null) {
setDataTypeName(dt, myDt.getName());
setDataTypeName(dt, myDt);
}
}
}
@@ -57,7 +57,6 @@ public class PropertyListMergeManager implements MergeResolver {
private int currentConflict;
private int totalConflictCount;
private ProgramMultiUserMergeManager mergeManager;
private int progressIndex;
private int propertyListChoice = ASK_USER;
/**
@@ -157,7 +156,7 @@ public class PropertyListMergeManager implements MergeResolver {
}
}
mergeManager.updateProgress(100);
currentMonitor.initialize(myNamesCount);
try {
processConflicts();
commit = true;
@@ -193,7 +192,6 @@ public class PropertyListMergeManager implements MergeResolver {
return;
}
addProperty(list, resultList, optionName);
currentMonitor.setProgress(++progressIndex);
}
}
@@ -251,7 +249,6 @@ public class PropertyListMergeManager implements MergeResolver {
if (latestValue.equals(origValue)) {
latestList.removeOption(propertyName);
currentMonitor.setProgress(++progressIndex);
}
else {
String listName = latestList.getName();
@@ -275,25 +272,28 @@ public class PropertyListMergeManager implements MergeResolver {
Object resultValue = getValue(resultList, propertyName);
Object origValue = getValue(origList, propertyName);
if (!SystemUtilities.isEqual(resultValue, myValue)) {
if (propertyName.equals(Program.ANALYZED) && (myValue instanceof Boolean)) {
// If latest or my version sets "Analyzed" to true, then it should result in true.
setValue(resultList, propertyName, myList.getType(propertyName), Boolean.TRUE);
currentMonitor.setProgress(++progressIndex);
return;
}
if (SystemUtilities.isEqual(resultValue, origValue)) {
setValue(resultList, propertyName, myList.getType(propertyName), myValue);
currentMonitor.setProgress(++progressIndex);
}
else {
String listName = resultList.getName();
ArrayList<ConflictInfo> mapList = getConflictList(listName);
mapList.add(new ConflictInfo(listName, propertyName,
resultList.getType(propertyName), myList.getType(propertyName),
origList.getType(propertyName), resultValue, myValue, origValue));
++totalConflictCount;
}
if (SystemUtilities.isEqual(origValue, myValue) ||
SystemUtilities.isEqual(resultValue, myValue)) {
// value was not modified in my program or it was changed the same as in latest
return;
}
if (propertyName.equals(Program.ANALYZED) && Boolean.TRUE.equals(myValue)) {
// If my version sets "Analyzed" to true, then it should result in true.
setValue(resultList, propertyName, myList.getType(propertyName), Boolean.TRUE);
return;
}
if (SystemUtilities.isEqual(resultValue, origValue)) {
// no change by latest - use my value
setValue(resultList, propertyName, myList.getType(propertyName), myValue);
}
else {
// my change conflicts with latest change
String listName = resultList.getName();
ArrayList<ConflictInfo> mapList = getConflictList(listName);
mapList.add(new ConflictInfo(listName, propertyName,
resultList.getType(propertyName), myList.getType(propertyName),
origList.getType(propertyName), resultValue, myValue, origValue));
++totalConflictCount;
}
}
@@ -322,7 +322,6 @@ public class PropertyListMergeManager implements MergeResolver {
if (!myValue.equals(origValue)) {
setValue(resultList, propertyName, myList.getType(propertyName), myValue);
currentMonitor.setProgress(++progressIndex);
}
}
@@ -412,7 +411,7 @@ public class PropertyListMergeManager implements MergeResolver {
String currentListName) throws CancelledException {
for (int i = 0; i < conflictList.size(); i++) {
currentMonitor.setProgress(++progressIndex);
currentMonitor.setProgress(i);
ConflictInfo info = conflictList.get(i);
@@ -67,7 +67,11 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
Settings originalSettings) {
super(title, true, false, true, false);
this.settingsDefinitions = settingDefinitions;
settings = new SettingsImpl(originalSettings);
settings = new SettingsImpl(originalSettings) {
public boolean isChangeAllowed(SettingsDefinition settingsDefinition) {
return originalSettings.isChangeAllowed(settingsDefinition);
}
};
defaultSettings = settings.getDefaultSettings();
if (originalSettings != null && defaultSettings == null) {
// ensure we have defaults to facilitate revert to default
@@ -206,6 +210,17 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
workPanel.add(scrollpane, BorderLayout.CENTER);
boolean hasImmutableSettings = false;
for (SettingsDefinition def : settingsDefinitions) {
if (!settings.isChangeAllowed(def)) {
hasImmutableSettings = true;
break;
}
}
if (hasImmutableSettings) {
workPanel.add(new JLabel("* Immutable setting"), BorderLayout.SOUTH);
}
return workPanel;
}
@@ -366,6 +381,10 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
return definition.getName();
}
boolean isEditable() {
return settings.isChangeAllowed(definition);
}
Object getSettingsObject() {
if (definition instanceof EnumSettingsDefinition) {
StringChoices choices = getChoices((EnumSettingsDefinition) definition);
@@ -464,7 +483,11 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
@Override
public boolean isCellEditable(int row, int col) {
return col != 0;
if (col == 0) {
return false;
}
SettingsRowObject rowObject = rows.get(row);
return rowObject.isEditable();
}
@Override
@@ -503,7 +526,11 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
public Object getColumnValueForRow(SettingsRowObject t, int columnIndex) {
switch (columnIndex) {
case 0:
return t.getName();
String name = t.getName();
if (!t.isEditable()) {
name += "*"; // append immutable indicator
}
return name;
case 1:
return t.getSettingsObject();
case 2:
@@ -63,6 +63,8 @@ public class DataTypeSettingsDialog extends AbstractSettingsDialog {
if (dtm instanceof DataTypeManagerDB) {
long id = dtm.getID(dt);
if (id > 0) {
// FIXME: this does not handle re-mapped BuiltIn datatypes
// since multiple instances may be defined
if (dt == dtm.getDataType(id)) {
return; // valid original instance
}
@@ -144,7 +144,7 @@ public class DataTypeSyncInfo {
}
public String getRefDtPath() {
return refDt.getPathName();
return refDt.getCategoryPath().getPath();
}
public long getLastChangeTime(boolean useSource) {
@@ -119,7 +119,7 @@ public class DataTypeSynchronizer {
long lastChangeTime = refDT.getLastChangeTime();
DataType sourceDT = sourceDTM.resolve(refDT, DataTypeConflictHandler.REPLACE_HANDLER);
if (!namesAreEquivalent(refDT, sourceDT)) {
renameDataType(sourceDTM, sourceDT, refDT.getName());
renameDataType(sourceDTM, sourceDT, refDT);
}
if (!StringUtils.equals(refDT.getDescription(), sourceDT.getDescription())) {
sourceDT.setDescription(refDT.getDescription());
@@ -132,7 +132,7 @@ public class DataTypeSynchronizer {
long lastChangeTime = sourceDT.getLastChangeTime();
DataType refDT = refDTM.resolve(sourceDT, DataTypeConflictHandler.REPLACE_HANDLER);
if (!namesAreEquivalent(refDT, sourceDT)) {
renameDataType(refDTM, refDT, sourceDT.getName());
renameDataType(refDTM, refDT, sourceDT);
}
if (!StringUtils.equals(sourceDT.getDescription(), refDT.getDescription())) {
refDT.setDescription(sourceDT.getDescription());
@@ -231,7 +231,15 @@ public class DataTypeSynchronizer {
}
}
private static void renameDataType(DataTypeManager sourceDTM, DataType sourceDT, String name) {
private static void renameDataType(DataTypeManager sourceDTM, DataType sourceDT,
DataType dtToCopy) {
if (isAutoNamedTypedef(dtToCopy)) {
if (sourceDT instanceof TypeDef) {
((TypeDef) sourceDT).enableAutoNaming();
return;
}
}
String name = dtToCopy.getName();
int index = name.indexOf(DataType.CONFLICT_SUFFIX);
if (index > 0) {
name = name.substring(0, index);
@@ -254,7 +262,21 @@ public class DataTypeSynchronizer {
}
}
private static boolean isAutoNamedTypedef(DataType dt) {
if (dt instanceof TypeDef) {
TypeDef td = (TypeDef) dt;
return td.isAutoNamed();
}
return false;
}
public static boolean namesAreEquivalent(DataType dt1, DataType dt2) {
if (isAutoNamedTypedef(dt1)) {
return isAutoNamedTypedef(dt2);
}
else if (isAutoNamedTypedef(dt2)) {
return false;
}
String name1 = dt1.getName();
String name2 = dt2.getName();
if (name1.equals(name2)) {
@@ -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,17 @@
*/
package ghidra.app.plugin.core.datamgr.actions;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.program.model.data.*;
import java.awt.Component;
import org.apache.commons.lang3.StringUtils;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.DockingAction;
import docking.widgets.tree.GTreeNode;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
import ghidra.program.model.data.*;
abstract class AbstractTypeDefAction extends DockingAction {
@@ -55,15 +55,27 @@ abstract class AbstractTypeDefAction extends DockingAction {
return null;
}
return createNewDataType(gTree, dataType, categoryPath, dataTypeManager, typeDefName);
if (StringUtils.isBlank(typeDefName)) {
// use auto-naming for pointer-typedef
if (dataType instanceof Pointer) {
// category ignored
TypeDef typedef = new PointerTypedef(null, (Pointer) dataType, dataTypeManager);
return createNewTypeDef(gTree, typedef, categoryPath, dataTypeManager);
}
// generate default typedef name
String baseName = getBaseName(dataType) + "Typedef";
typeDefName = dataTypeManager.getUniqueName(dataType.getCategoryPath(), baseName);
}
TypeDef typedef = new TypedefDataType(categoryPath, typeDefName, dataType);
return createNewTypeDef(gTree, typedef, categoryPath, dataTypeManager);
}
private DataType createNewDataType(Component parentComponent, DataType dataType,
CategoryPath categoryPath, DataTypeManager dataTypeManager, String name) {
private DataType createNewTypeDef(Component parentComponent, TypeDef typedef,
CategoryPath categoryPath, DataTypeManager dataTypeManager) {
DataType newdt = null;
int transactionID = dataTypeManager.startTransaction("Create Typedef");
try {
DataType typedef = new TypedefDataType(categoryPath, name, dataType);
newdt = dataTypeManager.addDataType(typedef, plugin.getConflictHandler());
}
finally {
@@ -72,4 +84,16 @@ abstract class AbstractTypeDefAction extends DockingAction {
return newdt;
}
protected static String getBaseName(DataType dt) {
if (dt instanceof Pointer) {
DataType dataType = ((Pointer) dt).getDataType();
if (dataType == null) {
// must be a generic pointer type
return dt.getName();
}
return getBaseName(dataType) + "Ptr";
}
return dt.getDisplayName();
}
}
@@ -113,16 +113,15 @@ public class CreateTypeDefAction extends AbstractTypeDefAction {
DataTypeNode dataTypeNode = (DataTypeNode) selectionPaths[0].getLastPathComponent();
DataType dataType = dataTypeNode.getDataType();
String baseName = getBaseName(dataType) + "Typedef";
DerivativeDataTypeInfo info =
new DerivativeDataTypeInfo(plugin, gTree, dataTypeNode, dataType);
DataTypeManager dataTypeManager = info.getDataTypeManager();
String name = dataTypeManager.getUniqueName(dataType.getCategoryPath(), baseName);
CategoryPath categoryPath = info.getCategoryPath();
DataType newTypeDef = createTypeDef(dataTypeManager, dataType, categoryPath, context,
dataTypeNode.getParent(), name);
dataTypeNode.getParent(), null);
if (newTypeDef == null) {
return;
}
@@ -133,15 +132,5 @@ public class CreateTypeDefAction extends AbstractTypeDefAction {
gTree.startEditing(finalParentNode, newNodeName);
}
private static String getBaseName(DataType dt) {
if (dt instanceof Pointer) {
DataType dataType = ((Pointer) dt).getDataType();
if (dataType == null) {
// must be a generic pointer type
return dt.getName();
}
return getBaseName(dataType) + "Ptr";
}
return dt.getDisplayName();
}
}
@@ -176,6 +176,13 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
return null;
}
if (programLocation instanceof MnemonicFieldLocation) {
CodeUnit cu = program.getListing().getCodeUnitAt(programLocation.getAddress());
if (!(cu instanceof Instruction)) {
return null; // defer to mnemonic hover for Data
}
}
Address refAddr = programLocation.getRefAddress();
if (refAddr != null && refAddr.isExternalAddress()) {
return createExternalToolTipComponent(program, refAddr);
@@ -118,7 +118,6 @@ class DbViewerComponent extends JPanel {
if (dbh == null) {
return;
}
Msg.info(this, "Updating dbViewer...");
synchronized (dbh) {
updateTableChoices((TableItem) combo.getSelectedItem());
updateTable();
@@ -70,11 +70,11 @@ public interface StructConverter {
/**
* Reusable 32-bit image base offset datatype.
*/
public final static DataType IBO32 = new ImageBaseOffset32DataType();
public final static DataType IBO32 = IBO32DataType.dataType;
/**
* Reusable 64-bit image base offset datatype.
*/
public final static DataType IBO64 = new ImageBaseOffset64DataType();
public final static DataType IBO64 = IBO64DataType.dataType;
/**
* Returns a structure datatype representing the
@@ -173,7 +173,7 @@ public class ControlFlowGuard {
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT;
// Pre-define base data types used to define table entry data type
DataType ibo32 = new ImageBaseOffset32DataType();
DataType ibo32 = new IBO32DataType();
DataType byteType = ByteDataType.dataType;
CategoryPath categoryPath = new CategoryPath(CategoryPath.ROOT, "CFG");
@@ -255,7 +255,7 @@ public class ControlFlowGuard {
program.getSymbolTable()
.createLabel(tableAddr, GuardCFAddressTakenIatTableName, SourceType.IMPORTED);
// Each table entry is an RVA (32-bit image base offset)
DataType ibo32 = new ImageBaseOffset32DataType();
DataType ibo32 = new IBO32DataType();
for (long i = 0; i < functionCount; i++) {
Data d =
PeUtils.createData(program, tableAddr.add(i * ibo32.getLength()), ibo32, log);
@@ -299,7 +299,7 @@ public class DelayImportDescriptor implements StructConverter {
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
DataType ibo32 = new ImageBaseOffset32DataType();
DataType ibo32 = new IBO32DataType();
StructureDataType struct = new StructureDataType(NAME, 0);
struct.add(DWORD, "grAttrs", null);
struct.add(ibo32, "szName", null);
@@ -84,7 +84,8 @@ public class LoadConfigDataDirectory extends DataDirectory {
if (monitor.isCancelled()) {
return;
}
DataType dt = ntHeader.getOptionalHeader().is64bit() ? new ImageBaseOffset64DataType() : new ImageBaseOffset32DataType();
DataType dt = ntHeader.getOptionalHeader().is64bit() ? IBO64DataType.dataType
: IBO32DataType.dataType;
PeUtils.createData(program, addr, dt, log);
@@ -34,7 +34,7 @@ public class PEx64UnwindInfoDataType extends DynamicDataType {
private final static int UNWIND_OP_INFO_FIELD_LENGTH = 0x04;
private final static DataType BYTE = ByteDataType.dataType;
private final static DataType IBO32 = new ImageBaseOffset32DataType();
private final static DataType IBO32 = new IBO32DataType();
public PEx64UnwindInfoDataType() {
this(null);
@@ -273,7 +273,7 @@ public class MSDataTypeUtils {
*/
public static DataType getReferenceDataType(Program program, DataType referredToDataType) {
DataTypeManager dtm = program.getDataTypeManager();
return is64Bit(program) ? new ImageBaseOffset32DataType(dtm)
return is64Bit(program) ? new IBO32DataType(dtm)
: new PointerDataType(referredToDataType);
}
}
@@ -86,9 +86,6 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
DataType basedataType = dataType;
while (basedataType instanceof TypeDef) {
basedataType = ((TypeDef) basedataType).getDataType();
while (basedataType instanceof Pointer) {
basedataType = ((Pointer) basedataType).getDataType();
}
}
return basedataType;
}
@@ -71,6 +71,11 @@ public class ByteCountSettingsDefinition implements EnumSettingsDefinition {
return BYTE_COUNT;
}
@Override
public String getStorageKey() {
return BYTE_COUNT;
}
@Override
public String getDescription() {
return "Selects the number of bytes to display";
@@ -90,6 +90,11 @@ public class CodeUnitCountSettingsDefinition implements EnumSettingsDefinition {
return CODE_UNIT_COUNT;
}
@Override
public String getStorageKey() {
return CODE_UNIT_COUNT;
}
@Override
public String getDescription() {
return "Selects the number of bytes to display";
@@ -92,6 +92,11 @@ public class CodeUnitOffsetSettingsDefinition implements EnumSettingsDefinition
return MEMORY_OFFSET;
}
@Override
public String getStorageKey() {
return MEMORY_OFFSET;
}
@Override
public String getDescription() {
return "Selects the relative byte offset from which to display";
@@ -77,6 +77,11 @@ public class FunctionInlineSettingsDefinition implements BooleanSettingsDefiniti
return NAME;
}
@Override
public String getStorageKey() {
return INLINE;
}
@Override
public boolean hasValue(Settings settings) {
return settings.getValue(INLINE) != null;
@@ -77,6 +77,11 @@ public class FunctionNoReturnSettingsDefinition implements BooleanSettingsDefini
return NAME;
}
@Override
public String getStorageKey() {
return NORETURN;
}
@Override
public boolean hasValue(Settings settings) {
return settings.getValue(NORETURN) != null;
@@ -76,6 +76,11 @@ public class FunctionThunkSettingsDefinition implements BooleanSettingsDefinitio
return NAME;
}
@Override
public String getStorageKey() {
return THUNK;
}
@Override
public boolean hasValue(Settings settings) {
return settings.getValue(THUNK) != null;
@@ -91,6 +91,11 @@ public class MemoryOffsetSettingsDefinition implements EnumSettingsDefinition {
return MEMORY_OFFSET;
}
@Override
public String getStorageKey() {
return MEMORY_OFFSET;
}
@Override
public String getDescription() {
return "Selects the relative byte offset from which to display";
@@ -24,6 +24,7 @@ import org.junit.Test;
import ghidra.docking.settings.Settings;
import ghidra.program.database.*;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.InvalidNameException;
@@ -1433,7 +1434,7 @@ public class DataTypeMerge5Test extends AbstractDataTypeMergeTest {
// NOTE: these are not viable settings but are intended to exercise all of them
Settings settings = td.getDefaultSettings();
PointerTypeSettingsDefinition.DEF.setType(settings,
PointerType.IBO);
PointerType.IMAGE_BASE_RELATIVE);
AddressSpaceSettingsDefinition.DEF.setValue(settings, "ROM");
ComponentOffsetSettingsDefinition.DEF.setValue(settings, 0x10);
OffsetMaskSettingsDefinition.DEF.setValue(settings, 0x1234);
@@ -1515,6 +1516,211 @@ public class DataTypeMerge5Test extends AbstractDataTypeMergeTest {
checkConflictCount(0);
}
private static String formatAttributes(String attrs) {
StringBuilder buf = new StringBuilder(DataType.TYPEDEF_ATTRIBUTE_PREFIX);
buf.append(attrs);
buf.append(DataType.TYPEDEF_ATTRIBUTE_SUFFIX);
return buf.toString();
}
@Test
public void testTypeDefs11() throws Exception {
// Exercise pointer-typedef auto-naming with setting changes
mtf.initialize("notepad2", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
// must specify datatype manager when constructing to allow for settings to be made
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
PointerTypedef td =
new PointerTypedef(null, foo, -1, dtm, PointerType.IMAGE_BASE_RELATIVE);
DataType dt = dtm.resolve(td, DataTypeConflictHandler.DEFAULT_HANDLER);
assertEquals("Foo * " + formatAttributes("image-base-relative"), dt.getName());
commit = true;
}
finally {
program.endTransaction(transactionID, commit);
}
}
@Override
public void modifyLatest(ProgramDB program) {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"),
"Foo * " + formatAttributes("image-base-relative"));
assertNotNull(td);
td.setName("Bob_Ptr_Td");
Settings settings = td.getDefaultSettings();
PointerTypeSettingsDefinition.DEF.setType(settings,
PointerType.RELATIVE);
Structure st = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
st.setName("Bob");
commit = true;
}
catch (InvalidNameException | DuplicateNameException e) {
failWithException("unexpected", e);
}
finally {
program.endTransaction(transactionID, commit);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"),
"Foo * " + formatAttributes("image-base-relative"));
assertNotNull(td);
Settings settings = td.getDefaultSettings();
ComponentOffsetSettingsDefinition.DEF.setValue(settings, 0x123);
Structure st = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
st.setName("Bill");
commit = true;
}
catch (InvalidNameException | DuplicateNameException e) {
failWithException("unexpected", e);
}
finally {
program.endTransaction(transactionID, commit);
}
}
});
executeMerge();
chooseOption(DataTypeMergeManager.OPTION_MY); // choose Bill rename of Foo
chooseOption(DataTypeMergeManager.OPTION_LATEST); // choose RELATIVE setting and Bob_Ptr_Td rename
waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"), "Bob_Ptr_Td");
assertNotNull(td);
DataType dt = DataTypeUtilities.getBaseDataType(td.getDataType());
assertTrue(dt instanceof Structure);
assertEquals("Bill", dt.getName());
Settings settings = td.getDefaultSettings();
assertEquals(
"Expected pointer-typedef type: relative",
PointerType.RELATIVE, PointerTypeSettingsDefinition.DEF.getType(settings));
assertFalse(
"Unexpected setting: " +
ComponentOffsetSettingsDefinition.DEF.getAttributeSpecification(settings),
ComponentOffsetSettingsDefinition.DEF.hasValue(settings));
checkConflictCount(0);
}
@Test
public void testTypeDefs12() throws Exception {
// Exercise pointer-typedef auto-naming with setting changes
mtf.initialize("notepad2", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
// must specify datatype manager when constructing to allow for settings to be made
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
PointerTypedef td =
new PointerTypedef(null, foo, -1, dtm, PointerType.IMAGE_BASE_RELATIVE);
DataType dt = dtm.resolve(td, DataTypeConflictHandler.DEFAULT_HANDLER);
assertEquals("Foo * " + formatAttributes("image-base-relative"), dt.getName());
commit = true;
}
finally {
program.endTransaction(transactionID, commit);
}
}
@Override
public void modifyLatest(ProgramDB program) {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"),
"Foo * " + formatAttributes("image-base-relative"));
assertNotNull(td);
td.setName("Bob_Ptr_Td");
Settings settings = td.getDefaultSettings();
PointerTypeSettingsDefinition.DEF.setType(settings,
PointerType.RELATIVE);
commit = true;
}
catch (InvalidNameException | DuplicateNameException e) {
failWithException("unexpected", e);
}
finally {
program.endTransaction(transactionID, commit);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
Structure st = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
st.setName("Bill");
commit = true;
}
catch (InvalidNameException | DuplicateNameException e) {
failWithException("unexpected", e);
}
finally {
program.endTransaction(transactionID, commit);
}
}
});
executeMerge(true);
DataTypeManager dtm = resultProgram.getDataTypeManager();
TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"), "Bob_Ptr_Td");
assertNotNull(td);
DataType dt = DataTypeUtilities.getBaseDataType(td.getDataType());
assertTrue(dt instanceof Structure);
assertEquals("Bill", dt.getName());
Settings settings = td.getDefaultSettings();
assertEquals(
"Expected pointer-typedef type: relative",
PointerType.RELATIVE, PointerTypeSettingsDefinition.DEF.getType(settings));
assertFalse(
"Unexpected setting: " +
ComponentOffsetSettingsDefinition.DEF.getAttributeSpecification(settings),
ComponentOffsetSettingsDefinition.DEF.hasValue(settings));
checkConflictCount(0);
}
@Test
public void testArrays() throws Exception {
@@ -15,8 +15,7 @@
*/
package ghidra.app.merge.listing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.*;
import java.awt.Window;
import java.math.BigInteger;
@@ -1165,7 +1164,7 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT
*/
@Override
public Address addr(String address) {
return mtf.getResultProgram().getAddressFactory().getAddress(address);
return mtf.getOriginalProgram().getAddressFactory().getAddress(address);
}
private void setRegValue(ProgramContext pc, Address start, Address end, Register reg,
@@ -522,19 +522,7 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest {
@Test
public void testAnalyzedFalseInLatest() throws Exception {
mtf.initialize("notepad", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
int transactionID = program.startTransaction("test");
try {
Options list = program.getOptions("Program Information");
list.setBoolean("Analyzed", false);
}
finally {
program.endTransaction(transactionID, true);
}
}
mtf.initialize("notepad", new ProgramModifierListener() {
@Override
public void modifyLatest(ProgramDB program) {
@@ -605,19 +593,7 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest {
@Test
public void testAnalyzedFalseInMy() throws Exception {
mtf.initialize("notepad", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
int transactionID = program.startTransaction("test");
try {
Options list = program.getOptions("Program Information");
list.setBoolean("Analyzed", false);
}
finally {
program.endTransaction(transactionID, true);
}
}
mtf.initialize("notepad", new ProgramModifierListener() {
@Override
public void modifyLatest(ProgramDB program) {
@@ -734,10 +710,20 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest {
public void testAnalyzedTrueInLatestFalseInMy() throws Exception {
// test case: conflict because both values changed
// Choose 'latest'
mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
mtf.initialize("notepad", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
int transactionID = program.startTransaction("test");
try {
Options list = program.getOptions("Program Information");
list.setBoolean("Analyzed", false); // revert to default state
}
finally {
program.endTransaction(transactionID, true);
}
}
@Override
public void modifyLatest(ProgramDB program) {
int transactionID = program.startTransaction("test");
@@ -777,10 +763,20 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest {
public void testAnalyzedFalseInLatestTrueInMy() throws Exception {
// test case: conflict because both values changed
// Choose 'latest'
mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
mtf.initialize("notepad", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
int transactionID = program.startTransaction("test");
try {
Options list = program.getOptions("Program Information");
list.setBoolean("Analyzed", false); // revert to default value
}
finally {
program.endTransaction(transactionID, true);
}
}
@Override
public void modifyLatest(ProgramDB program) {
int transactionID = program.startTransaction("test");
@@ -793,9 +789,6 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest {
}
}
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override
public void modifyPrivate(ProgramDB program) {
int transactionID = program.startTransaction("test");
@@ -25,6 +25,8 @@ import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.ProgramBasedDataTypeManager;
import ghidra.program.model.data.StringDataType;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
@@ -44,6 +46,10 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
private volatile boolean success;
private volatile String errMsg;
// Suitable settings allowed for StringDataType data
private static String LONG_SETTING_NAME = "mutability";
private static String STRING_SETTING_NAME = "charset";
private Program buildProgram1(String programName) throws Exception {
ProgramBuilder builder = new ProgramBuilder(programName, ProgramBuilder._TOY);
builder.createMemory(".text", Long.toHexString(0x1001000), 0x6600);
@@ -80,11 +86,13 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.initialize(block);
int transactionID = x8051.startTransaction("Set settings");
ProgramBasedDataTypeManager dtm = x8051.getDataTypeManager();
for (int i = 0; i < 10; i++) {
Address a = getAddr(x8051, "BITS", i);
dtm.setStringSettingsValue(a, "color", "red" + i);
dtm.setLongSettingsValue(a, "someLongValue", i);
Data d = x8051.getListing().createData(a, StringDataType.dataType, 1);
dtm.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i);
dtm.setLongSettingsValue(d, LONG_SETTING_NAME, i);
}
x8051.endTransaction(transactionID, true);
}
@@ -198,11 +206,13 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
ProgramBasedDataTypeManager dtm = x8051.getDataTypeManager();
for (int i = 0; i < 10; i++) {
Address a = getAddr(x8051, "CODE", 0x2000 + i);
Data d = x8051.getListing().getDataAt(a);
assertNotNull(d);
String s = dtm.getStringSettingsValue(a, "color");
String s = dtm.getStringSettingsValue(d, STRING_SETTING_NAME);
assertEquals("red" + i, s);
Long lvalue = dtm.getLongSettingsValue(a, "someLongValue");
Long lvalue = dtm.getLongSettingsValue(d, LONG_SETTING_NAME);
assertEquals(i, lvalue.longValue());
}
}
@@ -17,6 +17,8 @@ package ghidra.program.database.data;
import static org.junit.Assert.*;
import org.junit.*;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
@@ -29,8 +31,6 @@ import ghidra.program.model.mem.Memory;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.task.TaskMonitorAdapter;
import org.junit.*;
/**
*
* To change the template for this generated type comment go to
@@ -171,15 +171,15 @@ public class ArrayTest extends AbstractGhidraHeadedIntegrationTest {
for (int i = 0; i < 10; i++) {
Data comp = data.getComponent(i);
assertEquals(null, comp.getLong("MySetting"));
assertEquals(null, comp.getLong("format"));
}
Data component4 = data.getComponent(4);
component4.setLong("MySetting", 10L);
component4.setLong("format", 10L);
for (int i = 0; i < 10; i++) {
Data comp = data.getComponent(i);
assertEquals((Long) 10L, comp.getLong("MySetting"));
assertEquals((Long) 10L, comp.getLong("format"));
}
}
@@ -199,15 +199,15 @@ public class ArrayTest extends AbstractGhidraHeadedIntegrationTest {
for (int i = 0; i < 10; i++) {
Data comp = subData.getComponent(i);
assertEquals(null, comp.getLong("MySetting"));
assertEquals(null, comp.getLong("format"));
}
Data component4 = subData.getComponent(4);
component4.setLong("MySetting", 10L);
component4.setLong("format", 10L);
for (int i = 0; i < 10; i++) {
Data comp = subData.getComponent(i);
assertEquals((Long) 10L, comp.getLong("MySetting"));
assertEquals((Long) 10L, comp.getLong("format"));
}
}
@@ -27,6 +27,7 @@ import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.mem.Memory;
@@ -47,6 +48,10 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
private AddressSpace space;
private int transactionID;
// Suitable settings allowed for StringDataType data
private static String LONG_SETTING_NAME = "mutability";
private static String STRING_SETTING_NAME = "charset";
// NOTE: Datatypes must be resolved before settings may be changed
// with the exception of TypeDefDataType which does permit
// TypeDefSettingsDefinition settings defined by the base-datatype.
@@ -63,6 +68,16 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
listing = program.getListing();
transactionID = program.startTransaction("Test");
addBlock();
// pointer-typedef has the largest
// System.out.println("Defined string settings:");
// for (SettingsDefinition def : StringDataType.dataType.getSettingsDefinitions()) {
// System.out.println(def.getStorageKey());
// }
for (int i = 0; i < 40; i++) {
DataUtilities.createData(program, addr(i), StringDataType.dataType, 1, false,
ClearDataMode.CLEAR_ALL_CONFLICT_DATA);
}
}
@After
@@ -76,31 +91,33 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testSetDefaultSettings() throws Exception {
DataType dt = ByteDataType.dataType;
DataType dt = StringDataType.dataType;
Settings defaultSettings = dt.getDefaultSettings();
defaultSettings.setString("color", "red");
defaultSettings.setLong("someLongValue", 10);
assertNull(defaultSettings.getString("color"));
assertNull(defaultSettings.getLong("someLongValue"));
// immutable warnings expected
defaultSettings.setString(STRING_SETTING_NAME, "red");
defaultSettings.setLong(LONG_SETTING_NAME, 10);
assertNull(defaultSettings.getString(STRING_SETTING_NAME));
assertNull(defaultSettings.getLong(LONG_SETTING_NAME));
// May modify byte default settings after resolve
dt = dataMgr.resolve(dt, null);
defaultSettings = dt.getDefaultSettings();
defaultSettings.setString("color", "red");
defaultSettings.setLong("someLongValue", 10);
defaultSettings.setString(STRING_SETTING_NAME, "red");
defaultSettings.setLong(LONG_SETTING_NAME, 10);
assertEquals("red", defaultSettings.getString("color"));
Long lv = defaultSettings.getLong("someLongValue");
assertEquals("red", defaultSettings.getString(STRING_SETTING_NAME));
Long lv = defaultSettings.getLong(LONG_SETTING_NAME);
assertNotNull(lv);
assertEquals(10, lv.longValue());
defaultSettings.setValue("long", 10L);
Object obj = defaultSettings.getValue("long");
assertNotNull(obj);
assertEquals(10, ((Long) obj).longValue());
defaultSettings.setValue(LONG_SETTING_NAME, 20L);
Object obj = defaultSettings.getValue(LONG_SETTING_NAME);
assertTrue(obj instanceof Long);
assertEquals(20, ((Long) obj).longValue());
}
@Test
@@ -112,31 +129,32 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(0, ByteDataType.dataType.getTypeDefSettingsDefinitions().length);
Settings defaultSettings = typeDef.getDefaultSettings();
defaultSettings.setString("color", "red");
defaultSettings.setLong("someLongValue", 10);
assertNull(defaultSettings.getString("color"));
assertNull(defaultSettings.getLong("someLongValue"));
// immutable warnings expected
FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.CHAR);
EndianSettingsDefinition.DEF.setBigEndian(defaultSettings, false);
PaddingSettingsDefinition.DEF.setPadded(defaultSettings, true);
assertNull(defaultSettings.getLong("format"));
assertNull(defaultSettings.getLong("endian"));
assertNull(defaultSettings.getLong("padding"));
// May modify arbitrary typedef default settings after resolve
typeDef = (TypeDef) dataMgr.resolve(typeDef, null);
defaultSettings = typeDef.getDefaultSettings();
defaultSettings.setString("color", "red");
defaultSettings.setLong("someLongValue", 10);
FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.CHAR);
EndianSettingsDefinition.DEF.setBigEndian(defaultSettings, false);
PaddingSettingsDefinition.DEF.setPadded(defaultSettings, true);
assertEquals("red", defaultSettings.getString("color"));
Long lv = defaultSettings.getLong("someLongValue");
assertNotNull(lv);
assertEquals(10, lv.longValue());
defaultSettings.setValue("long", 10L);
Object obj = defaultSettings.getValue("long");
assertNotNull(obj);
assertEquals(10, ((Long) obj).longValue());
assertEquals(FormatSettingsDefinition.CHAR, defaultSettings.getLong("format").longValue());
assertEquals(EndianSettingsDefinition.LITTLE,
defaultSettings.getLong("endian").longValue());
assertEquals(PaddingSettingsDefinition.PADDED_VALUE,
defaultSettings.getLong("padded").longValue());
try {
defaultSettings.setValue("color", Color.RED);
defaultSettings.setValue("format", Color.RED);
Assert.fail("Should not be able to set arbitrary objects");
}
catch (IllegalArgumentException e) {
@@ -147,10 +165,10 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testIsEmpty() throws Exception {
DataType dt = dataMgr.resolve(ByteDataType.dataType, null);
Settings defaultSettings = dt.getDefaultSettings();
defaultSettings.setString("color", "red");
defaultSettings.setLong("someLongValue", 10);
Data data = listing.getDataAt(addr(10));
Settings defaultSettings = data.getDataType().getDefaultSettings();
defaultSettings.setString(STRING_SETTING_NAME, "red");
defaultSettings.setLong(LONG_SETTING_NAME, 10);
assertTrue(!defaultSettings.isEmpty());
@@ -161,107 +179,99 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testGetNames() throws Exception {
DataType dt = dataMgr.resolve(ByteDataType.dataType, null);
DataType dt = dataMgr.resolve(StringDataType.dataType, null);
Settings defaultSettings = dt.getDefaultSettings();
defaultSettings.setString("color", "red");
defaultSettings.setLong("someLongValue", 10);
defaultSettings.setString("endian", "big Endian");
defaultSettings.setString(STRING_SETTING_NAME, "red");
defaultSettings.setLong(LONG_SETTING_NAME, 10);
String[] names = defaultSettings.getNames();
assertEquals(3, names.length);
assertEquals(2, names.length);
}
@Test
public void testClearSetting() throws Exception {
DataType dt = dataMgr.resolve(ByteDataType.dataType, null);
DataType dt = dataMgr.resolve(StringDataType.dataType, null);
Settings defaultSettings = dt.getDefaultSettings();
defaultSettings.setString("color", "red");
defaultSettings.setLong("someLongValue", 10);
defaultSettings.setString(STRING_SETTING_NAME, "red");
defaultSettings.setLong(LONG_SETTING_NAME, 10);
defaultSettings.clearSetting("color");
assertNull(defaultSettings.getString("color"));
defaultSettings.clearSetting(STRING_SETTING_NAME);
assertNull(defaultSettings.getString(STRING_SETTING_NAME));
}
@Test
public void testInstanceSettings() throws Exception {
listing.createData(addr(10), new ByteDataType(), 1);
Data data = listing.getDataAt(addr(10));
ByteDataType dt = (ByteDataType) data.getDataType();
Data data = DataUtilities.createData(program, addr(10), ByteDataType.dataType, 1, false,
ClearDataMode.CLEAR_ALL_CONFLICT_DATA);
DataType dt = data.getDataType();
Settings defaultSettings = dt.getDefaultSettings();
defaultSettings.setLong("format", FormatSettingsDefinition.CHAR);
defaultSettings.setLong("signed", 0);
defaultSettings.setLong("padded", 1);
FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.CHAR);
EndianSettingsDefinition.DEF.setBigEndian(defaultSettings, false);
PaddingSettingsDefinition.DEF.setPadded(defaultSettings, true);
SettingsDefinition[] defs = dt.getSettingsDefinitions();
for (int i = 0; i < defs.length; i++) {
assertEquals(FormatSettingsDefinition.CHAR, data.getLong("format").longValue());
FormatSettingsDefinition.DEF.setChoice(data, FormatSettingsDefinition.DECIMAL);
assertEquals(FormatSettingsDefinition.DECIMAL, data.getLong("format").longValue());
if (defs[i] instanceof EnumSettingsDefinition) {
EnumSettingsDefinition enumDef = (EnumSettingsDefinition) defs[i];
int value = enumDef.getChoice(data);
enumDef.setChoice(data, value);
if (i == 0) {
assertEquals(FormatSettingsDefinition.CHAR, data.getLong("format").longValue());
}
else if (i == 1) {
assertEquals(0, data.getLong("signed").longValue());
}
else if (i == 2) {
assertEquals(1, data.getLong("padded").longValue());
}
assertEquals(EndianSettingsDefinition.LITTLE, data.getLong("endian").longValue());
EndianSettingsDefinition.DEF.setChoice(data, EndianSettingsDefinition.BIG);
assertEquals(EndianSettingsDefinition.BIG, data.getLong("endian").longValue());
}
}
assertEquals(PaddingSettingsDefinition.PADDED_VALUE, data.getLong("padded").longValue());
PaddingSettingsDefinition.DEF.setChoice(data, PaddingSettingsDefinition.UNPADDED_VALUE);
assertEquals(PaddingSettingsDefinition.UNPADDED_VALUE, data.getLong("padded").longValue());
FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.HEX);
EndianSettingsDefinition.DEF.clear(defaultSettings);
PaddingSettingsDefinition.DEF.clear(defaultSettings);
assertEquals(FormatSettingsDefinition.DECIMAL, data.getLong("format").longValue());
assertEquals(EndianSettingsDefinition.BIG, data.getLong("endian").longValue());
assertEquals(PaddingSettingsDefinition.UNPADDED_VALUE, data.getLong("padded").longValue());
}
@Test
public void testGetInstanceNames() throws Exception {
listing.createData(addr(10), new ByteDataType(), 1);
Data data = listing.getDataAt(addr(10));
data.setString("color", "red");
data.setLong("someLongValue", 10);
data.setString("endian", "big Endian");
data.setString(STRING_SETTING_NAME, "red");
data.setLong(LONG_SETTING_NAME, 10);
String[] names = data.getNames();
assertEquals(3, names.length);
assertEquals(2, names.length);
}
@Test
public void testClearInstanceSettings() throws Exception {
listing.createData(addr(10), new ByteDataType(), 1);
Data data = listing.getDataAt(addr(10));
data.setString("color", "red");
data.setLong("someLongValue", 10);
data.setString(STRING_SETTING_NAME, "red");
data.setLong(LONG_SETTING_NAME, 10);
data.clearSetting("color");
assertNull(data.getString("color"));
data.clearSetting(STRING_SETTING_NAME);
assertNull(data.getString(STRING_SETTING_NAME));
}
@Test
public void testClearAllInstanceSettings() throws Exception {
listing.createData(addr(10), new ByteDataType(), 1);
Data data = listing.getDataAt(addr(10));
data.setString("color", "red");
data.setLong("someLongValue", 10);
data.setString("endian", "big Endian");
data.setString(STRING_SETTING_NAME, "red");
data.setLong(LONG_SETTING_NAME, 10);
data.clearAllSettings();
assertNull(data.getString("color"));
assertNull(data.getLong("someLongValue"));
assertNull(data.getString("endian"));
assertNull(data.getString(STRING_SETTING_NAME));
assertNull(data.getLong(LONG_SETTING_NAME));
}
@Test
public void testIsEmptyInstanceSettings() throws Exception {
listing.createData(addr(10), new ByteDataType(), 1);
Data data = listing.getDataAt(addr(10));
data.setString("color", "red");
data.setLong("someLongValue", 10);
data.setString("endian", "big Endian");
data.setString(STRING_SETTING_NAME, "red");
data.setLong(LONG_SETTING_NAME, 10);
assertTrue(!data.isEmpty());
data.clearAllSettings();
@@ -269,23 +279,29 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(data.isEmpty());
}
private Data getDataAt(long offset) {
Data data = listing.getDataAt(addr(offset));
assertNotNull("expected data at address 0x" + Long.toHexString(offset));
return data;
}
@Test
public void testMoveSettings() throws Exception {
for (int i = 0; i < 10; i++) {
Address a = addr(i);
dataMgr.setStringSettingsValue(a, "color", "red" + i);
dataMgr.setLongSettingsValue(a, "someLongValue", i);
Data d = getDataAt(i);
dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i);
dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i);
}
dataMgr.moveAddressRange(addr(0), addr(20), 10, TaskMonitor.DUMMY);
int j = 0;
for (int i = 20; i < 30; i++, j++) {
Address a = addr(i);
Data d = getDataAt(i);
String s = dataMgr.getStringSettingsValue(a, "color");
String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME);
assertEquals("red" + j, s);
Long lvalue = dataMgr.getLongSettingsValue(a, "someLongValue");
Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME);
assertEquals(j, lvalue.longValue());
}
}
@@ -294,9 +310,9 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
public void testMoveSettings2() {
for (int i = 0; i < 10; i++) {
Address a = addr(i);
dataMgr.setStringSettingsValue(a, "color", "red" + i);
dataMgr.setLongSettingsValue(a, "someLongValue", i);
Data d = getDataAt(i);
dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i);
dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i);
}
try {
dataMgr.moveAddressRange(addr(0), addr(5), 10, TaskMonitor.DUMMY);
@@ -307,12 +323,12 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
int j = 0;
for (int i = 5; i < 15; i++, j++) {
Address a = addr(i);
Data d = getDataAt(i);
String s = dataMgr.getStringSettingsValue(a, "color");
String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME);
assertEquals("red" + j, s);
Long lvalue = dataMgr.getLongSettingsValue(a, "someLongValue");
Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME);
assertEquals(j, lvalue.longValue());
}
}
@@ -322,9 +338,9 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
int j = 20;
for (int i = 20; i < 30; i++, j++) {
Address a = addr(i);
dataMgr.setStringSettingsValue(a, "color", "red" + i);
dataMgr.setLongSettingsValue(a, "someLongValue", i);
Data d = getDataAt(i);
dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i);
dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i);
}
j = 20;
try {
@@ -334,12 +350,12 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
Assert.fail("Unexpected cancelled exception");
}
for (int i = 5; i < 15; i++, j++) {
Address a = addr(i);
Data d = getDataAt(i);
String s = dataMgr.getStringSettingsValue(a, "color");
String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME);
assertEquals("red" + j, s);
Long lvalue = dataMgr.getLongSettingsValue(a, "someLongValue");
Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME);
assertEquals(j, lvalue.longValue());
}
@@ -378,8 +394,7 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
DataType byteDT = dataMgr.resolve(new ByteDataType(), null);
SettingsDefinition[] settingsDefinitions = byteDT.getSettingsDefinitions();
Settings settings = byteDT.getDefaultSettings();
settings.setLong("format", FormatSettingsDefinition.OCTAL);
settings.setString("color", "red");
FormatSettingsDefinition.DEF.setChoice(settings, FormatSettingsDefinition.OCTAL);
TypedefDataType tdt = new TypedefDataType("ByteTypedef", byteDT);
TypeDef td = (TypeDef) dataMgr.addDataType(tdt, null);
@@ -388,11 +403,18 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(sdefs.length >= settingsDefinitions.length); // TypeDef may add some of its own
Settings defSettings = td.getDefaultSettings();
defSettings.setLong("someLongValue", 10);
assertEquals(FormatSettingsDefinition.OCTAL,
FormatSettingsDefinition.DEF.getChoice(defSettings));
FormatSettingsDefinition.DEF.setChoice(defSettings, FormatSettingsDefinition.DECIMAL);
assertEquals(FormatSettingsDefinition.DECIMAL,
FormatSettingsDefinition.DEF.getChoice(defSettings));
FormatSettingsDefinition.DEF.setChoice(settings, FormatSettingsDefinition.HEX);
assertEquals(FormatSettingsDefinition.DECIMAL,
FormatSettingsDefinition.DEF.getChoice(defSettings)); // unchanged
assertEquals((long) FormatSettingsDefinition.OCTAL, defSettings.getValue("format")); // inherits from byteDt
assertEquals("red", defSettings.getValue("color")); // inherits from byteDt
assertEquals(10L, defSettings.getValue("someLongValue"));
}
@Test
@@ -19,6 +19,8 @@ import static org.junit.Assert.*;
import org.junit.*;
import ghidra.docking.settings.Settings;
import ghidra.program.database.DatabaseObject;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.data.PointerTypedefInspector;
import ghidra.program.model.address.AddressSpace;
@@ -52,7 +54,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
program = buildProgram("notepad");
dtm = program.getDataTypeManager();
builtInDtm = BuiltInDataTypeManager.getDataTypeManager();
program.startTransaction("TEST");
}
@@ -63,75 +65,241 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT
}
@Test
public void testIBOBuiltIn() throws Exception {
public void testBuiltInIBODataTypes() throws Exception {
DataType dt = builtInDtm.getDataType(CategoryPath.ROOT, IBO32DataType.NAME);
assertTrue(dt instanceof TypeDef);
assertFalse(dt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB
assertTrue(dt instanceof IBO32DataType);
assertEquals(IBO32DataType.NAME, dt.getName());
assertFalse(dt.hasLanguageDependantLength());
assertTrue(dt.isEquivalent(dtm.resolve(dt, null)));
dt = new IBO32DataType(CharDataType.dataType, dtm);
assertTrue(dt instanceof TypeDef);
assertTrue(dt instanceof BuiltIn);
assertEquals("char *32 __attribute__((image-base-relative))", dt.getName());
DataType dbDt = dtm.resolve(dt, null);
assertTrue(dbDt instanceof TypeDef);
assertFalse(dbDt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB
assertTrue(dbDt instanceof IBO32DataType);
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
dt = builtInDtm.getDataType(CategoryPath.ROOT, IBO64DataType.NAME);
assertTrue(dt instanceof TypeDef);
assertFalse(dt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB
assertTrue(dt instanceof IBO64DataType);
assertEquals(IBO64DataType.NAME, dt.getName());
assertFalse(dt.hasLanguageDependantLength());
assertTrue(dt.isEquivalent(dtm.resolve(dt, null)));
dt = new IBO64DataType(CharDataType.dataType, dtm);
assertTrue(dt instanceof TypeDef);
assertTrue(dt instanceof BuiltIn);
assertEquals("char *64 __attribute__((image-base-relative))", dt.getName());
dbDt = dtm.resolve(dt, null);
assertTrue(dbDt instanceof TypeDef);
assertFalse(dbDt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB
assertTrue(dbDt instanceof IBO64DataType);
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
}
@Test
public void testPointerTypedef() throws Exception {
DataType dt = new PointerTypedef(null, CharDataType.dataType, -1, dtm,
program.getAddressFactory().getRegisterSpace());
DataType dt = new PointerTypedef(null, CharDataType.dataType, -1, dtm, 0x8);
assertTrue(dt.hasLanguageDependantLength());
assertEquals(4, dt.getLength());
assertTrue(dt instanceof TypeDef);
assertTrue(dt instanceof BuiltIn);
assertEquals("char * __attribute__((space(register)))", dt.getName());
assertEquals("char * " + formatAttributes("offset(0x8)"), dt.getName());
DataType dbDt = dtm.resolve(dt, null);
assertTrue(dbDt instanceof TypeDef);
assertFalse(dbDt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
AddressSpace space = PointerTypedefInspector.getPointerAddressSpace((TypeDef) dbDt,
program.getAddressFactory());
assertTrue(program.getAddressFactory().getRegisterSpace().equals(space));
assertNull(space);
dt = new PointerTypedef(null, CharDataType.dataType, -1, dtm,
PointerType.RELATIVE);
assertTrue(dt.hasLanguageDependantLength());
assertEquals(4, dt.getLength());
assertTrue(dt instanceof TypeDef);
assertTrue(dt instanceof BuiltIn);
assertEquals("char * __attribute__((relative))", dt.getName());
assertEquals("char * " + formatAttributes("relative"), dt.getName());
dbDt = dtm.resolve(dt, null);
assertTrue(dbDt instanceof TypeDef);
assertFalse(dbDt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
assertEquals(PointerType.RELATIVE, PointerTypedefInspector.getPointerType((TypeDef) dbDt));
}
@Test
public void testPointerTypedefWithAddrSpace() throws Exception {
DataType dt = new PointerTypedef(null, CharDataType.dataType, -1, dtm,
program.getAddressFactory().getRegisterSpace());
assertFalse(dt.hasLanguageDependantLength());
assertEquals(2, dt.getLength());
assertTrue(dt instanceof TypeDef);
assertEquals("char *16 " + formatAttributes("space(register)"), dt.getName());
DataType dbDt = dtm.resolve(dt, null);
assertTrue(dbDt instanceof TypeDef);
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
AddressSpace space = PointerTypedefInspector.getPointerAddressSpace((TypeDef) dbDt,
program.getAddressFactory());
assertTrue(program.getAddressFactory().getRegisterSpace().equals(space));
dt = new PointerTypedef(null, CharDataType.dataType, 4, dtm,
program.getAddressFactory().getRegisterSpace());
assertFalse(dt.hasLanguageDependantLength());
assertEquals(4, dt.getLength());
assertTrue(dt instanceof TypeDef);
assertEquals("char *32 " + formatAttributes("space(register)"), dt.getName());
dbDt = dtm.resolve(dt, null);
assertTrue(dbDt instanceof TypeDef);
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
space = PointerTypedefInspector.getPointerAddressSpace((TypeDef) dbDt,
program.getAddressFactory());
assertTrue(program.getAddressFactory().getRegisterSpace().equals(space));
}
@Test
public void testPointerTypedefAutoNaming() throws Exception {
DataType st = dtm.resolve(new StructureDataType("foo", 10), null);
DataType dt = new PointerTypedef(null, st, -1, dtm,
program.getAddressFactory().getRegisterSpace());
assertFalse(dt.hasLanguageDependantLength());
assertEquals(2, dt.getLength());
assertTrue(dt instanceof TypeDef);
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
DataType dbDt = dtm.resolve(dt, null);
assertTrue(dbDt instanceof TypeDef);
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
st.setName("bob");
// auto-name should update
assertEquals("bob *16 " + formatAttributes("space(register)"), dbDt.getName());
Settings settings = dbDt.getDefaultSettings();
OffsetMaskSettingsDefinition.DEF.setValue(settings, 0x123456789abcdef0L);
assertEquals("bob *16 " + formatAttributes("space(register),mask(0x123456789abcdef0)"),
dbDt.getName());
ComponentOffsetSettingsDefinition.DEF.setValue(settings, 0x123);
assertEquals(
"bob *16 " + formatAttributes("space(register),mask(0x123456789abcdef0),offset(0x123)"),
dbDt.getName());
OffsetShiftSettingsDefinition.DEF.setValue(settings, 16);
assertEquals(
"bob *16 " + formatAttributes(
"space(register),mask(0x123456789abcdef0),shift(16),offset(0x123)"),
dbDt.getName());
PointerTypeSettingsDefinition.DEF.setType(settings, PointerType.IMAGE_BASE_RELATIVE);
assertEquals(
"bob *16 " + formatAttributes(
"image-base-relative,space(register),mask(0x123456789abcdef0),shift(16),offset(0x123)"),
dbDt.getName());
st.setName("bill");
assertEquals(
"bill *16 " + formatAttributes(
"image-base-relative,space(register),mask(0x123456789abcdef0),shift(16),offset(0x123)"),
dbDt.getName());
PointerTypeSettingsDefinition.DEF.clear(settings);
assertEquals(
"bill *16 " + formatAttributes(
"space(register),mask(0x123456789abcdef0),shift(16),offset(0x123)"),
dbDt.getName());
ComponentOffsetSettingsDefinition.DEF.clear(settings);
assertEquals(
"bill *16 " + formatAttributes("space(register),mask(0x123456789abcdef0),shift(16)"),
dbDt.getName());
// NOTE: Changing address space setting will not alter pointer size
AddressSpaceSettingsDefinition.DEF.clear(settings);
assertEquals(
"bill *16 " + formatAttributes("mask(0x123456789abcdef0),shift(16)"),
dbDt.getName());
OffsetShiftSettingsDefinition.DEF.clear(settings);
assertEquals(
"bill *16 " + formatAttributes("mask(0x123456789abcdef0)"),
dbDt.getName());
}
@Test
public void testPointerTypedefEquivalence() throws Exception {
DataType st = dtm.resolve(new StructureDataType("foo", 10), null);
TypeDef dt = new PointerTypedef(null, st, -1, dtm,
program.getAddressFactory().getRegisterSpace());
assertTrue(dt.isAutoNamed());
assertFalse(dt.hasLanguageDependantLength());
assertEquals(2, dt.getLength());
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
TypeDef dbDt = (TypeDef) dtm.resolve(dt, null);
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
dt = (TypeDef) dt.copy(dtm);
assertTrue(dbDt == dtm.resolve(dt, null)); // should resolve to same instance
dt = (TypeDef) dt.copy(dtm);
PointerTypeSettingsDefinition.DEF.setType(dt.getDefaultSettings(), PointerType.IMAGE_BASE_RELATIVE);
TypeDef dbDt2 = (TypeDef) dtm.resolve(dt, null); // should resolve to new instance
assertTrue(dbDt != dbDt2);
assertEquals("foo *16 " + formatAttributes("image-base-relative,space(register)"),
dbDt2.getName());
PointerTypeSettingsDefinition.DEF.clear(dbDt2.getDefaultSettings());
assertEquals("foo *16 " + formatAttributes("space(register)") + DataType.CONFLICT_SUFFIX,
dbDt2.getName());
}
@Test
public void testPointerTypedefEquivalence2() throws Exception {
DataType st = dtm.resolve(new StructureDataType("foo", 10), null);
TypeDef dt = new PointerTypedef(null, st, -1, dtm,
program.getAddressFactory().getRegisterSpace());
assertTrue(dt.isAutoNamed());
assertFalse(dt.hasLanguageDependantLength());
assertEquals(2, dt.getLength());
assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName());
TypeDef dbDt = (TypeDef) dtm.resolve(dt, null);
assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB
assertTrue(dt.isEquivalent(dbDt));
assertEquals(dt.getName(), dbDt.getName());
TypeDef dt2 = new PointerTypedef("john", st, -1, dtm,
program.getAddressFactory().getRegisterSpace());
assertFalse(dt2.isAutoNamed());
assertFalse(dt2.hasLanguageDependantLength());
assertEquals(2, dt.getLength());
assertEquals("john", dt2.getName());
TypeDef dbDt2 = (TypeDef) dtm.resolve(dt2, null);
assertTrue(dbDt != dbDt2);
assertFalse(dbDt.isEquivalent(dbDt2));
assertFalse(dbDt2.isEquivalent(dbDt));
assertTrue(dbDt2 instanceof DatabaseObject); // transforms to TypedefDB
assertTrue(dt2.isEquivalent(dbDt2));
assertEquals(dt2.getName(), dbDt2.getName());
}
private static String formatAttributes(String attrs) {
StringBuilder buf = new StringBuilder(DataType.TYPEDEF_ATTRIBUTE_PREFIX);
buf.append(attrs);
buf.append(DataType.TYPEDEF_ATTRIBUTE_SUFFIX);
return buf.toString();
}
}
@@ -17,6 +17,7 @@ package ghidra.program.database;
import java.io.IOException;
import db.*;
import db.buffers.BufferFile;
import generic.test.AbstractGenericTest;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
@@ -28,6 +29,7 @@ import ghidra.program.model.listing.ProgramChangeSet;
import ghidra.test.TestEnv;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
/**
@@ -105,7 +107,7 @@ public abstract class AbstractMTFModel {
return privateChangeSet;
}
public ProgramChangeSet getResultChangeSet() {
public ProgramChangeSet getLatestChangeSet() {
return latestChangeSet;
}
@@ -113,7 +115,7 @@ public abstract class AbstractMTFModel {
return env;
}
protected void disableAutoAnalysis(Program p) {
protected static void disableAutoAnalysis(Program p) {
// Disable all analysis
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(p);
AbstractGenericTest.setInstanceField("isEnabled", analysisMgr, Boolean.FALSE);
@@ -169,4 +171,25 @@ public abstract class AbstractMTFModel {
throws Exception;
public abstract void initialize(String programName, ProgramModifierListener l) throws Exception;
/**
* Clone a program to a new instance. The new instance will be assigned an empty change-set.
* @param prog program to be cloned
* @param consumer new program consumer
* @return new program instance
* @throws IOException if a file IO error occurs
*/
public static ProgramDB cloneProgram(ProgramDB prog, Object consumer) throws IOException {
try {
DBHandle newDbh = DBTestUtils.cloneDbHandle(prog.getDBHandle());
ProgramDB newProg =
new ProgramDB(newDbh, DBConstants.UPDATE, TaskMonitor.DUMMY, consumer);
newProg.setChangeSet(new ProgramDBChangeSet(newProg.getAddressMap(), 20));
disableAutoAnalysis(newProg);
return newProg;
}
catch (CancelledException | VersionException e) {
throw new RuntimeException(e); // unexpected
}
}
}
@@ -90,7 +90,7 @@ public class MergeTestFacilitator {
* Get the change set for Result program.
*/
public ProgramChangeSet getResultChangeSet() {
return model.getResultChangeSet();
return model.getLatestChangeSet();
}
public void dispose() {
@@ -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.
@@ -30,11 +29,7 @@ import ghidra.util.exception.AssertException;
// TODO rename--this is no longer using real programs
public class RealProgramMTFModel extends AbstractMTFModel {
/**
* We install our test ID generator to ensure that all datatypes we create will share the
* same ID across all versions of the programs we create. If we do not do this, then they will
* be different, which breaks many tests.
*/
// Use simple ID generation
private TestUniversalIdGenerator universalIdGenerator = new TestUniversalIdGenerator();
RealProgramMTFModel(TestEnv env) {
@@ -46,12 +41,15 @@ public class RealProgramMTFModel extends AbstractMTFModel {
cleanup();
MergeProgramGenerator programGenerator = createProgramGenerator(programName);
generatePrograms(programName, programGenerator);
originalProgram = programGenerator.generateProgram(programName);
disableAutoAnalysis();
latestProgram = cloneProgram(originalProgram, this);
modifier.modifyLatest(latestProgram);
makeIncomingChanges(modifier);
makeLocalChanges(modifier);
resultProgram = cloneProgram(latestProgram, this);
privateProgram = cloneProgram(originalProgram, this);
modifier.modifyPrivate(privateProgram);
recordChanges();
clearChanges();
@@ -63,36 +61,21 @@ public class RealProgramMTFModel extends AbstractMTFModel {
cleanup();
MergeProgramGenerator programGenerator = createProgramGenerator(programName);
generatePrograms(programName, programGenerator);
originalProgram = programGenerator.generateProgram(programName);
modifier.modifyOriginal(originalProgram);
disableAutoAnalysis();
privateProgram = cloneProgram(originalProgram, this);
modifier.modifyPrivate(privateProgram);
createCustomStartingProgram(modifier);
clearChanges();
latestProgram = cloneProgram(originalProgram, this);
modifier.modifyLatest(latestProgram);
makeIncomingChanges(modifier);
makeLocalChanges(modifier);
resultProgram = cloneProgram(latestProgram, this);
recordChanges();
clearChanges();
}
/**
* Tests that use this method are creating a new 'original' program, rather than using
* a pre-fabricated one from a program builder.
*/
private void createCustomStartingProgram(OriginalProgramModifierListener modifier)
throws Exception {
universalIdGenerator.checkpoint();
modifier.modifyOriginal(originalProgram);
universalIdGenerator.restore();
modifier.modifyOriginal(privateProgram);
universalIdGenerator.restore();
modifier.modifyOriginal(latestProgram);
universalIdGenerator.restore();
modifier.modifyOriginal(resultProgram);
}
private MergeProgramGenerator createProgramGenerator(String programName) {
if (programName.toLowerCase().contains("notepad")) {
return new MergeProgramGenerator_Notepads(this);
@@ -117,35 +100,6 @@ public class RealProgramMTFModel extends AbstractMTFModel {
throw new UnsupportedOperationException();
}
private void disableAutoAnalysis() {
disableAutoAnalysis(privateProgram);
disableAutoAnalysis(resultProgram);
disableAutoAnalysis(latestProgram);
}
private void makeLocalChanges(ProgramModifierListener modifier) throws Exception {
modifier.modifyPrivate(privateProgram);
}
private void makeIncomingChanges(ProgramModifierListener modifier) throws Exception {
universalIdGenerator.checkpoint();
modifier.modifyLatest(latestProgram);
universalIdGenerator.restore();
modifier.modifyLatest(resultProgram);
}
private void generatePrograms(String programName, MergeProgramGenerator programGenerator)
throws Exception {
universalIdGenerator.checkpoint();
privateProgram = programGenerator.generateProgram(programName);
universalIdGenerator.restore();
originalProgram = programGenerator.generateProgram(programName);
universalIdGenerator.restore();
resultProgram = programGenerator.generateProgram(programName);
universalIdGenerator.restore();
latestProgram = programGenerator.generateProgram(programName);
}
private void recordChanges() {
// ...keep track of the changes we've made
latestChangeSet = latestProgram.getChanges();
@@ -581,8 +581,8 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
int addressSize = address.getSize();
if (addressSize == 64 && getIboIf64bit) {
ImageBaseOffset32DataType ibo32 =
new ImageBaseOffset32DataType(currentProgram.getDataTypeManager());
IBO32DataType ibo32 =
new IBO32DataType(currentProgram.getDataTypeManager());
int length = ibo32.getLength();
DumbMemBufferImpl compMemBuffer =
new DumbMemBufferImpl(currentProgram.getMemory(), address);
@@ -15,7 +15,7 @@
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import ghidra.app.cmd.data.*;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
@@ -116,7 +116,7 @@ public class EHCatchHandlerModel extends AbstractCreateDataTypeModel {
/* comps[1] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
struct.add(compDt, "dispType", null);
}
else {
@@ -136,7 +136,7 @@ public class EHCatchHandlerModel extends AbstractCreateDataTypeModel {
/* comps[3] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
struct.add(compDt, "dispOfHandler", null);
}
else {
@@ -15,7 +15,7 @@
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
@@ -101,7 +101,7 @@ public class EHESTypeListModel extends AbstractCreateDataTypeModel {
/* comps[1] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
struct.add(compDt, "dispTypeArray", null);
}
else {
@@ -15,7 +15,7 @@
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
@@ -150,7 +150,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel {
DataTypeManager dataTypeManager = getProgram().getDataTypeManager();
int intSize = new IntegerDataType(dataTypeManager).getLength();
int uintSize = new UnsignedIntegerDataType(dataTypeManager).getLength();
int ibo32Size = new ImageBaseOffset32DataType(dataTypeManager).getLength();
int ibo32Size = new IBO32DataType(dataTypeManager).getLength();
int defaultPointerSize = getDefaultPointerSize();
int size20;
int additional21;
@@ -316,7 +316,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel {
/* comps[2] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
struct.add(compDt, "dispUnwindMap", null);
}
else {
@@ -330,7 +330,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel {
/* comps[4] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
struct.add(compDt, "dispTryBlockMap", null);
}
else {
@@ -344,7 +344,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel {
/* comps[6] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
struct.add(compDt, "dispIPToStateMap", null);
}
else {
@@ -359,7 +359,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel {
if (isV2 || isV3) {
if (isRelative) { /* comps[8] */
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
struct.add(compDt, "dispESTypeList", null);
}
else { /* comps[7] */
@@ -15,7 +15,7 @@
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
@@ -102,7 +102,7 @@ public class EHIPToStateModel extends AbstractCreateDataTypeModel {
/* comps[0] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
}
else {
DataType dwordDt = new TypedefDataType(new CategoryPath("/WinDef.h"), "DWORD",
@@ -15,7 +15,7 @@
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
@@ -114,7 +114,7 @@ public class EHTryBlockModel extends AbstractCreateDataTypeModel {
/* comps[4] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
struct.add(compDt, "dispHandlerArray", null);
}
else {
@@ -15,7 +15,7 @@
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
@@ -106,7 +106,7 @@ public class EHUnwindModel extends AbstractCreateDataTypeModel {
/* comps[1] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
compDt = new IBO32DataType(dataTypeManager);
}
else {
@@ -232,7 +232,7 @@ public class Rtti1Model extends AbstractCreateRttiDataModel {
boolean is64Bit = MSDataTypeUtils.is64Bit(program);
Structure rtti1Struct = (Structure) DataTypeUtils.getBaseDataType(rtti1Dt);
DataType rtti3RefDt =
is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti3Dt);
is64Bit ? new IBO32DataType(dataTypeManager) : new PointerDataType(rtti3Dt);
rtti1Struct.replace(CLASS_HIERARCHY_POINTER_ORDINAL, rtti3RefDt, rtti3RefDt.getLength(),
"pClassHierarchyDescriptor", "ref to ClassHierarchyDescriptor (RTTI 3) for class");
}
@@ -248,9 +248,9 @@ public class Rtti1Model extends AbstractCreateRttiDataModel {
boolean is64Bit = MSDataTypeUtils.is64Bit(program);
DataType rtti0Dt = TypeDescriptorModel.getDataType(program);
DataType rtti0RefDt =
is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti0Dt);
is64Bit ? new IBO32DataType(dataTypeManager) : new PointerDataType(rtti0Dt);
DataType rtti3RefDt =
is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType();
is64Bit ? new IBO32DataType(dataTypeManager) : new PointerDataType();
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
@@ -15,7 +15,7 @@
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getReferencedAddress;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import java.util.ArrayList;
import java.util.List;
@@ -145,11 +145,8 @@ public class Rtti2Model extends AbstractCreateRttiDataModel {
DataTypeManager dataTypeManager = program.getDataTypeManager();
if (MSDataTypeUtils.is64Bit(program)) {
return new ImageBaseOffset32DataType(dataTypeManager);
}
return new PointerDataType(rtti1Dt, dataTypeManager);
return MSDataTypeUtils.is64Bit(program) ? IBO32DataType.createIBO32PointerTypedef(rtti1Dt)
: new PointerDataType(rtti1Dt, dataTypeManager);
}
/**
@@ -162,11 +159,8 @@ public class Rtti2Model extends AbstractCreateRttiDataModel {
DataTypeManager dataTypeManager = program.getDataTypeManager();
if (MSDataTypeUtils.is64Bit(program)) {
return new ImageBaseOffset32DataType(dataTypeManager);
}
return new PointerDataType(dataTypeManager);
return MSDataTypeUtils.is64Bit(program) ? IBO32DataType.dataType
: new PointerDataType(dataTypeManager);
}
/**
@@ -15,9 +15,6 @@
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getReferencedAddress;
import java.util.List;
import ghidra.app.cmd.data.EHDataTypeUtilities;
@@ -147,8 +144,8 @@ public class Rtti3Model extends AbstractCreateRttiDataModel {
boolean is64Bit = MSDataTypeUtils.is64Bit(program);
Structure rtti3Struct = (Structure) DataTypeUtils.getBaseDataType(rtti3Dt);
DataType individualRtti2EntryDt = Rtti2Model.getIndividualEntryDataType(program, rtti1Dt);
DataType rtti2RefDt = is64Bit ? new ImageBaseOffset32DataType(dataTypeManager)
: new PointerDataType(individualRtti2EntryDt);
DataType rtti2RefDt = is64Bit ? IBO32DataType.createIBO32PointerTypedef(individualRtti2EntryDt)
: new PointerDataType(individualRtti2EntryDt, dataTypeManager);
rtti3Struct.replace(BASE_ARRAY_PTR_ORDINAL, rtti2RefDt, rtti2RefDt.getLength(),
"pBaseClassArray", "ref to BaseClassArray (RTTI 2)");
}
@@ -165,7 +162,7 @@ public class Rtti3Model extends AbstractCreateRttiDataModel {
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
MSDataTypeUtils.getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
// Add the components.
DWordDataType dWordDataType = new DWordDataType(dataTypeManager);
@@ -174,8 +171,10 @@ public class Rtti3Model extends AbstractCreateRttiDataModel {
struct.add(dWordDataType, "numBaseClasses", "number of base classes (i.e. rtti1Count)");
DataType rtti2Dt = Rtti2Model.getSimpleIndividualEntryDataType(program);
// FIXME! I don't think we should be making a pointer-to-pointer
DataType rtti2RefDt =
is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti2Dt);
is64Bit ? IBO32DataType.createIBO32PointerTypedef(rtti2Dt)
: new PointerDataType(rtti2Dt, dataTypeManager);
struct.add(rtti2RefDt, "pBaseClassArray", "ref to BaseClassArray (RTTI 2)");
return new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
@@ -264,7 +263,7 @@ public class Rtti3Model extends AbstractCreateRttiDataModel {
Memory memory = program.getMemory();
Address rtti2CompAddress = rtti3Address.add(BASE_ARRAY_PTR_OFFSET);
Address pointedToAddress = getReferencedAddress(program, rtti2CompAddress);
Address pointedToAddress = MSDataTypeUtils.getReferencedAddress(program, rtti2CompAddress);
if (pointedToAddress == null || !memory.contains(pointedToAddress)) {
return null;
}
@@ -15,9 +15,8 @@
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAbsoluteAddress;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import static org.junit.Assert.*;
import ghidra.app.cmd.data.AbstractCreateDataTypeModelTest;
import ghidra.app.cmd.data.TypeDescriptorModel;
@@ -591,10 +590,10 @@ class AbstractRttiTest extends AbstractCreateDataTypeModelTest {
}
protected void checkRtti2Data(ProgramDB program, long address, int numEntries) {
DataType rtti1Dt = Rtti1Model.getDataType(program);
DataType expectedDataType =
MSDataTypeUtils.is64Bit(program) ? new ImageBaseOffset32DataType()
: new PointerDataType(Rtti1Model.getDataType(program),
program.getDataTypeManager());
MSDataTypeUtils.is64Bit(program) ? IBO32DataType.createIBO32PointerTypedef(rtti1Dt)
: new PointerDataType(rtti1Dt, program.getDataTypeManager());
checkArrayData(program, address, expectedDataType, numEntries);
}
@@ -679,11 +679,16 @@ public class DBHandle {
* @param outFile buffer file open for writing
* @param newDatabaseId database ID to be forced for new database or null to generate
* new database ID
* @param associateWithNewFile if true the outFile will be associated with this DBHandle as the
* current source file, if false no change will be made to this DBHandle's state and the outFile
* will be written and set as read-only. The caller is responsbile for disposing the outFile if
* this parameter is false.
* @param monitor progress monitor
* @throws IOException if IO error occurs
* @throws CancelledException if monitor cancels operation
*/
protected synchronized void saveAs(BufferFile outFile, Long newDatabaseId, TaskMonitor monitor)
protected synchronized void saveAs(BufferFile outFile, Long newDatabaseId,
boolean associateWithNewFile, TaskMonitor monitor)
throws IOException, CancelledException {
if (txStarted) {
@@ -704,7 +709,7 @@ public class DBHandle {
endTransaction(txId, true); // saved file may be corrupt on IOException
}
bufferMgr.saveAs(outFile, true, monitor);
bufferMgr.saveAs(outFile, associateWithNewFile, monitor);
}
/**
@@ -23,8 +23,9 @@ import java.util.Random;
import org.junit.Assert;
import db.buffers.BufferFileManager;
import db.buffers.DummyBufferFileMgr;
import db.buffers.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
*
@@ -803,6 +804,30 @@ public class DBTestUtils {
static BufferFileManager getBufferFileManager(File dir, String dbName) {
return new DummyBufferFileMgr(dir, dbName, false, false);
}
/**
* Clone a DBHandle backed by a new temporary source buffer file.
* The specified dbh will remain associated with its original source buffer file.
* @param dbh DBHandle to clone
* @return new DBhandle
* @throws IOException if a file IO error occurs
*/
public static DBHandle cloneDbHandle(DBHandle dbh) throws IOException {
try {
File tmpFile = File.createTempFile("tmp", ".db");
tmpFile.delete();
LocalBufferFile bf = new LocalBufferFile(tmpFile, dbh.getBufferSize());
dbh.saveAs(bf, dbh.getDatabaseId(), false, TaskMonitor.DUMMY);
tmpFile.deleteOnExit();
return new DBHandle(bf);
}
catch (CancelledException e) {
throw new IOException(e); // unexpected
}
}
}
class DuplicateKeyException extends Exception {

Some files were not shown because too many files have changed in this diff Show More