mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-26 11:56:21 +08:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
+84
-103
@@ -528,7 +528,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
}
|
||||
sb.append(dt.getDisplayName());
|
||||
sb.append(", ");
|
||||
if (info.index < 0) {
|
||||
if (info.resultOrdinal < 0) {
|
||||
if (dt instanceof FunctionDefinition) {
|
||||
sb.append("return-type");
|
||||
}
|
||||
@@ -539,16 +539,16 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
else {
|
||||
if (dt instanceof FunctionDefinition) {
|
||||
sb.append("param-");
|
||||
sb.append(info.index);
|
||||
sb.append(info.resultOrdinal);
|
||||
}
|
||||
else if (dt instanceof Union) {
|
||||
sb.append("component-");
|
||||
sb.append(info.index);
|
||||
sb.append(info.resultOrdinal);
|
||||
}
|
||||
else if (dt instanceof Structure) {
|
||||
Structure resultStruct = (Structure) info.ht.get(info.id);
|
||||
sb.append("component-");
|
||||
sb.append(info.index);
|
||||
sb.append(info.resultOrdinal);
|
||||
if (!resultStruct.isPackingEnabled()) {
|
||||
sb.append(", offset-");
|
||||
sb.append("0x");
|
||||
@@ -556,8 +556,8 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
}
|
||||
}
|
||||
else {
|
||||
sb.append("index-"); // unknown use case
|
||||
sb.append(info.index);
|
||||
sb.append("resultOrdinal-"); // unknown use case
|
||||
sb.append(info.resultOrdinal);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1254,36 +1254,19 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
// Add each of the defined components back in.
|
||||
DataTypeComponent[] comps = sourceDt.getDefinedComponents();
|
||||
|
||||
// Component sequence must be flipped for components that share the same offset
|
||||
// (i.e., when zero-length components exist) to ensure that the insert at offset
|
||||
// does not alter the intended order.
|
||||
List<DataTypeComponent> compList = new ArrayList<>();
|
||||
int prevOffset = -1;
|
||||
int index = -1;
|
||||
for (DataTypeComponent dtc : comps) {
|
||||
int offset = dtc.getOffset();
|
||||
if (offset == prevOffset) {
|
||||
compList.add(index, dtc);
|
||||
}
|
||||
else {
|
||||
prevOffset = offset;
|
||||
index = compList.size();
|
||||
compList.add(dtc);
|
||||
}
|
||||
}
|
||||
comps = null; // prevent improper use
|
||||
|
||||
// Track dependency errors to avoid duplicate popups
|
||||
HashMap<Long, String> badIdDtMsgs = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < compList.size(); i++) {
|
||||
for (int i = 0; i < comps.length; i++) {
|
||||
|
||||
DataTypeComponent sourceComp = compList.get(i);
|
||||
DataTypeComponent sourceComp = comps[i];
|
||||
DataTypeComponent resultComp;
|
||||
|
||||
DataType sourceCompDt = sourceComp.getDataType();
|
||||
BitFieldDataType bfDt = null;
|
||||
String comment = sourceComp.getComment();
|
||||
DataType resultCompDt = null;
|
||||
boolean fixupRequired = false;
|
||||
|
||||
if (sourceComp.isBitFieldComponent()) {
|
||||
// NOTE: primitive type will be used if unable to resolve base type
|
||||
@@ -1307,13 +1290,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
// Not added so should be in result if it wasn't deleted there.
|
||||
resultCompDt = dtms[RESULT].getDataType(sourceComponentID);
|
||||
}
|
||||
if (resultCompDt == null) {
|
||||
// Not added/resolved yet
|
||||
// put an entry in the fixup list
|
||||
fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID,
|
||||
sourceComp.getOrdinal(), sourceComp, resolvedDataTypes));
|
||||
fixUpIDSet.add(sourceDtID);
|
||||
}
|
||||
fixupRequired = (resultCompDt == null);
|
||||
if (bfDt != null &&
|
||||
(resultCompDt == null || !BitFieldDataType.isValidBaseDataType(resultCompDt))) {
|
||||
// use primitive type as fixup placeholder
|
||||
@@ -1335,7 +1312,8 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
if (packed) {
|
||||
if (bfDt != null) {
|
||||
try {
|
||||
destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(),
|
||||
resultComp =
|
||||
destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
@@ -1345,7 +1323,8 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
comment = buildDataTypeFailureComment(sourceCompDt, e.getMessage(),
|
||||
sourceComp.getComment());
|
||||
try {
|
||||
destStruct.addBitField(primitiveBaseDt, bfDt.getDeclaredBitSize(),
|
||||
resultComp = destStruct.addBitField(primitiveBaseDt,
|
||||
bfDt.getDeclaredBitSize(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
catch (InvalidDataTypeException exc) {
|
||||
@@ -1356,13 +1335,15 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
else if (badMsg == null) {
|
||||
try {
|
||||
// If I have compDt, it should now be from result DTM.
|
||||
destStruct.add(resultCompDt, length, sourceComp.getFieldName(),
|
||||
resultComp =
|
||||
destStruct.add(resultCompDt, length, sourceComp.getFieldName(),
|
||||
comment);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
comment =
|
||||
buildDataTypeFailureComment(sourceCompDt, e.getMessage(), comment);
|
||||
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
|
||||
resultComp =
|
||||
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
if (e.getCause() instanceof DataTypeDependencyException) {
|
||||
badIdDtMsgs.put(dtId, e.getMessage());
|
||||
@@ -1373,14 +1354,15 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
else {
|
||||
// Preserve non-fixup ordinal with bad placeholder
|
||||
comment = buildDataTypeFailureComment(sourceCompDt, badMsg, comment);
|
||||
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
|
||||
resultComp = destStruct.add(BadDataType.dataType, sourceComp.getLength(),
|
||||
sourceComp.getFieldName(), badMsg + "; " + comment);
|
||||
}
|
||||
}
|
||||
else if (bfDt != null) {
|
||||
// non-packed bitfield
|
||||
try {
|
||||
destStruct.insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(),
|
||||
resultComp = destStruct
|
||||
.insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(),
|
||||
bfDt.getBitOffset(), resultCompDt, bfDt.getDeclaredBitSize(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
@@ -1391,7 +1373,8 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
comment = buildDataTypeFailureComment(sourceCompDt, e.getMessage(),
|
||||
sourceComp.getComment());
|
||||
try {
|
||||
destStruct.addBitField(primitiveBaseDt, bfDt.getDeclaredBitSize(),
|
||||
resultComp =
|
||||
destStruct.addBitField(primitiveBaseDt, bfDt.getDeclaredBitSize(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
catch (InvalidDataTypeException exc) {
|
||||
@@ -1404,9 +1387,9 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
if (badMsg == null) {
|
||||
try {
|
||||
// If not last component must constrain length to original component size
|
||||
if (i < compList.size() - 1) {
|
||||
if (i < (comps.length - 1)) {
|
||||
int offset = sourceComp.getOffset();
|
||||
DataTypeComponent nextDtc = compList.get(i + 1);
|
||||
DataTypeComponent nextDtc = comps[i + 1];
|
||||
int available = nextDtc.getOffset() - offset;
|
||||
if (length > available) {
|
||||
// The data type is too big, so adjust the component length to what will fit.
|
||||
@@ -1423,13 +1406,15 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
}
|
||||
}
|
||||
|
||||
destStruct.insertAtOffset(sourceComp.getOffset(), resultCompDt, length,
|
||||
resultComp = destStruct.insertAtOffset(sourceComp.getOffset(),
|
||||
resultCompDt, length,
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
comment =
|
||||
buildDataTypeFailureComment(sourceCompDt, e.getMessage(), comment);
|
||||
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
|
||||
resultComp = destStruct.insertAtOffset(sourceComp.getOffset(),
|
||||
BadDataType.dataType,
|
||||
length, sourceComp.getFieldName(), comment);
|
||||
|
||||
if (e.getCause() instanceof DataTypeDependencyException) {
|
||||
@@ -1441,7 +1426,8 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
else {
|
||||
// Preserve non-fixup ordinal with bad placeholder
|
||||
comment = buildDataTypeFailureComment(sourceCompDt, badMsg, comment);
|
||||
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
|
||||
resultComp =
|
||||
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
|
||||
sourceComp.getLength(), sourceComp.getFieldName(), comment);
|
||||
}
|
||||
}
|
||||
@@ -1450,15 +1436,22 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
// Add fixup placeholder to prevent the ordinal values and component sizes from
|
||||
// changing. Nothing we can do about packing which may be affected.
|
||||
// These should get fixed-up later.
|
||||
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
|
||||
resultComp = destStruct.add(BadDataType.dataType, sourceComp.getLength(),
|
||||
sourceComp.getFieldName(), comment);
|
||||
}
|
||||
else {
|
||||
// Add fixup placeholder to prevent the ordinal values and component sizes from
|
||||
// changing. These should get fixed-up later.
|
||||
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
|
||||
resultComp = destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
|
||||
sourceComp.getLength(), sourceComp.getFieldName(), comment);
|
||||
}
|
||||
|
||||
if (fixupRequired) {
|
||||
// Component datatype has not been added/resolved yet, put an entry in the fixup list
|
||||
fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID,
|
||||
resultComp.getOrdinal(), sourceComp, resolvedDataTypes));
|
||||
fixUpIDSet.add(sourceDtID);
|
||||
}
|
||||
}
|
||||
if (!packed) {
|
||||
adjustStructureSize(destStruct, sourceDt.getLength());
|
||||
@@ -2548,22 +2541,22 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
long lastChangeTime = fd.getLastChangeTime(); // Don't let the time change.
|
||||
try {
|
||||
if (dt != null) {
|
||||
if (info.index < 0) { // -1 for return type
|
||||
if (info.resultOrdinal < 0) { // -1 for return type
|
||||
fd.setReturnType(dt);
|
||||
}
|
||||
else {
|
||||
ParameterDefinition[] args = fd.getArguments();
|
||||
args[info.index].setDataType(dt);
|
||||
args[info.resultOrdinal].setDataType(dt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.index < 0) { // -1 for return type
|
||||
if (info.resultOrdinal < 0) { // -1 for return type
|
||||
// nowhere to set error comment
|
||||
}
|
||||
else {
|
||||
ParameterDefinition[] args = fd.getArguments();
|
||||
ParameterDefinition arg = args[info.index];
|
||||
ParameterDefinition arg = args[info.resultOrdinal];
|
||||
String comment =
|
||||
buildDataTypeFailureComment(info.componentDataType, null, arg.getComment());
|
||||
arg.setComment(comment);
|
||||
@@ -2583,15 +2576,15 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
* @return false if component not found, else true
|
||||
*/
|
||||
private boolean fixUpPackedStructureComponent(FixUpInfo info, Structure struct, DataType dt) {
|
||||
int ordinal = info.index;
|
||||
int ordinal = info.resultOrdinal;
|
||||
|
||||
DataTypeComponent dtc = null;
|
||||
DataTypeComponent dtc;
|
||||
if (ordinal >= 0 || ordinal < struct.getNumComponents()) {
|
||||
dtc = struct.getComponent(ordinal);
|
||||
}
|
||||
|
||||
if (dtc == null) {
|
||||
throw new AssertException("Expected bad datatype placeholder");
|
||||
else {
|
||||
throw new AssertException(
|
||||
"Expected fixup component at ordinal " + ordinal + " in " + struct.getPathName());
|
||||
}
|
||||
|
||||
long lastChangeTime = struct.getLastChangeTime(); // Don't let the time change.
|
||||
@@ -2680,12 +2673,16 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
private boolean fixUpNonPackedStructureComponent(FixUpInfo info, Structure struct,
|
||||
DataType dt) {
|
||||
|
||||
int ordinal = info.index;
|
||||
int ordinal = info.resultOrdinal;
|
||||
|
||||
DataTypeComponent dtc = null;
|
||||
DataTypeComponent dtc;
|
||||
if (ordinal >= 0 || ordinal < struct.getNumComponents()) {
|
||||
dtc = struct.getComponent(ordinal);
|
||||
}
|
||||
else {
|
||||
throw new AssertException(
|
||||
"Expected fixup component at ordinal " + ordinal + " in " + struct.getPathName());
|
||||
}
|
||||
|
||||
long lastChangeTime = struct.getLastChangeTime(); // Don't let the time change.
|
||||
try {
|
||||
@@ -2740,35 +2737,13 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
}
|
||||
|
||||
// handle non-bitfield component fixup
|
||||
int offset = dtc.getOffset();
|
||||
int dtcLength = dtc.getLength();
|
||||
int length = dt.getLength();
|
||||
if (length <= 0) {
|
||||
length = dtcLength;
|
||||
}
|
||||
int bytesNeeded = length - dtcLength;
|
||||
if (bytesNeeded > 0) {
|
||||
int nextOffset = offset + dtcLength;
|
||||
DataTypeComponent nextDefinedDtc =
|
||||
struct.getDefinedComponentAtOrAfterOffset(nextOffset);
|
||||
if (nextDefinedDtc != null) {
|
||||
int bytesAvailable = nextDefinedDtc.getOffset() - nextOffset;
|
||||
if (bytesAvailable < bytesNeeded) {
|
||||
// The data type is too big, so adjust the component length to what will fit.
|
||||
length = dtcLength + bytesAvailable;
|
||||
// Output a warning indicating the structure has a data type that doesn't fit.
|
||||
String message = "Structure Merge: Not enough undefined bytes to fit " +
|
||||
dt.getPathName() + " in structure " + struct.getPathName() +
|
||||
" at offset 0x" + Integer.toHexString(offset) + "." +
|
||||
"\nIt needs " + (bytesNeeded - bytesAvailable) +
|
||||
" more byte(s) to be able to fit.";
|
||||
Msg.warn(this, message);
|
||||
}
|
||||
}
|
||||
length = dtc.getLength();
|
||||
}
|
||||
try {
|
||||
struct.replaceAtOffset(offset, dt, length, dtc.getFieldName(),
|
||||
dtc.getComment());
|
||||
struct.replace(ordinal, dt, length, dtc.getFieldName(), dtc.getComment());
|
||||
return true;
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
displayError(struct, e);
|
||||
@@ -2804,7 +2779,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
|
||||
String loc;
|
||||
if (struct.isPackingEnabled()) {
|
||||
loc = "ordinal " + info.index;
|
||||
loc = "ordinal " + info.resultOrdinal;
|
||||
}
|
||||
else {
|
||||
loc = "offset 0x" + Integer.toHexString(info.offset);
|
||||
@@ -2818,7 +2793,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
|
||||
DataType compDt = resolve(info.compID, info.getDataTypeManager(), info.ht);
|
||||
|
||||
int ordinal = info.index;
|
||||
int ordinal = info.resultOrdinal;
|
||||
|
||||
DataTypeComponent dtc = null;
|
||||
if (ordinal >= 0 && ordinal <= union.getNumComponents()) {
|
||||
@@ -2889,7 +2864,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
}
|
||||
Msg.warn(this,
|
||||
"Union Merge: Failed to resolve data type '" + info.componentDataType.getName() +
|
||||
"' at ordinal " + info.index + " in " + union.getPathName());
|
||||
"' at ordinal " + info.resultOrdinal + " in " + union.getPathName());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2913,7 +2888,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
FixUpInfo info = fixUpList.get(i); // assume info applies to union
|
||||
if (!fixUpUnionComponent(union, info)) {
|
||||
Msg.warn(this, "Union Merge: Failed to apply data type at ordinal " +
|
||||
info.index + " in " + union.getPathName());
|
||||
info.resultOrdinal + " in " + union.getPathName());
|
||||
unresolvedFixups.add(info);
|
||||
}
|
||||
}
|
||||
@@ -3447,13 +3422,18 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
*/
|
||||
private class FixUpInfo implements Comparable<FixUpInfo> {
|
||||
|
||||
final long id;
|
||||
final long compID;
|
||||
final DataType componentDataType;
|
||||
final int index;
|
||||
final long id; // Souce datatype ID
|
||||
final long compID; // Source datatype ID for component
|
||||
final DataType componentDataType; // Source component datatype
|
||||
|
||||
// Result ordinal when id represents a container such as a structure/union or
|
||||
// function definition. In such cases, 'compID' corresponds to the component datatype.
|
||||
// A -1 may be used when not applicable.
|
||||
final int resultOrdinal;
|
||||
|
||||
final MyIdentityHashMap<Long, DataType> ht;
|
||||
|
||||
// component offset - for display/logging use only
|
||||
// source component offset - for display/logging use only
|
||||
// only meaningful for non-packed structure
|
||||
// may not be unique (e.g., bitfields, 0-length components)
|
||||
int offset = -1;
|
||||
@@ -3468,16 +3448,16 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
* @param id source data type ID needing to be fixed up
|
||||
* @param compID source datatype ID of either param/component or bitfield base type
|
||||
* @param componentDataType source component/dependency datatype
|
||||
* @param index offset into non-packed structure, or ordinal into union or packed
|
||||
* structure; or parameter/return ordinal; for other data types index is not used (specify -1).
|
||||
* @param resultOrdinal the result ordinal into a structure/union; or
|
||||
* parameter/return ordinal; or -1 for other cases where index is not used
|
||||
* @param resolvedDataTypes hashtable used for resolving the data type
|
||||
*/
|
||||
FixUpInfo(long id, long compID, DataType componentDataType, int index,
|
||||
FixUpInfo(long id, long compID, DataType componentDataType, int resultOrdinal,
|
||||
MyIdentityHashMap<Long, DataType> resolvedDataTypes) {
|
||||
this.id = id;
|
||||
this.compID = compID;
|
||||
this.componentDataType = componentDataType;
|
||||
this.index = index;
|
||||
this.resultOrdinal = resultOrdinal;
|
||||
this.ht = resolvedDataTypes;
|
||||
|
||||
if (componentDataType instanceof BitFieldDataType) {
|
||||
@@ -3491,13 +3471,13 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
* or components were resolved.
|
||||
* @param id id of data type needing to be fixed up
|
||||
* @param compID datatype ID of either param/component or bitfield base type
|
||||
* @param destOrdinal component ordinal within destination composite
|
||||
* @param resultOrdinal component ordinal within result composite
|
||||
* @param sourceDtc associated composite datatype component
|
||||
* @param resolvedDataTypes hashtable used for resolving the data type
|
||||
*/
|
||||
FixUpInfo(long id, long compID, int destOrdinal, DataTypeComponent sourceDtc,
|
||||
FixUpInfo(long id, long compID, int resultOrdinal, DataTypeComponent sourceDtc,
|
||||
MyIdentityHashMap<Long, DataType> resolvedDataTypes) {
|
||||
this(id, compID, getDataType(sourceDtc), destOrdinal, resolvedDataTypes);
|
||||
this(id, compID, getDataType(sourceDtc), resultOrdinal, resolvedDataTypes);
|
||||
offset = sourceDtc.getOffset();
|
||||
if (sourceDtc.isBitFieldComponent()) {
|
||||
BitFieldDataType bfDt = (BitFieldDataType) sourceDtc.getDataType();
|
||||
@@ -3516,11 +3496,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
|
||||
@Override
|
||||
public int compareTo(FixUpInfo o) {
|
||||
// Compare such that items are grouped by id and sort such that the greatest index
|
||||
// Compare such that items are grouped by id and sort such that the greatest resultOrdinal
|
||||
// is first within that group.
|
||||
long c = id - o.id;
|
||||
if (c == 0) {
|
||||
c = Integer.toUnsignedLong(o.index) - Integer.toUnsignedLong(index);
|
||||
c = Integer.toUnsignedLong(o.resultOrdinal) - Integer.toUnsignedLong(resultOrdinal);
|
||||
}
|
||||
if (c == 0) {
|
||||
return 0;
|
||||
@@ -3546,7 +3526,8 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||
}
|
||||
return "\n" + "ID = " + Long.toHexString(id) + ",\ndt = " + dtm.getDataType(id) +
|
||||
",\ncomponent ID = " + Long.toHexString(compID) + ",\ncomponent dt = " +
|
||||
dtm.getDataType(compID) + ",\noffset/index = " + index + ",\n" + bitInfo + "ht = " +
|
||||
dtm.getDataType(compID) + ",\nresultOrdinal = " + resultOrdinal + ",\n" + bitInfo +
|
||||
"ht = " +
|
||||
htStr + "\n";
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -115,6 +115,8 @@ public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
|
||||
// choose MY for Bar conflict
|
||||
chooseOption(DataTypeMergeManager.OPTION_MY);
|
||||
|
||||
pressButtonByName(waitForWindow("Structure Update Failed"), "OK"); // expected dependency error on ABC
|
||||
|
||||
waitForCompletion();
|
||||
|
||||
FrontEndPlugin frontEndPlugin = getPlugin(frontEndTool, FrontEndPlugin.class);
|
||||
@@ -122,8 +124,9 @@ public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
|
||||
JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel);
|
||||
String statusText = label.getText();
|
||||
String expectedText =
|
||||
"Structure Merge: Not enough undefined bytes to fit /XYZ in structure " +
|
||||
"/MISC/ABC at offset 0x4. It needs 3 more byte(s) to be able to fit.";
|
||||
"Structure Update Failed: Some of your changes to ABC cannot be merged. " +
|
||||
"Problem: Not enough undefined bytes to fit /XYZ in structure /MISC/ABC at " +
|
||||
"offset 0x4. It needs 3 more byte(s) to be able to fit.";
|
||||
assertTrue("Wrong status text: " + statusText, statusText.contains(expectedText));
|
||||
}
|
||||
}
|
||||
|
||||
+81
@@ -367,4 +367,85 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest {
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonPackedZeroLengthComponentFixup() throws Exception {
|
||||
|
||||
// Goal is to fixup zero-length component at end of structure where its ordinal will
|
||||
// be revised during the merge processing
|
||||
|
||||
final CategoryPath rootPath = new CategoryPath("/");
|
||||
|
||||
mtf.initialize("notepad", new OriginalProgramModifierListener() {
|
||||
|
||||
@Override
|
||||
public void modifyOriginal(ProgramDB program) throws Exception {
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
|
||||
Union inner = new UnionDataType("inner");
|
||||
inner.add(DWordDataType.dataType);
|
||||
inner = (Union) dtm.addDataType(inner, null);
|
||||
|
||||
Structure other = new StructureDataType("other", 0);
|
||||
other.add(WordDataType.dataType);
|
||||
other = (Structure) dtm.addDataType(other, null);
|
||||
|
||||
Structure outer = new StructureDataType("outer", 20, dtm);
|
||||
outer.replaceAtOffset(0, other, -1, null, null); // prevent size change
|
||||
outer = (Structure) dtm.addDataType(outer, null);
|
||||
assertEquals(20, outer.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyLatest(ProgramDB program) throws Exception {
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
|
||||
// Increase size of other struct
|
||||
Structure other = (Structure) dtm.getDataType(rootPath, "other");
|
||||
other.add(DWordDataType.dataType);
|
||||
|
||||
// remove inner to trigger conflict with its modification
|
||||
Union inner = (Union) dtm.getDataType(rootPath, "inner");
|
||||
dtm.remove(inner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyPrivate(ProgramDB program) throws Exception {
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
Structure outer = (Structure) dtm.getDataType(rootPath, "outer");
|
||||
Union inner = (Union) dtm.getDataType(rootPath, "inner");
|
||||
|
||||
// change inner to trigger conflict with its removal
|
||||
inner.add(WordDataType.dataType);
|
||||
|
||||
// Add zero-length array at end of struct
|
||||
outer.insertAtOffset(20, new ArrayDataType(inner, 0), -1);
|
||||
assertEquals(20, outer.getLength());
|
||||
}
|
||||
});
|
||||
|
||||
executeMerge();
|
||||
|
||||
chooseOption(DataTypeMergeManager.OPTION_MY); // resolve inner conflict
|
||||
|
||||
chooseOption(DataTypeMergeManager.OPTION_MY); // resolve outer conflict
|
||||
|
||||
waitForCompletion();
|
||||
|
||||
DataTypeManager dtm = resultProgram.getDataTypeManager();
|
||||
|
||||
StructureInternal outer = (StructureInternal) dtm.getDataType(rootPath, "outer");
|
||||
assertNotNull(outer);
|
||||
|
||||
//@formatter:off
|
||||
assertEquals("/outer\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure outer {\n" +
|
||||
" 0 other 6 \"\"\n" +
|
||||
" 20 inner[0] 0 \"\"\n" +
|
||||
"}\n" +
|
||||
"Length: 20 Alignment: 1\n", outer.toString());
|
||||
//@formatter:on
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+177
-45
@@ -338,6 +338,74 @@ public class StructureDataTypeTest extends AbstractGenericTest {
|
||||
assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAtSameOffset1() {
|
||||
|
||||
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
|
||||
|
||||
struct = createStructure("Test", 100);
|
||||
assertFalse(struct.isPackingEnabled());
|
||||
|
||||
struct.insertAtOffset(0, zeroDt, -1, "a", "comment a");
|
||||
struct.insertAtOffset(0, zeroDt, -1, "b", "comment b");
|
||||
struct.insertAtOffset(0, WordDataType.dataType, -1, "c", "comment c");
|
||||
|
||||
DataTypeComponent[] definedComponents = struct.getDefinedComponents();
|
||||
assertEquals("a", definedComponents[0].getFieldName());
|
||||
assertEquals("b", definedComponents[1].getFieldName());
|
||||
assertEquals("c", definedComponents[2].getFieldName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAtSameOffset2() {
|
||||
|
||||
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
|
||||
|
||||
struct = createStructure("Test", 100);
|
||||
assertFalse(struct.isPackingEnabled());
|
||||
|
||||
struct.insertAtOffset(0, WordDataType.dataType, -1, "c", "comment c");
|
||||
struct.insertAtOffset(0, zeroDt, -1, "a", "comment a");
|
||||
struct.insertAtOffset(0, zeroDt, -1, "b", "comment b");
|
||||
|
||||
DataTypeComponent[] definedComponents = struct.getDefinedComponents();
|
||||
assertEquals("a", definedComponents[0].getFieldName());
|
||||
assertEquals("b", definedComponents[1].getFieldName());
|
||||
assertEquals("c", definedComponents[2].getFieldName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAtEndOffset() {
|
||||
|
||||
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
|
||||
|
||||
struct = createStructure("Test2", 100);
|
||||
assertFalse(struct.isPackingEnabled());
|
||||
|
||||
struct.insertAtOffset(100, zeroDt, -1, "a", "comment a");
|
||||
struct.insertAtOffset(100, zeroDt, -1, "b", "comment b");
|
||||
|
||||
assertEquals("/Test2\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test2 {\n" +
|
||||
" 100 byte[0] 0 a \"comment a\"\n" +
|
||||
" 100 byte[0] 0 b \"comment b\"\n" +
|
||||
"}\n" +
|
||||
"Length: 100 Alignment: 1\n", struct.toString());
|
||||
|
||||
// Insert non-zero-length component will increase struct size
|
||||
struct.insertAtOffset(100, WordDataType.dataType, -1, "c", "comment c");
|
||||
|
||||
assertEquals("/Test2\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test2 {\n" +
|
||||
" 100 byte[0] 0 a \"comment a\"\n" +
|
||||
" 100 byte[0] 0 b \"comment b\"\n" +
|
||||
" 100 word 2 c \"comment c\"\n" +
|
||||
"}\n" +
|
||||
"Length: 102 Alignment: 1\n", struct.toString());
|
||||
}
|
||||
|
||||
// test inserting at offset 0
|
||||
@Test
|
||||
public void testInsertAtOffset() {
|
||||
@@ -458,32 +526,18 @@ public class StructureDataTypeTest extends AbstractGenericTest {
|
||||
Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1);
|
||||
struct.insertAtOffset(2, zeroArray, -1);
|
||||
struct.insertAtOffset(2, FloatDataType.dataType, -1);
|
||||
assertEquals(13, struct.getLength());
|
||||
|
||||
DataTypeComponent[] comps = struct.getDefinedComponents();
|
||||
|
||||
assertEquals(6, comps.length);
|
||||
|
||||
assertEquals(0, comps[0].getOffset());
|
||||
assertEquals(0, comps[0].getOrdinal());
|
||||
assertEquals(ByteDataType.class, comps[0].getDataType().getClass());
|
||||
|
||||
assertEquals(2, comps[1].getOffset());
|
||||
assertEquals(2, comps[1].getOrdinal());
|
||||
assertEquals(FloatDataType.class, comps[1].getDataType().getClass());
|
||||
|
||||
assertEquals(6, comps[2].getOffset());
|
||||
assertEquals(3, comps[2].getOrdinal());
|
||||
assertTrue(zeroArray.isEquivalent(comps[2].getDataType()));
|
||||
|
||||
assertEquals(6, comps[3].getOffset());
|
||||
assertEquals(4, comps[3].getOrdinal());
|
||||
assertEquals(WordDataType.class, comps[3].getDataType().getClass());
|
||||
|
||||
assertEquals(8, comps[4].getOffset());
|
||||
assertEquals(5, comps[4].getOrdinal());
|
||||
assertEquals(DWordDataType.class, comps[4].getDataType().getClass());
|
||||
|
||||
assertEquals("/TestStruct\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure TestStruct {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
" 2 float[0] 0 \"\"\n" +
|
||||
" 2 float 4 \"\"\n" +
|
||||
" 6 word 2 \"Comment2\"\n" +
|
||||
" 8 dword 4 field3 \"\"\n" +
|
||||
" 12 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 13 Alignment: 1\n", struct.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -660,7 +714,15 @@ public class StructureDataTypeTest extends AbstractGenericTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertBitFieldAtLittleEndian() throws Exception {
|
||||
public void testInsertBitFieldAtLittleEndian2() throws Exception {
|
||||
|
||||
Array zeroArray = new ArrayDataType(CharDataType.dataType, 0, -1);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "A", null);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "B", null);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "C", null);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "D", null);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "E", null);
|
||||
struct.insertAtOffset(2, CharDataType.dataType, -1, "XXX", null);
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
|
||||
|
||||
@@ -670,15 +732,21 @@ public class StructureDataTypeTest extends AbstractGenericTest {
|
||||
"Structure TestStruct {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
// " 3 undefined 1 \"\"\n" +
|
||||
// " 4 undefined 1 \"\"\n" +
|
||||
// " 5 undefined 1 \"\"\n" +
|
||||
" 6 word 2 \"Comment2\"\n" +
|
||||
" 8 dword 4 field3 \"\"\n" +
|
||||
" 12 byte 1 field4 \"Comment4\"\n" +
|
||||
// " 5 undefined 1 \"\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 13 Alignment: 1", struct);
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
|
||||
@@ -688,17 +756,23 @@ public class StructureDataTypeTest extends AbstractGenericTest {
|
||||
"pack(disabled)\n" +
|
||||
"Structure TestStruct {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
|
||||
// " 3 undefined 1 \"\"\n" +
|
||||
// " 4 undefined 1 \"\"\n" +
|
||||
// " 5 undefined 1 \"\"\n" +
|
||||
" 6 word 2 \"Comment2\"\n" +
|
||||
" 8 dword 4 field3 \"\"\n" +
|
||||
" 12 byte 1 field4 \"Comment4\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 13 Alignment: 1", struct);
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment");
|
||||
@@ -708,16 +782,22 @@ public class StructureDataTypeTest extends AbstractGenericTest {
|
||||
"pack(disabled)\n" +
|
||||
"Structure TestStruct {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
|
||||
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
|
||||
// " 5 undefined 1 \"\"\n" +
|
||||
" 6 word 2 \"Comment2\"\n" +
|
||||
" 8 dword 4 field3 \"\"\n" +
|
||||
" 12 byte 1 field4 \"Comment4\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 13 Alignment: 1", struct);
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
try {
|
||||
@@ -737,15 +817,47 @@ public class StructureDataTypeTest extends AbstractGenericTest {
|
||||
"Structure TestStruct {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
|
||||
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
|
||||
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
|
||||
" 6 word 2 \"Comment2\"\n" +
|
||||
" 8 dword 4 field3 \"\"\n" +
|
||||
" 12 byte 1 field4 \"Comment4\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 13 Alignment: 1", struct);
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 0, "z", "zero bitfield");
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure TestStruct {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:0(0) 0 \"zero bitfield\"\n" + // field name discarded
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
|
||||
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
|
||||
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@@ -839,6 +951,26 @@ public class StructureDataTypeTest extends AbstractGenericTest {
|
||||
"}\n" +
|
||||
"Length: 13 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 31, IntegerDataType.dataType, 0, "z", "zero bitfield");
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure TestStruct {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 int:0(7) 0 \"zero bitfield\"\n" + // field name discarded
|
||||
" 2 int:3(5) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(2) 1 bf2 \"bf2Comment\"\n" +
|
||||
" 2 int:15(3) 3 bf3 \"bf3Comment\"\n" +
|
||||
" 4 int:11(0) 2 bf4 \"bf4Comment\"\n" +
|
||||
" 6 word 2 \"Comment2\"\n" +
|
||||
" 8 dword 4 field3 \"\"\n" +
|
||||
" 12 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 13 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import ghidra.util.UniversalID;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* Database implementation for a structure or union.
|
||||
* {@link CompositeDB} provides an abstract database implementation for a structure or union.
|
||||
*/
|
||||
abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
|
||||
|
||||
|
||||
+28
-2
@@ -1007,6 +1007,26 @@ class StructureDB extends CompositeDB implements StructureInternal {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find insertion index such that the index falls after all zero-length components at the
|
||||
* specified offset and before any bitfields at that offset.
|
||||
*
|
||||
* @param index any defined component index which contains offset.
|
||||
* @param offset offset within structure.
|
||||
* @return index of first non-zero-length defined checking in the forward direction.
|
||||
*/
|
||||
private int afterNonZeroComponentsAtOffset(int index, int offset) {
|
||||
int maxIndex = components.size();
|
||||
while (index < maxIndex) {
|
||||
DataTypeComponentDB dtc = components.get(index);
|
||||
if (dtc.getOffset() != offset || dtc.getLength() != 0) {
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify defined-component index of the first non-zero-length component which contains the
|
||||
* specified offset. If only zero-length components exist, the last zero-length component which
|
||||
@@ -1322,14 +1342,20 @@ class StructureDB extends CompositeDB implements StructureInternal {
|
||||
structLength = offset;
|
||||
}
|
||||
|
||||
// Any component insert at an offset should be placed after any zero-length components
|
||||
// at the same offset but before any non-zero-length component.
|
||||
|
||||
int index = Collections.binarySearch(components, Integer.valueOf(offset),
|
||||
OffsetComparator.INSTANCE);
|
||||
|
||||
int additionalShift = 0;
|
||||
if (index >= 0) {
|
||||
index = backupToFirstComponentContainingOffset(index, offset);
|
||||
DataTypeComponentDB dtc = components.get(index);
|
||||
additionalShift = offset - dtc.getOffset();
|
||||
index = afterNonZeroComponentsAtOffset(index, offset);
|
||||
if (index < components.size()) {
|
||||
DataTypeComponentDB dtc = components.get(index);
|
||||
additionalShift = offset - dtc.getOffset();
|
||||
}
|
||||
}
|
||||
else {
|
||||
index = -index - 1;
|
||||
|
||||
+8
@@ -184,6 +184,11 @@ public class BitFieldDataType extends AbstractDataType {
|
||||
* Get the packing storage size in bytes associated with this bit-field which may be
|
||||
* larger than the base type associated with the fields original definition.
|
||||
* Returned value is the same as {@link #getLength()}.
|
||||
* <p>
|
||||
* NOTE: Bitfields with a bit-size of zero will report a storage size of 1, although
|
||||
* {@link #isZeroLength()} will return true. This is consistent with other datatypes which
|
||||
* support a zero-length {@link DataTypeComponent} such as a zero-element Array.
|
||||
*
|
||||
* @return packing storage size in bytes
|
||||
*/
|
||||
public int getStorageSize() {
|
||||
@@ -331,6 +336,9 @@ public class BitFieldDataType extends AbstractDataType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getStorageSize()
|
||||
*/
|
||||
@Override
|
||||
public int getLength() {
|
||||
return storageSize;
|
||||
|
||||
+1
-1
@@ -192,7 +192,7 @@ public interface DataTypeComponent {
|
||||
if (dataType instanceof Array) {
|
||||
return true;
|
||||
}
|
||||
// assumes undefined types will ultimately have a non-zero length
|
||||
// assumes not-yet-defined types will ultimately have a non-zero length
|
||||
return !dataType.isNotYetDefined();
|
||||
}
|
||||
return false;
|
||||
|
||||
+3
@@ -61,6 +61,9 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
|
||||
this.fieldName = InternalDataTypeComponent.cleanupFieldName(fieldName);
|
||||
setDataType(dataType);
|
||||
setComment(comment);
|
||||
if (isZeroBitFieldComponent()) {
|
||||
this.length = 0; // previously stored as 1, force to 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-3
@@ -240,9 +240,16 @@ public interface Structure extends Composite {
|
||||
/**
|
||||
* Inserts a new datatype at the specified offset into this structure. Inserting a component
|
||||
* will cause any conflicting components to shift down to the extent necessary to avoid a
|
||||
* conflict.
|
||||
* conflict. The overall structure length will always increase when a non-zero-length
|
||||
* component is inserted. NOTE: bitfields may share an offset with other bitfields and
|
||||
* zero-length components.
|
||||
* <p>
|
||||
* This method does not support bit-field insertions which must use the method
|
||||
* Any component insert at an offset will be placed after any zero-length components
|
||||
* at the same offset but before any non-zero-length components. The components which
|
||||
* fall after the insertion point will have there ordinal incremented and offset
|
||||
* adjusted as needed.
|
||||
* <p>
|
||||
* This method will defer bit-field insertions to the method
|
||||
* {@link #insertBitFieldAt(int, int, int, DataType, int, String, String)}.
|
||||
*
|
||||
* @param offset the byte offset into the structure where the new datatype is to be inserted.
|
||||
@@ -389,7 +396,8 @@ public interface Structure extends Composite {
|
||||
/**
|
||||
* Replaces all components containing the specified byte offset with a new component using the
|
||||
* specified datatype, length, name and comment. If the offset corresponds to a bit-field
|
||||
* more than one component may be consumed by this replacement.
|
||||
* more than one component may be consumed by this replacement. In general, this method
|
||||
* should not be used to replace bitfield components.
|
||||
* <p>
|
||||
* This method may not be used to replace a zero-length component since there may be any number
|
||||
* of zero-length components at the same offset. If the only defined component(s) at the specified
|
||||
|
||||
+28
-2
@@ -523,14 +523,20 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
structLength = offset;
|
||||
}
|
||||
|
||||
// Any component insert at an offset should be placed after any zero-length components
|
||||
// at the same offset but before any non-zero-length component.
|
||||
|
||||
int index = Collections.binarySearch(components, Integer.valueOf(offset),
|
||||
OffsetComparator.INSTANCE);
|
||||
|
||||
int additionalShift = 0;
|
||||
if (index >= 0) {
|
||||
index = backupToFirstComponentContainingOffset(index, offset);
|
||||
DataTypeComponent dtc = components.get(index);
|
||||
additionalShift = offset - dtc.getOffset();
|
||||
index = afterNonZeroComponentsAtOffset(index, offset);
|
||||
if (index < components.size()) {
|
||||
DataTypeComponentImpl dtc = components.get(index);
|
||||
additionalShift = offset - dtc.getOffset();
|
||||
}
|
||||
}
|
||||
else {
|
||||
index = -index - 1;
|
||||
@@ -898,6 +904,26 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find insertion index such that the index falls after all zero-length components at the
|
||||
* specified offset and before any bitfields at that offset.
|
||||
*
|
||||
* @param index any defined component index which contains offset.
|
||||
* @param offset offset within structure.
|
||||
* @return index of first non-zero-length defined checking in the forward direction.
|
||||
*/
|
||||
private int afterNonZeroComponentsAtOffset(int index, int offset) {
|
||||
int maxIndex = components.size();
|
||||
while (index < maxIndex) {
|
||||
DataTypeComponentImpl dtc = components.get(index);
|
||||
if (dtc.getOffset() != offset || dtc.getLength() != 0) {
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify defined-component index of the first non-zero-length component which contains the specified offset.
|
||||
* If only zero-length components exist, the last zero-length component which contains the offset will be returned.
|
||||
|
||||
+227
-25
@@ -364,6 +364,74 @@ public class StructureDBTest extends AbstractGenericTest {
|
||||
assertEquals(ByteDataType.class, comps[2].getDataType().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAtSameOffset1() {
|
||||
|
||||
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
|
||||
|
||||
struct = createStructure("Test", 100);
|
||||
assertFalse(struct.isPackingEnabled());
|
||||
|
||||
struct.insertAtOffset(0, zeroDt, -1, "a", "comment a");
|
||||
struct.insertAtOffset(0, zeroDt, -1, "b", "comment b");
|
||||
struct.insertAtOffset(0, WordDataType.dataType, -1, "c", "comment c");
|
||||
|
||||
DataTypeComponentDB[] definedComponents = struct.getDefinedComponents();
|
||||
assertEquals("a", definedComponents[0].getFieldName());
|
||||
assertEquals("b", definedComponents[1].getFieldName());
|
||||
assertEquals("c", definedComponents[2].getFieldName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAtSameOffset2() {
|
||||
|
||||
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
|
||||
|
||||
struct = createStructure("Test", 100);
|
||||
assertFalse(struct.isPackingEnabled());
|
||||
|
||||
struct.insertAtOffset(0, WordDataType.dataType, -1, "c", "comment c");
|
||||
struct.insertAtOffset(0, zeroDt, -1, "a", "comment a");
|
||||
struct.insertAtOffset(0, zeroDt, -1, "b", "comment b");
|
||||
|
||||
DataTypeComponentDB[] definedComponents = struct.getDefinedComponents();
|
||||
assertEquals("a", definedComponents[0].getFieldName());
|
||||
assertEquals("b", definedComponents[1].getFieldName());
|
||||
assertEquals("c", definedComponents[2].getFieldName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertAtEndOffset() {
|
||||
|
||||
DataType zeroDt = new ArrayDataType(ByteDataType.dataType, 0);
|
||||
|
||||
struct = createStructure("Test2", 100);
|
||||
assertFalse(struct.isPackingEnabled());
|
||||
|
||||
struct.insertAtOffset(100, zeroDt, -1, "a", "comment a");
|
||||
struct.insertAtOffset(100, zeroDt, -1, "b", "comment b");
|
||||
|
||||
assertEquals("/Test2\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test2 {\n" +
|
||||
" 100 byte[0] 0 a \"comment a\"\n" +
|
||||
" 100 byte[0] 0 b \"comment b\"\n" +
|
||||
"}\n" +
|
||||
"Length: 100 Alignment: 1\n", struct.toString());
|
||||
|
||||
// Insert non-zero-length component will increase struct size
|
||||
struct.insertAtOffset(100, WordDataType.dataType, -1, "c", "comment c");
|
||||
|
||||
assertEquals("/Test2\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test2 {\n" +
|
||||
" 100 byte[0] 0 a \"comment a\"\n" +
|
||||
" 100 byte[0] 0 b \"comment b\"\n" +
|
||||
" 100 word 2 c \"comment c\"\n" +
|
||||
"}\n" +
|
||||
"Length: 102 Alignment: 1\n", struct.toString());
|
||||
}
|
||||
|
||||
// test inserting at offset 0
|
||||
@Test
|
||||
public void testInsertAtOffset() {
|
||||
@@ -485,32 +553,18 @@ public class StructureDBTest extends AbstractGenericTest {
|
||||
Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1);
|
||||
struct.insertAtOffset(2, zeroArray, -1);
|
||||
struct.insertAtOffset(2, FloatDataType.dataType, -1);
|
||||
assertEquals(13, struct.getLength());
|
||||
|
||||
DataTypeComponent[] comps = struct.getDefinedComponents();
|
||||
|
||||
assertEquals(6, comps.length);
|
||||
|
||||
assertEquals(0, comps[0].getOffset());
|
||||
assertEquals(0, comps[0].getOrdinal());
|
||||
assertEquals(ByteDataType.class, comps[0].getDataType().getClass());
|
||||
|
||||
assertEquals(2, comps[1].getOffset());
|
||||
assertEquals(2, comps[1].getOrdinal());
|
||||
assertEquals(FloatDataType.class, comps[1].getDataType().getClass());
|
||||
|
||||
assertEquals(6, comps[2].getOffset());
|
||||
assertEquals(3, comps[2].getOrdinal());
|
||||
assertTrue(zeroArray.isEquivalent(comps[2].getDataType()));
|
||||
|
||||
assertEquals(6, comps[3].getOffset());
|
||||
assertEquals(4, comps[3].getOrdinal());
|
||||
assertEquals(WordDataType.class, comps[3].getDataType().getClass());
|
||||
|
||||
assertEquals(8, comps[4].getOffset());
|
||||
assertEquals(5, comps[4].getOrdinal());
|
||||
assertEquals(DWordDataType.class, comps[4].getDataType().getClass());
|
||||
|
||||
assertEquals("/Test\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
" 2 float[0] 0 \"\"\n" +
|
||||
" 2 float 4 \"\"\n" +
|
||||
" 6 word 2 \"Comment2\"\n" +
|
||||
" 8 dword 4 field3 \"\"\n" +
|
||||
" 12 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 13 Alignment: 1\n", struct.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -815,6 +869,154 @@ public class StructureDBTest extends AbstractGenericTest {
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertBitFieldAtLittleEndian2() throws Exception {
|
||||
|
||||
Array zeroArray = new ArrayDataType(CharDataType.dataType, 0, -1);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "A", null);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "B", null);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "C", null);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "D", null);
|
||||
struct.insertAtOffset(2, zeroArray, -1, "E", null);
|
||||
struct.insertAtOffset(2, CharDataType.dataType, -1, "XXX", null);
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
// " 3 undefined 1 \"\"\n" +
|
||||
// " 4 undefined 1 \"\"\n" +
|
||||
// " 5 undefined 1 \"\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
|
||||
// " 3 undefined 1 \"\"\n" +
|
||||
// " 4 undefined 1 \"\"\n" +
|
||||
// " 5 undefined 1 \"\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment");
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
|
||||
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
|
||||
// " 5 undefined 1 \"\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
try {
|
||||
struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 12, "bf4", "bf4Comment");
|
||||
fail(
|
||||
"expected - IllegalArgumentException: Bitfield does not fit within specified constraints");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment");
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
|
||||
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
|
||||
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
|
||||
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 0, "z", "zero bitfield");
|
||||
|
||||
//@formatter:off
|
||||
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure Test {\n" +
|
||||
" 0 byte 1 field1 \"Comment1\"\n" +
|
||||
// " 1 undefined 1 \"\"\n" +
|
||||
" 2 char[0] 0 A \"\"\n" +
|
||||
" 2 char[0] 0 B \"\"\n" +
|
||||
" 2 char[0] 0 C \"\"\n" +
|
||||
" 2 char[0] 0 D \"\"\n" +
|
||||
" 2 char[0] 0 E \"\"\n" +
|
||||
" 2 int:0(0) 0 \"zero bitfield\"\n" + // field name discarded
|
||||
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
|
||||
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
|
||||
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
|
||||
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
|
||||
" 6 char 1 XXX \"\"\n" +
|
||||
" 7 word 2 \"Comment2\"\n" +
|
||||
" 9 dword 4 field3 \"\"\n" +
|
||||
" 13 byte 1 field4 \"Comment4\"\n" +
|
||||
"}\n" +
|
||||
"Length: 14 Alignment: 1", struct);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertBitFieldAtBigEndian() throws Exception {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user