This commit is contained in:
astrelsky 2020-02-13 09:34:48 -05:00
commit 8c35703cc4
No known key found for this signature in database
GPG key ID: FA88FA97D6877C37
77 changed files with 4546 additions and 2442 deletions

View file

@ -6,6 +6,24 @@
</HEAD> </HEAD>
<BODY> <BODY>
<H1 align="center">Ghidra 9.1.2 Change History (February 2020)</H1>
<blockquote><p><u>Bugs</u></p>
<ul>
<li><I>Data Types</I>. Improved PDB composite reconstruction to attempt <code>pack(1)</code> alignment if default alignment fails. (GT-3401)</li>
<li><I>Data Types</I>. Added missing support for multi-user merge of unions and structures containing bitfields or a trailing flexible array member. (GT-3479)</li>
<li><I>Data Types</I>. Corrected structure editor save button enablement issue when editing bitfields within an unaligned structure. (GT-3519, Issue #1297)</li>
<li><I>Disassembly</I>. Corrected potential infinite loop with disassembler caused by branch to self with invalid delay slot instruction. (GT-3511, Issue #1486)</li>
<li><I>GUI</I>. Corrected processor manual display for Microsoft Windows users, which was not displaying processor manual and was, instead, rendering a blank page in web browser. (GT-3444)</li>
<li><I>GUI:Bitfield Editor</I>. Added field comment support to composite bitfield editor. (GT-3410)</li>
<li><I>Importer:MachO</I>. A MachO loader regression, in Ghidra 9.1.1, when laying down symbols at the correct location, has been fixed. (GT-3487, Issue #1446)</li>
<li><I>Languages</I>. Corrected mnemonic for ARM thumb <code>RSB.w</code> instruction. (GT-3420, Issue #1365)</li>
<li><I>Languages</I>. Corrected issue in M68000 with some move instructions not creating correct array assignments. (GT-3429, Issue #1394)</li>
<li><I>Languages</I>. Updated x86 processor manual index file with latest Intel and AMD manuals. (GT-3489, Issue #1078)</li>
<li><I>Multi-User:Ghidra Server</I>. Corrected Ghidra Server remote interface errors that occur when running with Java 11.0.6 (and later) release, which would throw RemoteException <code>"Method is not Remote"</code> errors. (GT-3521, Issue #1440)</li>
<li><I>PDB</I>. Corrected PDB XML generation for zero-length classes and structures and resolved various datatype dependency issues encountered during PDB Analysis. Changed line numbers from hex to decimal. (GT-3462, Issue #1410)</li>
</ul>
</blockquote>
<H1 align="center">Ghidra 9.1.1 Change History (December 2019)</H1> <H1 align="center">Ghidra 9.1.1 Change History (December 2019)</H1>
<blockquote><p><u>Improvements</u></p> <blockquote><p><u>Improvements</u></p>
<ul> <ul>

View file

@ -314,7 +314,7 @@ public class SampleGraphProvider extends ComponentProviderAdapter {
return mainPanel; return mainPanel;
} }
private void showFitlerPanel(boolean selected) { private void showFilterPanel(boolean selected) {
if (selected) { if (selected) {
mainPanel.add(filterPanel, BorderLayout.SOUTH); mainPanel.add(filterPanel, BorderLayout.SOUTH);
} }
@ -331,7 +331,7 @@ public class SampleGraphProvider extends ComponentProviderAdapter {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
showFitlerPanel(isSelected()); showFilterPanel(isSelected());
} }
}; };

View file

@ -24,6 +24,8 @@ import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.fieldpanel.field.FieldElement; import docking.widgets.fieldpanel.field.FieldElement;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.fieldpanel.support.Highlight; import docking.widgets.fieldpanel.support.Highlight;
@ -450,16 +452,14 @@ public class ListingHighlightProvider
text = StringUtilities.findWord(text, pos, UNDERSCORE_AND_PERIOD_OK); text = StringUtilities.findWord(text, pos, UNDERSCORE_AND_PERIOD_OK);
} }
if (text != null) { if (StringUtils.isBlank(text)) {
text = text.trim(); text = null;
if (text.length() == 0) {
text = null;
}
} }
else {
if (text != null) { text = text.trim();
currentHighlightPattern = Pattern.compile(text, Pattern.LITERAL); currentHighlightPattern = Pattern.compile(text, Pattern.LITERAL);
} }
return text; return text;
} }

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

@ -60,7 +60,8 @@ public class FavoritesAction extends CompositeEditorTableAction {
@Override @Override
public void adjustEnablement() { public void adjustEnablement() {
// Do nothing since we always want it enabled so the user gets a "doesn't fit" message. // we always want it enabled so the user gets a "doesn't fit" message.
setEnabled(true);
} }
@Override @Override
@ -75,6 +76,6 @@ public class FavoritesAction extends CompositeEditorTableAction {
@Override @Override
public boolean isAddToPopup(ActionContext context) { public boolean isAddToPopup(ActionContext context) {
return super.isEnabledForContext(context); return isEnabledForContext(context);
} }
} }

View file

@ -42,10 +42,6 @@ public abstract class AbstractConvertAction extends ListingContextAction {
this.isSigned = isSigned; this.isSigned = isSigned;
setPopupMenuData(new MenuData(new String[] { "Convert", "" }, "Convert")); setPopupMenuData(new MenuData(new String[] { "Convert", "" }, "Convert"));
setEnabled(true); setEnabled(true);
JMenuItem item = new JMenuItem();
Font font = item.getFont();
metrics = plugin.getTool().getActiveWindow().getFontMetrics(font);
} }
@Override @Override
@ -132,14 +128,28 @@ public abstract class AbstractConvertAction extends ListingContextAction {
return isSigned; return isSigned;
} }
private int stringWidth(String s) {
if (metrics == null) {
JMenuItem item = new JMenuItem();
Font font = item.getFont();
metrics = plugin.getTool().getActiveWindow().getFontMetrics(font);
}
int w = metrics.stringWidth(s);
if (w == 0) {
// use default computation if metrics report 0
return 10 * s.length();
}
return w;
}
String getStandardLengthString(String baseString) { String getStandardLengthString(String baseString) {
int baseWidth = metrics.stringWidth(baseString); int baseWidth = stringWidth(baseString);
int spaceWidth = metrics.stringWidth(" "); int spaceWidth = stringWidth(" ");
int paddingSize = (140 - baseWidth) / spaceWidth; int paddingSize = (140 - baseWidth) / spaceWidth;
if (paddingSize <= 0) { if (paddingSize <= 0) {
return baseString; return baseString;
} }
StringBuffer buf = new StringBuffer(baseString); StringBuilder buf = new StringBuilder(baseString);
for (int i = 0; i < paddingSize; i++) { for (int i = 0; i < paddingSize; i++) {
buf.append(" "); buf.append(" ");
} }

View file

@ -116,6 +116,10 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
addCancelButton(); addCancelButton();
setHelpLocation(new HelpLocation("ExporterPlugin", "Exporter_Dialog")); setHelpLocation(new HelpLocation("ExporterPlugin", "Exporter_Dialog"));
// This dialog is temporary and will be closed when the task is finished. Mark
// it transient so no other windows will be parented to this dialog.
setTransient(true);
// need to initialize a few things // need to initialize a few things
selectedFormatChanged(); selectedFormatChanged();
validate(); validate();
@ -503,7 +507,7 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
Object tmpConsumer = new Object(); Object tmpConsumer = new Object();
obj.addConsumer(tmpConsumer); obj.addConsumer(tmpConsumer);
SystemUtilities.runSwingLater(() -> { Swing.runLater(() -> {
try { try {
AboutDomainObjectUtils.displayInformation(tool, obj.getDomainFile(), AboutDomainObjectUtils.displayInformation(tool, obj.getDomainFile(),
obj.getMetadata(), "Export Results Summary", resultsBuffer.toString(), obj.getMetadata(), "Export Results Summary", resultsBuffer.toString(),
@ -516,9 +520,10 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
} }
/************************************************** //==================================================================================================
* Methods for testing // Methods for Testing
**************************************************/ //==================================================================================================
JCheckBox getSelectionCheckBox() { JCheckBox getSelectionCheckBox() {
return selectionCheckBox; return selectionCheckBox;
} }

View file

@ -125,7 +125,8 @@ public class LocationReferencesPlugin extends Plugin
private void displayProviderForLocation(ProgramLocation location, Navigatable navigatable) { private void displayProviderForLocation(ProgramLocation location, Navigatable navigatable) {
LocationDescriptor locationDescriptor = getLocationDescriptor(location); LocationDescriptor locationDescriptor = getLocationDescriptor(location);
if (locationDescriptor == null) { if (locationDescriptor == null) {
throw new IllegalArgumentException("Unable to display provider - unknown location"); throw new IllegalArgumentException(
"Unable to display provider - unknown location: " + location);
} }
LocationReferencesProvider provider = findProvider(locationDescriptor, navigatable); LocationReferencesProvider provider = findProvider(locationDescriptor, navigatable);

View file

@ -398,12 +398,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
if (event != null) { return new ActionContext(this, referencesPanel.getTable());
if (referencesPanel.selectRow(event)) {
return new ActionContext(this, referencesPanel.getTable());
}
}
return null;
} }
//================================================================================================== //==================================================================================================

View file

@ -711,16 +711,9 @@ public final class ReferenceUtils {
return null; return null;
} }
List<Object> objects = variableOffset.getObjects();
Object object = objects.get((int) variableOffset.getOffset());
if (!(object instanceof LabelString)) {
return null;
}
Variable variable = variableOffset.getVariable(); Variable variable = variableOffset.getVariable();
DataType type = variable.getDataType(); DataType type = variable.getDataType();
LabelString label = (LabelString) object; String string = variableOffset.getDataTypeDisplayText();
String string = label.toString();
GenericDataTypeLocationDescriptor descriptor = GenericDataTypeLocationDescriptor descriptor =
createGenericDataTypeLocationDescriptor(program, type, string); createGenericDataTypeLocationDescriptor(program, type, string);
return descriptor; return descriptor;

View file

@ -537,7 +537,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
public ColumnConstraintFilterMode getColumnConstraintFilterMode() { public ColumnConstraintFilterMode getColumnConstraintFilterMode() {
// not sure about this: it could be USE_COLUMN_CONSTRAINTS_ONLY, but then the text // not sure about this: it could be USE_COLUMN_CONSTRAINTS_ONLY, but then the text
// filter would not match the formatted date. This allows for both. // filter would not match the formatted date. This allows for both.
return ColumnConstraintFilterMode.USE_BOTH_COLUMN_RENDERER_FITLER_STRING_AND_CONSTRAINTS; return ColumnConstraintFilterMode.ALLOW_ALL_FILTERS;
} }
@Override @Override

View file

@ -633,17 +633,18 @@ class StackEditorModel extends CompositeEditorModel {
newLength = compDt.getLength(); newLength = compDt.getLength();
} }
int offset = comp.getOffset(); int offset = comp.getOffset();
if (((StackFrameDataType) viewComposite).growsNegative()) { // TODO: not sure we need to prevent creating local variables in 'save' area,
if (offset >= 0 && offset < getParameterOffset()) { // since doing so just leads to confusion when using stack frame editor
return false; // if (((StackFrameDataType) viewComposite).growsNegative()) {
} // if (offset >= 0 && offset < getParameterOffset()) {
} // return false;
else { // }
if (offset < 0 && offset > getParameterOffset()) { // }
return false; // else {
} // if (offset < 0 && offset > getParameterOffset()) {
// return false;
} // }
// }
int maxBytes = ((StackFrameDataType) viewComposite).getMaxLength(offset); int maxBytes = ((StackFrameDataType) viewComposite).getMaxLength(offset);
if (newLength > maxBytes) { if (newLength > maxBytes) {
return false; return false;

View file

@ -16,14 +16,14 @@
package ghidra.app.util.exporter; package ghidra.app.util.exporter;
import java.io.*; import java.io.*;
import java.util.ArrayList; import java.util.List;
import org.junit.Assert; import org.junit.Assert;
import ghidra.util.Msg; import ghidra.util.Msg;
public class Compare { public class StringComparer {
public static void compare(ArrayList<String> expectedList, File actualFile) throws Exception { public static void compareLines(List<String> expectedList, File actualFile) throws Exception {
int index = 0; int index = 0;
boolean hasFailure = false; boolean hasFailure = false;
@ -46,26 +46,26 @@ public class Compare {
expectedLine = expectedLine.trim(); expectedLine = expectedLine.trim();
boolean match = boolean match =
expectedLine.equals(actualLine) || actualLine.startsWith(expectedLine); expectedLine.equals(actualLine) || actualLine.startsWith(expectedLine);
hasFailure |= !match; hasFailure |= !match;
if (!match) { if (!match) {
Msg.debug(Compare.class, "Expected line does not match actual line (" + index + Msg.debug(StringComparer.class, "Expected line does not match actual line (" + index +
"): \nExpected: " + expectedLine + "\nActual: " + actualLine); "): \nExpected: " + expectedLine + "\nActual: " + actualLine);
} }
} }
if (excess > 0) { if (excess > 0) {
String message = "Actual file contains " + excess + " more lines than expected"; String message = "Actual file contains " + excess + " more lines than expected";
Msg.debug(Compare.class, message); Msg.debug(StringComparer.class, message);
Assert.fail(message); Assert.fail(message);
} }
else if (!hasFailure && index < expectedList.size()) { else if (!hasFailure && index < expectedList.size()) {
int fewer = expectedList.size() - index; int fewer = expectedList.size() - index;
String message = "Actual file contains " + fewer + String message = "Actual file contains " + fewer +
" fewer lines than expected"; " fewer lines than expected";
Msg.debug(Compare.class, message); Msg.debug(StringComparer.class, message);
Assert.fail(message); Assert.fail(message);
} }

View file

@ -37,11 +37,13 @@ public class LcsHintLoadSpecChooser implements LoadSpecChooser {
* {@link CompilerSpec}. * {@link CompilerSpec}.
* *
* @param language The {@link Language} to use (should not be null) * @param language The {@link Language} to use (should not be null)
* @param compilerSpec The {@link CompilerSpec} to use (should not be null) * @param compilerSpec The {@link CompilerSpec} to use (f null default compiler spec will be used)
*/ */
public LcsHintLoadSpecChooser(Language language, CompilerSpec compilerSpec) { public LcsHintLoadSpecChooser(Language language, CompilerSpec compilerSpec) {
this.languageID = language.getLanguageID(); this.languageID = language.getLanguageID();
this.compilerSpecID = compilerSpec.getCompilerSpecID(); this.compilerSpecID =
(compilerSpec == null) ? language.getDefaultCompilerSpec().getCompilerSpecID()
: compilerSpec.getCompilerSpecID();
} }
@Override @Override

View file

@ -802,7 +802,7 @@ public class CodeUnitFormat {
long originalValue = (addr.isStackAddress() && long originalValue = (addr.isStackAddress() &&
originalScalar.bitLength() == addr.getAddressSpace().getSize()) originalScalar.bitLength() == addr.getAddressSpace().getSize())
? originalScalar.getSignedValue() ? originalScalar.getSignedValue()
: originalScalar.getValue(); : originalScalar.getUnsignedValue();
long addrOffset; long addrOffset;
if (addr instanceof SegmentedAddress) { if (addr instanceof SegmentedAddress) {
addrOffset = ((SegmentedAddress) addr).getSegmentOffset(); addrOffset = ((SegmentedAddress) addr).getSegmentOffset();

View file

@ -25,6 +25,7 @@ import org.junit.*;
import docking.widgets.table.model.TestDataModel; import docking.widgets.table.model.TestDataModel;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.Msg;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
public class GTableTest extends AbstractGhidraHeadedIntegrationTest { public class GTableTest extends AbstractGhidraHeadedIntegrationTest {
@ -44,6 +45,9 @@ public class GTableTest extends AbstractGhidraHeadedIntegrationTest {
frame.getContentPane().add(new JScrollPane(table)); frame.getContentPane().add(new JScrollPane(table));
frame.pack(); frame.pack();
frame.setVisible(true); frame.setVisible(true);
// showing the table will trigger a call to sort; wait for sorting to finish
waitForSort();
} }
@After @After
@ -59,26 +63,31 @@ public class GTableTest extends AbstractGhidraHeadedIntegrationTest {
setSelectedRow(table, 0); setSelectedRow(table, 0);
triggerText(table, "a"); triggerText(table, "a");
assertEquals(11, table.getSelectedRow()); assertSelectedRow(11, "a");
triggerText(table, "c"); triggerText(table, "c");
assertEquals(12, table.getSelectedRow()); assertSelectedRow(12, "c");
timeout(); timeout();
triggerText(table, "ad"); triggerText(table, "ad");
assertEquals(24, table.getSelectedRow()); assertSelectedRow(24, "ad");
timeout(); timeout();
triggerText(table, "av"); triggerText(table, "av");
assertEquals(70, table.getSelectedRow()); assertSelectedRow(70, "av");
timeout(); timeout();
triggerText(table, "x"); triggerText(table, "x");
assertEquals(1920, table.getSelectedRow()); assertSelectedRow(1920, "x");
timeout(); timeout();
triggerText(table, "a"); triggerText(table, "a");
assertEquals(11, table.getSelectedRow()); assertSelectedRow(11, "a");
// test the case where no match is found // test the case where no match is found
table.setAutoLookupTimeout(1000); // longer timeout needed for multiple keys table.setAutoLookupTimeout(1000); // longer timeout needed for multiple keys
triggerText(table, "zed"); triggerText(table, "zed");
assertEquals(11, table.getSelectedRow()); // no change assertSelectedRow(11, "zed"); // no change
} }
@Test @Test
@ -92,31 +101,31 @@ public class GTableTest extends AbstractGhidraHeadedIntegrationTest {
setSelectedRow(table, 0); setSelectedRow(table, 0);
triggerText(table, "a"); triggerText(table, "a");
assertEquals(1846, table.getSelectedRow()); assertSelectedRow(1846, "a");
triggerText(table, "c"); triggerText(table, "c");
assertEquals(1902, table.getSelectedRow()); assertSelectedRow(1902, "c");
timeout(); timeout();
triggerText(table, "ad"); triggerText(table, "ad");
assertEquals(1885, table.getSelectedRow()); assertSelectedRow(1885, "ad");
timeout(); timeout();
triggerText(table, "av"); triggerText(table, "av");
assertEquals(1848, table.getSelectedRow()); assertSelectedRow(1848, "av");
timeout(); timeout();
triggerText(table, "x"); triggerText(table, "x");
assertEquals(0, table.getSelectedRow()); assertSelectedRow(0, "x");
timeout(); timeout();
triggerText(table, "a"); triggerText(table, "a");
assertEquals(1846, table.getSelectedRow()); assertSelectedRow(1846, "a");
// test the case where no match is found // test the case where no match is found
table.setAutoLookupTimeout(1000); // longer timeout needed for multiple keys table.setAutoLookupTimeout(1000); // longer timeout needed for multiple keys
triggerText(table, "zed"); triggerText(table, "zed");
assertEquals(1846, table.getSelectedRow()); // no change assertSelectedRow(1846, "zed"); // no change
} }
@Test @Test
@ -132,26 +141,31 @@ public class GTableTest extends AbstractGhidraHeadedIntegrationTest {
// note: the order checked here is the same as the sorted order, since we did not move // note: the order checked here is the same as the sorted order, since we did not move
// any rows after disabling the sort // any rows after disabling the sort
triggerText(table, "a"); triggerText(table, "a");
assertEquals(11, table.getSelectedRow()); assertSelectedRow(11, "a");
triggerText(table, "c"); triggerText(table, "c");
assertEquals(12, table.getSelectedRow()); assertSelectedRow(12, "c");
timeout(); timeout();
triggerText(table, "ad"); triggerText(table, "ad");
assertEquals(24, table.getSelectedRow()); assertSelectedRow(24, "ad");
timeout(); timeout();
triggerText(table, "av"); triggerText(table, "av");
assertEquals(70, table.getSelectedRow()); assertSelectedRow(70, "av");
timeout(); timeout();
triggerText(table, "x"); triggerText(table, "x");
assertEquals(1920, table.getSelectedRow()); assertSelectedRow(1920, "x");
timeout(); timeout();
triggerText(table, "a"); triggerText(table, "a");
assertEquals(11, table.getSelectedRow()); assertSelectedRow(11, "a");
// test the case where no match is found // test the case where no match is found
table.setAutoLookupTimeout(1000); // longer timeout needed for multiple keys table.setAutoLookupTimeout(1000); // longer timeout needed for multiple keys
triggerText(table, "zed"); triggerText(table, "zed");
assertEquals(11, table.getSelectedRow()); // no change assertSelectedRow(11, "zed"); // no change
} }
@Test @Test
@ -192,9 +206,31 @@ public class GTableTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals("Auto-lookup failed to change the table row", 11, table.getSelectedRow()); assertEquals("Auto-lookup failed to change the table row", 11, table.getSelectedRow());
} }
private void assertSelectedRow(int row, String lookupText) {
int actual = runSwing(() -> table.getSelectedRow());
if (row != actual) {
int col = 4; // String 'Name' column
String expectedString = (String) table.getValueAt(row, col);
String actualString = (String) table.getValueAt(actual, col);
String message = "Auto-lookup row not selected for '" + lookupText + "'.\n\t" +
"Expected text: '" + expectedString + "'; Actual text: '" + actualString + "'";
Msg.out(message);
assertEquals(message, row, actual);
}
}
private void removeSortColumn(int column) { private void removeSortColumn(int column) {
waitForSwing(); waitForSwing();
runSwing(() -> TableUtils.columnAlternativelySelected(table, column)); runSwing(() -> TableUtils.columnAlternativelySelected(table, column));
waitForSort();
}
private void waitForSort() {
// the call to sort may be run in an invokeLater()
waitForSwing();
waitForCondition(() -> !model.isSortPending());
waitForSwing(); waitForSwing();
} }
@ -202,7 +238,7 @@ public class GTableTest extends AbstractGhidraHeadedIntegrationTest {
TableSortState descendingSortState = TableSortState.createDefaultSortState(column, false); TableSortState descendingSortState = TableSortState.createDefaultSortState(column, false);
runSwing(() -> model.setTableSortState(descendingSortState)); runSwing(() -> model.setTableSortState(descendingSortState));
waitForSwing(); waitForSort();
} }
private void timeout() { private void timeout() {

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;
} }
@ -77,10 +77,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2"));
Union union = (Union) c.getDataType("CoolUnion");
// choose MY // choose MY
chooseOption(DataTypeMergeManager.OPTION_MY);// DLL_Table from MY chooseOption(DataTypeMergeManager.OPTION_MY);// DLL_Table from MY
@ -90,6 +86,11 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2"));
Union union = (Union) c.getDataType("CoolUnion");
// DLL_Table should have a Word data type as the last component // DLL_Table should have a Word data type as the last component
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
DataTypeComponent dtc = s.getComponent(s.getNumComponents() - 1); DataTypeComponent dtc = s.getComponent(s.getNumComponents() - 1);
@ -119,7 +120,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;
} }
@ -154,7 +155,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// choose DLL_Table from LATEST which means delete it // choose DLL_Table from LATEST which means delete it
chooseOption(DataTypeMergeManager.OPTION_LATEST); chooseOption(DataTypeMergeManager.OPTION_LATEST);
@ -164,6 +164,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2"));
Union union = (Union) c.getDataType("CoolUnion"); Union union = (Union) c.getDataType("CoolUnion");
@ -191,7 +193,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;
} }
@ -226,7 +228,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// choose DLL_Table from ORIGINAL chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// choose DLL_Table from ORIGINAL
@ -234,6 +235,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2"));
Union union = (Union) c.getDataType("CoolUnion"); Union union = (Union) c.getDataType("CoolUnion");
@ -296,10 +299,11 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
setErrorsExpected(true); setErrorsExpected(true);
executeMerge(); executeMerge(true);
DataTypeManager dtm = resultProgram.getDataTypeManager();
waitForCompletion(); setErrorsExpected(false);
DataTypeManager dtm = resultProgram.getDataTypeManager();
checkConflictCount(0); checkConflictCount(0);
@ -372,13 +376,17 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge();
chooseOption(DataTypeMergeManager.OPTION_LATEST);// LATEST CoolUnion
setErrorsExpected(true); setErrorsExpected(true);
executeMerge();
chooseOption(DataTypeMergeManager.OPTION_LATEST);// LATEST CoolUnion
chooseOption(DataTypeMergeManager.OPTION_MY);// MY Foo chooseOption(DataTypeMergeManager.OPTION_MY);// MY Foo
waitForCompletion(); waitForCompletion();
setErrorsExpected(false);
checkConflictCount(0); checkConflictCount(0);
DataTypeManager dtm = resultProgram.getDataTypeManager(); DataTypeManager dtm = resultProgram.getDataTypeManager();
@ -452,10 +460,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
setErrorsExpected(true);
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_MY);// MY CoolUnion chooseOption(DataTypeMergeManager.OPTION_MY);// MY CoolUnion
@ -463,6 +468,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Union union = Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
@ -523,10 +530,12 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge();
chooseOption(DataTypeMergeManager.OPTION_LATEST);// Latest CoolUnion
setErrorsExpected(true); setErrorsExpected(true);
executeMerge();
chooseOption(DataTypeMergeManager.OPTION_LATEST);// Latest CoolUnion
chooseOption(DataTypeMergeManager.OPTION_MY);// My Bar chooseOption(DataTypeMergeManager.OPTION_MY);// My Bar
// //
@ -534,6 +543,9 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
// //
OptionDialog errorDialog = OptionDialog errorDialog =
waitForDialogComponent(null, OptionDialog.class, DEFAULT_WINDOW_TIMEOUT); waitForDialogComponent(null, OptionDialog.class, DEFAULT_WINDOW_TIMEOUT);
setErrorsExpected(false);
assertNotNull(errorDialog); assertNotNull(errorDialog);
errorDialog.close(); errorDialog.close();
window.setVisible(false); window.setVisible(false);
@ -573,7 +585,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 {
@ -603,17 +615,20 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
setErrorsExpected(true);
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
//
chooseOption(DataTypeMergeManager.OPTION_MY);// choose My Bar chooseOption(DataTypeMergeManager.OPTION_MY);// choose My Bar
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL); setErrorsExpected(true);
// choose Structure_1 from ORIGINAL
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL); // choose Structure_1 from ORIGINAL
setErrorsExpected(false);
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// Bar should contain original Structure_1 // Bar should contain original Structure_1
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
DataTypeComponent[] dtcs = bar.getDefinedComponents(); DataTypeComponent[] dtcs = bar.getDefinedComponents();
@ -646,7 +661,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;
} }
@ -677,14 +692,15 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
//
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// choose original Bar
chooseOption(DataTypeMergeManager.OPTION_MY); chooseOption(DataTypeMergeManager.OPTION_ORIGINAL); // choose original Bar
// choose Structure_1 from MY
chooseOption(DataTypeMergeManager.OPTION_MY); // choose Structure_1 from MY
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// Bar should contain original Structure_1 // Bar should contain original Structure_1
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
assertEquals(6, bar.getLength()); assertEquals(6, bar.getLength());
@ -720,7 +736,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;
} }
@ -751,14 +767,15 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
//
chooseOption(DataTypeMergeManager.OPTION_MY);// choose my Bar chooseOption(DataTypeMergeManager.OPTION_MY);// choose my Bar
chooseOption(DataTypeMergeManager.OPTION_LATEST);// delele Structure_1 chooseOption(DataTypeMergeManager.OPTION_LATEST);// delele Structure_1 (choose Structure_1 from MY)
// choose Structure_1 from MY
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// Bar should contain undefined to replace Structure_1 // Bar should contain undefined to replace Structure_1
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
assertEquals(7, bar.getLength()); assertEquals(7, bar.getLength());
@ -796,7 +813,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);
@ -849,7 +866,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// conflict on ArrayStruct (6) // conflict on ArrayStruct (6)
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// use ORIGINAL ArrayStruct chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// use ORIGINAL ArrayStruct
@ -860,6 +876,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
// conflict on FloatStruct (2) // conflict on FloatStruct (2)
chooseOption(DataTypeMergeManager.OPTION_LATEST);// delete FloatStruct chooseOption(DataTypeMergeManager.OPTION_LATEST);// delete FloatStruct
DataTypeManager dtm = resultProgram.getDataTypeManager();
assertNull( assertNull(
dtm.getDataType(new CategoryPath("/Category1/Category2/Category5"), "FloatStruct")); dtm.getDataType(new CategoryPath("/Category1/Category2/Category5"), "FloatStruct"));
@ -884,6 +902,198 @@ 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(true);
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(true);
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 +1106,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;
} }
@ -940,7 +1150,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// choose DLL_Table from ORIGINAL chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// choose DLL_Table from ORIGINAL
@ -948,6 +1157,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// DLL_Table should exist // DLL_Table should exist
Structure dll = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure dll = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
@ -988,7 +1199,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;
} }
@ -1029,7 +1240,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_LATEST);// delete DLL_Table chooseOption(DataTypeMergeManager.OPTION_LATEST);// delete DLL_Table
@ -1037,6 +1247,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// DLL_Table should not exist // DLL_Table should not exist
Structure dll = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure dll = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
@ -1070,10 +1282,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 {
@ -1119,7 +1331,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// original DLL_Table chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// original DLL_Table
@ -1127,6 +1338,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// CoolUnion should not be null // CoolUnion should not be null
Union union = Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
@ -1157,10 +1370,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 {
@ -1206,7 +1419,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_LATEST);// delete DLL_Table chooseOption(DataTypeMergeManager.OPTION_LATEST);// delete DLL_Table
@ -1214,6 +1426,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// CoolUnion should not be null // CoolUnion should not be null
Union union = Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
@ -1245,10 +1459,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 {
@ -1294,7 +1508,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_MY);// my DLL_Table chooseOption(DataTypeMergeManager.OPTION_MY);// my DLL_Table
@ -1302,6 +1515,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// CoolUnion should be null // CoolUnion should be null
Union union = Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
@ -1332,10 +1547,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 {
@ -1383,7 +1598,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// original DLL_Table chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// original DLL_Table
@ -1391,6 +1605,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// CoolUnion should not be null // CoolUnion should not be null
Union union = Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
@ -1425,10 +1641,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 {
@ -1479,7 +1695,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// original DLL_Table chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// original DLL_Table
@ -1487,6 +1702,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// CoolUnion should not be null // CoolUnion should not be null
Union union = Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
@ -1526,10 +1743,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 {
@ -1584,7 +1801,6 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
} }
}); });
executeMerge(); executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// original DLL_Table chooseOption(DataTypeMergeManager.OPTION_ORIGINAL);// original DLL_Table
@ -1592,6 +1808,8 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
waitForCompletion(); waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// CoolUnion should not be null // CoolUnion should not be null
Union union = Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
@ -1624,4 +1842,110 @@ 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();
chooseOption(DataTypeMergeManager.OPTION_MY);// MY bitfields w/ enum
waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// 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

