mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
Merge branch 'master' of https://github.com/NationalSecurityAgency/ghidra into DocFix
This commit is contained in:
commit
8c35703cc4
77 changed files with 4546 additions and 2442 deletions
|
@ -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>
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(" ");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(' ');
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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...");
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataType getParent() {
|
public Composite getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<String> myList = new ArrayList<String>() {
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // class definition
|
||||||
|
* public class MyList implements List<String> {
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Whereas this case will not work:
|
||||||
|
* <pre>
|
||||||
|
* // local variable with the type specified
|
||||||
|
* List<String> myList = new ArrayList<String>();
|
||||||
|
* </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();
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue