Merge remote-tracking branch 'origin/patch'

Conflicts:
	Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java
This commit is contained in:
ghidra1 2020-02-03 15:33:06 -05:00
commit c081a87ede
9 changed files with 1187 additions and 481 deletions

View file

@ -162,8 +162,7 @@ public abstract class CompositeEditorPanel extends JPanel
BitFieldEditorDialog dlg = new BitFieldEditorDialog(model.viewComposite, BitFieldEditorDialog dlg = new BitFieldEditorDialog(model.viewComposite,
provider.dtmService, editingRow, ordinal -> { provider.dtmService, editingRow, ordinal -> {
model.fireTableDataChanged(); model.notifyCompositeChanged();
model.compositeInfoChanged();
}); });
Component c = provider.getComponent(); Component c = provider.getComponent();
Window w = SwingUtilities.windowForComponent(c); Window w = SwingUtilities.windowForComponent(c);

View file

@ -17,6 +17,8 @@ package ghidra.app.merge.datatypes;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -35,38 +37,48 @@ import ghidra.util.task.TaskMonitorAdapter;
public class DataTypeMerge1Test extends AbstractDataTypeMergeTest { public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
@Test @Test
public void testCategoryAddRemoveDTAdd() throws Exception { public void testCategoryAddRemoveDTAdd() throws Exception {
TypeDef td = new TypedefDataType("BF", IntegerDataType.dataType);
AtomicReference<Structure> structRef = new AtomicReference<>();
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
// Make no changes to Latest. // Make no changes to Latest.
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager(); DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test"); int transactionID = program.startTransaction("test");
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2"));
try { try {
c.removeCategory("Category5", TaskMonitorAdapter.DUMMY_MONITOR);
Category c5 = c.createCategory("Category5");
Structure dt = new StructureDataType("Test", 0);
dt.add(new ByteDataType());
dt.add(new WordDataType());
dt = (Structure) c5.addDataType(dt, DataTypeConflictHandler.DEFAULT_HANDLER); Structure struct =
dt.add(new QWordDataType()); new StructureDataType("Test", 0, program.getDataTypeManager());
struct.add(new ByteDataType());
struct.add(new WordDataType());
struct.insertBitFieldAt(3, 2, 6, td, 2, "bf1", null);
struct.insertBitFieldAt(3, 2, 4, td, 2, "bf2", null);
struct.add(new QWordDataType());
struct.setFlexibleArrayComponent(td, "flex", "my flex");
structRef.set(struct);
c.removeCategory("Category5", TaskMonitorAdapter.DUMMY);
Category c5 = c.createCategory("Category5");
c5.addDataType(struct, DataTypeConflictHandler.DEFAULT_HANDLER);
commit = true; commit = true;
} }
catch (InvalidNameException e) { catch (Exception e) {
Assert.fail("got InvalidNameException!"); e.printStackTrace();
Assert.fail(e.toString());
} }
finally { finally {
program.endTransaction(transactionID, commit); program.endTransaction(transactionID, commit);
@ -79,19 +91,19 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category5")); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category5"));
assertNotNull(c); assertNotNull(c);
assertNotNull(c.getDataType("Test")); DataType dt = c.getDataType("Test");
assertNotNull(dt);
assertTrue(structRef.get().isEquivalent(dt));
} }
@Test @Test
public void testDataTypeAddedInMy() throws Exception { public void testDataTypeAddedInMy() throws Exception {
// A category was added to Category5 in the latest; // A category was added to Category5 in the latest;
// in My program, rename Category5 to "My Category5" and add a new data type // in My program, rename Category5 to "My Category5" and add a new data type
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -111,9 +123,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -157,20 +166,19 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeAddedInMy2() throws Exception { public void testDataTypeAddedInMy2() throws Exception {
TypeDef td = new TypedefDataType("BF", IntegerDataType.dataType);
AtomicReference<Structure> structRef = new AtomicReference<>();
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
// Make no changes to Latest. // Make no changes to Latest.
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -179,15 +187,24 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3"));
try { try {
Structure s = (Structure) c.getDataType("IntStruct"); Structure s = (Structure) c.getDataType("IntStruct");
c.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); c.remove(s, TaskMonitorAdapter.DUMMY);
s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0); s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0, dtm);
s.add(new QWordDataType()); s.add(new QWordDataType(), "f1", "my f1");
s.add(new FloatDataType()); s.add(new FloatDataType());
s.add(new ByteDataType()); s.add(new ByteDataType());
s.insertBitFieldAt(16, 2, 6, td, 2, "bf1", "my bf1");
s.insertBitFieldAt(16, 2, 4, td, 2, "bf2", "my bf2");
s.add(new WordDataType()); s.add(new WordDataType());
structRef.set(s);
c.addDataType(s, DataTypeConflictHandler.DEFAULT_HANDLER); c.addDataType(s, DataTypeConflictHandler.DEFAULT_HANDLER);
commit = true; commit = true;
} }
catch (Exception e) {
e.printStackTrace();
Assert.fail(e.toString());
}
finally { finally {
program.endTransaction(transactionID, commit); program.endTransaction(transactionID, commit);
} }
@ -199,30 +216,25 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3"));
DataType dt = c.getDataType("IntStruct"); DataType dt = c.getDataType("IntStruct");
assertNotNull(dt); assertNotNull(dt);
assertTrue(dt instanceof Structure); assertTrue(structRef.get().isEquivalent(dt));
Structure s = (Structure) dt;
assertTrue(new QWordDataType().isEquivalent(s.getComponent(0).getDataType()));
assertTrue(new FloatDataType().isEquivalent(s.getComponent(1).getDataType()));
assertTrue(new ByteDataType().isEquivalent(s.getComponent(2).getDataType()));
assertTrue(new WordDataType().isEquivalent(s.getComponent(3).getDataType()));
Structure s = (Structure) dt;
assertEquals("my f1", s.getComponent(0).getComment());
DataTypeComponent dtc = s.getComponentAt(17);
assertEquals(7, dtc.getOrdinal());
assertEquals("my bf1", dtc.getComment());
} }
@Test @Test
public void testDataTypeAddedInMy3() throws Exception { public void testDataTypeAddedInMy3() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
// Make no changes to Latest. // Make no changes to Latest.
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -231,7 +243,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3"));
try { try {
Structure s = (Structure) c.getDataType("IntStruct"); Structure s = (Structure) c.getDataType("IntStruct");
c.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); c.remove(s, TaskMonitorAdapter.DUMMY);
s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0); s = new StructureDataType(c.getCategoryPath(), "IntStruct", 0);
s.add(new QWordDataType()); s.add(new QWordDataType());
s.add(new FloatDataType()); s.add(new FloatDataType());
@ -270,15 +282,13 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeAddedInLatest() throws Exception { public void testDataTypeAddedInLatest() throws Exception {
// Add A category to Category5 in the latest, add // Add A category to Category5 in the latest, add
// add a new data type; // add a new data type;
// in My program, rename Category5 to "My Category5" // in My program, rename Category5 to "My Category5"
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -303,9 +313,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -341,14 +348,12 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeAddedInLatest2() throws Exception { public void testDataTypeAddedInLatest2() throws Exception {
// A category was added to Category5 in the latest; // A category was added to Category5 in the latest;
// in My program, rename Category5 to "My Category5" and add a new data type // in My program, rename Category5 to "My Category5" and add a new data type
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -372,9 +377,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -418,20 +420,15 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeDeletedInMy() throws Exception { public void testDataTypeDeletedInMy() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
// Make no changes to Latest. // Make no changes to Latest.
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -443,7 +440,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
"IntStruct"); "IntStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -461,20 +458,15 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeAddedDeletedInMy() throws Exception { public void testDataTypeAddedDeletedInMy() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
// Make no changes to Latest. // Make no changes to Latest.
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -487,7 +479,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
try { try {
DataType dt = dtm.addDataType(s, DataTypeConflictHandler.DEFAULT_HANDLER); DataType dt = dtm.addDataType(s, DataTypeConflictHandler.DEFAULT_HANDLER);
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -505,12 +497,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeDeletedChanged() throws Exception { public void testDataTypeDeletedChanged() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -529,9 +519,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -543,7 +530,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
"IntStruct"); "IntStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -561,12 +548,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeDeletedChanged2() throws Exception { public void testDataTypeDeletedChanged2() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -585,9 +570,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -599,7 +581,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
"FloatStruct"); "FloatStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -616,12 +598,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeDeletedChanged3() throws Exception { public void testDataTypeDeletedChanged3() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -631,7 +611,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
"IntStruct"); "IntStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -639,9 +619,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -669,12 +646,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeDeletedInLatest() throws Exception { public void testDataTypeDeletedInLatest() throws Exception {
mtf.initialize("notepad2", new ProgramModifierListener() { mtf.initialize("notepad2", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -684,7 +659,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -692,9 +667,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -728,12 +700,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeDeletedInBoth() throws Exception { public void testDataTypeDeletedInBoth() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -745,7 +715,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
"IntStruct"); "IntStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -753,9 +723,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -767,7 +734,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
"IntStruct"); "IntStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -785,20 +752,15 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDataTypeRenamedInMy() throws Exception { public void testDataTypeRenamedInMy() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
// Make no changes to Latest. // Make no changes to Latest.
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -834,12 +796,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testRenamedBoth() throws Exception { public void testRenamedBoth() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -865,9 +825,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -902,12 +859,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testRenamedBoth2() throws Exception { public void testRenamedBoth2() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -933,9 +888,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -971,12 +923,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDeletedInMyRenamedInLatest() throws Exception { public void testDeletedInMyRenamedInLatest() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1002,9 +952,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1016,7 +963,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
"IntStruct"); "IntStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1032,12 +979,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDeletedInLatestRenamedInMy() throws Exception { public void testDeletedInLatestRenamedInMy() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1047,7 +992,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
"IntStruct"); "IntStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1055,9 +1000,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1090,12 +1032,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDeletedInLatestChangedInMy() throws Exception { public void testDeletedInLatestChangedInMy() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1110,7 +1050,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
Structure s = (Structure) dt; Structure s = (Structure) dt;
s.add(new ByteDataType()); s.add(new ByteDataType());
Category parent = dtm.getCategory(new CategoryPath("/Category1/Category2")); Category parent = dtm.getCategory(new CategoryPath("/Category1/Category2"));
parent.removeCategory("Category3", TaskMonitorAdapter.DUMMY_MONITOR); parent.removeCategory("Category3", TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1118,9 +1058,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1161,12 +1098,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testDeletedInLatestAddedInMy() throws Exception { public void testDeletedInLatestAddedInMy() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1181,7 +1116,7 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
Structure s = (Structure) dt; Structure s = (Structure) dt;
s.add(new ByteDataType()); s.add(new ByteDataType());
Category parent = dtm.getCategory(new CategoryPath("/Category1/Category2")); Category parent = dtm.getCategory(new CategoryPath("/Category1/Category2"));
parent.removeCategory("Category3", TaskMonitorAdapter.DUMMY_MONITOR); parent.removeCategory("Category3", TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1189,9 +1124,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1230,12 +1162,10 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
@Test @Test
public void testCompositeCommentChanged() throws Exception { public void testCompositeCommentChanged() throws Exception {
mtf.initialize("notepad", new ProgramModifierListener() { mtf.initialize("notepad", new ProgramModifierListener() {
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
boolean commit = false; boolean commit = false;
@ -1258,9 +1188,6 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
} }
} }
/* (non-Javadoc)
* @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
*/
@Override @Override
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
boolean commit = false; boolean commit = false;

View file

@ -21,11 +21,11 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import ghidra.program.database.ProgramDB; import ghidra.program.database.*;
import ghidra.program.database.ProgramModifierListener;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter; import ghidra.util.task.TaskMonitorAdapter;
/** /**
@ -47,7 +47,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@ -119,7 +119,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@ -191,7 +191,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@ -296,10 +296,9 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
setErrorsExpected(true); setErrorsExpected(true);
executeMerge(); executeMerge(true);
DataTypeManager dtm = resultProgram.getDataTypeManager();
waitForCompletion(); DataTypeManager dtm = resultProgram.getDataTypeManager();
checkConflictCount(0); checkConflictCount(0);
@ -573,7 +572,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -646,7 +645,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
// causes Bar to be marked as changed // causes Bar to be marked as changed
commit = true; commit = true;
} }
@ -720,7 +719,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
// causes Bar to be marked as changed // causes Bar to be marked as changed
commit = true; commit = true;
} }
@ -796,7 +795,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Structure ms = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), Structure ms = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"MyStruct"); "MyStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
Structure s1 = new StructureDataType( Structure s1 = new StructureDataType(
new CategoryPath("/Category1/Category2/Category5"), "s1", 0); new CategoryPath("/Category1/Category2/Category5"), "s1", 0);
s1.add(ms); s1.add(ms);
@ -884,6 +883,196 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
assertEquals(3, dtcs.length); assertEquals(3, dtcs.length);
} }
@Test
public void testConflictUpdate5() throws Exception {
TypeDef td = new TypedefDataType(new CategoryPath("/Category1/Category2"), "BF",
IntegerDataType.dataType);
mtf.initialize("notepad2", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
dtm.addDataType(td, null);
}
finally {
program.endTransaction(transactionID, true);
}
}
@Override
public void modifyLatest(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "BF");
try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
}
finally {
program.endTransaction(transactionID, true);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"Structure_1");
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
try {
s1.insertBitFieldAt(3, 2, 6, td, 2, "bf1", "my bf1");
s1.insertBitFieldAt(3, 2, 4, td, 2, "bf2", "my bf2");
foo.add(new FloatDataType());
}
catch (Exception e) {
e.printStackTrace();
Assert.fail(e.toString());
}
finally {
program.endTransaction(transactionID, true);
}
}
});
// bitfield silently transitions to int since typedef BF was removed
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Structure s1 =
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
assertNotNull(s1);
DataTypeComponent[] dtcs = s1.getComponents();
assertEquals(7, dtcs.length);
assertEquals(4, dtcs[3].getOffset()); // base on original 2-byte length 1st byte remains undefined
assertEquals("bf1", dtcs[3].getFieldName());
assertEquals("my bf1", dtcs[3].getComment());
DataType dt = dtcs[3].getDataType();
assertTrue(dt instanceof BitFieldDataType);
BitFieldDataType bfDt = (BitFieldDataType) dt;
assertTrue(bfDt.getBaseDataType() instanceof IntegerDataType);
assertEquals(2, bfDt.getDeclaredBitSize());
assertEquals(6, bfDt.getBitOffset());
assertEquals(4, dtcs[4].getOffset()); // base on original 2-byte length 1st byte remains undefined
assertEquals("bf2", dtcs[4].getFieldName());
assertEquals("my bf2", dtcs[4].getComment());
dt = dtcs[4].getDataType();
assertTrue(dt instanceof BitFieldDataType);
bfDt = (BitFieldDataType) dt;
assertTrue(bfDt.getBaseDataType() instanceof IntegerDataType);
assertEquals(2, bfDt.getDeclaredBitSize());
assertEquals(4, bfDt.getBitOffset());
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
// Structure_1 should contain MY Foo
assertEquals(foo, dtcs[5].getDataType());
dtcs = foo.getComponents();
assertEquals(5, dtcs.length);
assertTrue(dtcs[4].getDataType().isEquivalent(new FloatDataType()));
checkConflictCount(0);
}
@Test
public void testConflictUpdate6() throws Exception {
TypeDef td = new TypedefDataType(new CategoryPath("/Category1/Category2"), "BF",
IntegerDataType.dataType);
mtf.initialize("notepad2", new ProgramModifierListener() {
@Override
public void modifyLatest(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
// add new BF not compatible with BitFields
dtm.addDataType(
new StructureDataType(new CategoryPath("/Category1/Category2"), "BF", 0),
null);
}
finally {
program.endTransaction(transactionID, true);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"Structure_1");
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
try {
s1.insertBitFieldAt(3, 2, 6, td, 2, "bf1", "my bf1");
s1.insertBitFieldAt(3, 2, 4, td, 2, "bf2", "my bf2");
foo.add(new FloatDataType());
}
catch (Exception e) {
e.printStackTrace();
Assert.fail(e.toString());
}
finally {
program.endTransaction(transactionID, true);
}
}
});
// bitfield silently transitions to BF.conflict since two different BF types were added
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Structure s1 =
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
assertNotNull(s1);
DataTypeComponent[] dtcs = s1.getComponents();
assertEquals(7, dtcs.length);
assertEquals(4, dtcs[3].getOffset()); // base on original 2-byte length 1st byte remains undefined
assertEquals("bf1", dtcs[3].getFieldName());
assertEquals("my bf1", dtcs[3].getComment());
DataType dt = dtcs[3].getDataType();
assertTrue(dt instanceof BitFieldDataType);
BitFieldDataType bfDt = (BitFieldDataType) dt;
DataType bdt = bfDt.getBaseDataType();
assertEquals("/Category1/Category2/BF.conflict", bdt.getPathName());
assertTrue(bdt.isEquivalent(td));
assertEquals(2, bfDt.getDeclaredBitSize());
assertEquals(6, bfDt.getBitOffset());
assertEquals(4, dtcs[4].getOffset()); // base on original 2-byte length 1st byte remains undefined
assertEquals("bf2", dtcs[4].getFieldName());
assertEquals("my bf2", dtcs[4].getComment());
dt = dtcs[4].getDataType();
assertTrue(dt instanceof BitFieldDataType);
bfDt = (BitFieldDataType) dt;
bdt = bfDt.getBaseDataType();
assertEquals("/Category1/Category2/BF.conflict", bdt.getPathName());
assertTrue(bdt.isEquivalent(td));
assertEquals(2, bfDt.getDeclaredBitSize());
assertEquals(4, bfDt.getBitOffset());
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
// Structure_1 should contain MY Foo
assertEquals(foo, dtcs[5].getDataType());
dtcs = foo.getComponents();
assertEquals(5, dtcs.length);
assertTrue(dtcs[4].getDataType().isEquivalent(new FloatDataType()));
checkConflictCount(1);
}
@Test @Test
public void testEditUnions() throws Exception { public void testEditUnions() throws Exception {
@ -896,7 +1085,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@ -988,7 +1177,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@ -1070,10 +1259,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1157,10 +1346,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1245,10 +1434,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1332,10 +1521,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1425,10 +1614,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1526,10 +1715,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@ -1624,4 +1813,109 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
checkConflictCount(0); checkConflictCount(0);
} }
@Test
public void testEditUnions9() throws Exception {
mtf.initialize("notepad", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "XYZ", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
dtm.addDataType(
new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm),
null);
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 {
DataType enumm = dtm.getDataType(new CategoryPath("/Category1"), "XYZ");
dtm.remove(enumm, TaskMonitor.DUMMY);
Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"CoolUnion");
// NOTE: bit field component byte sizing is currently auto-sized and packed within unions
union.insertBitField(1, IntegerDataType.dataType, 4, "bf1", "latest bf1");
union.insertBitField(2, IntegerDataType.dataType, 2, "bf2", "latest bf2");
commit = true;
}
catch (InvalidDataTypeException e) {
e.printStackTrace();
Assert.fail();
}
finally {
program.endTransaction(transactionID, commit);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
DataType enumm = dtm.getDataType(new CategoryPath("/Category1"), "XYZ");
assertTrue(enumm instanceof Enum);
Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"CoolUnion");
// NOTE: bit field component byte sizing is currently auto-sized and packed within unions
union.insertBitField(1, enumm, 4, "BF1", "my bf1");
union.insertBitField(2, enumm, 2, "BF2", "my bf2");
commit = true;
}
catch (InvalidDataTypeException e) {
e.printStackTrace();
Assert.fail();
}
finally {
program.endTransaction(transactionID, commit);
}
}
});
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_MY);// MY bitfields w/ enum
waitForCompletion();
// primitive type of byte used in absence of enum
Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
//@formatter:off
assertEquals("/Category1/Category2/CoolUnion\n" +
"Unaligned\n" +
"Union CoolUnion {\n" +
" 0 qword 8 null \"\"\n" +
" 0 byte:4(4) 1 BF1 \"my bf1\"\n" +
" 0 byte:2(6) 1 BF2 \"my bf2\"\n" +
" 0 word 2 null \"\"\n" +
" 0 undefined * * * * * 4 null \"\"\n" +
" 0 DLL_Table 96 null \"\"\n" +
" 0 DLL_Table * 4 null \"\"\n" +
"}\n" +
"Size = 96 Actual Alignment = 1\n", union.toString());
//@formatter:on
}
} }

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.app.merge.datatypes; package ghidra.app.merge.datatypes;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -37,7 +36,7 @@ import ghidra.util.task.TaskMonitorAdapter;
public class DataTypeMerge8Test extends AbstractDataTypeMergeTest { public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
@Test @Test
public void testConflictFixUpForNonFittingStruct() throws Exception { public void testConflictFixUpForNonFittingStruct() throws Exception {
final CategoryPath miscPath = new CategoryPath("/MISC"); final CategoryPath miscPath = new CategoryPath("/MISC");
final CategoryPath rootPath = new CategoryPath("/"); final CategoryPath rootPath = new CategoryPath("/");
@ -163,7 +162,7 @@ public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel); JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel);
String statusText = label.getText(); String statusText = label.getText();
String expectedText = String expectedText =
"Merging Data Types: Not enough undefined bytes to fit /XYZ in structure " + "Structure Merge: Not enough undefined bytes to fit /XYZ in structure " +
"/MISC/ABC at offset 0x4.\nIt needs 3 more byte(s) to be able to fit."; "/MISC/ABC at offset 0x4.\nIt needs 3 more byte(s) to be able to fit.";
assertTrue(statusText.contains(expectedText)); assertTrue(statusText.contains(expectedText));
} }

View file

@ -141,7 +141,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
} }
@Override @Override
public DataType getParent() { public Composite getParent() {
return parent; return parent;
} }

View file

@ -25,6 +25,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult; import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
/** /**
@ -640,6 +641,13 @@ class StructureDB extends CompositeDB implements Structure {
} }
} }
/**
* Create copy of structure for target dtm (source archive information is discarded).
* WARNING! copying unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* @param dtm target data type manager
* @return cloned structure
*/
@Override @Override
public DataType copy(DataTypeManager dtm) { public DataType copy(DataTypeManager dtm) {
StructureDataType struct = StructureDataType struct =
@ -649,6 +657,13 @@ class StructureDB extends CompositeDB implements Structure {
return struct; return struct;
} }
/**
* Create cloned structure for target dtm preserving source archive information.
* WARNING! cloning unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* @param dtm target data type manager
* @return cloned structure
*/
@Override @Override
public DataType clone(DataTypeManager dtm) { public DataType clone(DataTypeManager dtm) {
StructureDataType struct = StructureDataType struct =
@ -891,12 +906,28 @@ class StructureDB extends CompositeDB implements Structure {
@Override @Override
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name, public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name,
String comment) { String comment) {
if (offset < 0) {
throw new IllegalArgumentException("Offset cannot be negative.");
}
if (dataType instanceof BitFieldDataType) {
BitFieldDataType bfDt = (BitFieldDataType) dataType;
if (length <= 0) {
length = dataType.getLength();
}
try {
return insertBitFieldAt(offset, length, bfDt.getBitOffset(), bfDt.getBaseDataType(),
bfDt.getDeclaredBitSize(), name, comment);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e);
}
}
lock.acquire(); lock.acquire();
try { try {
checkDeleted(); checkDeleted();
if (offset < 0) {
throw new IllegalArgumentException("Offset cannot be negative.");
}
validateDataType(dataType); validateDataType(dataType);
dataType = resolve(dataType); dataType = resolve(dataType);
@ -966,6 +997,10 @@ class StructureDB extends CompositeDB implements Structure {
if (ordinal < 0 || ordinal >= numComponents) { if (ordinal < 0 || ordinal >= numComponents) {
throw new ArrayIndexOutOfBoundsException(ordinal); throw new ArrayIndexOutOfBoundsException(ordinal);
} }
if (dataType instanceof BitFieldDataType) {
throw new IllegalArgumentException(
"Components may not be replaced with a bit-field");
}
validateDataType(dataType); validateDataType(dataType);
DataTypeComponent origDtc = getComponent(ordinal); DataTypeComponent origDtc = getComponent(ordinal);
@ -1085,19 +1120,14 @@ class StructureDB extends CompositeDB implements Structure {
componentAdapter.removeRecord(dtc.getKey()); componentAdapter.removeRecord(dtc.getKey());
} }
components.clear(); components.clear();
numComponents = 0;
structLength = 0;
if (flexibleArrayComponent != null) { if (flexibleArrayComponent != null) {
flexibleArrayComponent.getDataType().removeParent(this); flexibleArrayComponent.getDataType().removeParent(this);
componentAdapter.removeRecord(flexibleArrayComponent.getKey()); componentAdapter.removeRecord(flexibleArrayComponent.getKey());
flexibleArrayComponent = null; flexibleArrayComponent = null;
} }
if (struct.isNotYetDefined()) {
numComponents = 0;
structLength = 0;
}
else {
structLength = struct.getLength();
numComponents = isInternallyAligned() ? 0 : structLength;
}
setAlignment(struct, false); setAlignment(struct, false);
@ -1154,14 +1184,17 @@ class StructureDB extends CompositeDB implements Structure {
private void doReplaceWithUnaligned(Structure struct) throws IOException { private void doReplaceWithUnaligned(Structure struct) throws IOException {
// assumes components is clear and that alignment characteristics have been set. // assumes components is clear and that alignment characteristics have been set.
if (struct.isNotYetDefined()) {
return;
}
// NOTE: unaligned bitfields should remain unchanged when structLength = struct.getLength();
// transitioning endianess even though it makes little sense. numComponents = structLength;
// Unaligned structures are not intended to be portable!
DataTypeComponent[] otherComponents = struct.getDefinedComponents(); DataTypeComponent[] otherComponents = struct.getDefinedComponents();
for (int i = 0; i < otherComponents.length; i++) { for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i]; DataTypeComponent dtc = otherComponents[i];
DataType dt = resolve(dtc.getDataType()); DataType dt = resolve(dtc.getDataType());
checkAncestry(dt); checkAncestry(dt);

View file

@ -196,9 +196,8 @@ public interface Structure extends Composite {
public void deleteAtOffset(int offset); public void deleteAtOffset(int offset);
/** /**
* Remove all components from this structure, effectively setting the * Remove all components from this structure (including flex-array),
* length to zero. * effectively setting the length to zero.
*
*/ */
public void deleteAll(); public void deleteAll();

View file

@ -23,6 +23,7 @@ import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.UniversalID; import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
/** /**
@ -297,9 +298,25 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
@Override @Override
public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length, public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length,
String componentName, String comment) { String componentName, String comment) {
if (offset < 0) { if (offset < 0) {
throw new IllegalArgumentException("Offset cannot be negative."); throw new IllegalArgumentException("Offset cannot be negative.");
} }
if (dataType instanceof BitFieldDataType) {
BitFieldDataType bfDt = (BitFieldDataType) dataType;
if (length <= 0) {
length = dataType.getLength();
}
try {
return insertBitFieldAt(offset, length, bfDt.getBitOffset(), bfDt.getBaseDataType(),
bfDt.getDeclaredBitSize(), componentName, comment);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e);
}
}
validateDataType(dataType); validateDataType(dataType);
dataType = dataType.clone(dataMgr); dataType = dataType.clone(dataMgr);
@ -524,7 +541,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
} }
@Override @Override
public DataTypeComponent insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset, public DataTypeComponentImpl insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment) DataType baseDataType, int bitSize, String componentName, String comment)
throws InvalidDataTypeException { throws InvalidDataTypeException {
@ -847,6 +864,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return available; return available;
} }
/**
* Create copy of structure for target dtm (source archive information is discarded).
* WARNING! copying unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* @param dtm target data type manager
* @return cloned structure
*/
@Override @Override
public DataType copy(DataTypeManager dtm) { public DataType copy(DataTypeManager dtm) {
StructureDataType struct = new StructureDataType(categoryPath, getName(), getLength(), dtm); StructureDataType struct = new StructureDataType(categoryPath, getName(), getLength(), dtm);
@ -855,6 +879,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return struct; return struct;
} }
/**
* Create cloned structure for target dtm preserving source archive information.
* WARNING! cloning unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* @param dtm target data type manager
* @return cloned structure
*/
@Override @Override
public DataType clone(DataTypeManager dtm) { public DataType clone(DataTypeManager dtm) {
if (dataMgr == dtm) { if (dataMgr == dtm) {
@ -907,15 +938,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
int oldLength = structLength; int oldLength = structLength;
components.clear(); components.clear();
structLength = 0;
numComponents = 0;
flexibleArrayComponent = null; flexibleArrayComponent = null;
if (struct.isNotYetDefined()) {
structLength = 0;
numComponents = 0;
}
else {
structLength = struct.getLength();
numComponents = isInternallyAligned() ? 0 : structLength;
}
setAlignment(struct); setAlignment(struct);
@ -950,14 +975,17 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
private void doReplaceWithUnaligned(Structure struct) { private void doReplaceWithUnaligned(Structure struct) {
// assumes components is clear and that alignment characteristics have been set. // assumes components is clear and that alignment characteristics have been set.
if (struct.isNotYetDefined()) {
return;
}
// NOTE: unaligned bitfields should remain unchanged when structLength = struct.getLength();
// transitioning endianess even though it makes little sense. numComponents = structLength;
// Unaligned structures are not intended to be portable!
DataTypeComponent[] otherComponents = struct.getDefinedComponents(); DataTypeComponent[] otherComponents = struct.getDefinedComponents();
for (int i = 0; i < otherComponents.length; i++) { for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i]; DataTypeComponent dtc = otherComponents[i];
DataType dt = dtc.getDataType().clone(dataMgr); DataType dt = dtc.getDataType().clone(dataMgr);
checkAncestry(dt); checkAncestry(dt);