@ -485,9 +485,8 @@ public class EquateMergeManager1Test extends AbstractListingMergeManagerTest {
try { try {
Listing listing = program.getListing(); Listing listing = program.getListing();
Address startAddr = addr(program, "0x1002d20"); Address startAddr = addr(program, "0x1002d20");
program.getMemory() program.getMemory().setBytes(startAddr, new byte[] { (byte) 0x8d, (byte) 0x04,
.setBytes(startAddr, new byte[] { (byte) 0x8d, (byte) 0x04, (byte) 0x8d, (byte) 0x8d, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0 }); //LEA EAX,[0x0 + ECX*0x4]
(byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0 }); //LEA EAX,[0x0 + ECX*0x4]
createInstruction(program, startAddr); createInstruction(program, startAddr);
Instruction instruction = listing.getInstructionAt(startAddr); Instruction instruction = listing.getInstructionAt(startAddr);
Assert.assertTrue(instruction != null); Assert.assertTrue(instruction != null);
@ -579,8 +578,8 @@ public class EquateMergeManager1Test extends AbstractListingMergeManagerTest {
setupAddNameDiffOnSubOperand(); setupAddNameDiffOnSubOperand();
executeMerge(ASK_USER); executeMerge(ASK_USER);
chooseEquate("0x1002d20", 1, KEEP_MY); // 0x4 Order seems to have been switched
chooseEquate("0x1002d20", 1, KEEP_MY); // 0x0 chooseEquate("0x1002d20", 1, KEEP_MY); // 0x0
chooseEquate("0x1002d20", 1, KEEP_MY); // 0x4
waitForMergeCompletion(); waitForMergeCompletion();
EquateTable equateTab = resultProgram.getEquateTable(); EquateTable equateTab = resultProgram.getEquateTable();
@ -602,31 +601,6 @@ public class EquateMergeManager1Test extends AbstractListingMergeManagerTest {
// MY 0x0=ZERO 0x4=QUAD // MY 0x0=ZERO 0x4=QUAD
setupAddNameDiffOnSubOperand(); setupAddNameDiffOnSubOperand();
executeMerge(ASK_USER);
chooseEquate("0x1002d20", 1, KEEP_LATEST); // 0x4
chooseEquate("0x1002d20", 1, KEEP_MY); // 0x0
waitForMergeCompletion();
EquateTable equateTab = resultProgram.getEquateTable();
List<Equate> equates = equateTab.getEquates(addr("0x1002d20"), 1);
assertEquals(2, equates.size());
Equate eq;
eq = equates.get(0);
assertEquals("FOUR", eq.getName());
assertEquals(4L, eq.getValue());
eq = equates.get(1);
assertEquals("ZERO", eq.getName());
assertEquals(0L, eq.getValue());
}
@Test
public void testAddNameDiffOnSubOperandPickMyLatest() throws Exception {
// 0x1002d20 LEA EAX,[0x0 + ECX*0x4]
//
// LATEST 0x0=NADA 0x4=FOUR
// MY 0x0=ZERO 0x4=QUAD
setupAddNameDiffOnSubOperand();
executeMerge(ASK_USER); executeMerge(ASK_USER);
chooseEquate("0x1002d20", 1, KEEP_MY); // 0x4 chooseEquate("0x1002d20", 1, KEEP_MY); // 0x4
chooseEquate("0x1002d20", 1, KEEP_LATEST); // 0x0 chooseEquate("0x1002d20", 1, KEEP_LATEST); // 0x0
@ -644,6 +618,31 @@ public class EquateMergeManager1Test extends AbstractListingMergeManagerTest {
assertEquals(4L, eq.getValue()); assertEquals(4L, eq.getValue());
} }
@Test
public void testAddNameDiffOnSubOperandPickMyLatest() throws Exception {
// 0x1002d20 LEA EAX,[0x0 + ECX*0x4]
//
// LATEST 0x0=NADA 0x4=FOUR
// MY 0x0=ZERO 0x4=QUAD
setupAddNameDiffOnSubOperand();
executeMerge(ASK_USER);
chooseEquate("0x1002d20", 1, KEEP_LATEST); // 0x4
chooseEquate("0x1002d20", 1, KEEP_MY); // 0x0
waitForMergeCompletion();
EquateTable equateTab = resultProgram.getEquateTable();
List<Equate> equates = equateTab.getEquates(addr("0x1002d20"), 1);
assertEquals(2, equates.size());
Equate eq;
eq = equates.get(0);
assertEquals("FOUR", eq.getName());
assertEquals(4L, eq.getValue());
eq = equates.get(1);
assertEquals("ZERO", eq.getName());
assertEquals(0L, eq.getValue());
}
@Test @Test
public void testAddSameNameDiffValue() throws Exception { public void testAddSameNameDiffValue() throws Exception {
mtf.initialize("NotepadMergeListingTest", new ProgramModifierListener() { mtf.initialize("NotepadMergeListingTest", new ProgramModifierListener() {

View file

@ -477,12 +477,8 @@ public class BookmarkPluginTest extends AbstractGhidraHeadedIntegrationTest {
waitForTable(); waitForTable();
selectAllTableRows(); selectAllTableRows();
runSwing(() -> provider.delete()); runSwing(() -> provider.delete());
waitForCondition(() -> table.getRowCount() == 0, "Bookmarks not deleted");
waitForTable();
assertEquals(0, table.getRowCount());
} }
@Test @Test

View file

@ -315,7 +315,7 @@ public class StackEditorActions4Test extends AbstractStackEditorTest {
assertEquals(0xa, getOffset(model.getNumComponents() - 1)); assertEquals(0xa, getOffset(model.getNumComponents() - 1));
FavoritesAction fav = getFavorite("word"); FavoritesAction fav = getFavorite("word");
assertTrue(fav.isEnabled()); assertTrue(fav.isEnabledForContext(null)); // context not utilized
assertEquals("", model.getStatus()); assertEquals("", model.getStatus());
invoke(fav); invoke(fav);
assertEquals("", model.getStatus()); assertEquals("", model.getStatus());

View file

@ -575,6 +575,37 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(comps.length - 1, newDt.getDefinedComponents().length); assertEquals(comps.length - 1, newDt.getDefinedComponents().length);
} }
@Test
public void testDataTypeConflictHandling() throws Exception {
Category sub1 = root.createCategory("Cat1");
DataType dt1 = new StructureDataType("DT", 1);
DataType dt2 = new StructureDataType("DT", 2);
DataType added1 = sub1.addDataType(dt1, null);
DataType added2 = sub1.addDataType(dt2, null);
assertEquals("DT", added1.getName());
assertEquals("DT.conflict", added2.getName());
List<DataType> list = sub1.getDataTypesByBaseName("DT");
assertEquals(2, list.size());
assertEquals(added1, list.get(0));
assertEquals(added2, list.get(1));
list = sub1.getDataTypesByBaseName("DT.conflict");
assertEquals(2, list.size());
assertEquals(added1, list.get(0));
assertEquals(added2, list.get(1));
sub1.remove(added2, TaskMonitor.DUMMY);
list = sub1.getDataTypesByBaseName("DT");
assertEquals(1, list.size());
assertEquals(added1, list.get(0));
list = sub1.getDataTypesByBaseName("DT.conflict");
assertEquals(1, list.size());
assertEquals(added1, list.get(0));
}
@Test @Test
public void testGetDataTypeManager() throws Exception { public void testGetDataTypeManager() throws Exception {
Category sub1 = root.createCategory("SubCat-A"); Category sub1 = root.createCategory("SubCat-A");
@ -772,10 +803,7 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
clearEvents(); clearEvents();
struct2 = (Structure) newDt.insert(3, struct2).getDataType(); struct2 = (Structure) newDt.insert(3, struct2).getDataType();
int eventCount = getEventCount();
if (4 != eventCount) {
System.err.println("halt!");
}
assertEquals(5, getEventCount()); assertEquals(5, getEventCount());
Event ev = getEvent(4); Event ev = getEvent(4);
assertEquals("DT Changed", ev.evName); assertEquals("DT Changed", ev.evName);

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.app.plugin.core.functioncompare; package ghidra.app.plugin.core.functioncompare;
import static org.junit.Assert.assertNull; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.util.Date; import java.util.Date;
import java.util.Set; import java.util.Set;
@ -57,7 +56,7 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
private Function five; private Function five;
private FunctionComparisonPlugin plugin; private FunctionComparisonPlugin plugin;
private FunctionComparisonProvider provider; private FunctionComparisonProvider provider;
private FunctionComparisonProvider provider0; private FunctionComparisonProvider provider2;
private FunctionComparisonModel model; private FunctionComparisonModel model;
@Before @Before
@ -70,23 +69,17 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
model = createTestModel(); model = createTestModel();
} }
/**
*
* Tests for {@link FunctionComparisonService#compareFunctions(Set)}
*
*/
@Test @Test
public void testSetNoFunctions() throws Exception { public void testSetNoFunctions() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet();
FunctionComparisonProvider provider = plugin.compareFunctions(functions); provider = compare(functions);
assertNull(provider); assertNull(provider);
} }
@Test @Test
public void testSetOneFunction() throws Exception { public void testSetOneFunction() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo);
provider = plugin.compareFunctions(functions); provider = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo);
} }
@ -94,23 +87,23 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testSetDuplicateFunctionDifferentProviders() throws Exception { public void testSetDuplicateFunctionDifferentProviders() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo);
provider = plugin.compareFunctions(functions); provider = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo);
provider0 = plugin.compareFunctions(functions); provider2 = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, foo); CompareFunctionsTestUtility.checkSourceFunctions(provider2, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, foo, foo); CompareFunctionsTestUtility.checkTargetFunctions(provider2, foo, foo);
} }
@Test @Test
public void testSetDuplicateFunctionSameProvider() throws Exception { public void testSetDuplicateFunctionSameProvider() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo);
provider = plugin.compareFunctions(functions); provider = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo);
plugin.compareFunctions(functions, provider); compare(functions, provider);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo);
} }
@ -118,7 +111,7 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testSetMultipleFunctions() throws Exception { public void testSetMultipleFunctions() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, junk, stuff); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, junk, stuff);
provider = plugin.compareFunctions(functions); provider = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, junk, stuff); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, junk, stuff);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, junk, stuff); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, junk, stuff);
CompareFunctionsTestUtility.checkTargetFunctions(provider, junk, foo, junk, stuff); CompareFunctionsTestUtility.checkTargetFunctions(provider, junk, foo, junk, stuff);
@ -130,16 +123,16 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(one, two); Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(one, two);
Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(three, four, five); Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(three, four, five);
provider = plugin.compareFunctions(functions1); provider = compare(functions1);
provider0 = plugin.compareFunctions(functions2); provider2 = compare(functions2);
CompareFunctionsTestUtility.checkSourceFunctions(provider, one, two); CompareFunctionsTestUtility.checkSourceFunctions(provider, one, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider, one, one, two); CompareFunctionsTestUtility.checkTargetFunctions(provider, one, one, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider, two, one, two); CompareFunctionsTestUtility.checkTargetFunctions(provider, two, one, two);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, three, four, five); CompareFunctionsTestUtility.checkSourceFunctions(provider2, three, four, five);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, three, three, four, five); CompareFunctionsTestUtility.checkTargetFunctions(provider2, three, three, four, five);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, four, three, four, five); CompareFunctionsTestUtility.checkTargetFunctions(provider2, four, three, four, five);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, five, three, four, five); CompareFunctionsTestUtility.checkTargetFunctions(provider2, five, three, four, five);
} }
@Test @Test
@ -147,8 +140,8 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(foo, two); Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(foo, two);
Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(bar, three, four); Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(bar, three, four);
provider = plugin.compareFunctions(functions1); provider = compare(functions1);
plugin.compareFunctions(functions2, provider); compare(functions2, provider);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, two, bar, three, four); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, two, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, two, bar, three, four); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, two, bar, three, four);
@ -160,47 +153,35 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
four); four);
} }
/**
*
* Tests for {@link FunctionComparisonService#compareFunctions(Set, FunctionComparisonProvider)}
*
*/
@Test @Test
public void testSetAddToSpecificProvider() throws Exception { public void testSetAddToSpecificProvider() throws Exception {
Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(foo, two); Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(foo, two);
Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(bar, three); Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(bar, three);
Set<Function> functions3 = CompareFunctionsTestUtility.getFunctionsAsSet(four); Set<Function> functions3 = CompareFunctionsTestUtility.getFunctionsAsSet(four);
provider = plugin.compareFunctions(functions1); provider = compare(functions1);
provider0 = plugin.compareFunctions(functions2); provider2 = compare(functions2);
plugin.compareFunctions(functions3, provider0); compare(functions3, provider2);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, two); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, two);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, bar, three, four); CompareFunctionsTestUtility.checkSourceFunctions(provider2, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, two); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider, two, foo, two); CompareFunctionsTestUtility.checkTargetFunctions(provider, two, foo, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, bar, bar, three, four); CompareFunctionsTestUtility.checkTargetFunctions(provider2, bar, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, three, bar, three, four); CompareFunctionsTestUtility.checkTargetFunctions(provider2, three, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, four, bar, three, four); CompareFunctionsTestUtility.checkTargetFunctions(provider2, four, bar, three, four);
} }
/**
*
* Tests for {@link FunctionComparisonService#removeFunction(Function)}
*
*/
@Test @Test
public void testRemoveFunction() throws Exception { public void testRemoveFunction() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions); provider = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
plugin.removeFunction(foo); remove(foo);
CompareFunctionsTestUtility.checkSourceFunctions(provider, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, bar);
@ -209,15 +190,17 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testRemoveFunctionTargetOnly() throws Exception { public void testRemoveFunctionTargetOnly() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions); provider = compare(functions);
plugin.compareFunctions(foo, two, provider); // add a target to foo, which is not also a source
// add a target to foo, which is not also a source
runSwing(() -> plugin.compareFunctions(foo, two, provider));
// Verify the structure with the new target // Verify the structure with the new target
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar, two); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
plugin.removeFunction(two); remove(two);
// Verify the new target is gone // Verify the new target is gone
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
@ -228,85 +211,67 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testRemoveFunctionMultipleProviders() throws Exception { public void testRemoveFunctionMultipleProviders() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions); provider = compare(functions);
provider0 = plugin.compareFunctions(functions); provider2 = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider2, foo, bar);
plugin.removeFunction(foo); remove(foo);
CompareFunctionsTestUtility.checkSourceFunctions(provider, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider2, bar);
} }
@Test @Test
public void testRemoveNonexistentFunction() throws Exception { public void testRemoveNonexistentFunction() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions); provider = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
plugin.removeFunction(two); // nothing should happen remove(two); // nothing should happen
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
} }
/**
*
* Tests for {@link FunctionComparisonService#removeFunction(Function, FunctionComparisonProvider)}
*
*/
@Test @Test
public void testRemoveFunctionFromSpecificProvider() throws Exception { public void testRemoveFunctionFromSpecificProvider() throws Exception {
Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar); Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions1); provider = compare(functions);
provider0 = plugin.compareFunctions(functions1); provider2 = compare(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider2, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, foo, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider2, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, bar, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider2, bar, foo, bar);
plugin.removeFunction(foo, provider); remove(foo, provider);
CompareFunctionsTestUtility.checkSourceFunctions(provider, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, foo, bar); CompareFunctionsTestUtility.checkSourceFunctions(provider2, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, foo, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider2, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, bar, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider2, bar, foo, bar);
} }
/**
*
* Tests for {@link FunctionComparisonService#compareFunctions(Function, Function)}
*
*/
@Test @Test
public void testDualCompare() { public void testDualCompare() {
provider = plugin.compareFunctions(foo, bar); provider = compare(foo, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, bar); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, bar);
} }
/**
*
* Tests for {@link FunctionComparisonService#compareFunctions(Function, Function, FunctionComparisonProvider)}
*
*/
@Test @Test
public void testDualCompareAddToExisting() { public void testDualCompareAddToExisting() {
provider = plugin.compareFunctions(foo, bar); provider = compare(foo, bar);
plugin.compareFunctions(foo, two, provider); runSwing(() -> plugin.compareFunctions(foo, two, provider));
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo); CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, bar, two); CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, bar, two);
@ -321,7 +286,7 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testGetTargets() { public void testGetTargets() {
Set<Function> targets = model.getTargetFunctions(); Set<Function> targets = model.getTargetFunctions();
assertTrue(targets.size() == 6); assertEquals(6, targets.size());
assertTrue(targets.contains(bar)); assertTrue(targets.contains(bar));
assertTrue(targets.contains(two)); assertTrue(targets.contains(two));
assertTrue(targets.contains(three)); assertTrue(targets.contains(three));
@ -333,7 +298,7 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void testGetTargetsForSource() { public void testGetTargetsForSource() {
Set<Function> targets = model.getTargetFunctions(bar); Set<Function> targets = model.getTargetFunctions(bar);
assertTrue(targets.size() == 3); assertEquals(3, targets.size());
assertTrue(targets.contains(three)); assertTrue(targets.contains(three));
assertTrue(targets.contains(four)); assertTrue(targets.contains(four));
assertTrue(targets.contains(five)); assertTrue(targets.contains(five));
@ -342,7 +307,7 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
@Test @Test
public void getSources() { public void getSources() {
Set<Function> sources = model.getSourceFunctions(); Set<Function> sources = model.getSourceFunctions();
assertTrue(sources.size() == 3); assertEquals(3, sources.size());
assertTrue(sources.contains(foo)); assertTrue(sources.contains(foo));
assertTrue(sources.contains(bar)); assertTrue(sources.contains(bar));
assertTrue(sources.contains(junk)); assertTrue(sources.contains(junk));
@ -353,19 +318,39 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
model.removeFunction(bar); model.removeFunction(bar);
Set<Function> sources = model.getSourceFunctions(); Set<Function> sources = model.getSourceFunctions();
assertTrue(sources.size() == 2); assertEquals(2, sources.size());
assertTrue(sources.contains(foo)); assertTrue(sources.contains(foo));
assertTrue(sources.contains(junk)); assertTrue(sources.contains(junk));
Set<Function> targets = model.getTargetFunctions(foo); Set<Function> targets = model.getTargetFunctions(foo);
assertTrue(targets.size() == 1); assertEquals(1, targets.size());
assertTrue(targets.contains(two)); assertTrue(targets.contains(two));
targets = model.getTargetFunctions(junk); targets = model.getTargetFunctions(junk);
assertTrue(targets.size() == 1); assertEquals(1, targets.size());
assertTrue(targets.contains(stuff)); assertTrue(targets.contains(stuff));
} }
private void remove(Function f) {
runSwing(() -> plugin.removeFunction(f));
}
private void remove(Function f, FunctionComparisonProvider fp) {
runSwing(() -> plugin.removeFunction(f, fp));
}
private void compare(Set<Function> functions, FunctionComparisonProvider fp) {
runSwing(() -> plugin.compareFunctions(functions, fp));
}
private FunctionComparisonProvider compare(Set<Function> functions) {
return runSwing(() -> plugin.compareFunctions(functions));
}
private FunctionComparisonProvider compare(Function f1, Function f2) {
return runSwing(() -> plugin.compareFunctions(f1, f2));
}
private ProgramBuilder buildTestProgram1() throws Exception { private ProgramBuilder buildTestProgram1() throws Exception {
ProgramBuilder builder = new ProgramBuilder("TestPgm1", ProgramBuilder._TOY_BE); ProgramBuilder builder = new ProgramBuilder("TestPgm1", ProgramBuilder._TOY_BE);
builder.createMemory(".text", "0x1001000", 0x6600); builder.createMemory(".text", "0x1001000", 0x6600);
@ -404,26 +389,26 @@ public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
} }
private FunctionComparisonModel createTestModel() { private FunctionComparisonModel createTestModel() {
FunctionComparisonModel model = new FunctionComparisonModel(); FunctionComparisonModel newModel = new FunctionComparisonModel();
FunctionComparison c1 = new FunctionComparison(); FunctionComparison c1 = new FunctionComparison();
c1.setSource(foo); c1.setSource(foo);
c1.addTarget(bar); c1.addTarget(bar);
c1.addTarget(two); c1.addTarget(two);
model.addComparison(c1); newModel.addComparison(c1);
FunctionComparison c2 = new FunctionComparison(); FunctionComparison c2 = new FunctionComparison();
c2.setSource(bar); c2.setSource(bar);
c2.addTarget(three); c2.addTarget(three);
c2.addTarget(four); c2.addTarget(four);
c2.addTarget(five); c2.addTarget(five);
model.addComparison(c2); newModel.addComparison(c2);
FunctionComparison c3 = new FunctionComparison(); FunctionComparison c3 = new FunctionComparison();
c3.setSource(junk); c3.setSource(junk);
c3.addTarget(stuff); c3.addTarget(stuff);
model.addComparison(c3); newModel.addComparison(c3);
return model; return newModel;
} }
} }

View file

@ -228,6 +228,18 @@ public class DecompilerNavigationTest extends AbstractDecompilerTest {
"The Listing is not at the expected address"); "The Listing is not at the expected address");
} }
@Override
public void assertCurrentAddress(Address expected) {
codeBrowser.updateNow();
waitForSwing();
waitForCondition(() -> {
ProgramLocation loc = codeBrowser.getCurrentLocation();
Address actual = loc.getAddress();
return expected.equals(actual);
}, "Listing is not at the expected address");
}
private void assertExternalNavigationPerformed() { private void assertExternalNavigationPerformed() {
// going to the 'external linkage' means we went to the thunk function and not the // going to the 'external linkage' means we went to the thunk function and not the
// external program // external program

View file

@ -63,6 +63,16 @@ public abstract class DecompilerVariable {
return dataType; return dataType;
} }
// The parent variable declaration node has the type
ClangNode parent = variable.Parent();
if (parent instanceof ClangVariableDecl) {
ClangVariableDecl decl = (ClangVariableDecl) parent;
dataType = decl.getDataType();
if (dataType != null) {
return dataType;
}
}
// Prefer the type of the first input varnode, unless that type is a 'void *'. // Prefer the type of the first input varnode, unless that type is a 'void *'.
// Usually, in that special case, the output varnode has the correct type information. // Usually, in that special case, the output varnode has the correct type information.
PcodeOp op = variable.getPcodeOp(); PcodeOp op = variable.getPcodeOp();

View file

@ -328,7 +328,7 @@ class VTFunctionAssociationTableModel extends AddressBasedTableModel<VTFunctionR
} }
private boolean hasNoFilter() { private boolean hasNoFilter() {
return filterSettings == FilterSettings.SHOW_ALL && !hasFitler(); return filterSettings == FilterSettings.SHOW_ALL && !hasFilter();
} }
private boolean passesUnmatchedFunctionFilter(FunctionAssociationInfo info) { private boolean passesUnmatchedFunctionFilter(FunctionAssociationInfo info) {

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +15,8 @@
*/ */
package ghidra.feature.vt.gui.provider.matchtable; package ghidra.feature.vt.gui.provider.matchtable;
import javax.swing.*;
import ghidra.feature.vt.api.main.VTMatch; import ghidra.feature.vt.api.main.VTMatch;
import ghidra.feature.vt.gui.filters.*; import ghidra.feature.vt.gui.filters.*;
import ghidra.feature.vt.gui.plugin.VTController; import ghidra.feature.vt.gui.plugin.VTController;
@ -23,8 +24,6 @@ import ghidra.util.HelpLocation;
import ghidra.util.layout.VariableRowHeightGridLayout; import ghidra.util.layout.VariableRowHeightGridLayout;
import ghidra.util.layout.VerticalLayout; import ghidra.util.layout.VerticalLayout;
import javax.swing.*;
public class MatchesFilterDialogComponentProvider extends public class MatchesFilterDialogComponentProvider extends
AncillaryFilterDialogComponentProvider<VTMatch> { AncillaryFilterDialogComponentProvider<VTMatch> {
@ -58,9 +57,9 @@ public class MatchesFilterDialogComponentProvider extends
// Row 1 - Right Component // Row 1 - Right Component
// association status filter // association status filter
AssociationStatusFilter associationStatusFitler = new AssociationStatusFilter(); AssociationStatusFilter associationStatusFilter = new AssociationStatusFilter();
addFilter(associationStatusFitler); addFilter(associationStatusFilter);
rowOnePanel.add(associationStatusFitler.getComponent()); rowOnePanel.add(associationStatusFilter.getComponent());
// Row 2 - Left Component // Row 2 - Left Component
// symbol type filter // symbol type filter

View file

@ -15,12 +15,77 @@
*/ */
package db.buffers; package db.buffers;
import java.io.IOException;
import java.rmi.Remote; import java.rmi.Remote;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.NoSuchElementException;
/** /**
* <code>RemoteBufferFileHandle</code> facilitates access to a remote BufferFile * <code>RemoteBufferFileHandle</code> facilitates access to a remote BufferFile
* via RMI. * via RMI.
* <p>
* Methods from {@link BufferFileHandle} <b>must</b> be re-declared here
* so they may be properly marshalled for remote invocation via RMI.
* This became neccessary with an OpenJDK 11.0.6 change made to
* {@link RemoteObjectInvocationHandler}.
*/ */
public interface RemoteBufferFileHandle extends BufferFileHandle, Remote { public interface RemoteBufferFileHandle extends BufferFileHandle, Remote {
// provides combined interface @Override
public boolean isReadOnly() throws IOException;
@Override
public boolean setReadOnly() throws IOException;
@Override
public int getParameter(String name) throws NoSuchElementException, IOException;
@Override
public void setParameter(String name, int value) throws IOException;
@Override
public void clearParameters() throws IOException;
@Override
public String[] getParameterNames() throws IOException;
@Override
public int getBufferSize() throws IOException;
@Override
public int getIndexCount() throws IOException;
@Override
public int[] getFreeIndexes() throws IOException;
@Override
public void setFreeIndexes(int[] indexes) throws IOException;
@Override
public void close() throws IOException;
@Override
public boolean delete() throws IOException;
@Override
public DataBuffer get(int index) throws IOException;
@Override
public void put(DataBuffer buf, int index) throws IOException;
@Override
public void dispose() throws IOException;
@Override
public InputBlockStream getInputBlockStream() throws IOException;
@Override
public OutputBlockStream getOutputBlockStream(int blockCount) throws IOException;
@Override
public BlockStreamHandle<InputBlockStream> getInputBlockStreamHandle() throws IOException;
@Override
public BlockStreamHandle<OutputBlockStream> getOutputBlockStreamHandle(int blockCount)
throws IOException;
} }

View file

@ -15,12 +15,116 @@
*/ */
package db.buffers; package db.buffers;
import java.io.IOException;
import java.rmi.Remote; import java.rmi.Remote;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.NoSuchElementException;
/** /**
* <code>RemoteManagedBufferFileHandle</code> facilitates access to a ManagedBufferFile * <code>RemoteManagedBufferFileHandle</code> facilitates access to a ManagedBufferFile
* via RMI. * via RMI.
* <p>
* Methods from {@link BufferFileHandle} and {@link ManagedBufferFile} <b>must</b>
* be re-declared here so they may be properly marshalled for remote invocation via RMI.
* This became neccessary with an OpenJDK 11.0.6 change made to
* {@link RemoteObjectInvocationHandler}.
*/ */
public interface RemoteManagedBufferFileHandle extends ManagedBufferFileHandle, Remote { public interface RemoteManagedBufferFileHandle extends ManagedBufferFileHandle, Remote {
// provides combined interface
//--------------------------------------------------------------------------
// BufferFileHandle methods
//--------------------------------------------------------------------------
@Override
public boolean isReadOnly() throws IOException;
@Override
public boolean setReadOnly() throws IOException;
@Override
public int getParameter(String name) throws NoSuchElementException, IOException;
@Override
public void setParameter(String name, int value) throws IOException;
@Override
public void clearParameters() throws IOException;
@Override
public String[] getParameterNames() throws IOException;
@Override
public int getBufferSize() throws IOException;
@Override
public int getIndexCount() throws IOException;
@Override
public int[] getFreeIndexes() throws IOException;
@Override
public void setFreeIndexes(int[] indexes) throws IOException;
@Override
public void close() throws IOException;
@Override
public boolean delete() throws IOException;
@Override
public DataBuffer get(int index) throws IOException;
@Override
public void put(DataBuffer buf, int index) throws IOException;
@Override
public void dispose() throws IOException;
@Override
public InputBlockStream getInputBlockStream() throws IOException;
@Override
public OutputBlockStream getOutputBlockStream(int blockCount) throws IOException;
@Override
public BlockStreamHandle<InputBlockStream> getInputBlockStreamHandle() throws IOException;
@Override
public BlockStreamHandle<OutputBlockStream> getOutputBlockStreamHandle(int blockCount)
throws IOException;
//--------------------------------------------------------------------------
// ManagedBufferFileHandle methods
//--------------------------------------------------------------------------
@Override
public ManagedBufferFileHandle getSaveFile() throws IOException;
@Override
public void saveCompleted(boolean commit) throws IOException;
@Override
public boolean canSave() throws IOException;
@Override
public void setVersionComment(String comment) throws IOException;
@Override
public BufferFileHandle getNextChangeDataFile(boolean getFirst) throws IOException;
@Override
public BufferFileHandle getSaveChangeDataFile() throws IOException;
@Override
public long getCheckinID() throws IOException;
@Override
public byte[] getForwardModMapData(int oldVersion) throws IOException;
@Override
public InputBlockStream getInputBlockStream(byte[] changeMapData) throws IOException;
@Override
public BlockStreamHandle<InputBlockStream> getInputBlockStreamHandle(byte[] changeMapData)
throws IOException;
} }

View file

@ -1854,20 +1854,16 @@ public class DockingWindowManager implements PropertyChangeListener, Placeholder
// as message dialogs will too be closed // as message dialogs will too be closed
DockingDialog d = (DockingDialog) activeWindow; DockingDialog d = (DockingDialog) activeWindow;
Window ancestor = SwingUtilities.getWindowAncestor(d); Window ancestor = SwingUtilities.getWindowAncestor(d);
if (!d.isShowing()) { if (d.isShowing() && isNonTransientWindow(d)) {
if (!ancestor.isShowing()) { return d;
return null; }
}
// The active window is not a suitable parent; try its parent
if (ancestor.isShowing() && isNonTransientWindow(ancestor)) {
return ancestor; return ancestor;
} }
DialogComponentProvider provider = d.getComponent(); return null;
if (provider.isTransient()) {
return ancestor;
}
return d;
} }
public ComponentProvider getActiveComponentProvider() { public ComponentProvider getActiveComponentProvider() {

View file

@ -41,8 +41,15 @@ import resources.ResourceManager;
* *
* @param <T> The type of DockingAction to build * @param <T> The type of DockingAction to build
* @param <B> the Type of action builder * @param <B> the Type of action builder
* @param <C> The type of ActionContext. By default, the ActionContext type always starts as
* the base ActionContext class. If the client calls the {@link #withContext(Class)} method on
* the builder, then that class (which must be a subclass of ActionContext) becomes the ActionContext
* type that will be used for future calls to the builder methods that take predicates with
* ActionContext (i.e. {@link #enabledWhen(Predicate)} and {@link #validContextWhen(Predicate)}.
* This works by substituting a builder with a different ActionContext type when chaining after
* the {@link #withContext(Class)} call.
*/ */
public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends AbstractActionBuilder<T, B>> { public abstract class AbstractActionBuilder<T extends DockingActionIf, C extends ActionContext, B extends AbstractActionBuilder<T, C, B>> {
/** /**
* Name for the {@code DockingAction} * Name for the {@code DockingAction}
@ -54,6 +61,11 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
*/ */
protected String owner; protected String owner;
/**
* Specifies the type of ActionContext that the built action works on.
*/
protected Class<? extends ActionContext> actionContextClass;
/** /**
* The {@code KeyBindingType} for this {@code DockingAction} * The {@code KeyBindingType} for this {@code DockingAction}
*/ */
@ -62,7 +74,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
/** /**
* The callback to perform when the action is invoked * The callback to perform when the action is invoked
*/ */
protected Consumer<ActionContext> actionCallback; protected Consumer<C> actionCallback;
/** /**
* Description for the {@code DockingAction}. (optional) * Description for the {@code DockingAction}. (optional)
@ -147,17 +159,17 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
/** /**
* Predicate for determining if an action is enabled for a given context * Predicate for determining if an action is enabled for a given context
*/ */
private Predicate<ActionContext> enabledPredicate; private Predicate<C> enabledPredicate;
/** /**
* Predicate for determining if an action should be included on the pop-up menu * Predicate for determining if an action should be included on the pop-up menu
*/ */
private Predicate<ActionContext> popupPredicate; private Predicate<C> popupPredicate;
/** /**
* Predicate for determining if an action is applicable for a given context * Predicate for determining if an action is applicable for a given context
*/ */
private Predicate<ActionContext> validContextPredicate; private Predicate<C> validContextPredicate;
/** /**
* Builder constructor * Builder constructor
@ -167,6 +179,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
public AbstractActionBuilder(String name, String owner) { public AbstractActionBuilder(String name, String owner) {
this.name = name; this.name = name;
this.owner = owner; this.owner = owner;
this.actionContextClass = ActionContext.class;
} }
/** /**
@ -370,7 +383,8 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
* @see #popupMenuGroup(String) * @see #popupMenuGroup(String)
*/ */
public B popupMenuGroup(String group, String subGroup) { public B popupMenuGroup(String group, String subGroup) {
popupSubGroup = group; popupGroup = group;
popupSubGroup = subGroup;
return self(); return self();
} }
@ -446,7 +460,8 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
* @see #toolBarGroup(String) * @see #toolBarGroup(String)
*/ */
public B toolBarGroup(String group, String subGroup) { public B toolBarGroup(String group, String subGroup) {
toolBarSubGroup = group; toolBarGroup = group;
toolBarSubGroup = subGroup;
return self(); return self();
} }
@ -484,7 +499,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
* @param action the callback to execute when the action is invoked * @param action the callback to execute when the action is invoked
* @return this builder (for chaining) * @return this builder (for chaining)
*/ */
public B onAction(Consumer<ActionContext> action) { public B onAction(Consumer<C> action) {
actionCallback = action; actionCallback = action;
return self(); return self();
} }
@ -501,7 +516,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
* enabled state * enabled state
* @return this builder (for chaining) * @return this builder (for chaining)
*/ */
public B enabledWhen(Predicate<ActionContext> predicate) { public B enabledWhen(Predicate<C> predicate) {
enabledPredicate = predicate; enabledPredicate = predicate;
return self(); return self();
} }
@ -524,7 +539,7 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
* @return this builder (for chaining) * @return this builder (for chaining)
* @see #popupMenuPath(String...) * @see #popupMenuPath(String...)
*/ */
public B popupWhen(Predicate<ActionContext> predicate) { public B popupWhen(Predicate<C> predicate) {
popupPredicate = predicate; popupPredicate = predicate;
return self(); return self();
} }
@ -540,11 +555,68 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
* validity for a given {@link ActionContext} * validity for a given {@link ActionContext}
* @return this builder (for chaining) * @return this builder (for chaining)
*/ */
public B validContextWhen(Predicate<ActionContext> predicate) { public B validContextWhen(Predicate<C> predicate) {
validContextPredicate = predicate; validContextPredicate = predicate;
return self(); return self();
} }
/**
* Sets the specific ActionContext type to use for the various predicate calls
* ({@link #validContextWhen(Predicate)}, {@link #enabledWhen(Predicate)}, and
* {@link #popupWhen(Predicate)}).
* <P>
* In other words, this allows the client to specify the type of ActionContext that is valid for
* the action being built.
* <P>
* To be effective, this method must be called <b>before</b> setting any of the predicates
* such as the {@link #enabledWhen(Predicate)}. Once this method is called you can define your
* predicates using the more specific ActionContext and be assured your predicates will only
* be called when the current action context is the type (or sub-type) of the context you have
* specified here.
* <P>
* For example, assume you have an action that is only enabled when the context is of type
* FooActionContext. If you don't call this method to set the ActionContext type, you would have
* to write your predicate something like this:
* <pre>
* builder.enabledWhen(context -> {
* if (!(context instanceof FooContext)) {
* return false;
* }
* return ((FooContext) context).isAwesome();
* });
* </pre>
* But by first calling the builder method <CODE>withContext(FooContext.class)</CODE>, you can
* simply write:
*
* <pre>
* builder.enabledWhen(context -> return context.isAwesome() }
* </pre>
*
* @param newActionContextClass the more specific ActionContext type.
* @param <AC2> The new ActionContext type (as determined by the newActionContextClass) that
* the returned builder will have.
* @param <B2> the new builder type.
* @return an ActionBuilder whose generic types have been modified to match the new ActionContext.
* It still contains all the configuration that has been applied so far.
*/
@SuppressWarnings("unchecked")
public <AC2 extends ActionContext, B2 extends AbstractActionBuilder<T, AC2, B2>> B2 withContext(
Class<AC2> newActionContextClass) {
// To make this work, we need to return a builder whose ActionContext is AC2 and not AC
// (which is what this builder is now)
//
// Since we "know" that the only thing that matters regarding the ActionContext type is that
// the template type (AC) must match the type of actionContextClass instance variable, we
// can get away with returning this same builder and casting it to be a builder with type
// AC2 instead of AC. We can do this since we set the actionContextClass below
actionContextClass = newActionContextClass;
B2 newSelf = (B2) self();
return newSelf;
}
protected void validate() { protected void validate() {
if (actionCallback == null) { if (actionCallback == null) {
throw new IllegalStateException( throw new IllegalStateException(
@ -566,16 +638,36 @@ public abstract class AbstractActionBuilder<T extends DockingActionIf, B extends
} }
if (enabledPredicate != null) { if (enabledPredicate != null) {
action.enabledWhen(enabledPredicate); action.enabledWhen(adaptPredicate(enabledPredicate));
} }
if (validContextPredicate != null) { if (validContextPredicate != null) {
action.validContextWhen(validContextPredicate); action.validContextWhen(adaptPredicate(validContextPredicate));
} }
if (popupPredicate != null) { if (popupPredicate != null) {
action.popupWhen(enabledPredicate); action.popupWhen(adaptPredicate(popupPredicate));
} }
} }
/**
* Since the built action will need a predicate that handles any action type, this method
* creates a predicate that adapts a user supplied predicate for a more specific ActionContext
* to a general predicate that can accept any ActionContext.
* @param predicate the client supplied predicate that expects a more specific ActionContext
* @return a predicate that can handle any ActionContext
*/
@SuppressWarnings("unchecked")
private Predicate<ActionContext> adaptPredicate(Predicate<C> predicate) {
if (actionContextClass == ActionContext.class) {
// don't wrap the predicate if it doesn't need it
return (Predicate<ActionContext>) predicate;
}
// Convert a sub-classed ActionContext predicate to a plain ActionContext predicate
Predicate<ActionContext> predicateAdapter = (ac) -> {
return actionContextClass.isInstance(ac) && predicate.test((C) ac);
};
return predicateAdapter;
}
protected boolean isPopupAction() { protected boolean isPopupAction() {
return popupPath != null; return popupPath != null;
} }

View file

@ -21,7 +21,7 @@ import docking.action.DockingAction;
* Builder for {@link DockingAction}s * Builder for {@link DockingAction}s
*/ */
public class ActionBuilder public class ActionBuilder
extends AbstractActionBuilder<DockingAction, ActionBuilder> { extends AbstractActionBuilder<DockingAction, ActionContext, ActionBuilder> {
/** /**
* Builder constructor * Builder constructor

View file

@ -26,7 +26,7 @@ import docking.menu.MultiActionDockingAction;
* Builder for {@link MultiActionDockingAction} * Builder for {@link MultiActionDockingAction}
*/ */
public class MultiActionBuilder public class MultiActionBuilder
extends AbstractActionBuilder<MultiActionDockingAction, MultiActionBuilder> { extends AbstractActionBuilder<MultiActionDockingAction, ActionContext, MultiActionBuilder> {
/** /**
* List of actions for the the MultActionDockingAction * List of actions for the the MultActionDockingAction
*/ */

View file

@ -27,7 +27,7 @@ import docking.widgets.EventTrigger;
* @param <T> The action state type * @param <T> The action state type
*/ */
public class MultiStateActionBuilder<T> extends public class MultiStateActionBuilder<T> extends
AbstractActionBuilder<MultiStateDockingAction<T>, MultiStateActionBuilder<T>> { AbstractActionBuilder<MultiStateDockingAction<T>, ActionContext, MultiStateActionBuilder<T>> {
private BiConsumer<ActionState<T>, EventTrigger> actionStateChangedCallback; private BiConsumer<ActionState<T>, EventTrigger> actionStateChangedCallback;
private boolean performActionOnButtonClick; private boolean performActionOnButtonClick;
@ -77,7 +77,7 @@ public class MultiStateActionBuilder<T> extends
public MultiStateDockingAction<T> build() { public MultiStateDockingAction<T> build() {
validate(); validate();
MultiStateDockingAction<T> action = MultiStateDockingAction<T> action =
new MultiStateDockingAction<T>(name, owner, isToolbarAction()) { new MultiStateDockingAction<>(name, owner, isToolbarAction()) {
@Override @Override
public void actionStateChanged(ActionState<T> newActionState, public void actionStateChanged(ActionState<T> newActionState,

View file

@ -21,7 +21,7 @@ import docking.action.ToggleDockingAction;
* Builder for {@link ToggleDockingAction}s * Builder for {@link ToggleDockingAction}s
*/ */
public class ToggleActionBuilder extends public class ToggleActionBuilder extends
AbstractActionBuilder<ToggleDockingAction, ToggleActionBuilder> { AbstractActionBuilder<ToggleDockingAction, ActionContext, ToggleActionBuilder> {
/** /**
* The initial toggle state for the action * The initial toggle state for the action

View file

@ -190,10 +190,23 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
return pendingSortState; return pendingSortState;
} }
/**
* Returns true if there is a pending change to the current sort state
* (this includes a sort state that signals no sort will be applied)
*
* @return true if there is a pending change to the current sort state
*/
public boolean isSortPending() { public boolean isSortPending() {
return isSortPending; return isSortPending;
} }
/**
* Returns true if this model has been sorted and does not have a new pending sort that will
* be applied
*
* @return true if sorted
* @see #isSortPending()
*/
public boolean isSorted() { public boolean isSorted() {
return !isSortPending && !sortState.isUnsorted(); return !isSortPending && !sortState.isUnsorted();
} }

View file

@ -116,7 +116,7 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
} }
ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode(); ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode();
return mode == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY; return mode == ColumnConstraintFilterMode.ALLOW_CONSTRAINTS_FILTER_ONLY;
} }
private String getRenderedColumnValue(Object columnValue, int columnIndex) { private String getRenderedColumnValue(Object columnValue, int columnIndex) {

View file

@ -196,7 +196,7 @@ public class GTableHeader extends JTableHeader {
int realIndex = gTable.convertColumnIndexToModel(columnIndex); int realIndex = gTable.convertColumnIndexToModel(columnIndex);
String columnFilterToolTip = getColumnFilterToolTip(model, columnIndex); String columnFilterToolTip = getColumnFilterToolTip(model, realIndex);
VariableColumnTableModel variableModel = VariableColumnTableModel.from(model); VariableColumnTableModel variableModel = VariableColumnTableModel.from(model);
if (variableModel != null) { if (variableModel != null) {
String description = variableModel.getColumnDescription(realIndex); String description = variableModel.getColumnDescription(realIndex);

View file

@ -22,11 +22,11 @@ import ghidra.util.SystemUtilities;
/** /**
* Class that maps one type of column constraint into another. Typically, these are created * Class that maps one type of column constraint into another. Typically, these are created
* automatically based on {@link ColumnTypeMapper} that are discovered by the system. For example, * automatically based on {@link ColumnTypeMapper} that are discovered by the system. For example,
*{@literal if you have a column type of "Foo", and you create a ColumnTypeMapper<Foo, String>, then all the} * {@literal if you have a column type of "Foo", and you create a ColumnTypeMapper<Foo, String>,
* string constraints would now be available that column. * then all the} string constraints would now be available that column.
* *
* @param <T> The column type. * @param <T> The column type
* @param <M> the converted (mapped) type. * @param <M> the converted (mapped) type
*/ */
public class MappedColumnConstraint<T, M> implements ColumnConstraint<T> { public class MappedColumnConstraint<T, M> implements ColumnConstraint<T> {

View file

@ -27,10 +27,14 @@ import ghidra.util.table.column.GColumnRenderer;
import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode; import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
/** /**
* Class for maintaining information about a table's column for the purpose of configuring filters * This class provides all known {@link ColumnConstraint}s for a given table column.
* based on that columns values. These are generated by examining a table's column types and finding *
* out if there are any ColumnConstraints that support that type. If so, a ColumnFilterData is * <P>Class for maintaining information about a particular table's column for the purpose of
* created for that column which then allows filtering on that columns data. * configuring filters based on that column's values. Instances of this class are generated
* by examining a table's column types and finding any {@link ColumnConstraint}s that support
* that type. If column constraints are found, a {@link ColumnFilterData} is created for that column
* which then allows filtering on that columns data via the column constraints mechanism (which
* is different than the traditional text filter).
* *
* @param <T> the column type. * @param <T> the column type.
*/ */
@ -61,12 +65,20 @@ public class ColumnFilterData<T> implements Comparable<ColumnFilterData<T>> {
private List<ColumnConstraint<T>> initializeConstraints(RowObjectFilterModel<?> model, private List<ColumnConstraint<T>> initializeConstraints(RowObjectFilterModel<?> model,
Class<T> columnClass) { Class<T> columnClass) {
//
// Case 1: the column is not dynamic and thus has no way of overriding the column
// constraint filtering mechanism.
//
Collection<ColumnConstraint<T>> defaultConstraints = Collection<ColumnConstraint<T>> defaultConstraints =
DiscoverableTableUtils.getColumnConstraints(columnClass); DiscoverableTableUtils.getColumnConstraints(columnClass);
if (!(model instanceof DynamicColumnTableModel)) { if (!(model instanceof DynamicColumnTableModel)) {
return new ArrayList<>(defaultConstraints); return new ArrayList<>(defaultConstraints);
} }
//
// Case 2: the column is dynamic, but does not supply a specialized column renderer,
// which is the means for overriding the column constraint filtering mechanism.
//
DynamicColumnTableModel<?> columnBasedModel = (DynamicColumnTableModel<?>) model; DynamicColumnTableModel<?> columnBasedModel = (DynamicColumnTableModel<?>) model;
DynamicTableColumn<?, ?, ?> column = columnBasedModel.getColumn(modelIndex); DynamicTableColumn<?, ?, ?> column = columnBasedModel.getColumn(modelIndex);
GColumnRenderer<?> columnRenderer = column.getColumnRenderer(); GColumnRenderer<?> columnRenderer = column.getColumnRenderer();
@ -74,24 +86,36 @@ public class ColumnFilterData<T> implements Comparable<ColumnFilterData<T>> {
return new ArrayList<>(defaultConstraints); return new ArrayList<>(defaultConstraints);
} }
//
// Case 3: the column renderer has signaled that it uses only column constraint filtering
// and does not support the traditional text based filtering.
//
ColumnConstraintFilterMode mode = columnRenderer.getColumnConstraintFilterMode(); ColumnConstraintFilterMode mode = columnRenderer.getColumnConstraintFilterMode();
if (mode == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY) { if (mode == ColumnConstraintFilterMode.ALLOW_CONSTRAINTS_FILTER_ONLY) {
return new ArrayList<>(defaultConstraints); return new ArrayList<>(defaultConstraints);
} }
@SuppressWarnings("unchecked") //
// Case 4: the column supports text filtering. Find any column constraints for the
// column's type. Then, create string-based constraints that will filter on
// the column's conversion from its type to a string (via
// GColumnRenderer.getFilterString()).
//
@SuppressWarnings("unchecked") // See type note on the class below
GColumnRenderer<T> asT = (GColumnRenderer<T>) columnRenderer; GColumnRenderer<T> asT = (GColumnRenderer<T>) columnRenderer;
ColumnRendererMapper mapper = new ColumnRendererMapper(asT, columnBasedModel, modelIndex); ColumnRendererMapper mapper = new ColumnRendererMapper(asT, columnBasedModel, modelIndex);
Collection<ColumnConstraint<T>> rendererStringConstraints = Collection<ColumnConstraint<T>> rendererStringConstraints =
DiscoverableTableUtils.getColumnConstraints(mapper); DiscoverableTableUtils.getColumnConstraints(mapper);
if (mode == ColumnConstraintFilterMode.ALLOW_RENDERER_STRING_FILTER_ONLY) {
List<ColumnConstraint<T>> results = new ArrayList<>(rendererStringConstraints); return new ArrayList<>(rendererStringConstraints);
if (mode == ColumnConstraintFilterMode.USE_BOTH_COLUMN_RENDERER_FITLER_STRING_AND_CONSTRAINTS) {
// also use the normal constraints with the renderer constraints
results.addAll(defaultConstraints);
} }
//
// Case 5: the renderer supports both text filtering and column constraint filtering.
//
// assume: mode == ColumnConstraintFilterMode.ALLOW_ALL_FILTERS
List<ColumnConstraint<T>> results = new ArrayList<>(rendererStringConstraints);
results.addAll(defaultConstraints);
return results; return results;
} }
@ -213,6 +237,13 @@ public class ColumnFilterData<T> implements Comparable<ColumnFilterData<T>> {
* This class allows us to turn client columns of type <code>T</code> to a String. We use * This class allows us to turn client columns of type <code>T</code> to a String. We use
* the renderer provided at construction time to generate a filter string when * the renderer provided at construction time to generate a filter string when
* {@link #convert(Object)} is called. * {@link #convert(Object)} is called.
*
* <P>Implementation Note: the type 'T' here is used to satisfy the external client's
* expected list of constraints. We will not be able to identify 'T' at runtime. Rather,
* our parent's {@link #getSourceType()} will simply be {@link Object}. This is fine, as
* this particular class will not have {@link #getSourceType()} called, due to how we
* are using it. (Normally, the source type is used to find compatible constraints; we
* are not using the discovery mechanism with this private class.)
*/ */
private class ColumnRendererMapper extends ColumnTypeMapper<T, String> { private class ColumnRendererMapper extends ColumnTypeMapper<T, String> {
@ -229,14 +260,13 @@ public class ColumnFilterData<T> implements Comparable<ColumnFilterData<T>> {
@Override @Override
public String convert(T value) { public String convert(T value) {
Settings settings = model.getColumnSettings(columnModelIndex);
if (value == null) { if (value == null) {
return null; return null;
} }
Settings settings = model.getColumnSettings(columnModelIndex);
String s = renderer.getFilterString(value, settings); String s = renderer.getFilterString(value, settings);
return s; return s;
} }
} }
} }

View file

@ -102,9 +102,9 @@ public class ColumnFilterDialogModel<R> {
} }
/** /**
* Creates a new filter fow (a new major row in the dialog filter panel) * Creates a new filter row (a new major row in the dialog filter panel)
* @param logicOperation the logical operation for how this row interacts with the rows before it * @param logicOperation the logical operation for how this row interacts with preceding rows
* @return the new filter row that represents a major row in the dialog filter panel. * @return the new filter row that represents a major row in the dialog filter panel
*/ */
public DialogFilterRow createFilterRow(LogicOperation logicOperation) { public DialogFilterRow createFilterRow(LogicOperation logicOperation) {

View file

@ -15,12 +15,13 @@
*/ */
package docking.widgets.table.sort; package docking.widgets.table.sort;
import static ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode.*;
import java.util.Comparator; import java.util.Comparator;
import docking.widgets.table.*; import docking.widgets.table.*;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.Settings;
import ghidra.util.table.column.GColumnRenderer; import ghidra.util.table.column.GColumnRenderer;
import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
/** /**
* A special version of the backup comparator that uses the column's rendered value for * A special version of the backup comparator that uses the column's rendered value for
@ -47,7 +48,7 @@ public class ColumnRenderedValueBackupComparator<T> implements Comparator<Object
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer(); GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
if (renderer != null) { if (renderer != null) {
if (renderer.getColumnConstraintFilterMode() == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY) { if (renderer.getColumnConstraintFilterMode() == ALLOW_CONSTRAINTS_FILTER_ONLY) {
// this implies that the column has signaled that it does not support // this implies that the column has signaled that it does not support
// filtering/sorting using its rendered value // filtering/sorting using its rendered value
supportsColumnSorting = false; supportsColumnSorting = false;

View file

@ -74,9 +74,9 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
* This variable can be in one of three states: * This variable can be in one of three states:
* <ul> * <ul>
* <li>null - signals that there is no filter change taking place</li> * <li>null - signals that there is no filter change taking place</li>
* <li>An instance of <code>NullTableFitler</code> - the client has removed the current * <li>An instance of <code>NullTableFilter</code> - the client has removed the current
* filter by calling {@link #setTableFilter(TableFilter)} with a null value</li> * filter by calling {@link #setTableFilter(TableFilter)} with a null value</li>
* <li>An instance of a custom <code>TableFitler</code> - the client has changed the * <li>An instance of a custom <code>TableFilter</code> - the client has changed the
* filter to a non-null value by calling {@link #setTableFilter(TableFilter)}</li> * filter to a non-null value by calling {@link #setTableFilter(TableFilter)}</li>
* </ul> * </ul>
*/ */
@ -399,7 +399,7 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
* *
* @return true if there is a table filter set. * @return true if there is a table filter set.
*/ */
public boolean hasFitler() { public boolean hasFilter() {
TableFilter<ROW_OBJECT> currentFilter = getTableFilter(); TableFilter<ROW_OBJECT> currentFilter = getTableFilter();
return !currentFilter.isEmpty(); return !currentFilter.isEmpty();
} }
@ -429,7 +429,7 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
return data; return data;
} }
if (!hasFitler()) { if (!hasFilter()) {
return data; return data;
} }
@ -463,14 +463,14 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
} }
/** /**
* Sets the given <code>TableFitler</code> on this model. This table filter will then be used * Sets the given <code>TableFilter</code> on this model. This table filter will then be used
* by this model in the default {@link #doFilter(List, TableSortingContext, TaskMonitor)} * by this model in the default {@link #doFilter(List, TableSortingContext, TaskMonitor)}
* method. * method.
* @param tableFitler The filter to use for table filtering. * @param tableFilter The filter to use for table filtering.
*/ */
@Override @Override
public void setTableFilter(TableFilter<ROW_OBJECT> tableFitler) { public void setTableFilter(TableFilter<ROW_OBJECT> tableFilter) {
this.pendingTableFilter = tableFitler; this.pendingTableFilter = tableFilter;
if (pendingTableFilter == null) { if (pendingTableFilter == null) {
// Don't allow the pending filter to be null in this case. The client has changed // Don't allow the pending filter to be null in this case. The client has changed
// the filter. If we use null, then we don't know the difference between a client // the filter. If we use null, then we don't know the difference between a client
@ -480,8 +480,8 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
reFilter(); reFilter();
} }
private void setAppliedTableFitler(TableFilter<ROW_OBJECT> tableFitler) { private void setAppliedTableFilter(TableFilter<ROW_OBJECT> tableFilter) {
if (tableFitler == null) { if (tableFilter == null) {
// null means there was no change to the text filter--so don't set it (see the // null means there was no change to the text filter--so don't set it (see the
// javadoc for the filter variables) // javadoc for the filter variables)
return; return;
@ -532,7 +532,7 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
this.allData = allData; this.allData = allData;
this.filteredData = filteredData; this.filteredData = filteredData;
setAppliedTableFitler(pendingTableFilter); setAppliedTableFilter(pendingTableFilter);
pendingSortContext = null; pendingSortContext = null;
TableSortingContext<ROW_OBJECT> newSortingContext = filteredData.getSortContext(); TableSortingContext<ROW_OBJECT> newSortingContext = filteredData.getSortContext();

View file

@ -36,7 +36,7 @@ public interface AbstractWrapperTypeColumnRenderer<T> extends GColumnRenderer<T>
// Overridden to only allow the constraint filtering mechanism. // Overridden to only allow the constraint filtering mechanism.
@Override @Override
public default ColumnConstraintFilterMode getColumnConstraintFilterMode() { public default ColumnConstraintFilterMode getColumnConstraintFilterMode() {
return ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY; return ColumnConstraintFilterMode.ALLOW_CONSTRAINTS_FILTER_ONLY;
} }
@Override @Override

View file

@ -36,12 +36,12 @@ import ghidra.util.exception.AssertException;
* columns. The specifics of how the text filter works are defined by the * columns. The specifics of how the text filter works are defined by the
* {@link RowFilterTransformer}, which is controlled by the user via the button at the right * {@link RowFilterTransformer}, which is controlled by the user via the button at the right
* of the filter field. (In the absence of this button, filters are typically a 'contains' * of the filter field. (In the absence of this button, filters are typically a 'contains'
* filter. * filter.)
* *
* <P>The default transformer turns items to strings by, in order,: * <P>The default transformer turns items to strings by, in order,:
* <OL> * <OL>
* <LI>checking the the renderer's {@link #getFilterString(Object, Settings)}, * <LI>checking the the <b>column</b> renderer's
* if a renderer is installed * {@link #getFilterString(Object, Settings)},if a column renderer is installed
* </LI> * </LI>
* <LI>checking to see if the column value is an instance of {@link DisplayStringProvider} * <LI>checking to see if the column value is an instance of {@link DisplayStringProvider}
* </LI> * </LI>
@ -68,6 +68,10 @@ import ghidra.util.exception.AssertException;
* </LI> * </LI>
* </OL> * </OL>
* *
* <P><B>Note: The default filtering behavior of this class is to only filter on the aforementioned
* filter text field. That is, column constraints will not be enabled by default. To
* change this, change the value returned by {@link #getColumnConstraintFilterMode()}.</B>
*
* @param <T> the column type * @param <T> the column type
*/ */
public interface GColumnRenderer<T> extends TableCellRenderer { public interface GColumnRenderer<T> extends TableCellRenderer {
@ -79,21 +83,14 @@ public interface GColumnRenderer<T> extends TableCellRenderer {
public enum ColumnConstraintFilterMode { public enum ColumnConstraintFilterMode {
//@formatter:off //@formatter:off
/**
* Signals that the programmer didn't make a decision about how filtering for this
* column should work. This currently will treat all filtering as if
* {@link #USE_COLUMN_RENDERER_FITLER_STRING_ONLY} was chosen.
*/
DEFAULT,
/** Use only {@link GColumnRenderer#getFilterString(Object, Settings)} value; no constraints */ /** Use only {@link GColumnRenderer#getFilterString(Object, Settings)} value; no constraints */
USE_COLUMN_RENDERER_FITLER_STRING_ONLY, ALLOW_RENDERER_STRING_FILTER_ONLY,
/** Use only column constraints when filtering */ /** Use only column constraints when filtering */
USE_COLUMN_CONSTRAINTS_ONLY, ALLOW_CONSTRAINTS_FILTER_ONLY,
/** Use both the rendered filter String and any found column constraints */ /** Use both the rendered filter String and any found column constraints */
USE_BOTH_COLUMN_RENDERER_FITLER_STRING_AND_CONSTRAINTS, ALLOW_ALL_FILTERS,
//@formatter:on //@formatter:on
} }
@ -107,7 +104,7 @@ public interface GColumnRenderer<T> extends TableCellRenderer {
* @return the mode * @return the mode
*/ */
public default ColumnConstraintFilterMode getColumnConstraintFilterMode() { public default ColumnConstraintFilterMode getColumnConstraintFilterMode() {
return ColumnConstraintFilterMode.DEFAULT; return ColumnConstraintFilterMode.ALLOW_RENDERER_STRING_FILTER_ONLY;
} }
/** /**

View file

@ -1747,14 +1747,23 @@ public class GhidraFileChooserTest extends AbstractDockingTest {
public void testHistoryRestoresSelectedFiles() throws Exception { public void testHistoryRestoresSelectedFiles() throws Exception {
File startDir = createTempDir(); File startDir = createTempDir();
setDir(startDir); File subDir = createFileSubFile(startDir, 3);
createFileSubFile(startDir, 3); setDir(subDir);
// // debug
// DirectoryList list = getListView();
// ListSelectionModel sm = list.getSelectionModel();
// sm.addListSelectionListener(e -> {
// Msg.debug(this, "selection changed: " + e);
// });
pressUp(); pressUp();
selectFile(getListView(), 1); selectFile(getListView(), 1);
assertSelectedIndex(getListView(), 1);
pressUp(); pressUp();
selectFile(getListView(), 2); selectFile(getListView(), 2);
assertSelectedIndex(getListView(), 2);
pressBack(); pressBack();
assertSelectedIndex(getListView(), 1); assertSelectedIndex(getListView(), 1);
@ -1914,7 +1923,12 @@ public class GhidraFileChooserTest extends AbstractDockingTest {
private void assertSelectedIndex(DirectoryList list, int expected) { private void assertSelectedIndex(DirectoryList list, int expected) {
int actual = runSwing(() -> list.getSelectedIndex()); int actual = runSwing(() -> list.getSelectedIndex());
assertEquals("Wrong list index selected", expected, actual);
// debug code
if (expected != actual) {
waitForCondition(() -> expected == actual,
"Wrong list index selected ");
}
} }
private void assertSelectedIndex(GTable table, int expected) { private void assertSelectedIndex(GTable table, int expected) {
@ -1923,6 +1937,11 @@ public class GhidraFileChooserTest extends AbstractDockingTest {
} }
private File selectFile(DirectoryList list, int index) { private File selectFile(DirectoryList list, int index) {
// TODO debug - remove when all tests passing on server
int size = list.getModel().getSize();
Msg.debug(this, "selectFile() - new index: " + index + "; list size: " + size);
runSwing(() -> list.setSelectedIndex(index)); runSwing(() -> list.setSelectedIndex(index));
return runSwing(() -> list.getSelectedFile()); return runSwing(() -> list.getSelectedFile());
} }

View file

@ -0,0 +1,247 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.action;
import static org.junit.Assert.*;
import javax.swing.KeyStroke;
import org.junit.Test;
import docking.ActionContext;
import docking.action.builder.ActionBuilder;
import resources.Icons;
public class ActionBuilderTest {
private int actionCount = 0;
@Test
public void testDescription() {
DockingAction action = new ActionBuilder("Test", "Test")
.description("foo")
.onAction(e -> actionCount++)
.build();
assertEquals("foo", action.getDescription());
}
@Test
public void testMenuPath() {
DockingAction action = new ActionBuilder("Test", "Test")
.menuPath("foo", "bar")
.onAction(e -> actionCount++)
.build();
MenuData data = action.getMenuBarData();
assertEquals("foo->bar", data.getMenuPathAsString());
}
@Test
public void testMenuGroup() {
DockingAction action = new ActionBuilder("Test", "Test")
.menuPath("foo", "bar")
.menuGroup("A", "B")
.onAction(e -> actionCount++)
.build();
MenuData data = action.getMenuBarData();
assertEquals("A", data.getMenuGroup());
assertEquals("B", data.getMenuSubGroup());
}
@Test
public void testMenuIcon() {
DockingAction action = new ActionBuilder("Test", "Test")
.menuPath("foo", "bar")
.menuIcon(Icons.ADD_ICON)
.onAction(e -> actionCount++)
.build();
MenuData data = action.getMenuBarData();
assertEquals(Icons.ADD_ICON, data.getMenuIcon());
}
@Test
public void testMenuMnemonic() {
DockingAction action = new ActionBuilder("Test", "Test")
.menuPath("foo", "bar")
.menuMnemonic(5)
.onAction(e -> actionCount++)
.build();
MenuData data = action.getMenuBarData();
assertEquals(5, data.getMnemonic());
}
@Test
public void testPopupPath() {
DockingAction action = new ActionBuilder("Test", "Test")
.popupMenuPath("foo", "bar")
.onAction(e -> actionCount++)
.build();
MenuData data = action.getPopupMenuData();
assertEquals("foo->bar", data.getMenuPathAsString());
}
@Test
public void testPopupGroup() {
DockingAction action = new ActionBuilder("Test", "Test")
.popupMenuPath("foo", "bar")
.popupMenuGroup("A", "B")
.onAction(e -> actionCount++)
.build();
MenuData data = action.getPopupMenuData();
assertEquals("A", data.getMenuGroup());
assertEquals("B", data.getMenuSubGroup());
}
@Test
public void testPopupIcon() {
DockingAction action = new ActionBuilder("Test", "Test")
.popupMenuPath("foo", "bar")
.popupMenuIcon(Icons.ADD_ICON)
.onAction(e -> actionCount++)
.build();
MenuData data = action.getPopupMenuData();
assertEquals(Icons.ADD_ICON, data.getMenuIcon());
}
@Test
public void testToolbarIcon() {
DockingAction action = new ActionBuilder("Test", "Test")
.toolBarIcon(Icons.ADD_ICON)
.onAction(e -> actionCount++)
.build();
ToolBarData data = action.getToolBarData();
assertEquals(Icons.ADD_ICON, data.getIcon());
}
@Test
public void testToolbarGroup() {
DockingAction action = new ActionBuilder("Test", "Test")
.toolBarIcon(Icons.ADD_ICON)
.toolBarGroup("A", "B")
.onAction(e -> actionCount++)
.build();
ToolBarData data = action.getToolBarData();
assertEquals("A", data.getToolBarGroup());
assertEquals("B", data.getToolBarSubGroup());
}
@Test
public void testKeyBindingKeyStroke() {
DockingAction action = new ActionBuilder("Test", "Test")
.keyBinding(KeyStroke.getKeyStroke("A"))
.onAction(e -> actionCount++)
.build();
assertEquals(KeyStroke.getKeyStroke("A"), action.getKeyBinding());
}
@Test
public void testKeyBindingKeyString() {
DockingAction action = new ActionBuilder("Test", "Test")
.keyBinding("ALT A")
.onAction(e -> actionCount++)
.build();
assertEquals(KeyStroke.getKeyStroke("ALT A"), action.getKeyBinding());
}
@Test
public void testOnAction() {
DockingAction action = new ActionBuilder("Test", "Test")
.onAction(e -> actionCount = 6)
.build();
assertEquals(0, actionCount);
action.actionPerformed(new ActionContext());
assertEquals(6, actionCount);
}
@Test
public void testEnabled() {
DockingAction action = new ActionBuilder("Test", "Test")
.enabled(true)
.onAction(e -> actionCount++)
.build();
assertTrue(action.isEnabled());
action = new ActionBuilder("Test", "Test")
.enabled(false)
.onAction(e -> actionCount++)
.build();
assertFalse(action.isEnabled());
}
@Test
public void testEnabledWhen() {
DockingAction action = new ActionBuilder("Test", "Test")
.enabledWhen(c -> c.getContextObject() == this)
.onAction(e -> actionCount++)
.build();
assertTrue(action.isEnabledForContext(new ActionContext(null, this, null)));
assertFalse(action.isEnabledForContext(new ActionContext()));
}
@Test
public void testValidContextWhen() {
DockingAction action = new ActionBuilder("Test", "Test")
.validContextWhen(c -> c.getContextObject() == this)
.onAction(e -> actionCount++)
.build();
assertTrue(action.isValidContext(new ActionContext(null, this, null)));
assertFalse(action.isValidContext(new ActionContext()));
}
@Test
public void testPopupWhen() {
DockingAction action = new ActionBuilder("Test", "Test")
.popupWhen(c -> c.getContextObject() == this)
.onAction(e -> actionCount++)
.build();
assertTrue(action.isAddToPopup(new ActionContext(null, this, null)));
assertFalse(action.isAddToPopup(new ActionContext()));
}
@Test
public void testWithContext() {
DockingAction action = new ActionBuilder("Test", "Test")
.withContext(FooActionContext.class)
.enabledWhen(c -> c.foo())
.onAction(e -> actionCount++)
.build();
assertFalse(action.isEnabledForContext(new ActionContext()));
assertTrue(action.isEnabledForContext(new FooActionContext()));
}
static class FooActionContext extends ActionContext {
public boolean foo() {
return true;
}
}
}

View file

@ -15,11 +15,121 @@
*/ */
package ghidra.framework.remote; package ghidra.framework.remote;
import java.io.IOException;
import java.rmi.Remote; import java.rmi.Remote;
import java.rmi.server.RemoteObjectInvocationHandler;
import db.buffers.ManagedBufferFileHandle;
import ghidra.framework.store.*;
import ghidra.util.InvalidNameException;
/** /**
* <code>RepositoryHandle</code> provides access to a remote repository via RMI. * <code>RepositoryHandle</code> provides access to a remote repository via RMI.
* <p>
* Methods from {@link RepositoryHandle} <b>must</b> be re-declared here
* so they may be properly marshalled for remote invocation via RMI.
* This became neccessary with an OpenJDK 11.0.6 change made to
* {@link RemoteObjectInvocationHandler}.
*/ */
public interface RemoteRepositoryHandle extends RepositoryHandle, Remote { public interface RemoteRepositoryHandle extends RepositoryHandle, Remote {
@Override
String getName() throws IOException;
@Override
User getUser() throws IOException;
@Override
User[] getUserList() throws IOException;
@Override
boolean anonymousAccessAllowed() throws IOException;
@Override
String[] getServerUserList() throws IOException;
@Override
void setUserList(User[] users, boolean anonymousAccessAllowed) throws IOException;
@Override
String[] getSubfolderList(String folderPath) throws IOException;
@Override
int getItemCount() throws IOException;
@Override
RepositoryItem[] getItemList(String folderPath) throws IOException;
@Override
RepositoryItem getItem(String parentPath, String name) throws IOException;
@Override
RepositoryItem getItem(String fileID) throws IOException;
@Override
ManagedBufferFileHandle createDatabase(String parentPath, String itemName, String fileID,
int bufferSize, String contentType, String projectPath)
throws IOException, InvalidNameException;
@Override
ManagedBufferFileHandle openDatabase(String parentPath, String itemName, int version,
int minChangeDataVer) throws IOException;
@Override
ManagedBufferFileHandle openDatabase(String parentPath, String itemName, long checkoutId)
throws IOException;
@Override
Version[] getVersions(String parentPath, String itemName) throws IOException;
@Override
void deleteItem(String parentPath, String itemName, int version) throws IOException;
@Override
void moveFolder(String oldParentPath, String newParentPath, String oldFolderName,
String newFolderName) throws InvalidNameException, IOException;
@Override
void moveItem(String oldParentPath, String newParentPath, String oldItemName,
String newItemName) throws InvalidNameException, IOException;
@Override
ItemCheckoutStatus checkout(String parentPath, String itemName, CheckoutType checkoutType,
String projectPath) throws IOException;
@Override
void terminateCheckout(String parentPath, String itemName, long checkoutId, boolean notify)
throws IOException;
@Override
ItemCheckoutStatus getCheckout(String parentPath, String itemName, long checkoutId)
throws IOException;
@Override
ItemCheckoutStatus[] getCheckouts(String parentPath, String itemName) throws IOException;
@Override
boolean folderExists(String folderPath) throws IOException;
@Override
boolean fileExists(String parentPath, String itemName) throws IOException;
@Override
long getLength(String parentPath, String itemName) throws IOException;
@Override
boolean hasCheckouts(String parentPath, String itemName) throws IOException;
@Override
boolean isCheckinActive(String parentPath, String itemName) throws IOException;
@Override
void updateCheckoutVersion(String parentPath, String itemName, long checkoutId,
int checkoutVersion) throws IOException;
@Override
RepositoryChangeEvent[] getEvents() throws IOException;
@Override
void close() throws IOException;
} }

View file

@ -15,11 +15,54 @@
*/ */
package ghidra.framework.remote; package ghidra.framework.remote;
import java.io.IOException;
import java.rmi.Remote; import java.rmi.Remote;
import java.rmi.server.RemoteObjectInvocationHandler;
/** /**
* <code>RepositoryServerHandle</code> provides access to a remote repository server via RMI. * <code>RepositoryServerHandle</code> provides access to a remote repository server via RMI.
* <p>
* Methods from {@link RepositoryServerHandle} <b>must</b> be re-declared here
* so they may be properly marshalled for remote invocation via RMI.
* This became neccessary with an OpenJDK 11.0.6 change made to
* {@link RemoteObjectInvocationHandler}.
*/ */
public interface RemoteRepositoryServerHandle extends RepositoryServerHandle, Remote { public interface RemoteRepositoryServerHandle extends RepositoryServerHandle, Remote {
@Override
boolean anonymousAccessAllowed() throws IOException;
@Override
boolean isReadOnly() throws IOException;
@Override
RepositoryHandle createRepository(String name) throws IOException;
@Override
RepositoryHandle getRepository(String name) throws IOException;
@Override
void deleteRepository(String name) throws IOException;
@Override
String[] getRepositoryNames() throws IOException;
@Override
String getUser() throws IOException;
@Override
String[] getAllUsers() throws IOException;
@Override
boolean canSetPassword() throws IOException;
@Override
long getPasswordExpiration() throws IOException;
@Override
boolean setPassword(char[] saltedSHA256PasswordHash) throws IOException;
@Override
void connected() throws IOException;
} }

View file

@ -158,12 +158,18 @@ public class TextLayoutGraphics extends Graphics2D {
//Insert spaces to account for distance past last field in row //Insert spaces to account for distance past last field in row
FontMetrics metrics = COMPONENT.getFontMetrics(sortedTextInfo.font); FontMetrics metrics = COMPONENT.getFontMetrics(sortedTextInfo.font);
int spaceWidth = metrics.charWidth(' '); int spaceWidth = metrics.charWidth(' ');
if (spaceWidth == 0) {
// some environments report 0 for some fonts
spaceWidth = 4;
}
int fillSpaces = int fillSpaces =
Math.round((float) (sortedTextInfo.point.x - lastXPos) / (float) spaceWidth); Math.round((float) (sortedTextInfo.point.x - lastXPos) / (float) spaceWidth);
//Account for the case where there's a very small amount of space between fields //Account for the case where there's a very small amount of space between fields
if (fillSpaces == 0 && sortedTextInfo.point.x > lastXPos) { if (fillSpaces == 0 && sortedTextInfo.point.x > lastXPos) {
fillSpaces = 1; fillSpaces = 1;
} }
for (int j = 0; j < fillSpaces; j++) { for (int j = 0; j < fillSpaces; j++) {
buffer.append(' '); buffer.append(' ');
} }

View file

@ -18,6 +18,7 @@ package utilities.util.reflection;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.junit.Test; import org.junit.Test;
@ -153,6 +154,84 @@ public class ReflectionUtilitiesTest {
babyTypeArguments.get(1)); babyTypeArguments.get(1));
} }
@Test(expected = NullPointerException.class)
public void testRuntimeTypeDiscovery_Null() {
ReflectionUtilities.getTypeArguments(List.class, null);
}
@Test
public void testRumtimeTypeDiscovery_AnonymousClass() {
List<String> myList = new ArrayList<String>() {
// stub
};
List<Class<?>> types = ReflectionUtilities.getTypeArguments(List.class, myList.getClass());
assertEquals(1, types.size());
assertEquals(String.class, types.get(0));
}
@Test
public void testRumtimeTypeDiscovery_LocalVariable() {
List<String> myList = new ArrayList<String>();
List<Class<?>> types = ReflectionUtilities.getTypeArguments(List.class, myList.getClass());
assertEquals(1, types.size());
assertNull(types.get(0));
}
@Test
public void testRuntimeTypeDiscovery_MixedHierarchy_AbstractClassAndInterfaceBothDefineValues() {
//
// Test to make sure that we get not only a directly hierarchy, but the lateral one
// as well, where we pursue interfaces that may have defined some types.
//
List<Class<?>> types = ReflectionUtilities.getTypeArguments(RuntimeBaseInterface.class,
ChildExtendingPartiallyDefinedTypes.class);
assertEquals(2, types.size());
assertEquals(String.class, types.get(0));
assertEquals(Double.class, types.get(1));
}
@Test
public void testRuntimeTypeDiscovery_SubInterfaceDefinesValues() {
//
// Test to make sure that we get not only a directly hierarchy, but the lateral one
// as well, where we pursue interfaces that may have defined some types.
//
List<Class<?>> types = ReflectionUtilities.getTypeArguments(RuntimeBaseInterface.class,
ChildExtendingWhollyDefinedTypes.class);
assertEquals(2, types.size());
assertEquals(String.class, types.get(0));
assertEquals(Double.class, types.get(1));
}
@Test
public void testRuntimeTypeDiscovery_MixedHierarchy_UnrelatedParents() {
//
// Test to make sure that we get not only a directly hierarchy, but the lateral one
// as well, where we pursue interfaces that may have defined some types.
//
// This test also verifies that in a mixed type hierarchy, we can correctly locate types
// depending upon the parent type we pass in.
//
List<Class<?>> types = ReflectionUtilities.getTypeArguments(RuntimeBaseInterface.class,
ChildWithMixedParentTypes.class);
assertEquals(2, types.size());
assertEquals(String.class, types.get(0));
assertEquals(Double.class, types.get(1));
types = ReflectionUtilities.getTypeArguments(List.class,
ChildWithMixedParentTypes.class);
assertEquals(1, types.size());
assertEquals(Integer.class, types.get(0));
}
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
@ -179,6 +258,39 @@ public class ReflectionUtilitiesTest {
} }
} }
private interface RuntimeBaseInterface<T, J> {
// stub
}
private interface PartiallyDefinedInterface<J> extends RuntimeBaseInterface<String, J> {
// stub
}
private interface WhollyDefinedInterface extends RuntimeBaseInterface<String, Double> {
// stub
}
private class AbstractPartiallyDefinedClass<I> implements RuntimeBaseInterface<I, Double> {
// stub
}
private class ChildExtendingPartiallyDefinedTypes
extends AbstractPartiallyDefinedClass<String>
implements PartiallyDefinedInterface<Double> {
// stub
}
private class ChildExtendingWhollyDefinedTypes
implements WhollyDefinedInterface {
// stub
}
private class ChildWithMixedParentTypes
extends ArrayList<Integer>
implements WhollyDefinedInterface {
// stub
}
private class RuntimeBaseType<T, J> { private class RuntimeBaseType<T, J> {
// stub // stub
} }

View file

@ -165,7 +165,7 @@ public abstract class VisualGraphComponentProvider<V extends VisualVertex,
Undo/redo for graph operations (delete; group/ungroup; move) Undo/redo for graph operations (delete; group/ungroup; move)
-rapid pressing will shortcut items -rapid pressing will shortcut items
-undo/redo allows us to prune nodes -undo/redo allows us to prune nodes
--how to maintain old nodes/edges? (FitleringVisualGraph) --how to maintain old nodes/edges? (FilteringVisualGraph)
*/ */

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.graph.job; package ghidra.graph.job;
import static util.CollectionUtils.asSet; import static util.CollectionUtils.*;
import static util.CollectionUtils.asStream;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
@ -108,7 +107,7 @@ public class FilterVerticesJob<V extends VisualVertex, E extends VisualEdge<V>>
passedVertices = matching; passedVertices = matching;
// 2) // 2)
failedVertices = findCurrentVerticesFailingTheFitler(matching); failedVertices = findCurrentVerticesFailingTheFilter(matching);
failedEdges = filterGraph.getAllEdges(failedVertices); failedEdges = filterGraph.getAllEdges(failedVertices);
Set<E> allRelatedEdges = filterGraph.getAllEdges(passedVertices); Set<E> allRelatedEdges = filterGraph.getAllEdges(passedVertices);
@ -119,7 +118,7 @@ public class FilterVerticesJob<V extends VisualVertex, E extends VisualEdge<V>>
filterGraph.unfilterVertices(passedVertices); filterGraph.unfilterVertices(passedVertices);
} }
private Set<V> findCurrentVerticesFailingTheFitler(Set<V> validVertices) { private Set<V> findCurrentVerticesFailingTheFilter(Set<V> validVertices) {
UnmodifiableIterator<V> nonMatchingIterator = UnmodifiableIterator<V> nonMatchingIterator =
Iterators.filter(filterGraph.getUnfilteredVertices(), v -> !validVertices.contains(v)); Iterators.filter(filterGraph.getUnfilteredVertices(), v -> !validVertices.contains(v));

View file

@ -580,7 +580,7 @@ public class GhidraFileData {
boolean isVersioned() { boolean isVersioned() {
synchronized (fileSystem) { synchronized (fileSystem) {
if (versionedFolderItem == null) { if (versionedFolderItem == null) {
return false; return isCheckedOut();
} }
return !isHijacked(); return !isHijacked();
} }

View file

@ -149,7 +149,7 @@ public class AssemblySentential<NT extends AssemblyNonTerminal> extends
return Collections.singleton(new WhiteSpaceParseToken(grammar, this, "")); return Collections.singleton(new WhiteSpaceParseToken(grammar, this, ""));
} }
if (Character.isLetterOrDigit(buffer.charAt(b)) && if (Character.isLetterOrDigit(buffer.charAt(b)) &&
Character.isLetterOrDigit(buffer.charAt(b - 1))) { (b == 0 || Character.isLetterOrDigit(buffer.charAt(b - 1)))) {
return Collections.emptySet(); return Collections.emptySet();
} }
} }

View file

@ -2141,8 +2141,9 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* Translate language * Translate language
* @param translator language translator, if null only re-disassembly will occur. * @param translator language translator, if null only re-disassembly will occur.
* @param newCompilerSpecID new compiler specification which corresponds to new language, may be null. * @param newCompilerSpecID new compiler specification which corresponds to new language, may be null.
* @param monitor * @param forceRedisassembly if true a redisassembly will be forced even if not required
* @throws LockException * @param monitor task monitor
* @throws LockException if exclusive access is missing
*/ */
public void setLanguage(LanguageTranslator translator, CompilerSpecID newCompilerSpecID, public void setLanguage(LanguageTranslator translator, CompilerSpecID newCompilerSpecID,
boolean forceRedisassembly, TaskMonitor monitor) throws LockException { boolean forceRedisassembly, TaskMonitor monitor) throws LockException {
@ -2153,7 +2154,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
try { try {
setEventsEnabled(false); setEventsEnabled(false);
try { try {
boolean notifyCodeManager = true; boolean redisassemblyRequired = true;
int oldLanguageVersion = languageVersion; int oldLanguageVersion = languageVersion;
int oldLanguageMinorVersion = languageMinorVersion; int oldLanguageMinorVersion = languageMinorVersion;
if (translator != null) { if (translator != null) {
@ -2168,7 +2169,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
} }
else if (!forceRedisassembly && language.getVersion() == languageVersion && else if (!forceRedisassembly && language.getVersion() == languageVersion &&
language.getMinorVersion() == languageMinorVersion) { language.getMinorVersion() == languageMinorVersion) {
notifyCodeManager = false; // compiler spec change only redisassemblyRequired = false; // compiler spec change only
Msg.info(this, "Setting compiler spec for Program " + getName() + ": " + Msg.info(this, "Setting compiler spec for Program " + getName() + ": " +
compilerSpecID + " -> " + newCompilerSpecID); compilerSpecID + " -> " + newCompilerSpecID);
} }
@ -2207,15 +2208,14 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
monitor.setProgress(0); monitor.setProgress(0);
ProgramRegisterContextDB contextMgr = ProgramRegisterContextDB contextMgr =
(ProgramRegisterContextDB) getProgramContext(); (ProgramRegisterContextDB) getProgramContext();
if (translator != null) { if (redisassemblyRequired) {
contextMgr.setLanguage(translator, compilerSpec, memoryManager, monitor); contextMgr.setLanguage(translator, compilerSpec, memoryManager, monitor);
} }
else { else {
// force re-initialization
contextMgr.initializeDefaultValues(language, compilerSpec); contextMgr.initializeDefaultValues(language, compilerSpec);
} }
if (notifyCodeManager) { if (redisassemblyRequired) {
Disassembler.clearUnimplementedPcodeWarnings(this, null, monitor); Disassembler.clearUnimplementedPcodeWarnings(this, null, monitor);
repairContext(oldLanguageVersion, oldLanguageMinorVersion, translator, monitor); repairContext(oldLanguageVersion, oldLanguageMinorVersion, translator, monitor);
monitor.setMessage("Updating instructions..."); monitor.setMessage("Updating instructions...");

View file

@ -16,8 +16,7 @@
package ghidra.program.database.data; package ghidra.program.database.data;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import db.Record; import db.Record;
@ -26,6 +25,7 @@ import ghidra.program.database.DatabaseObject;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult; import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
import ghidra.util.Lock;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -41,6 +41,7 @@ class CategoryDB extends DatabaseObject implements Category {
private LazyLoadingCachingMap<String, CategoryDB> subcategoryMap; private LazyLoadingCachingMap<String, CategoryDB> subcategoryMap;
private LazyLoadingCachingMap<String, DataType> dataTypeMap; private LazyLoadingCachingMap<String, DataType> dataTypeMap;
private ConflictMap conflictMap;
/** /**
* Category Constructor * Category Constructor
@ -57,19 +58,19 @@ class CategoryDB extends DatabaseObject implements Category {
this.name = name; this.name = name;
this.parent = parent; this.parent = parent;
subcategoryMap = new LazyLoadingCachingMap<>(mgr.lock, CategoryDB.class) { subcategoryMap = new LazyLoadingCachingMap<>(mgr.lock) {
@Override @Override
public Map<String, CategoryDB> loadMap() { public Map<String, CategoryDB> loadMap() {
return buildSubcategoryMap(); return buildSubcategoryMap();
} }
}; };
dataTypeMap = new LazyLoadingCachingMap<>(mgr.lock, DataType.class) { dataTypeMap = new LazyLoadingCachingMap<>(mgr.lock) {
@Override @Override
public Map<String, DataType> loadMap() { public Map<String, DataType> loadMap() {
return createDataTypeMap(); return createDataTypeMap();
} }
}; };
conflictMap = new ConflictMap(mgr.lock);
} }
/** /**
@ -102,6 +103,7 @@ class CategoryDB extends DatabaseObject implements Category {
protected boolean refresh(Record rec) { protected boolean refresh(Record rec) {
subcategoryMap.clear(); subcategoryMap.clear();
dataTypeMap.clear(); dataTypeMap.clear();
conflictMap.clear();
if (isRoot()) { if (isRoot()) {
return true; return true;
@ -210,13 +212,26 @@ class CategoryDB extends DatabaseObject implements Category {
return map; return map;
} }
private String getBaseName(String dataTypeName) {
int indexOf = dataTypeName.indexOf(DataType.CONFLICT_SUFFIX);
if (indexOf <= 0) {
return dataTypeName;
}
return dataTypeName.substring(0, indexOf);
}
private boolean isConflictName(String dataTypeName) {
return dataTypeName.contains(DataType.CONFLICT_SUFFIX);
}
/** /**
* @see ghidra.program.model.data.Category#getCategories() * @see ghidra.program.model.data.Category#getCategories()
*/ */
@Override @Override
public Category[] getCategories() { public Category[] getCategories() {
validate(mgr.lock); validate(mgr.lock);
return subcategoryMap.valuesToArray(); Collection<CategoryDB> categories = subcategoryMap.values();
return categories.toArray(new Category[categories.size()]);
} }
/** /**
@ -225,7 +240,8 @@ class CategoryDB extends DatabaseObject implements Category {
@Override @Override
public DataType[] getDataTypes() { public DataType[] getDataTypes() {
validate(mgr.lock); validate(mgr.lock);
return dataTypeMap.valuesToArray(); Collection<DataType> dataTypes = dataTypeMap.values();
return dataTypes.toArray(new DataType[dataTypes.size()]);
} }
/** /**
@ -587,19 +603,144 @@ class CategoryDB extends DatabaseObject implements Category {
} }
void dataTypeRenamed(DataType childDataType, String oldName) { void dataTypeRenamed(DataType childDataType, String oldName) {
dataTypeMap.remove(oldName); dataTypeRemoved(oldName);
dataTypeMap.put(childDataType.getName(), childDataType); dataTypeAdded(childDataType);
} }
void dataTypeAdded(DataType childDataType) { void dataTypeAdded(DataType dataType) {
dataTypeMap.put(childDataType.getName(), childDataType); String dtName = dataType.getName();
dataTypeMap.put(dtName, dataType);
if (isConflictName(dtName)) {
conflictMap.addDataType(dataType);
}
} }
void dataTypeRemoved(String dataTypeName) { void dataTypeRemoved(String dataTypeName) {
dataTypeMap.remove(dataTypeName); dataTypeMap.remove(dataTypeName);
if (isConflictName(dataTypeName)) {
conflictMap.removeDataTypeName(dataTypeName);
}
} }
void categoryAdded(CategoryDB cat) { void categoryAdded(CategoryDB cat) {
subcategoryMap.put(cat.getName(), cat); subcategoryMap.put(cat.getName(), cat);
} }
@Override
public List<DataType> getDataTypesByBaseName(String dataTypeName) {
List<DataType> list = new ArrayList<>();
String baseName = getBaseName(dataTypeName);
DataType baseType = dataTypeMap.get(baseName);
if (baseType != null) {
list.add(baseType);
}
List<DataType> relatedNameDataTypes = conflictMap.getDataTypesByBaseName(baseName);
list.addAll(relatedNameDataTypes);
return list;
}
/**
* Class to handle the complexities of having a map as the value in a LazyLoadingCachingMap
* This map uses the data type's base name as the key (i.e. all .conflict suffixes stripped off.)
* The value is another map that maps the actual data type's name to the data type. This map
* effectively provides an efficient way to get all data types in a category that have the
* same name, but possibly have had their name modified (by appending .conflict) to get around
* the requirement that names have to be unique in the same category.
*/
private class ConflictMap extends LazyLoadingCachingMap<String, Map<String, DataType>> {
ConflictMap(Lock lock) {
super(lock);
}
/**
* Creates a map of all data types whose name has a .conflict suffix where the key
* is the base name and {@link LazyLoadingCachingMap} the value is a map of actual name
* to data type. This mapping is
* maintained as a lazy cache map. This is only called by the super class when the
* cached needs to be populated and we are depending on it to acquire the necessary
* database lock. (See {@link LazyLoadingCachingMap#loadMap()}
* @return the loaded map
*/
@Override
protected Map<String, Map<String, DataType>> loadMap() {
Map<String, Map<String, DataType>> map = new HashMap<>();
Collection<DataType> values = dataTypeMap.values();
for (DataType dataType : values) {
String dataTypeName = dataType.getName();
if (isConflictName(dataTypeName)) {
String baseName = getBaseName(dataTypeName);
Map<String, DataType> innerMap =
map.computeIfAbsent(baseName, b -> new HashMap<>());
innerMap.put(dataTypeName, dataType);
}
}
return map;
}
/**
* Adds the data type to the conflict mapping structure. If the mapping is currently not
* loaded then this method can safely do nothing. This method is synchronized to provide
* thread safe access/manipulation of the map.
* @param dataType the data type to add to the mapping if the mapping is already loaded
*/
synchronized void addDataType(DataType dataType) {
// if the cache is not currently populated, don't need to do anything
Map<String, Map<String, DataType>> map = getMap();
if (map == null) {
return;
}
String dataTypeName = dataType.getName();
String baseName = getBaseName(dataTypeName);
Map<String, DataType> innerMap = map.computeIfAbsent(baseName, b -> new HashMap<>());
innerMap.put(dataTypeName, dataType);
}
/**
* Removes the data type with the given name from the conflict mapping structure. If the
* mapping is currently not loaded then this method can safely do nothing. This method is
* synchronized to provide thread safe access/manipulate of the map.
* @param dataTypeName the name of the data type to remove from this mapping
*/
synchronized void removeDataTypeName(String dataTypeName) {
Map<String, Map<String, DataType>> map = getMap();
if (map == null) {
return;
}
String baseName = getBaseName(dataTypeName);
Map<String, DataType> innerMap = map.get(baseName);
if (innerMap == null) {
return;
}
innerMap.remove(dataTypeName);
}
/**
* Returns a list of all data types that have conflict names for the given base name
* @param baseName the data type base name to search for (i.e. the .conflict suffix removed)
* @return a list of all conflict named data types that would have the given base name if
* no conflicts existed
*/
List<DataType> getDataTypesByBaseName(String baseName) {
// Note that the following call to get MUST NOT be in a synchronized block because
// it may trigger a loading of the cache which requires a database lock and you
// can't be synchronized on this class when acquiring a database lock or else a
// deadlock will occur.
Map<String, DataType> map = get(baseName);
if (map == null) {
return Collections.emptyList();
}
// the following must be synchronized so that the implied iterator can complete without
// another thread changing the map's values.
synchronized (this) {
return new ArrayList<>(map.values());
}
}
}
} }

View file

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

View file

@ -774,7 +774,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
private DataType resolveBitFieldDataType(BitFieldDataType bitFieldDataType, private DataType resolveBitFieldDataType(BitFieldDataType bitFieldDataType,
DataTypeConflictHandler handler) { DataTypeConflictHandler handler) {
// NOTE: When a bit-field is getting adding added it will get resolved more than once. // NOTE: When a bit-field is getting added it will get resolved more than once.
// The first time we will ensure that the base data type, which may be a TypeDef, gets // The first time we will ensure that the base data type, which may be a TypeDef, gets
// resolved. If the bit-offset is too large it will be set to 0 // resolved. If the bit-offset is too large it will be set to 0
// with the expectation that it will get corrected during subsequent packing. // with the expectation that it will get corrected during subsequent packing.
@ -788,7 +788,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
int storageSizeBits = 8 * storageSize; int storageSizeBits = 8 * storageSize;
if ((bitOffset + bitSize) > storageSizeBits) { if ((bitOffset + bitSize) > storageSizeBits) {
// should get recomputed during packing when used within aligned structure // should get recomputed during packing when used within aligned structure
bitOffset = getDataOrganization().isBigEndian() ? baseLengthBits - bitSize : 0; int effectiveBitSize = Math.min(bitSize, baseLengthBits);
bitOffset = getDataOrganization().isBigEndian() ? baseLengthBits - effectiveBitSize : 0;
storageSize = baseLength; storageSize = baseLength;
} }
try { try {
@ -933,14 +934,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
if (category == null) { if (category == null) {
return null; return null;
} }
String namePrefix = dtName + DataType.CONFLICT_SUFFIX; List<DataType> relatedByName = category.getDataTypesByBaseName(dtName);
DataType[] dataTypes = category.getDataTypes();
for (DataType candidate : dataTypes) { for (DataType candidate : relatedByName) {
String candidateName = candidate.getName(); String candidateName = candidate.getName();
if (candidateName.startsWith(namePrefix)) { if (!candidateName.equals(excludedName) && candidate.isEquivalent(dataType)) {
if (!candidateName.equals(excludedName) && candidate.isEquivalent(dataType)) { return candidate;
return candidate;
}
} }
} }
return null; return null;
@ -3207,13 +3206,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
lock.acquire(); lock.acquire();
try { try {
long[] ids = parentChildAdapter.getParentIds(childID); long[] ids = parentChildAdapter.getParentIds(childID);
// TODO: consider deduping ids using Set
List<DataType> dts = new ArrayList<>(); List<DataType> dts = new ArrayList<>();
for (int i = 0; i < ids.length; i++) { for (long id : ids) {
DataType dt = getDataType(ids[i]); DataType dt = getDataType(id);
if (dt == null) { if (dt == null) {
// cleanup invalid records for missing parent // cleanup invalid records for missing parent
attemptRecordRemovalForParent(ids[i]); attemptRecordRemovalForParent(id);
} }
else { else {
dts.add(dt); dts.add(dt);

View file

@ -16,8 +16,7 @@
package ghidra.program.database.data; package ghidra.program.database.data;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.lang.reflect.Array; import java.util.*;
import java.util.Map;
import ghidra.util.Lock; import ghidra.util.Lock;
@ -39,15 +38,14 @@ public abstract class LazyLoadingCachingMap<K, V> {
private Lock lock; private Lock lock;
private SoftReference<Map<K, V>> softRef; private SoftReference<Map<K, V>> softRef;
private Class<V> valueClass;
protected LazyLoadingCachingMap(Lock lock, Class<V> valueClass) { protected LazyLoadingCachingMap(Lock lock) {
this.lock = lock; this.lock = lock;
this.valueClass = valueClass;
} }
/** /**
* This method will reload the map data from scratch. * This method will reload the map data from scratch. Subclass may assume that the database
* lock has been acquired.
* @return a map containing all current key, value pairs. * @return a map containing all current key, value pairs.
*/ */
protected abstract Map<K, V> loadMap(); protected abstract Map<K, V> loadMap();
@ -96,13 +94,13 @@ public abstract class LazyLoadingCachingMap<K, V> {
} }
} }
public V[] valuesToArray() { /**
* Returns an unmodifiable view of the values in this map.
* @return an unmodifiable view of the values in this map.
*/
public Collection<V> values() {
Map<K, V> map = getOrLoadMap(); Map<K, V> map = getOrLoadMap();
synchronized (this) { return Collections.unmodifiableCollection(map.values());
@SuppressWarnings("unchecked")
V[] array = (V[]) Array.newInstance(valueClass, map.size());
return map.values().toArray(array);
}
} }
private Map<K, V> getOrLoadMap() { private Map<K, V> getOrLoadMap() {
@ -113,6 +111,15 @@ public abstract class LazyLoadingCachingMap<K, V> {
return map; return map;
} }
} }
// We must get the database lock before calling loadMap(). Also, we can't get the
// database lock while having the synchronization lock for this class or a deadlock can
// occur, since the other methods may be called while the client has the db lock.
// Note: all other places where the map is being used or manipulated, it must be done
// while having the class's synchronization lock since the map itself is not thread safe.
// It should be safe here since it creates a new map and then in one operation it sets it
// as the map to be used elsewhere.
lock.acquire(); lock.acquire();
try { try {
map = getMap(); map = getMap();
@ -132,11 +139,12 @@ public abstract class LazyLoadingCachingMap<K, V> {
* "lock". * "lock".
* @return the underlying map of key,value pairs or null if it is currently not loaded. * @return the underlying map of key,value pairs or null if it is currently not loaded.
*/ */
private Map<K, V> getMap() { protected Map<K, V> getMap() {
if (softRef == null) { if (softRef == null) {
return null; return null;
} }
return softRef.get(); return softRef.get();
} }
} }

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,7 @@ class StructureDB extends CompositeDB implements Structure {
if (ordinal < 0 || ordinal >= numComponents) { if (ordinal < 0 || ordinal >= numComponents) {
throw new ArrayIndexOutOfBoundsException(ordinal); throw new ArrayIndexOutOfBoundsException(ordinal);
} }
validateDataType(dataType); validateDataType(dataType);
DataTypeComponent origDtc = getComponent(ordinal); DataTypeComponent origDtc = getComponent(ordinal);
@ -1009,9 +1041,7 @@ class StructureDB extends CompositeDB implements Structure {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Offset " + offset + " is beyond end of structure (" + structLength + ")."); "Offset " + offset + " is beyond end of structure (" + structLength + ").");
} }
if (dataType instanceof BitFieldDataType) {
throw new IllegalArgumentException("Components may not be replaced with a bit-field");
}
lock.acquire(); lock.acquire();
try { try {
checkDeleted(); checkDeleted();
@ -1085,19 +1115,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 +1179,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

@ -72,12 +72,12 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
} }
if (openMode == DBConstants.UPGRADE && oldContextDataExists) { if (openMode == DBConstants.UPGRADE && oldContextDataExists) {
// TODO: Make sure upgrade is working correctly before uncommenting try {
// try { OldProgramContextDB.removeOldContextData(dbHandle);
// OldProgramContextDB.removeOldContextData(dbHandle); }
// } catch (IOException e) { catch (IOException e) {
// errorHandler.dbError(e); errorHandler.dbError(e);
// } }
} }
} }
@ -161,6 +161,12 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
} }
} }
/**
* Intialize context with default values defined by pspec and cspec.
* NOTE: cspec values take precedence
* @param lang processor language
* @param compilerSpec compiler specification
*/
public void initializeDefaultValues(Language lang, CompilerSpec compilerSpec) { public void initializeDefaultValues(Language lang, CompilerSpec compilerSpec) {
defaultRegisterValueMap.clear(); defaultRegisterValueMap.clear();
lang.applyContextSettings(this); lang.applyContextSettings(this);
@ -288,9 +294,31 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
} }
} }
/**
* Perform context upgrade due to a language change
* @param translator language translator required by major upgrades (may be null)
* @param newCompilerSpec new compiler specification
* @param programMemory program memory
* @param monitor task monitor
* @throws CancelledException thrown if monitor cancelled
*/
public void setLanguage(LanguageTranslator translator, CompilerSpec newCompilerSpec, public void setLanguage(LanguageTranslator translator, CompilerSpec newCompilerSpec,
AddressSetView programMemory, TaskMonitor monitor) throws CancelledException { AddressSetView programMemory, TaskMonitor monitor) throws CancelledException {
if (translator == null) {
Language lang = program.getLanguage();
boolean clearContext = Boolean.valueOf(
lang.getProperty(GhidraLanguagePropertyKeys.RESET_CONTEXT_ON_UPGRADE));
if (clearContext) {
RegisterValueStore store = registerValueMap.get(baseContextRegister);
if (store != null) {
store.clearAll();
}
}
initializeDefaultValues(lang, newCompilerSpec);
return;
}
Language newLanguage = translator.getNewLanguage(); Language newLanguage = translator.getNewLanguage();
// Sort the registers by size so that largest come first. // Sort the registers by size so that largest come first.
@ -309,8 +337,11 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
continue; continue;
} }
boolean clearContext = register.isProcessorContext() && Boolean.valueOf(
newLanguage.getProperty(GhidraLanguagePropertyKeys.RESET_CONTEXT_ON_UPGRADE));
// Update storage range map // Update storage range map
if (!store.setLanguage(translator, monitor)) { if (clearContext || !store.setLanguage(translator, monitor)) {
// Clear and remove old register value store // Clear and remove old register value store
Msg.warn(this, Msg.warn(this,
"WARNING! Discarding all context for register " + register.getName()); "WARNING! Discarding all context for register " + register.getName());

View file

@ -1150,12 +1150,12 @@ public class Disassembler implements DisassemblerConflictHandler {
throws InsufficientBytesException, UnknownInstructionException, throws InsufficientBytesException, UnknownInstructionException,
AddressOverflowException, NestedDelaySlotException { AddressOverflowException, NestedDelaySlotException {
List<PseudoInstruction> delaySlotList = parseDelaySlots(inst, blockMemBuffer, block);
if (followFlow) { if (followFlow) {
processInstructionFlows(inst, block); processInstructionFlows(inst, block);
} }
List<PseudoInstruction> delaySlotList = parseDelaySlots(inst, blockMemBuffer, block);
block.addInstruction(inst); block.addInstruction(inst);
if (delaySlotList != null) { if (delaySlotList != null) {

View file

@ -174,9 +174,6 @@ class DisassemblerQueue {
branchFlow = currentBranchQueue.first(); branchFlow = currentBranchQueue.first();
currentBranchQueue.remove(branchFlow); currentBranchQueue.remove(branchFlow);
} }
if (processedBranchFlows.contains(branchFlow)) {
continue;
}
processedBranchFlows.add(branchFlow); processedBranchFlows.add(branchFlow);
Address blockAddr = branchFlow.getDestinationAddress(); Address blockAddr = branchFlow.getDestinationAddress();

View file

@ -15,6 +15,8 @@
*/ */
package ghidra.program.model.data; package ghidra.program.model.data;
import java.util.List;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -48,6 +50,19 @@ public interface Category extends Comparable<Category> {
*/ */
public abstract DataType[] getDataTypes(); public abstract DataType[] getDataTypes();
/**
* Get all data types in this category whose base name matches the base name of the given name.
* The base name of a name is the first part of the string up to where the first ".conflict"
* occurs. In other words, finds all data types whose name matches the given name once
* any conflict suffixes have been removed from both the given name and the data types
* that are being scanned.
* @param name the name for which to get conflict related data types in this category. Note: the
* name that is passed in will be normalized to its base name, so you may pass in names with .conflict
* appended as a convenience.
* @return a list of data types that have the same base name as the base name of the given name
*/
public abstract List<DataType> getDataTypesByBaseName(String name);
/** /**
* Adds the given datatype to this category. * Adds the given datatype to this category.
* @param dt the datatype to add to this category. * @param dt the datatype to add to this category.

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);
@ -1126,9 +1154,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
if (index < 0 || index >= numComponents) { if (index < 0 || index >= numComponents) {
throw new ArrayIndexOutOfBoundsException(index); throw new ArrayIndexOutOfBoundsException(index);
} }
if (dataType instanceof BitFieldDataType) {
throw new IllegalArgumentException("Components may not be replaced with a bit-field");
}
validateDataType(dataType); validateDataType(dataType);
@ -1168,9 +1193,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Offset " + offset + " is beyond end of structure (" + structLength + ")."); "Offset " + offset + " is beyond end of structure (" + structLength + ").");
} }
if (dataType instanceof BitFieldDataType) {
throw new IllegalArgumentException("Components may not be replaced with a bit-field");
}
validateDataType(dataType); validateDataType(dataType);

View file

@ -112,4 +112,11 @@ public final class GhidraLanguagePropertyKeys {
* following the call. Non-returning functions can be detected in many cases. * following the call. Non-returning functions can be detected in many cases.
*/ */
public static final String ENABLE_NO_RETURN_ANALYSIS = "enableNoReturnAnalysis"; public static final String ENABLE_NO_RETURN_ANALYSIS = "enableNoReturnAnalysis";
/**
* Property to indicate that all stored instruction context should be cleared
* during a language upgrade operation which requires redisassembly.
* NOTE: This is an experimental concept which may be removed in the future
*/
public static final String RESET_CONTEXT_ON_UPGRADE = "resetContextOnUpgrade";
} }

View file

@ -120,16 +120,22 @@ public class VariableOffset {
} }
/** /**
* Get list of markup objects * Returns the data type access portion of this variable offset as a string
* @return list of markup objects * @return the text
*/ */
public List<Object> getObjects() { public String getDataTypeDisplayText() {
List<Object> objects = getObjects(false);
LabelString labelString = (LabelString) objects.get(0);
return labelString.toString();
}
private List<Object> getObjects(boolean showScalarAdjustment) {
DataType dt = variable.getDataType(); DataType dt = variable.getDataType();
StringBuffer name = new StringBuffer(variable.getName()); StringBuffer name = new StringBuffer(variable.getName());
long scalarAdjustment = 0; long scalarAdjustment = 0;
if (includeScalarAdjustment && (replacedElement instanceof Scalar)) { if (showScalarAdjustment && (replacedElement instanceof Scalar)) {
Scalar s = (Scalar) replacedElement; Scalar s = (Scalar) replacedElement;
scalarAdjustment = variable.isStackVariable() ? s.getSignedValue() : s.getValue(); scalarAdjustment = variable.isStackVariable() ? s.getSignedValue() : s.getValue();
scalarAdjustment -= offset; scalarAdjustment -= offset;
@ -214,6 +220,14 @@ public class VariableOffset {
return list; return list;
} }
/**
* Get list of markup objects
* @return list of markup objects
*/
public List<Object> getObjects() {
return getObjects(includeScalarAdjustment);
}
public Variable getVariable() { public Variable getVariable() {
return variable; return variable;
} }

View file

@ -560,15 +560,46 @@ public class ReflectionUtilities {
} }
} }
/**
* Returns the type arguments for the given base class and extension.
*
* <p>Caveat: this lookup will only work if the given child class is a concrete class that
* has its type arguments specified. For example, these cases will work:
* <pre>
* // anonymous class definition
* List&lt;String&gt; myList = new ArrayList&lt;String&gt;() {
* ...
* };
*
* // class definition
* public class MyList implements List&lt;String&gt; {
* </pre>
*
* Whereas this case will not work:
* <pre>
* // local variable with the type specified
* List&lt;String&gt; myList = new ArrayList&lt;String&gt;();
* </pre>
*
* <p>Note: a null entry in the result list will exist for any type that was unrecoverable
*
*
* @param <T> the type of the base and child class
* @param baseClass the base class
* @param childClass the child class
* @return the type arguments
*/
public static <T> List<Class<?>> getTypeArguments(Class<T> baseClass, public static <T> List<Class<?>> getTypeArguments(Class<T> baseClass,
Class<? extends T> childClass) { Class<? extends T> childClass) {
Map<Type, Type> resolvedTypesDictionary = new HashMap<>();
Objects.requireNonNull(baseClass);
Objects.requireNonNull(childClass);
Map<Type, Type> resolvedTypesDictionary = new HashMap<>();
Type baseClassAsType = Type baseClassAsType =
walkClassHierarchyAndResolveTypes(baseClass, resolvedTypesDictionary, childClass); walkClassHierarchyAndResolveTypes(baseClass, resolvedTypesDictionary, childClass);
// now see if we can resolve the type arguments defined by 'baseClass' to the raw runtime // try to resolve type arguments defined by 'baseClass' to the raw runtime class
// class that is in use
Type[] baseClassDeclaredTypeArguments = getDeclaredTypeArguments(baseClassAsType); Type[] baseClassDeclaredTypeArguments = getDeclaredTypeArguments(baseClassAsType);
return resolveBaseClassTypeArguments(resolvedTypesDictionary, return resolveBaseClassTypeArguments(resolvedTypesDictionary,
baseClassDeclaredTypeArguments); baseClassDeclaredTypeArguments);
@ -577,29 +608,69 @@ public class ReflectionUtilities {
private static <T> Type walkClassHierarchyAndResolveTypes(Class<T> baseClass, private static <T> Type walkClassHierarchyAndResolveTypes(Class<T> baseClass,
Map<Type, Type> resolvedTypes, Type type) { Map<Type, Type> resolvedTypes, Type type) {
while (!getClass(type).equals(baseClass)) { if (type == null) {
if (type instanceof Class) { return null;
type = ((Class<?>) type).getGenericSuperclass(); }
}
else {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
if (!rawType.equals(baseClass)) { if (equals(type, baseClass)) {
type = rawType.getGenericSuperclass(); return type;
}
if (type instanceof Class) {
Class<?> clazz = (Class<?>) type;
Type[] interfaceTypes = clazz.getGenericInterfaces();
Set<Type> toCheck = new HashSet<>();
toCheck.addAll(Arrays.asList(interfaceTypes));
Type parentType = clazz.getGenericSuperclass();
toCheck.add(parentType);
for (Type t : toCheck) {
Type result = walkClassHierarchyAndResolveTypes(baseClass, resolvedTypes, t);
if (equals(result, baseClass)) {
return result;
} }
} }
if (type == null) { return parentType;
return type; }
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
if (rawType.equals(baseClass)) {
return rawType;
}
Type[] interfaceTypes = rawType.getGenericInterfaces();
Set<Type> toCheck = new HashSet<>();
toCheck.addAll(Arrays.asList(interfaceTypes));
Type parentType = rawType.getGenericSuperclass();
toCheck.add(parentType);
for (Type t : toCheck) {
Type result = walkClassHierarchyAndResolveTypes(baseClass, resolvedTypes, t);
if (equals(result, baseClass)) {
return result;
} }
} }
return type;
return parentType;
}
private static boolean equals(Type type, Class<?> c) {
Class<?> typeClass = getClass(type);
if (typeClass == null) {
return false;
}
return typeClass.equals(c);
} }
private static Class<?> getClass(Type type) { private static Class<?> getClass(Type type) {
@ -637,7 +708,6 @@ public class ReflectionUtilities {
return typeArgumentsAsClasses; return typeArgumentsAsClasses;
} }
// we checked
private static Type[] getDeclaredTypeArguments(Type type) { private static Type[] getDeclaredTypeArguments(Type type) {
if (type instanceof Class) { if (type instanceof Class) {
return ((Class<?>) type).getTypeParameters(); return ((Class<?>) type).getTypeParameters();

View file

@ -6,7 +6,7 @@
</global> </global>
<stackpointer register="SP" space="RAM" growth="negative"/> <stackpointer register="SP" space="RAM" growth="negative"/>
<default_proto> <default_proto>
<prototype name="__stdcall" extrapop="-2" stackshift="-2"> <prototype name="__stdcall" extrapop="2" stackshift="2">
<input> <input>
<pentry minsize="1" maxsize="1"> <pentry minsize="1" maxsize="1">
<register name="A"/> <register name="A"/>

View file

@ -6,7 +6,7 @@
</global> </global>
<stackpointer register="SP" space="ram" growth="negative"/> <stackpointer register="SP" space="ram" growth="negative"/>
<default_proto> <default_proto>
<prototype name="__stdcall" extrapop="-2" stackshift="-2"> <prototype name="__stdcall" extrapop="2" stackshift="2">
<input> <input>
<pentry minsize="1" maxsize="1"> <pentry minsize="1" maxsize="1">
<register name="A"/> <register name="A"/>

View file

@ -76,7 +76,6 @@
<symbol name="USART3_UDRE" address="code:0x006E" entry="true"/> <symbol name="USART3_UDRE" address="code:0x006E" entry="true"/>
<symbol name="USART3_TX" address="code:0x0070" entry="true"/> <symbol name="USART3_TX" address="code:0x0070" entry="true"/>
<!-- See /usr/lib/avr/include/avr/iom64.h -->
<symbol name="PINA" address="mem:0x20"/> <symbol name="PINA" address="mem:0x20"/>
<symbol name="DDRA" address="mem:0x21"/> <symbol name="DDRA" address="mem:0x21"/>
<symbol name="PORTA" address="mem:0x22"/> <symbol name="PORTA" address="mem:0x22"/>
@ -134,14 +133,19 @@
<symbol name="SPMCSR" address="mem:0x57"/> <symbol name="SPMCSR" address="mem:0x57"/>
<symbol name="RAMPZ" address="mem:0x5b"/>
<symbol name="EIND" address="mem:0x5c"/> <symbol name="EIND" address="mem:0x5c"/>
<symbol name="_SPL" address="mem:0x5d"/>
<symbol name="_SPH" address="mem:0x5e"/>
<symbol name="SREG" address="mem:0x5f"/>
<symbol name="WDTCSR" address="mem:0x60"/> <symbol name="WDTCSR" address="mem:0x60"/>
<symbol name="CLKPR" address="mem:0x61"/> <symbol name="CLKPR" address="mem:0x61"/>
<symbol name="PRR2" address="mem:0x63"/>
<symbol name="PRR0" address="mem:0x64"/> <symbol name="PRR0" address="mem:0x64"/>
<symbol name="PRR1" address="mem:0x65"/> <symbol name="PRR1" address="mem:0x65"/>
<symbol name="OSCCAL" address="mem:0x66"/> <symbol name="OSCCAL" address="mem:0x66"/>
<symbol name="BGCR" address="mem:0x67"/>
<symbol name="PCICR" address="mem:0x68"/> <symbol name="PCICR" address="mem:0x68"/>
<symbol name="EICRA" address="mem:0x69"/> <symbol name="EICRA" address="mem:0x69"/>
<symbol name="EICRB" address="mem:0x6a"/> <symbol name="EICRB" address="mem:0x6a"/>
@ -154,9 +158,10 @@
<symbol name="TIMSK3" address="mem:0x71"/> <symbol name="TIMSK3" address="mem:0x71"/>
<symbol name="TIMSK4" address="mem:0x72"/> <symbol name="TIMSK4" address="mem:0x72"/>
<symbol name="TIMSK5" address="mem:0x73"/> <symbol name="TIMSK5" address="mem:0x73"/>
<symbol name="XMCRA" address="mem:0x74"/>
<symbol name="XMCRB" address="mem:0x75"/>
<symbol name="NEMCR" address="mem:0x75"/>
<symbol name="ADCSRC" address="mem:0x77"/>
<symbol name="ADCL" address="mem:0x78"/> <symbol name="ADCL" address="mem:0x78"/>
<symbol name="ADCH" address="mem:0x79"/> <symbol name="ADCH" address="mem:0x79"/>
<symbol name="ADCSRA" address="mem:0x7a"/> <symbol name="ADCSRA" address="mem:0x7a"/>
@ -224,7 +229,8 @@
<symbol name="TWDR" address="mem:0xbb"/> <symbol name="TWDR" address="mem:0xbb"/>
<symbol name="TWCR" address="mem:0xbc"/> <symbol name="TWCR" address="mem:0xbc"/>
<symbol name="TWAMR" address="mem:0xbd"/> <symbol name="TWAMR" address="mem:0xbd"/>
<symbol name="IRQ_MASK1" address="mem:0xbe"/>
<symbol name="IRQ_STATUS1" address="mem:0xbf"/>
<symbol name="UCSR0A" address="mem:0xc0"/> <symbol name="UCSR0A" address="mem:0xc0"/>
<symbol name="UCSR0B" address="mem:0xc1"/> <symbol name="UCSR0B" address="mem:0xc1"/>
<symbol name="UCSR0C" address="mem:0xc2"/> <symbol name="UCSR0C" address="mem:0xc2"/>
@ -245,22 +251,63 @@
<symbol name="UCSR2B" address="mem:0xd1"/> <symbol name="UCSR2B" address="mem:0xd1"/>
<symbol name="UCSR2C" address="mem:0xd2"/> <symbol name="UCSR2C" address="mem:0xd2"/>
<symbol name="UBRR2L" address="mem:0xd4"/> <symbol name="SCRSTRLL" address="mem:0xd7"/>
<symbol name="UBRR2H" address="mem:0xd5"/> <symbol name="SCRSTRLH " address="mem:0xd8"/>
<symbol name="UDR2" address="mem:0xd6"/> <symbol name="SCRSTRHL" address="mem:0xd9"/>
<symbol name="SCRSTRHH" address="mem:0xda"/>
<symbol name="SCCSR" address="mem:0xdb"/>
<symbol name="SCCR0 " address="mem:0xdc"/>
<symbol name="SCCR1" address="mem:0xdd"/>
<symbol name="SCSR" address="mem:0xde"/>
<symbol name="SCIRQM" address="mem:0xdf"/>
<symbol name="SCIRQS" address="mem:0xe0"/>
<symbol name="SCCNTLL " address="mem:0xe1"/>
<symbol name="SCCNTLH" address="mem:0xe2"/>
<symbol name="SCCNTHL" address="mem:0xe3"/>
<symbol name="SCCNTHH" address="mem:0xe4"/>
<symbol name="SCBTSRLL " address="mem:0xe5"/>
<symbol name="SCBTSRLH" address="mem:0xe6"/>
<symbol name="SCBTSRHL" address="mem:0xe7"/>
<symbol name="SCBTSRHH" address="mem:0xe8"/>
<symbol name="SCTSRLL" address="mem:0xe9"/>
<symbol name="SCTSRLH" address="mem:0xea"/>
<symbol name="SCTSRHL" address="mem:0xeb"/>
<symbol name="SCTSRHH" address="mem:0xec"/>
<symbol name="SCOCR3LL" address="mem:0xed"/>
<symbol name="SCOCR3LH" address="mem:0xee"/>
<symbol name="SCOCR3HL" address="mem:0xef"/>
<symbol name="SCOCR3HH" address="mem:0xf0"/>
<symbol name="SCOCR2LL " address="mem:0xf1"/>
<symbol name="SCOCR2LH" address="mem:0xf2"/>
<symbol name="SCOCR2HL" address="mem:0xf3"/>
<symbol name="SCOCR2HH" address="mem:0xf4"/>
<symbol name="SCOCR1LL " address="mem:0xf5"/>
<symbol name="SCOCR1LH" address="mem:0xf6"/>
<symbol name="SCOCR1HL" address="mem:0xf7"/>
<symbol name="SCOCR1HH" address="mem:0xf8"/>
<symbol name="SCTSTRLL" address="mem:0xf9"/>
<symbol name="SCTSTRLH" address="mem:0xfa"/>
<symbol name="SCTSTRHL" address="mem:0xfb"/>
<symbol name="SCTSTRHH" address="mem:0xfc"/>
<symbol name="PINH" address="mem:0x100"/> <symbol name="MAFCR0" address="mem:0x100c"/>
<symbol name="DDRH" address="mem:0x101"/> <symbol name="MAFCR1" address="mem:0x10d"/>
<symbol name="PORTH" address="mem:0x102"/> <symbol name="MAFSA0L" address="mem:0x10e"/>
<symbol name="PINJ" address="mem:0x103"/> <symbol name="MAFSA0H" address="mem:0x10f"/>
<symbol name="DDRJ" address="mem:0x104"/> <symbol name="MAFPA0L" address="mem:0x110"/>
<symbol name="PORTJ" address="mem:0x105"/> <symbol name="MAFPA0H" address="mem:0x111"/>
<symbol name="PINK" address="mem:0x106"/> <symbol name="MAFSA1L" address="mem:0x112"/>
<symbol name="DDRK" address="mem:0x107"/> <symbol name="MAFSA1H" address="mem:0x113"/>
<symbol name="PORK" address="mem:0x108"/> <symbol name="MAFPA1L" address="mem:0x114"/>
<symbol name="PINL" address="mem:0x109"/> <symbol name="MAFPA1H" address="mem:0x115"/>
<symbol name="DDRL" address="mem:0x10a"/> <symbol name="MAFSA2L" address="mem:0x116"/>
<symbol name="PORTL" address="mem:0x10b"/> <symbol name="MAFSA2H" address="mem:0x117"/>
<symbol name="MAFPA2L" address="mem:0x1018"/>
<symbol name="MAFPA2H" address="mem:0x119"/>
<symbol name="MAFSA3L" address="mem:0x11a"/>
<symbol name="MAFSA3H" address="mem:0x11b"/>
<symbol name="MAFPA3L" address="mem:0x11c"/>
<symbol name="MAFPA3H" address="mem:0x11d"/>
<symbol name="TCCR5A" address="mem:0x120"/> <symbol name="TCCR5A" address="mem:0x120"/>
<symbol name="TCCR5B" address="mem:0x121"/> <symbol name="TCCR5B" address="mem:0x121"/>
@ -277,21 +324,87 @@
<symbol name="OCR5CL" address="mem:0x12c"/> <symbol name="OCR5CL" address="mem:0x12c"/>
<symbol name="OCR5CH" address="mem:0x12d"/> <symbol name="OCR5CH" address="mem:0x12d"/>
<symbol name="UCSR3A" address="mem:0x130"/> <symbol name="LLCR" address="mem:0x12f"/>
<symbol name="UCSR3B" address="mem:0x131"/> <symbol name="LLDRL" address="mem:0x130"/>
<symbol name="UCSR3C" address="mem:0x132"/> <symbol name="LLDRH" address="mem:0x131"/>
<symbol name="DRTRAM3" address="mem:0x132"/>
<symbol name="DRTRAM2" address="mem:0x133"/>
<symbol name="DRTRAM1" address="mem:0x134"/>
<symbol name="DRTRAM0" address="mem:0x135"/>
<symbol name="DPDS0" address="mem:0x136"/>
<symbol name="DPDS1" address="mem:0x137"/>
<symbol name="PARCR" address="mem:0x138"/>
<symbol name="TRXPR" address="mem:0x139"/>
<symbol name="UBRR3L" address="mem:0x134"/> <symbol name="AES_CTRL" address="mem:0x13c"/>
<symbol name="UBRR3H" address="mem:0x135"/> <symbol name="AES_STATUS" address="mem:0x13d"/>
<symbol name="UDR3" address="mem:0x136"/> <symbol name="AES_STATE" address="mem:0x13e"/>
<symbol name="AES_KEY" address="mem:0x13f"/>
<symbol name="TRX_STATUS" address="mem:0x141"/>
<symbol name="TRX_STATE" address="mem:0x142"/>
<symbol name="TRX_CTRL_0" address="mem:0x143"/>
<symbol name="TRX_CTRL_1" address="mem:0x144"/>
<symbol name="PHY_TX_PWR" address="mem:0x145"/>
<symbol name="PHY_RSSI" address="mem:0x146"/>
<symbol name="PHY_ED_LEVEL" address="mem:0x147"/>
<symbol name="PHY_CC_CCA" address="mem:0x148"/>
<symbol name="CCA_THRES" address="mem:0x149"/>
<symbol name="RX_CTRL" address="mem:0x14a"/>
<symbol name="SFD_VALUE" address="mem:0x14b"/>
<symbol name="TRX_CTRL_2" address="mem:0x14c"/>
<symbol name="ANT_DIV" address="mem:0x14d"/>
<symbol name="IRQ_MASK" address="mem:0x14e"/>
<symbol name="IRQ_STATUS" address="mem:0x14f"/>
<symbol name="VREG_CTRL" address="mem:0x150"/>
<symbol name="BATMON" address="mem:0x151"/>
<symbol name="XOSC_CTRL" address="mem:0x152"/>
<symbol name="CC_CTRL_0" address="mem:0x153"/>
<symbol name="CC_CTRL_1" address="mem:0x154"/>
<symbol name="RX_SYN" address="mem:0x155"/>
<symbol name="TRX_RPC" address="mem:0x156"/>
<symbol name="XAH_CTRL_1" address="mem:0x157"/>
<symbol name="FTN_CTRL" address="mem:0x158"/>
<symbol name="PLL_CF" address="mem:0x15a"/>
<symbol name="PLL_DCU" address="mem:0x15b"/>
<symbol name="PART_NUM" address="mem:0x15c"/>
<symbol name="VERSION_NUM " address="mem:0x15d"/>
<symbol name="MAN_ID_0" address="mem:0x15e"/>
<symbol name="MAN_ID_1" address="mem:0x15f"/>
<symbol name="SHORT_ADDR_0" address="mem:0x160"/>
<symbol name="SHORT_ADDR_1" address="mem:0x161"/>
<symbol name="PAN_ID_0" address="mem:0x162"/>
<symbol name="PAN_ID_1" address="mem:0x163"/>
<symbol name="IEEE_ADDR_0" address="mem:0x164"/>
<symbol name="IEEE_ADDR_1" address="mem:0x165"/>
<symbol name="IEEE_ADDR_2" address="mem:0x166"/>
<symbol name="IEEE_ADDR_3" address="mem:0x167"/>
<symbol name="IEEE_ADDR_4" address="mem:0x168"/>
<symbol name="IEEE_ADDR_5" address="mem:0x169"/>
<symbol name="IEEE_ADDR_6" address="mem:0x16a"/>
<symbol name="IEEE_ADDR_7" address="mem:0x16b"/>
<symbol name="XAH_CTRL_0" address="mem:0x16c"/>
<symbol name="CSMA_SEED_0" address="mem:0x16d"/>
<symbol name="CSMA_SEED_1" address="mem:0x16e"/>
<symbol name="CSMA_BE" address="mem:0x16f"/>
<symbol name="TST_CTRL_DIGI" address="mem:0x176"/>
<symbol name="TST_RX_LENGTH" address="mem:0x17b"/>
<symbol name="TST_AGC" address="mem:0x17c"/>
<symbol name="TST_SDM" address="mem:0x17d"/>
<symbol name="TRXFBST" address="mem:0x180"/>
<symbol name="TRXFBEND" address="mem:0x1ff"/>
</default_symbols> </default_symbols>
<default_memory_blocks> <default_memory_blocks>
<memory_block name="regalias" start_address="mem:0x00" length="0x20" initialized="false"/> <memory_block name="regalias" start_address="mem:0x00" length="0x20" initialized="false"/>
<memory_block name="iospace" start_address="mem:0x20" length="0xd0" initialized="false"/> <memory_block name="iospace" start_address="mem:0x20" length="0x1e0" initialized="false"/>
<memory_block name="mem" start_address="mem:0x200" length="0xf00" initialized="false"/> <memory_block name="data" start_address="mem:0x200" length="0x2000" initialized="false"/>
</default_memory_blocks> </default_memory_blocks>

View file

@ -6,7 +6,7 @@
</global> </global>
<stackpointer register="sp" space="ram" growth="positive"/> <stackpointer register="sp" space="ram" growth="positive"/>
<default_proto> <default_proto>
<prototype name="__stdcall" extrapop="unknown" stackshift="4"> <prototype name="__stdcall" extrapop="unknown" stackshift="-4">
<input> <input>
<pentry minsize="1" maxsize="4"> <pentry minsize="1" maxsize="4">
<register name="r12"/> <register name="r12"/>

File diff suppressed because it is too large Load diff