Candidate release of source code.

This commit is contained in:
Dan 2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions

View file

@ -0,0 +1,55 @@
<?xml version='1.0' encoding='ISO-8859-1' ?>
<!--
This is an XML file intended to be parsed by the Ghidra help system. It is loosely based
upon the JavaHelp table of contents document format. The Ghidra help system uses a
TOC_Source.xml file to allow a module with help to define how its contents appear in the
Ghidra help viewer's table of contents. The main document (in the Base module)
defines a basic structure for the
Ghidra table of contents system. Other TOC_Source.xml files may use this structure to insert
their files directly into this structure (and optionally define a substructure).
In this document, a tag can be either a <tocdef> or a <tocref>. The former is a definition
of an XML item that may have a link and may contain other <tocdef> and <tocref> children.
<tocdef> items may be referred to in other documents by using a <tocref> tag with the
appropriate id attribute value. Using these two tags allows any module to define a place
in the table of contents system (<tocdef>), which also provides a place for
other TOC_Source.xml files to insert content (<tocref>).
During the help build time, all TOC_Source.xml files will be parsed and validated to ensure
that all <tocref> tags point to valid <tocdef> tags. From these files will be generated
<module name>_TOC.xml files, which are table of contents files written in the format
desired by the JavaHelp system. Additionally, the genated files will be merged together
as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra
help GUI, there will be on table of contents that has been created from the definitions in
all of the modules' TOC_Source.xml files.
Tags and Attributes
<tocdef>
-id - the name of the definition (this must be unique across all TOC_Source.xml files)
-text - the display text of the node, as seen in the help GUI
-target** - the file to display when the node is clicked in the GUI
-sortgroup - this is a string that defines where a given node should appear under a given
parent. The string values will be sorted by the JavaHelp system using
a javax.text.RulesBasedCollator. If this attribute is not specified, then
the text of attribute will be used.
<tocref>
-id - The id of the <tocdef> that this reference points to
**The URL for the target is relative and should start with 'help/topics'. This text is
used by the Ghidra help system to provide a universal starting point for all links so that
they can be resolved at runtime, across modules.
-->
<tocroot>
<tocref id="Code Browser">
<tocdef id="Program Differences" sortgroup="e" text="Program Differences" target="help/topics/Diff/Diff.htm" />
</tocref>
</tocroot>

View file

@ -0,0 +1,58 @@
/* ###
* 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.
*/
/*
WARNING!
This file is copied to all help directories. If you change this file, you must copy it
to each src/main/help/help/shared directory.
Java Help Note: JavaHelp does not accept sizes (like in 'margin-top') in anything but
px (pixel) or with no type marking.
*/
body { margin-bottom: 50px; margin-left: 10px; margin-right: 10px; margin-top: 10px; } /* some padding to improve readability */
li { font-family:times new roman; font-size:14pt; }
h1 { color:#000080; font-family:times new roman; font-size:36pt; font-style:italic; font-weight:bold; text-align:center; }
h2 { margin: 10px; margin-top: 20px; color:#984c4c; font-family:times new roman; font-size:18pt; font-weight:bold; }
h3 { margin-left: 10px; margin-top: 20px; color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; }
h4 { margin-left: 10px; margin-top: 20px; font-family:times new roman; font-size:14pt; font-style:italic; }
/*
P tag code. Most of the help files nest P tags inside of blockquote tags (the was the
way it had been done in the beginning). The net effect is that the text is indented. In
modern HTML we would use CSS to do this. We need to support the Ghidra P tags, nested in
blockquote tags, as well as naked P tags. The following two lines accomplish this. Note
that the 'blockquote p' definition will inherit from the first 'p' definition.
*/
p { margin-left: 40px; font-family:times new roman; font-size:14pt; }
blockquote p { margin-left: 10px; }
p.providedbyplugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
p.ProvidedByPlugin { color:#7f7f7f; margin-left: 10px; font-size:14pt; margin-top:100px }
p.relatedtopic { color:#800080; margin-left: 10px; font-size:14pt; }
p.RelatedTopic { color:#800080; margin-left: 10px; font-size:14pt; }
/*
We wish for a tables to have space between it and the preceding element, so that text
is not too close to the top of the table. Also, nest the table a bit so that it is clear
the table relates to the preceding text.
*/
table { margin-left: 20px; margin-top: 10px; width: 80%;}
td { font-family:times new roman; font-size:14pt; vertical-align: top; }
th { font-family:times new roman; font-size:14pt; font-weight:bold; background-color: #EDF3FE; }
code { color: black; font-family: courier new; font-size: 14pt; }

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,000 B

View file

@ -0,0 +1,158 @@
/* ###
* IP: GHIDRA
* NOTE: This disables auto analysis while differences are applied and restores auto analysis enablement at end.
*
* 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 ghidra.app.plugin.core.diff;
import ghidra.app.events.ProgramSelectionPluginEvent;
import ghidra.app.plugin.core.analysis.AnalysisWorker;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
import docking.widgets.dialogs.ReadTextDialog;
/**
* Command to apply diffs to current program.
*
*/
class ApplyDiffCommand extends BackgroundCommand implements AnalysisWorker {
private AddressSetView p1AddressSet;
private DiffController diffControl;
private String title;
private String applyMsg;
private boolean applied;
private ProgramDiffPlugin plugin;
/**
* Constructor.
*/
ApplyDiffCommand(ProgramDiffPlugin plugin, AddressSetView program1AddressSet,
DiffController diffControl) {
super("Apply Differences", false, true, true);
this.plugin = plugin;
this.p1AddressSet = program1AddressSet;
this.diffControl = diffControl;
}
@Override
public boolean analysisWorkerCallback(Program program, Object workerContext, TaskMonitor monitor)
throws Exception, CancelledException {
// Diff apply done with analysis disabled
return diffControl.apply(p1AddressSet, monitor);
}
@Override
public String getWorkerName() {
return getName();
}
/**
* @see ghidra.framework.cmd.BackgroundCommand#applyTo(ghidra.framework.model.DomainObject, ghidra.util.task.TaskMonitor)
*/
@Override
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
monitor.setMessage("ApplyDiffTask starting...");
applied = false;
final ProgramLocation origLocation = plugin.getProgramLocation();
if (!plugin.isTaskInProgress()) {
plugin.setTaskInProgress(true);
String statusMsg = "One or more differences couldn't be applied.";
title = "Program Diff: One or more differences couldn't be applied.";
applyMsg = null;
setStatusMsg(null);
try {
AutoAnalysisManager autoAnalysisManager =
AutoAnalysisManager.getAnalysisManager(plugin.getFirstProgram());
boolean merged = autoAnalysisManager.scheduleWorker(this, null, false, monitor);
if (merged) {
statusMsg =
"Apply differences has finished."
+ " If your expected change didn't occur, check your Diff Apply Settings.";
title = "Program Diff: Apply differences has finished.";
applied = true;
}
else {
applyMsg = diffControl.getApplyMessage();
}
}
catch (InterruptedException e) {
applyMsg = "Unexpected InterruptedException\n" + diffControl.getApplyMessage();
}
catch (InvocationTargetException e) {
Throwable t = e.getCause();
String message = "";
// Protect against dereferencing the getCause call above, which may return null.
if (t != null) {
String excMessage = t.getMessage();
if (excMessage != null && excMessage.length() > 0) {
message = excMessage + "\n";
}
}
Msg.showError(this, plugin.getListingPanel(), "Error Applying Diff",
"An error occured while applying differences.\n"
+ "Only some of the differences may have been applied.",
(t != null) ? t : e);
applyMsg = message + diffControl.getApplyMessage();
}
catch (CancelledException e) {
statusMsg =
"User cancelled \"Apply Differences\". "
+ "Differences were only partially applied.";
applyMsg = diffControl.getApplyMessage();
}
finally {
setStatusMsg(statusMsg);
plugin.getTool().setStatusInfo(statusMsg);
plugin.setTaskInProgress(false);
Runnable r = new Runnable() {
@Override
public void run() {
plugin.adjustDiffDisplay();
plugin.firePluginEvent(new ProgramSelectionPluginEvent(plugin.getName(),
plugin.getCurrentSelection(), plugin.getCurrentProgram()));
plugin.programLocationChanged(origLocation, null);
if (applyMsg != null && applyMsg.length() > 0) {
ReadTextDialog detailsDialog = new ReadTextDialog(title, applyMsg);
plugin.getTool().showDialog(detailsDialog, plugin.getListingPanel());
}
}
};
// // The events were disabled while doing apply Diff. Now re-enable them by firing object restored event.
// ((DomainObjectAdapter)currentProgram).fireEvent(new DomainObjectChangeRecord(
// DomainObject.DO_OBJECT_RESTORED));
// ((DomainObjectAdapter)currentProgram).flushEvents();
if (!monitor.isCancelled()) {
SwingUtilities.invokeLater(r);
}
}
}
return applied;
}
}

View file

@ -0,0 +1,213 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import javax.swing.SwingUtilities;
import ghidra.framework.model.DomainObjectException;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.program.util.*;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.ClosedException;
import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
/**
* Task that generates the address set containing the differences between
* two programs.
*
*/
class CreateDiffTask extends Task {
private ProgramDiffPlugin plugin;
private Program program1;
private Program program2;
private AddressSetView limitedAddressSet;
private ProgramDiffFilter diffFilter;
private ProgramMergeFilter applyFilter;
private DiffApplySettingsProvider diffApplySettingsProvider;
private boolean isLimitedToSelection;
/**
* Construct new LoadDiffTask that loads the dialog with the two
* programs and indicates their differences. The differences should be
* restricted to the limitedAddressSet.
*
*/
CreateDiffTask(ProgramDiffPlugin plugin, Program program1, Program program2,
AddressSetView limitedAddressSet, boolean isLimitedToSelection,
ProgramDiffFilter diffFilter, ProgramMergeFilter applyFilter) {
super("Checking Program Differences", true, false, false);
this.plugin = plugin;
this.program1 = program1;
this.program2 = program2;
this.limitedAddressSet = limitedAddressSet;
this.isLimitedToSelection = isLimitedToSelection;
this.diffFilter = diffFilter;
this.applyFilter = applyFilter;
}
/**
* This is the method TaskMonitor called to do the work.
*
* @param monitor The TaskMonitor that will monitor the executing Task. Will be null if
* this task declared that it does not use a TaskMonitor
*/
@Override
public void run(TaskMonitor monitor) {
if (plugin.isTaskInProgress()) {
return;
}
try {
DiffController dc = null;
plugin.setTaskInProgress(true);
monitor.setIndeterminate(true);
monitor.setMessage("Checking Program Differences");
try {
dc = new DiffController(program1, program2, limitedAddressSet, this.diffFilter,
this.applyFilter, monitor);
AddressSetView filteredDifferences = dc.getFilteredDifferences(monitor);
boolean noFilteredDifferencesFound = filteredDifferences.isEmpty();
plugin.setDiffController(dc);
dc.differencesChanged(monitor);
dc.setLocation(plugin.getCurrentAddress());
monitor.setMessage("Done");
Runnable r = () -> displayDifferencesMessageIfNecessary(noFilteredDifferencesFound);
SwingUtilities.invokeLater(r);
}
catch (DomainObjectException e) {
Throwable cause = e.getCause();
if (cause instanceof ClosedException) {
// this can happen if you close the tool while this task is calculating diffs
}
else {
throw e;
}
}
catch (ProgramConflictException e) {
showErrorMessage(e.getMessage());
return;
}
catch (CancelledException e) {
plugin.setDiffController(dc);
}
}
finally {
completed();
}
}
private void displayDifferencesMessageIfNecessary(final boolean noFilteredDifferencesFound) {
try {
ProgramMemoryComparator programMemoryComparator =
new ProgramMemoryComparator(program1, program2);
boolean hasMemoryDifferences = programMemoryComparator.hasMemoryDifferences();
String title = null;
String message = null;
if (isLimitedToSelection) {
if (noFilteredDifferencesFound) {
title = "No Differences In Selection";
message = "No differences were found for the addresses in the selection" +
"\nand for the types of program information being compared by this Diff.";
}
}
else {
if (hasMemoryDifferences) {
title = "Memory Differs";
message = getMemoryDifferenceMessage(noFilteredDifferencesFound,
programMemoryComparator);
}
else if (noFilteredDifferencesFound) {
// Not a Diff on a selection, memory is the same, and no differences found
// for current filter and compatible addresses.
title = "No Differences";
message =
"No differences were found for the addresses that are compatible between the two" +
"\nprograms for the types of program information being compared by this Diff.";
}
}
if (title != null) {
String note =
"\n \nNote: Some parts of the program are not handled by Diff (for example:" +
"\n Markup where only one program has that memory address," +
"\n Registers that are not common to both programs' languages," +
"\n Program Trees, Data Types that haven't been applied to memory, etc.)";
Msg.showInfo(getClass(), plugin.getListingPanel(), title, message + note);
}
}
catch (ProgramConflictException e) {
Msg.showError(getClass(), plugin.getListingPanel(), "Can't Compare Memory",
"Diff can't compare the two programs memory. " + e.getMessage());
return;
}
}
private String getMemoryDifferenceMessage(final boolean noFilteredDifferencesFound,
ProgramMemoryComparator programMemoryComparator) {
String message;
message = "The memory addresses defined by the two programs are not the same.\n \n" +
(noFilteredDifferencesFound ? "However, no differences were found "
: "Differences are highlighted ") +
"for the addresses that are compatible between" +
"\nthe two programs for the types of program information being compared by this Diff.";
AddressSet addressesOnlyInOne = programMemoryComparator.getAddressesOnlyInOne();
if (!addressesOnlyInOne.isEmpty()) {
message +=
"\n \nSome addresses are only in program 1 : " + addressesOnlyInOne.toString();
}
AddressSet addressesOnlyInTwo = programMemoryComparator.getAddressesOnlyInTwo();
if (!addressesOnlyInTwo.isEmpty()) {
message +=
"\n \nSome addresses are only in program 2 : " + addressesOnlyInTwo.toString();
}
return message;
}
private void showErrorMessage(String message) {
SystemUtilities.runSwingLater(() -> Msg.showError(getClass(),
plugin.getTool().getToolFrame(), "Can't Perform Diff", message));
}
private void completed() {
if (plugin.isDisposed()) {
// the tool was closed while this task was running
return;
}
if (plugin.getCurrentProgram() == null) {
// the program was closed while this task was running
return;
}
SystemUtilities.runSwingLater(() -> {
diffApplySettingsProvider = plugin.getDiffApplySettingsProvider();
diffApplySettingsProvider.configure(applyFilter);
plugin.adjustDiffDisplay();
});
plugin.setTaskInProgress(false);
}
}

View file

@ -0,0 +1,459 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import docking.ActionContext;
import docking.action.*;
import docking.widgets.OptionDialog;
import ghidra.app.plugin.core.codebrowser.OtherPanelContext;
import ghidra.app.services.CodeViewerService;
import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.util.ToolConstants;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import resources.ResourceManager;
/**
* Creates the actions for the program diff plugin.
*/
class DiffActionManager {
private static final String SELECT_GROUP = "Select";
private static final String GET_DIFF_GROUP = "GetDiff";
private static final String DIFF_INFO_GROUP = "DiffInfo";
private static final String DIFF_NAVIGATE_GROUP = "DiffNavigate";
private static final String TOGGLE_VIEW_ICON_NAME = "images/table_relationship.png";
private static final String GROUP = "Diff";
private ProgramDiffPlugin plugin;
private CodeViewerService codeViewerService;
static final String APPLY_DIFFS_ACTION = "Apply Differences";
static final String APPLY_DIFFS_NEXT_ACTION = "Apply Differences and Goto Next Difference";
static final String IGNORE_DIFFS_NEXT_ACTION = "Ignore Selection and Goto Next Difference";
static final String NEXT_DIFF_ACTION = "Next Difference";
static final String PREVIOUS_DIFF_ACTION = "Previous Difference";
static final String DIFF_DETAILS_ACTION = "Diff Location Details";
static final String SHOW_DIFF_SETTINGS_ACTION = "Show Diff Apply Settings";
static final String GET_DIFFS_ACTION = "Get Differences";
static final String SELECT_ALL_DIFFS_ACTION = "Select All Differences";
static final String P1_SELECT_TO_P2_ACTION = "Set Program1 Selection On Program2";
static final String OPEN_CLOSE_PROGRAM2_ACTION = "Open/Close Program View";
static final String VIEW_PROGRAM_DIFF_ACTION = "View Program Differences";
private DockingAction applyDiffsAction;
private DockingAction applyDiffsNextAction;
private DockingAction ignoreDiffsAction;
private DockingAction nextDiffAction;
private DockingAction previousDiffAction;
private DockingAction diffDetailsAction;
private DockingAction showDiffSettingsAction;
private DockingAction getDiffsAction;
private DockingAction selectAllDiffsAction;
private DockingAction p1SelectToP2Action;
private ToggleDockingAction openCloseProgram2Action;
private DockingAction viewProgramDiffAction;
/**
* Creates an action manager for the Program Diff plugin.
*/
public DiffActionManager(ProgramDiffPlugin plugin) {
this.plugin = plugin;
createActions();
}
/**
* Sets the code viewer service that the Program Diff will use for setting local actions.
* @param codeViewerService the code viewer service
*/
void setCodeViewerService(CodeViewerService codeViewerService) {
this.codeViewerService = codeViewerService;
codeViewerService.addLocalAction(openCloseProgram2Action);
}
/**
* Adds the Program Diff's local actions to the code viewer.
*/
void addActions() {
codeViewerService.addLocalAction(applyDiffsAction);
codeViewerService.addLocalAction(applyDiffsNextAction);
codeViewerService.addLocalAction(ignoreDiffsAction);
codeViewerService.addLocalAction(diffDetailsAction);
codeViewerService.addLocalAction(nextDiffAction);
codeViewerService.addLocalAction(previousDiffAction);
codeViewerService.addLocalAction(showDiffSettingsAction);
codeViewerService.addLocalAction(getDiffsAction);
codeViewerService.addLocalAction(selectAllDiffsAction);
codeViewerService.addLocalAction(p1SelectToP2Action);
}
/**
* Removes the Program Diff's local actions from the code viewer.
*/
void removeActions() {
codeViewerService.removeLocalAction(applyDiffsAction);
codeViewerService.removeLocalAction(applyDiffsNextAction);
codeViewerService.removeLocalAction(ignoreDiffsAction);
codeViewerService.removeLocalAction(diffDetailsAction);
codeViewerService.removeLocalAction(nextDiffAction);
codeViewerService.removeLocalAction(previousDiffAction);
codeViewerService.removeLocalAction(showDiffSettingsAction);
codeViewerService.removeLocalAction(getDiffsAction);
codeViewerService.removeLocalAction(selectAllDiffsAction);
codeViewerService.removeLocalAction(p1SelectToP2Action);
}
/**
* Called to adjust the Program Diff's actions when a program is closed.
* @param program the closed program
*/
void programClosed(Program program) {
boolean hasProgram = (plugin.getCurrentProgram() != null);
openCloseProgram2Action.setEnabled(hasProgram && !plugin.isTaskInProgress());
}
/**
* Called to adjust or add/remove the Program Diff's actions when a program
* becomes the active program.
* @param program the newly active program
*/
void setActiveProgram(Program program) {
viewProgramDiffAction.setEnabled(program != null);
boolean enabled = program != null && !plugin.isTaskInProgress();
openCloseProgram2Action.setEnabled(enabled);
if (enabled) {
if (openCloseProgram2Action.isSelected()) {
// we are diffing--is the current program the diff program?
Program firstProgram = plugin.getFirstProgram();
if (firstProgram == program) {
openCloseProgram2Action.setDescription("Close Diff View");
}
else {
openCloseProgram2Action.setDescription("Bring Diff View to Front");
}
}
}
else {
// no active diff
openCloseProgram2Action.setDescription("Open Diff View");
}
}
/**
* Notification to the action manager that a program was opened as the
* second program to the program Diff. Actions are adjusted accordingly.
*/
void secondProgramOpened() {
openCloseProgram2Action.setSelected(true);
openCloseProgram2Action.setDescription("Close Diff View");
Program firstProgram = plugin.getFirstProgram();
Program secondProgram = plugin.getSecondProgram();
String firstName = firstProgram.getName();
String secondName = secondProgram.getName();
//@formatter:off
openCloseProgram2Action.setDescription("<html><center>Close Diff View</center><br>" +
"Current diff: " +
"<b>"+firstName+"</b> to <b>" +secondName+"</b>");
//@formatter:on
}
/**
* Notification to the action manager that the second program to the
* program Diff was closed. Actions are adjusted accordingly.
*/
void secondProgramClosed() {
openCloseProgram2Action.setSelected(false);
openCloseProgram2Action.setDescription("Open Diff View");
showDiffSettingsAction.setEnabled(false);
diffDetailsAction.setEnabled(false);
removeActions();
}
void setP1SelectToP2ActionEnabled(boolean enabled) {
p1SelectToP2Action.setEnabled(enabled);
}
void setOpenCloseActionSelected(boolean selected) {
openCloseProgram2Action.setSelected(selected);
}
void updateActions(boolean taskInProgress, boolean inDiff, boolean hasSelectionInView,
boolean applyFilterIsSet, boolean hasProgram2, boolean hasHighlights) {
DiffController diffControl = plugin.getDiffController();
applyDiffsAction.setEnabled(!taskInProgress && inDiff && hasSelectionInView);
applyDiffsNextAction.setEnabled(
!taskInProgress && inDiff && hasSelectionInView && diffControl.hasNext());
ignoreDiffsAction.setEnabled(!taskInProgress && inDiff && hasSelectionInView);
nextDiffAction.setEnabled(!taskInProgress && inDiff && diffControl.hasNext());
previousDiffAction.setEnabled(!taskInProgress && inDiff && diffControl.hasPrevious());
diffDetailsAction.setEnabled(!taskInProgress && hasProgram2);
showDiffSettingsAction.setEnabled(!taskInProgress && inDiff);
getDiffsAction.setEnabled(!taskInProgress && hasProgram2);
selectAllDiffsAction.setEnabled(!taskInProgress && !inDiff || hasHighlights);
p1SelectToP2Action.setEnabled(hasProgram2 && !plugin.getCurrentSelection().isEmpty());
Program currentProgram = plugin.getCurrentProgram();
boolean hasProgram = (currentProgram != null);
openCloseProgram2Action.setEnabled(hasProgram && !taskInProgress);
}
/**
* Removes all the actions.
*/
void dispose() {
codeViewerService.removeLocalAction(openCloseProgram2Action);
plugin.getTool().removeAction(viewProgramDiffAction);
removeActions();
}
private void createActions() {
viewProgramDiffAction = new DockingAction(VIEW_PROGRAM_DIFF_ACTION, plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
plugin.selectProgram2();
}
};
String[] menuPath = { ToolConstants.MENU_TOOLS, "Program &Differences..." };
viewProgramDiffAction.setEnabled(plugin.getCurrentProgram() != null);
viewProgramDiffAction.setMenuBarData(new MenuData(menuPath, "View"));
viewProgramDiffAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_2, 0));
viewProgramDiffAction.setHelpLocation(
new HelpLocation(HelpTopics.DIFF, "Program_Differences"));
plugin.getTool().addAction(viewProgramDiffAction);
applyDiffsAction = new DiffAction(APPLY_DIFFS_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.applyDiff();
}
};
ImageIcon icon = ResourceManager.loadImage("images/pencil16.png");
applyDiffsAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_F3, 0));
applyDiffsAction.setPopupMenuData(
new MenuData(new String[] { "Apply Selection" }, icon, GROUP));
applyDiffsAction.setDescription(
"Applies the differences from the second program's selection using the settings.");
applyDiffsAction.setToolBarData(new ToolBarData(icon, GROUP));
applyDiffsNextAction = new DiffAction(APPLY_DIFFS_NEXT_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.applyDiffAndGoNext();
}
};
icon = ResourceManager.loadImage("images/pencil_arrow16.png");
String[] applyDiffsPath = { "Apply Selection and Goto Next Difference" };
applyDiffsNextAction.setPopupMenuData(new MenuData(applyDiffsPath, icon, GROUP));
applyDiffsNextAction.setKeyBindingData(
new KeyBindingData(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK));
applyDiffsNextAction.setToolBarData(new ToolBarData(icon, GROUP));
applyDiffsNextAction.setDescription(
"Applies the differences from the second program's selection using the settings. " +
"Then moves the cursor to the next difference.");
ignoreDiffsAction = new DiffAction(IGNORE_DIFFS_NEXT_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.ignoreDiff();
}
};
icon = ResourceManager.loadImage("images/eraser_arrow16.png");
ignoreDiffsAction.setPopupMenuData(
new MenuData(new String[] { "Ignore Selection and Goto Next Difference" }, icon,
GROUP));
ignoreDiffsAction.setDescription(
"Ignores the selected program differences and moves the cursor to the next difference.");
ignoreDiffsAction.setToolBarData(new ToolBarData(icon, GROUP));
nextDiffAction = new DiffAction(NEXT_DIFF_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.nextDiff();
}
};
icon = ResourceManager.loadImage("images/down.png");
nextDiffAction.setKeyBindingData(
new KeyBindingData('N', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
nextDiffAction.setPopupMenuData(
new MenuData(new String[] { "Next Difference" }, icon, DIFF_NAVIGATE_GROUP));
nextDiffAction.setToolBarData(new ToolBarData(icon, DIFF_NAVIGATE_GROUP));
nextDiffAction.setDescription("Go to the next highlighted difference.");
previousDiffAction = new DiffAction(PREVIOUS_DIFF_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.previousDiff();
}
};
icon = ResourceManager.loadImage("images/up.png");
previousDiffAction.setKeyBindingData(
new KeyBindingData('P', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
previousDiffAction.setPopupMenuData(
new MenuData(new String[] { "Previous Difference" }, icon, DIFF_NAVIGATE_GROUP));
previousDiffAction.setToolBarData(new ToolBarData(icon, DIFF_NAVIGATE_GROUP));
previousDiffAction.setDescription("Go to previous highlighted difference.");
diffDetailsAction = new DiffAction(DIFF_DETAILS_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.showDiffDetails();
}
};
icon = ResourceManager.loadImage("images/xmag.png");
diffDetailsAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_F5, 0));
diffDetailsAction.setPopupMenuData(
new MenuData(new String[] { "Location Details..." }, icon, DIFF_INFO_GROUP));
diffDetailsAction.setToolBarData(new ToolBarData(icon, DIFF_INFO_GROUP));
diffDetailsAction.setDescription(
"Show details of the program differences at the current location.");
showDiffSettingsAction = new DiffAction(SHOW_DIFF_SETTINGS_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.showDiffApplySettings();
}
};
showDiffSettingsAction.setPopupMenuData(
new MenuData(new String[] { "Show Diff Apply Settings..." }, GET_DIFF_GROUP));
showDiffSettingsAction.setDescription(
"Displays the current program difference apply settings.");
showDiffSettingsAction.setToolBarData(
new ToolBarData(DiffApplySettingsProvider.ICON, GET_DIFF_GROUP));
getDiffsAction = new DiffAction(GET_DIFFS_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.diff();
}
};
icon = ResourceManager.loadImage("images/Diff16.png");
getDiffsAction.setPopupMenuData(
new MenuData(new String[] { "Get Differences..." }, icon, GET_DIFF_GROUP));
getDiffsAction.setToolBarData(new ToolBarData(icon, GET_DIFF_GROUP));
getDiffsAction.setDescription("Determines program differences.");
selectAllDiffsAction = new DiffAction(SELECT_ALL_DIFFS_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.selectAllDiffs();
}
};
selectAllDiffsAction.setPopupMenuData(
new MenuData(new String[] { "Select All Differences" }, SELECT_GROUP));
selectAllDiffsAction.setDescription(
"Selects all highlighted differences in the second program.");
p1SelectToP2Action = new DiffAction(P1_SELECT_TO_P2_ACTION) {
@Override
public void actionPerformed(ActionContext context) {
plugin.setP1SelectionOnP2();
}
};
icon = ResourceManager.loadImage("images/DiffSelect16.png");
p1SelectToP2Action.setDescription(
"Select Program 2 highlights using selection in Program 1.");
p1SelectToP2Action.setToolBarData(new ToolBarData(icon, SELECT_GROUP));
openCloseProgram2Action =
new ToggleDockingAction(OPEN_CLOSE_PROGRAM2_ACTION, plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
//
// No active diff--start one.
//
if (openCloseProgram2Action.isSelected()) {
plugin.selectProgram2();
return;
}
//
// We have a diff session--is the current program the one for the diff session?
// If not, simply make the diff program the active program (this is useful for
// users when they were diffing, but then opened a different program).
//
if (activateDiffProgram()) {
// clicking the action will change the selected state--keep it selected
setSelected(true);
setDescription("Close Diff View");
return;
}
//
// Otherwise, close the diff.
//
closeDiff();
}
private void closeDiff() {
int choice = OptionDialog.showYesNoCancelDialog(null, "Close Diff Session",
"Close the current diff session?");
if (choice == OptionDialog.YES_OPTION) {
plugin.closeProgram2();
setDescription("Open Diff View");
}
else {
// clicking the action will change the selected state--keep it selected
setSelected(true);
setDescription("Close Diff View");
}
}
private boolean activateDiffProgram() {
Program currentProgram = plugin.getCurrentProgram();
Program firstProgram = plugin.getFirstProgram();
boolean isFirstProgram = (firstProgram == currentProgram);
if (isFirstProgram) {
return false; // already active--nothing to do!
}
plugin.activeProgram(firstProgram);
return true;
}
};
icon = ResourceManager.loadImage(TOGGLE_VIEW_ICON_NAME);
openCloseProgram2Action.setEnabled(false);
openCloseProgram2Action.setKeyBindingData(
new KeyBindingData('C', InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
openCloseProgram2Action.setToolBarData(new ToolBarData(icon, "zzz"));
openCloseProgram2Action.setHelpLocation(
new HelpLocation(HelpTopics.DIFF, OPEN_CLOSE_PROGRAM2_ACTION));
openCloseProgram2Action.setSelected(false);
openCloseProgram2Action.setDescription("Open Diff View");
}
private abstract class DiffAction extends DockingAction {
DiffAction(String name) {
super(name, plugin.getName());
setHelpLocation(new HelpLocation(HelpTopics.DIFF, name));
setEnabled(false);
}
@Override
public boolean isAddToPopup(ActionContext context) {
return (context instanceof OtherPanelContext);
}
}
}

View file

@ -0,0 +1,544 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.util.ProgramMergeFilter;
import ghidra.util.HelpLocation;
/**
* Manages the options for the Diff apply settings.
*/
class DiffApplySettingsOptionManager {
public static final String DIFF_OPTIONS = "Diff";
public static final String DIFF_APPLY_SETTINGS_OPTIONS = "Default Apply Settings";
private static final int PROGRAM_CONTEXT = 1 << 0;
private static final int BYTES = 1 << 1;
private static final int CODE_UNITS = 1 << 2;
private static final int REFERENCES = 1 << 3;
private static final int PLATE_COMMENTS = 1 << 4;
private static final int PRE_COMMENTS = 1 << 5;
private static final int EOL_COMMENTS = 1 << 6;
private static final int REPEATABLE_COMMENTS = 1 << 7;
private static final int POST_COMMENTS = 1 << 8;
private static final int SYMBOLS = 1 << 9;
private static final int BOOKMARKS = 1 << 10;
private static final int PROPERTIES = 1 << 11;
private static final int FUNCTIONS = 1 << 12;
private static final int FUNCTION_TAGS = 1 << 13;
private static final String OPTION_PROGRAM_CONTEXT = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "Program Context";
private static final String OPTION_BYTES = DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER +
"Bytes";
private static final String OPTION_CODE_UNITS = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "Code Units";
private static final String OPTION_REFERENCES = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "References";
private static final String OPTION_PLATE_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "Plate Comments";
private static final String OPTION_PRE_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "Pre Comments";
private static final String OPTION_EOL_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "End Of Line Comments";
private static final String OPTION_REPEATABLE_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "Repeatable Comments";
private static final String OPTION_POST_COMMENTS = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "Post Comments";
private static final String OPTION_SYMBOLS = DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER +
"Labels";
private static final String OPTION_BOOKMARKS = DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER +
"Bookmarks";
private static final String OPTION_PROPERTIES = DIFF_APPLY_SETTINGS_OPTIONS +
Options.DELIMITER + "Properties";
private static final String OPTION_FUNCTIONS = DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER +
"Functions";
private static final String OPTION_FUNCTION_TAGS =
DIFF_APPLY_SETTINGS_OPTIONS + Options.DELIMITER + "Function Tags";
// public static final String MERGE = "Merge";
// public static final String MERGE_SYMBOLS_1 = "Merge";
// public static final String MERGE_SYMBOLS_2 = "Merge & Set Primary";
public static enum REPLACE_CHOICE {
IGNORE("Ignore"), REPLACE("Replace");
private String description;
REPLACE_CHOICE(String description) {
this.description = description;
}
@Override
public String toString() {
return description;
}
}
public static enum MERGE_CHOICE {
IGNORE("Ignore"), REPLACE("Replace"), MERGE("Merge");
private String description;
MERGE_CHOICE(String description) {
this.description = description;
}
@Override
public String toString() {
return description;
}
}
// Special choice for symbols in that if merged, should the primary attribute be set.
public static enum SYMBOL_MERGE_CHOICE {
IGNORE("Ignore"),
REPLACE("Replace"),
MERGE_DONT_SET_PRIMARY("Merge"),
MERGE_AND_SET_PRIMARY("Merge & Set Primary");
private String description;
SYMBOL_MERGE_CHOICE(String description) {
this.description = description;
}
@Override
public String toString() {
return description;
}
}
private Plugin plugin;
private HelpLocation help;
private String HELP_TOPIC = "Diff";
/**
* Creates a new option manager for Diff apply settings.
* @param plugin the plugin that owns this options manager.
*/
DiffApplySettingsOptionManager(Plugin plugin) {
this.plugin = plugin;
init();
}
/**
* Initializes class variables for this option manager and gets the default options.
*/
private void init() {
help = new HelpLocation(HELP_TOPIC, "DiffApplySettingsToolOptions");
Options options = plugin.getTool().getOptions(DIFF_OPTIONS);
options.setOptionsHelpLocation(help);
// Set the help strings
options.registerOption(
OPTION_PROGRAM_CONTEXT,
REPLACE_CHOICE.REPLACE,
help,
getReplaceDescription("program context register value",
"program context register values"));
options.registerOption(OPTION_BYTES, REPLACE_CHOICE.REPLACE, help,
getReplaceDescription("byte", "bytes"));
options.registerOption(OPTION_CODE_UNITS, REPLACE_CHOICE.REPLACE, help,
getReplaceDescription("code unit", "code units"));
options.registerOption(OPTION_REFERENCES, REPLACE_CHOICE.REPLACE, help,
getReplaceDescription("reference", "references"));
options.registerOption(OPTION_PLATE_COMMENTS, MERGE_CHOICE.MERGE, help,
getMergeDescription("plate comment", "plate comments"));
options.registerOption(OPTION_PRE_COMMENTS, MERGE_CHOICE.MERGE, help,
getMergeDescription("pre-comment", "pre-comments"));
options.registerOption(OPTION_EOL_COMMENTS, MERGE_CHOICE.MERGE, help,
getMergeDescription("end of line comment", "end of line comments"));
options.registerOption(OPTION_REPEATABLE_COMMENTS, MERGE_CHOICE.MERGE, help,
getMergeDescription("repeatable comment", "repeatable comments"));
options.registerOption(OPTION_POST_COMMENTS, MERGE_CHOICE.MERGE, help,
getMergeDescription("post comment", "post comments"));
options.registerOption(OPTION_SYMBOLS, SYMBOL_MERGE_CHOICE.MERGE_AND_SET_PRIMARY, help,
getSymbolDescription());
options.registerOption(OPTION_BOOKMARKS, REPLACE_CHOICE.REPLACE, help,
getReplaceDescription("bookmark", "bookmarks"));
options.registerOption(OPTION_PROPERTIES, REPLACE_CHOICE.REPLACE, help,
getReplaceDescription("property", "properties"));
options.registerOption(OPTION_FUNCTIONS, REPLACE_CHOICE.REPLACE, help,
getReplaceDescription("function", "functions"));
options.registerOption(OPTION_FUNCTION_TAGS, MERGE_CHOICE.MERGE, help,
getReplaceDescription("function tag", "function tags"));
getDefaultApplyFilter();
}
/**
* Gets the merge filter for the default apply settings.
* @return merge filter indicating default apply settings.
*/
public ProgramMergeFilter getDefaultApplyFilter() {
PluginTool tool = plugin.getTool();
Options options = tool.getOptions(DIFF_OPTIONS);
// case PROGRAM_CONTEXT:
// case BYTES:
// case CODE_UNITS:
// case REFERENCES:
// case BOOKMARKS:
// case PROPERTIES:
// case FUNCTIONS:
// Get the current settings as options
REPLACE_CHOICE programContext =
options.getEnum(OPTION_PROGRAM_CONTEXT, REPLACE_CHOICE.REPLACE);
REPLACE_CHOICE bytes = options.getEnum(OPTION_BYTES, REPLACE_CHOICE.REPLACE);
REPLACE_CHOICE codeUnits = options.getEnum(OPTION_CODE_UNITS, REPLACE_CHOICE.REPLACE);
REPLACE_CHOICE references = options.getEnum(OPTION_REFERENCES, REPLACE_CHOICE.REPLACE);
MERGE_CHOICE plateComments = options.getEnum(OPTION_PLATE_COMMENTS, MERGE_CHOICE.MERGE);
MERGE_CHOICE preComments = options.getEnum(OPTION_PRE_COMMENTS, MERGE_CHOICE.MERGE);
MERGE_CHOICE eolComments = options.getEnum(OPTION_EOL_COMMENTS, MERGE_CHOICE.MERGE);
MERGE_CHOICE repeatableComments =
options.getEnum(OPTION_REPEATABLE_COMMENTS, MERGE_CHOICE.MERGE);
MERGE_CHOICE postComments = options.getEnum(OPTION_POST_COMMENTS, MERGE_CHOICE.MERGE);
SYMBOL_MERGE_CHOICE symbols =
options.getEnum(OPTION_SYMBOLS, SYMBOL_MERGE_CHOICE.MERGE_AND_SET_PRIMARY);
REPLACE_CHOICE bookmarks = options.getEnum(OPTION_BOOKMARKS, REPLACE_CHOICE.REPLACE);
REPLACE_CHOICE properties = options.getEnum(OPTION_PROPERTIES, REPLACE_CHOICE.REPLACE);
REPLACE_CHOICE functions = options.getEnum(OPTION_FUNCTIONS, REPLACE_CHOICE.REPLACE);
MERGE_CHOICE functionTags = options.getEnum(OPTION_FUNCTION_TAGS, MERGE_CHOICE.MERGE);
// Convert the options to a merge filter.
ProgramMergeFilter filter = new ProgramMergeFilter();
filter.setFilter(ProgramMergeFilter.PROGRAM_CONTEXT, programContext.ordinal());
filter.setFilter(ProgramMergeFilter.BYTES, bytes.ordinal());
filter.setFilter(ProgramMergeFilter.CODE_UNITS, codeUnits.ordinal());
filter.setFilter(ProgramMergeFilter.REFERENCES, references.ordinal());
filter.setFilter(ProgramMergeFilter.PLATE_COMMENTS, plateComments.ordinal());
filter.setFilter(ProgramMergeFilter.PRE_COMMENTS, preComments.ordinal());
filter.setFilter(ProgramMergeFilter.EOL_COMMENTS, eolComments.ordinal());
filter.setFilter(ProgramMergeFilter.REPEATABLE_COMMENTS, repeatableComments.ordinal());
filter.setFilter(ProgramMergeFilter.POST_COMMENTS, postComments.ordinal());
filter.setFilter(ProgramMergeFilter.SYMBOLS,
convertSymbolMergeChoiceToMergeChoice(symbols).ordinal());
filter.setFilter(ProgramMergeFilter.BOOKMARKS, bookmarks.ordinal());
filter.setFilter(ProgramMergeFilter.PROPERTIES, properties.ordinal());
filter.setFilter(ProgramMergeFilter.FUNCTIONS, functions.ordinal());
filter.setFilter(ProgramMergeFilter.FUNCTION_TAGS, functionTags.ordinal());
filter.setFilter(ProgramMergeFilter.PRIMARY_SYMBOL,
convertSymbolMergeChoiceToReplaceChoiceForPrimay(symbols).ordinal());
return filter;
}
private String getReplaceDescription(String settingName, String pluralName) {
return getBaseDescription(settingName) + "\n" + getIgnoreDescription(settingName) + "\n" +
getReplaceDescription(pluralName);
}
private String getMergeDescription(String settingName, String pluralName) {
return getBaseDescription(settingName) + "\n" + getIgnoreDescription(settingName) + "\n" +
getReplaceDescription(pluralName) + "\n" + getMergeDescription(pluralName);
}
private String getBaseDescription(String settingName) {
return "The default Diff setting for applying " + settingName + " differences.";
}
private String getIgnoreDescription(String settingName) {
return " Ignore - Don't apply " + settingName + " differences.";
}
private String getReplaceDescription(String pluralName) {
return " Replace - Replace " + pluralName + " in the program with those from program 2.";
}
private String getMergeDescription(String pluralName) {
return " Merge - Merge " + pluralName + " from program 2 into the current program.";
}
private String getSymbolDescription() {
String settingName = "label";
String pluralName = "labels";
return getBaseDescription(settingName) + "\n" + getIgnoreDescription(settingName) + "\n" +
getReplaceDescription(pluralName) + "\n" + " " + "Merge" + " - Merge " + pluralName +
" from program 2 into the current program and don't change which " + settingName +
" is primary.\n" + " " + "Merge & Set Primary" + " - Merge " + pluralName +
" from program 2 into the current program and set the primary " + settingName +
" as it is in program 2, if possible.";
}
/**
* Saves the indicated merge filter as the default apply settings.
* @param defaultApplyFilter merge filter indicating default apply settings.
*/
public void saveDefaultApplyFilter(ProgramMergeFilter newDefaultApplyFilter) {
PluginTool tool = plugin.getTool();
Options options = tool.getOptions(DIFF_OPTIONS);
saveReplaceOption(options, newDefaultApplyFilter, PROGRAM_CONTEXT);
saveReplaceOption(options, newDefaultApplyFilter, BYTES);
saveReplaceOption(options, newDefaultApplyFilter, REFERENCES);
saveReplaceOption(options, newDefaultApplyFilter, BOOKMARKS);
saveReplaceOption(options, newDefaultApplyFilter, PROPERTIES);
saveReplaceOption(options, newDefaultApplyFilter, FUNCTIONS);
saveMergeOption(options, newDefaultApplyFilter, PLATE_COMMENTS);
saveMergeOption(options, newDefaultApplyFilter, PRE_COMMENTS);
saveMergeOption(options, newDefaultApplyFilter, EOL_COMMENTS);
saveMergeOption(options, newDefaultApplyFilter, REPEATABLE_COMMENTS);
saveMergeOption(options, newDefaultApplyFilter, POST_COMMENTS);
saveMergeOption(options, newDefaultApplyFilter, FUNCTION_TAGS);
saveCodeUnitReplaceOption(options, newDefaultApplyFilter, CODE_UNITS);
saveSymbolMergeOption(options, newDefaultApplyFilter, SYMBOLS);
tool.setConfigChanged(true);
}
private void saveCodeUnitReplaceOption(Options options, ProgramMergeFilter defaultApplyFilter,
int setting) {
int filter =
(defaultApplyFilter.getFilter(ProgramMergeFilter.INSTRUCTIONS) >= defaultApplyFilter.getFilter(ProgramMergeFilter.DATA)) ? ProgramMergeFilter.INSTRUCTIONS
: ProgramMergeFilter.DATA;
REPLACE_CHOICE defaultSetting = REPLACE_CHOICE.REPLACE;
REPLACE_CHOICE optionSetting = options.getEnum(getOptionName(setting), defaultSetting);
REPLACE_CHOICE diffSetting = convertTypeToReplaceEnum(defaultApplyFilter, filter);
if (!diffSetting.equals(optionSetting)) {
options.setEnum(getOptionName(setting), diffSetting);
}
}
private void saveReplaceOption(Options options, ProgramMergeFilter defaultApplyFilter,
int setting) {
REPLACE_CHOICE defaultSetting = REPLACE_CHOICE.REPLACE;
REPLACE_CHOICE optionSetting = options.getEnum(getOptionName(setting), defaultSetting);
REPLACE_CHOICE diffSetting =
convertTypeToReplaceEnum(defaultApplyFilter, getMergeFilterType(setting));
if (!diffSetting.equals(optionSetting)) {
options.setEnum(getOptionName(setting), diffSetting);
}
}
private void saveMergeOption(Options options, ProgramMergeFilter defaultApplyFilter, int setting) {
MERGE_CHOICE defaultSetting = MERGE_CHOICE.MERGE;
MERGE_CHOICE optionSetting = options.getEnum(getOptionName(setting), defaultSetting);
MERGE_CHOICE diffSetting =
convertTypeToMergeEnum(defaultApplyFilter, getMergeFilterType(setting));
if (!diffSetting.equals(optionSetting)) {
options.setEnum(getOptionName(setting), diffSetting);
}
}
private void saveSymbolMergeOption(Options options, ProgramMergeFilter defaultApplyFilter,
int setting) {
SYMBOL_MERGE_CHOICE defaultSetting = SYMBOL_MERGE_CHOICE.MERGE_AND_SET_PRIMARY;
SYMBOL_MERGE_CHOICE optionSetting = options.getEnum(getOptionName(setting), defaultSetting);
SYMBOL_MERGE_CHOICE diffSetting = getSymbolMergeEnum(defaultApplyFilter);
if (!diffSetting.equals(optionSetting)) {
options.setEnum(getOptionName(setting), diffSetting);
}
}
/**
* @param applySetting
* @return
*/
private int getMergeFilterType(int applySetting) {
switch (applySetting) {
case PROGRAM_CONTEXT:
return ProgramMergeFilter.PROGRAM_CONTEXT;
case BYTES:
return ProgramMergeFilter.BYTES;
case CODE_UNITS:
return ProgramMergeFilter.CODE_UNITS;
case REFERENCES:
return ProgramMergeFilter.REFERENCES;
case BOOKMARKS:
return ProgramMergeFilter.BOOKMARKS;
case PROPERTIES:
return ProgramMergeFilter.PROPERTIES;
case FUNCTIONS:
return ProgramMergeFilter.FUNCTIONS;
case PLATE_COMMENTS:
return ProgramMergeFilter.PLATE_COMMENTS;
case PRE_COMMENTS:
return ProgramMergeFilter.PRE_COMMENTS;
case EOL_COMMENTS:
return ProgramMergeFilter.EOL_COMMENTS;
case REPEATABLE_COMMENTS:
return ProgramMergeFilter.REPEATABLE_COMMENTS;
case POST_COMMENTS:
return ProgramMergeFilter.POST_COMMENTS;
case SYMBOLS:
return ProgramMergeFilter.SYMBOLS;
case FUNCTION_TAGS:
return ProgramMergeFilter.FUNCTION_TAGS;
default:
return 0;
}
}
/**
*
* @param applySetting
* @return
*/
private String getOptionName(int applySetting) {
switch (applySetting) {
case PROGRAM_CONTEXT:
return OPTION_PROGRAM_CONTEXT;
case BYTES:
return OPTION_BYTES;
case CODE_UNITS:
return OPTION_CODE_UNITS;
case REFERENCES:
return OPTION_REFERENCES;
case BOOKMARKS:
return OPTION_BOOKMARKS;
case PROPERTIES:
return OPTION_PROPERTIES;
case FUNCTIONS:
return OPTION_FUNCTIONS;
case PLATE_COMMENTS:
return OPTION_PLATE_COMMENTS;
case PRE_COMMENTS:
return OPTION_PRE_COMMENTS;
case EOL_COMMENTS:
return OPTION_EOL_COMMENTS;
case REPEATABLE_COMMENTS:
return OPTION_REPEATABLE_COMMENTS;
case POST_COMMENTS:
return OPTION_POST_COMMENTS;
case SYMBOLS:
return OPTION_SYMBOLS;
case FUNCTION_TAGS:
return OPTION_FUNCTION_TAGS;
default:
return null;
}
}
// /**
// * Converts the current value of the indicated StringEnum to a ProgramMergeFilter type's individual filter setting.
// * @param option The StringEnum with a value to be checked.
// * @return the individual filter for the apply setting associated with the StringEnum.
// */
// private int convertOptionToFilter(StringEnum option) {
// return convertIndexToFilter(option.getSelectedValueIndex());
// }
/**
* Converts the "combined" option for symbols into just the MERGE_CHOICE which is used
* to decide if the symbol should be ignored, replaced, or merged.
* @param the SYMBOL_MERGE_CHOICE
* @return the MERGE_CHOICE
*/
MERGE_CHOICE convertSymbolMergeChoiceToMergeChoice(SYMBOL_MERGE_CHOICE symbolMergeChoice) {
switch (symbolMergeChoice) {
default:
case IGNORE:
return MERGE_CHOICE.IGNORE;
case REPLACE:
return MERGE_CHOICE.REPLACE;
case MERGE_DONT_SET_PRIMARY:
case MERGE_AND_SET_PRIMARY:
return MERGE_CHOICE.MERGE;
}
}
/**
* Converts the "combined" option for symbols into just the REPLACE_CHOICE which is used
* to decide if the "primary" attribute should be used.
* @param the SYMBOL_MERGE_CHOICE
* @return the REPLACE_CHOICE
*/
REPLACE_CHOICE convertSymbolMergeChoiceToReplaceChoiceForPrimay(
SYMBOL_MERGE_CHOICE symbolMergeChoice) {
switch (symbolMergeChoice) {
default:
case IGNORE:
case MERGE_DONT_SET_PRIMARY:
return REPLACE_CHOICE.IGNORE;
case REPLACE:
case MERGE_AND_SET_PRIMARY:
return REPLACE_CHOICE.REPLACE;
}
}
/**
* Gets the standard index number of a StringEnum for the "Symbols" option for the indicated
* filter values of the symbols apply filter and primary symbol filter.
* @param symbolFilter the ProgramMergeFilter.SYMBOLS filter.
* @param primaryFilter the ProgramMergeFilter.PRIMARY_SYMBOL filter.
* @return the index for setting the StringEnum to the value associated with the filter values.
*/
int convertFiltersToSymbolIndex(int symbolFilter, int primaryFilter) {
switch (symbolFilter) {
case ProgramMergeFilter.IGNORE:
default:
return 0; // Ignore
case ProgramMergeFilter.REPLACE:
return 1; // Replace
case ProgramMergeFilter.MERGE:
if (primaryFilter == ProgramMergeFilter.REPLACE) {
return 3; // Merge 2 (Set Primary as in Program 2)
}
return 2; // Merge 1 (Set Primary as in Program 1)
}
}
/**
* Creates a standard StringEnum for handling an option of the indicated type that allows Ignore and Replace.
* The current selected item will be based on the filter's value.
* @param defaultApplyFilter the ProgramMergeFilter value to use to select an item initially.
* @param type the ProgramMergeFilter filter type
* @return the StringEnum
*/
private REPLACE_CHOICE convertTypeToReplaceEnum(ProgramMergeFilter defaultApplyFilter, int type) {
int filter = defaultApplyFilter.getFilter(type);
return REPLACE_CHOICE.values()[filter];
}
/**
* Creates a standard StringEnum for handling an option of the indicated type that allows Ignore, Replace and Merge.
* The current selected item will be based on the filter's value.
* @param defaultApplyFilter the ProgramMergeFilter value to use to select an item initially.
* @param type the ProgramMergeFilter filter type
* @return the StringEnum
*/
private MERGE_CHOICE convertTypeToMergeEnum(ProgramMergeFilter defaultApplyFilter, int type) {
int filter = defaultApplyFilter.getFilter(type);
return MERGE_CHOICE.values()[filter];
}
/**
* Creates a standard StringEnum for the Symbols option.
* The current selected item will be based on the filter's value.
* @param defaultApplyFilter the ProgramMergeFilter value to use to select an item initially.
* @param type the ProgramMergeFilter filter type
* @return the StringEnum
*/
private SYMBOL_MERGE_CHOICE getSymbolMergeEnum(ProgramMergeFilter defaultApplyFilter) {
int symbolsFilter = defaultApplyFilter.getFilter(ProgramMergeFilter.SYMBOLS);
int primarySymbolFilter = defaultApplyFilter.getFilter(ProgramMergeFilter.PRIMARY_SYMBOL);
int filter = convertFiltersToSymbolIndex(symbolsFilter, primarySymbolFilter);
return SYMBOL_MERGE_CHOICE.values()[filter];
}
public void dispose() {
// Don't have any options change listeners to clean up.
}
}

View file

@ -0,0 +1,468 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.*;
import docking.WindowPosition;
import docking.widgets.VariableHeightPanel;
import ghidra.app.plugin.core.diff.DiffApplySettingsOptionManager.*;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.util.ProgramMergeFilter;
import ghidra.util.HelpLocation;
import resources.ResourceManager;
/**
* The DiffSettingsDialog is used to change the types of differences currently
* highlighted. It also allows the user to change the types of differences being
* applied and whether labels and/or comments are being merged or replaced.
*/
public class DiffApplySettingsProvider extends ComponentProviderAdapter {
public static final String APPLY_FILTER_CHANGED_ACTION = "Apply Filter Changed";
public static final ImageIcon ICON = ResourceManager.loadImage("images/settings16.gif");
public static final String TITLE = "Diff Apply Settings";
private ProgramDiffPlugin plugin;
private ArrayList<Choice> choices;
private Choice programContextCB;
private Choice bytesCB;
private Choice codeUnitsCB;
private Choice refsCB;
private SymbolsChoice symbolsCB;
private Choice plateCommentsCB;
private Choice preCommentsCB;
private Choice eolCommentsCB;
private Choice repeatableCommentsCB;
private Choice postCommentsCB;
private Choice bookmarksCB;
private Choice propertiesCB;
private Choice functionsCB;
private Choice functionTagsCB;
private int applyProgramContext;
private int applyBytes;
private int applyCodeUnits;
private int applyReferences;
private int applyPlateComments;
private int applyPreComments;
private int applyEolComments;
private int applyRepeatableComments;
private int applyPostComments;
private int applySymbols;
private int applyBookmarks;
private int applyProperties;
private int applyFunctions;
private int applyFunctionTags;
private int replacePrimary;
private ProgramMergeFilter applyFilter;
private boolean adjustingApplyFilter = false;
private JComponent applyPanel;
private ArrayList<ActionListener> listenerList = new ArrayList<>();
private boolean pgmContextEnabled = true;
public DiffApplySettingsProvider(ProgramDiffPlugin plugin) {
super(plugin.getTool(), TITLE, plugin.getName());
this.plugin = plugin;
setIcon(ICON);
setWindowMenuGroup("Diff");
setDefaultWindowPosition(WindowPosition.BOTTOM);
// transient so that is only there while the Diff is available
setTransient();
setHelpLocation(new HelpLocation("Diff", "Diff_Apply_Settings"));
applyPanel = createApplyFilterPanel();
applyPanel.setName("Diff Apply Settings Panel");
}
public void configure(ProgramMergeFilter apply) {
setApplyFilter(apply);
}
public void addActions() {
plugin.getTool().addLocalAction(this,
new SaveApplySettingsAction(this, plugin.applySettingsMgr));
plugin.getTool().addLocalAction(this, new DiffIgnoreAllAction(this));
plugin.getTool().addLocalAction(this, new DiffReplaceAllAction(this));
plugin.getTool().addLocalAction(this, new DiffMergeAllAction(this));
}
/**
* Create a panel for the user choices to indicate the filter settings.
*/
private void createChoices() {
choices = new ArrayList<>();
programContextCB = new Choice("Program Context", false);
programContextCB.addActionListener(e -> {
applyProgramContext = programContextCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.PROGRAM_CONTEXT, applyProgramContext);
applyFilterChanged();
});
choices.add(programContextCB);
bytesCB = new Choice("Bytes", false);
bytesCB.addActionListener(e -> {
applyBytes = bytesCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.BYTES, applyBytes);
applyFilterChanged();
});
choices.add(bytesCB);
codeUnitsCB = new Choice("Code Units", false);
codeUnitsCB.addActionListener(e -> {
applyCodeUnits = codeUnitsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.CODE_UNITS | ProgramMergeFilter.EQUATES,
applyCodeUnits);
applyFilterChanged();
});
choices.add(codeUnitsCB);
refsCB = new Choice("References", false);
refsCB.addActionListener(e -> {
applyReferences = refsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.REFERENCES, applyReferences);
applyFilterChanged();
});
choices.add(refsCB);
plateCommentsCB = new Choice("Plate Comments", true);
plateCommentsCB.addActionListener(e -> {
applyPlateComments = plateCommentsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.PLATE_COMMENTS, applyPlateComments);
applyFilterChanged();
});
choices.add(plateCommentsCB);
preCommentsCB = new Choice("Pre Comments", true);
preCommentsCB.addActionListener(e -> {
applyPreComments = preCommentsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.PRE_COMMENTS, applyPreComments);
applyFilterChanged();
});
choices.add(preCommentsCB);
eolCommentsCB = new Choice("Eol Comments", true);
eolCommentsCB.addActionListener(e -> {
applyEolComments = eolCommentsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.EOL_COMMENTS, applyEolComments);
applyFilterChanged();
});
choices.add(eolCommentsCB);
repeatableCommentsCB = new Choice("Repeatable Comments", true);
repeatableCommentsCB.addActionListener(e -> {
applyRepeatableComments = repeatableCommentsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.REPEATABLE_COMMENTS, applyRepeatableComments);
applyFilterChanged();
});
choices.add(repeatableCommentsCB);
postCommentsCB = new Choice("Post Comments", true);
postCommentsCB.addActionListener(e -> {
applyPostComments = postCommentsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.POST_COMMENTS, applyPostComments);
applyFilterChanged();
});
choices.add(postCommentsCB);
symbolsCB = new SymbolsChoice();
symbolsCB.addActionListener(e -> {
SYMBOL_MERGE_CHOICE symbols =
SYMBOL_MERGE_CHOICE.values()[symbolsCB.getSelectedIndex()];
MERGE_CHOICE merge =
plugin.applySettingsMgr.convertSymbolMergeChoiceToMergeChoice(symbols);
REPLACE_CHOICE primary =
plugin.applySettingsMgr.convertSymbolMergeChoiceToReplaceChoiceForPrimay(symbols);
applySymbols = merge.ordinal();
replacePrimary = primary.ordinal();
applyFilter.setFilter(ProgramMergeFilter.SYMBOLS, applySymbols);
applyFilter.setFilter(ProgramMergeFilter.PRIMARY_SYMBOL, replacePrimary);
applyFilterChanged();
});
choices.add(symbolsCB);
bookmarksCB = new Choice("Bookmarks", false);
bookmarksCB.addActionListener(e -> {
applyBookmarks = bookmarksCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.BOOKMARKS, applyBookmarks);
applyFilterChanged();
});
choices.add(bookmarksCB);
propertiesCB = new Choice("Properties", false);
propertiesCB.addActionListener(e -> {
applyProperties = propertiesCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.PROPERTIES, applyProperties);
applyFilterChanged();
});
choices.add(propertiesCB);
functionsCB = new Choice("Functions", false);
functionsCB.addActionListener(e -> {
applyFunctions = functionsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.FUNCTIONS, applyFunctions);
applyFilterChanged();
});
choices.add(functionsCB);
functionTagsCB = new Choice("Function Tags", true);
functionTagsCB.addActionListener(e -> {
applyFunctionTags = functionTagsCB.getSelectedIndex();
applyFilter.setFilter(ProgramMergeFilter.FUNCTION_TAGS, applyFunctionTags);
applyFilterChanged();
});
choices.add(functionTagsCB);
int maxLabelWidth = 0;
int maxComboWidth = 0;
for (Choice choice : choices) {
maxLabelWidth = Math.max(maxLabelWidth, choice.label.getPreferredSize().width);
maxComboWidth = Math.max(maxComboWidth, choice.applyCB.getPreferredSize().width);
}
for (Choice choice : choices) {
int height = choice.label.getPreferredSize().height;
choice.label.setPreferredSize(new Dimension(maxLabelWidth, height));
height = choice.applyCB.getPreferredSize().height;
choice.applyCB.setPreferredSize(new Dimension(maxComboWidth, height));
}
Collections.sort(choices);
}
/**
* Create a panel for the checkboxes to indicate the filter settings.
*/
private JComponent createApplyFilterPanel() {
createChoices();
VariableHeightPanel panel = new VariableHeightPanel(false, 10, 3);
panel.setToolTipText(
"<HTML>" + "For each difference type, select whether to ignore, replace, or merge." +
"<BR>&nbsp&nbsp<B>Ignore</B> - don't apply this type of difference." +
"<BR>&nbsp&nbsp<B>Replace</B> - replace the difference type with the one from program 2." +
"<BR>&nbsp&nbsp<B>Merge</B> - merge the difference type from program 2 with what's there." +
"</HTML>");
for (Choice choice : choices) {
panel.add(choice);
}
return new JScrollPane(panel);
}
private void adjustApplyFilter() {
try {
adjustingApplyFilter = true;
programContextCB.setSelectedIndex(applyProgramContext);
bytesCB.setSelectedIndex(applyBytes);
codeUnitsCB.setSelectedIndex(applyCodeUnits);
refsCB.setSelectedIndex(applyReferences);
plateCommentsCB.setSelectedIndex(applyPlateComments);
preCommentsCB.setSelectedIndex(applyPreComments);
eolCommentsCB.setSelectedIndex(applyEolComments);
repeatableCommentsCB.setSelectedIndex(applyRepeatableComments);
postCommentsCB.setSelectedIndex(applyPostComments);
int symbolsIndex =
plugin.applySettingsMgr.convertFiltersToSymbolIndex(applySymbols, replacePrimary);
symbolsCB.setSelectedIndex(symbolsIndex);
bookmarksCB.setSelectedIndex(applyBookmarks);
propertiesCB.setSelectedIndex(applyProperties);
functionsCB.setSelectedIndex(applyFunctions);
functionTagsCB.setSelectedIndex(applyFunctionTags);
}
finally {
adjustingApplyFilter = false;
applyFilterChanged();
}
}
void setPgmContextEnabled(boolean enable) {
pgmContextEnabled = enable;
if (!pgmContextEnabled) {
applyProgramContext = 0;
programContextCB.setSelectedIndex(applyProgramContext);
}
}
/**
* Get a copy of the merge filter for applying differences.
* @return the current merge Filter settings.
*/
ProgramMergeFilter getApplyFilter() {
return new ProgramMergeFilter(applyFilter);
}
/**
* Sets the diff tool merge filter settings for applying differences.
* @param filter the new apply Filter settings.
*/
void setApplyFilter(ProgramMergeFilter filter) {
applyFilter = new ProgramMergeFilter(filter);
applyProgramContext = applyFilter.getFilter(ProgramMergeFilter.PROGRAM_CONTEXT);
applyBytes = applyFilter.getFilter(ProgramMergeFilter.BYTES);
applyCodeUnits = Math.max(applyFilter.getFilter(ProgramMergeFilter.INSTRUCTIONS),
applyFilter.getFilter(ProgramMergeFilter.DATA));
applyFilter.setFilter(ProgramMergeFilter.CODE_UNITS, applyCodeUnits);
applyReferences = applyFilter.getFilter(ProgramMergeFilter.REFERENCES);
applyPlateComments = applyFilter.getFilter(ProgramMergeFilter.PLATE_COMMENTS);
applyPreComments = applyFilter.getFilter(ProgramMergeFilter.PRE_COMMENTS);
applyEolComments = applyFilter.getFilter(ProgramMergeFilter.EOL_COMMENTS);
applyRepeatableComments = applyFilter.getFilter(ProgramMergeFilter.REPEATABLE_COMMENTS);
applyPostComments = applyFilter.getFilter(ProgramMergeFilter.POST_COMMENTS);
applySymbols = applyFilter.getFilter(ProgramMergeFilter.SYMBOLS);
applyBookmarks = applyFilter.getFilter(ProgramMergeFilter.BOOKMARKS);
applyProperties = applyFilter.getFilter(ProgramMergeFilter.PROPERTIES);
applyFunctions = applyFilter.getFilter(ProgramMergeFilter.FUNCTIONS);
applyFunctionTags = applyFilter.getFilter(ProgramMergeFilter.FUNCTION_TAGS);
replacePrimary = applyFilter.getFilter(ProgramMergeFilter.PRIMARY_SYMBOL);
adjustApplyFilter();
}
public void addActionListener(ActionListener listener) {
listenerList.add(listener);
}
public void removeActionListener(ActionListener listener) {
listenerList.remove(listener);
}
/**
* Return true if at least one of the checkboxes for the filter
* has been selected.
*/
boolean hasApplySelection() {
return ((applyProgramContext | applyBytes | applyCodeUnits | applyReferences |
applyPlateComments | applyPreComments | applyEolComments | applyRepeatableComments |
applyPostComments | applySymbols | applyBookmarks | applyProperties | applyFunctions |
applyFunctionTags) != 0);
}
protected void applyFilterChanged() {
if (adjustingApplyFilter) {
return;
}
for (int i = 0; i < listenerList.size(); i++) {
ActionListener listener = listenerList.get(i);
listener.actionPerformed(new ActionEvent(this, 0, APPLY_FILTER_CHANGED_ACTION));
}
}
@Override
public void closeComponent() {
// overridden to not remove this transient provider
plugin.getTool().showComponentProvider(this, false);
}
@Override
public JComponent getComponent() {
return applyPanel;
}
/**
* Gets the plugin associated with this provider.
*/
Plugin getPlugin() {
return plugin;
}
class Choice extends JPanel implements Comparable<Choice> {
private final static long serialVersionUID = 1L;
String type;
boolean allowMerge;
JLabel label;
JComboBox<Enum<?>> applyCB;
public Choice(String type, boolean allowMerge) {
setLayout(new BorderLayout());
this.type = type;
this.allowMerge = allowMerge;
init();
}
protected void init() {
applyCB =
new JComboBox<>(allowMerge ? DiffApplySettingsOptionManager.MERGE_CHOICE.values()
: DiffApplySettingsOptionManager.REPLACE_CHOICE.values());
applyCB.setName(type + " Diff Apply CB");
String typeName = type;
if (typeName.endsWith(" Comments")) {
typeName = "Comments, " + typeName.substring(0, typeName.length() - 9);
}
label = new JLabel(" " + typeName + " ");
label.setHorizontalAlignment(SwingConstants.RIGHT);
add(applyCB, BorderLayout.EAST);
add(label, BorderLayout.CENTER);
}
void setSelectedIndex(int index) {
applyCB.setSelectedIndex(index);
}
int getSelectedIndex() {
return applyCB.getSelectedIndex();
}
public void addActionListener(ActionListener listener) {
applyCB.addActionListener(listener);
}
public void removeActionListener(ActionListener listener) {
applyCB.removeActionListener(listener);
}
void setLabelSize(Dimension preferredSize) {
label.setPreferredSize(preferredSize);
}
void setComboSize(Dimension preferredSize) {
applyCB.setPreferredSize(preferredSize);
}
@Override
public int compareTo(Choice o) {
return label.toString().compareTo(o.label.toString());
}
}
class SymbolsChoice extends Choice {
private final static long serialVersionUID = 1L;
public SymbolsChoice() {
super("Labels", true);
}
@Override
protected void init() {
applyCB = new JComboBox<>(DiffApplySettingsOptionManager.SYMBOL_MERGE_CHOICE.values());
applyCB.setName(type + " Diff Apply CB");
label = new JLabel(" " + type + " ");
label.setHorizontalAlignment(SwingConstants.RIGHT);
add(applyCB, BorderLayout.EAST);
add(label, BorderLayout.CENTER);
}
}
}

View file

@ -0,0 +1,402 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 ghidra.app.plugin.core.diff;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.util.ArrayList;
/**
* DiffController controls a program Diff. It maintains address sets indicating
* the differences between two programs. It can limit the determined differences
* to an address set. It allows differences to be applied or ignored. It has a
* diff filter that controls the differences being indicated. It has a merge
* filter that controls the types of differences being applied. It allows
* differences at particular addresses to be ignored.
*
* The Diff controller also maintains the current location. It provides a way
* to navigate from one difference to the next or previous difference.
*/
public class DiffController {
private ProgramMergeManager mergeEngine;
private AddressSetView p1LastDiffs;
private AddressSetView p1LimitSet;
private Address p1CurrentAddress;
private ArrayList<DiffControllerListener> listenerList =
new ArrayList<DiffControllerListener>();
/**
* Constructor
* <P>Note: This method is potentially time consuming and should normally
* be called from within a background task.
* @param p1 The first program to apply differences to.
* @param p2 The second program to apply differences from.
* @param p1LimitSet Address set the determined differences are limited to.
* The addresses in this set should be derived from p1.
* @param diffFilter filter indicating difference types to mark.
* @param mergeFilter filter indicating difference types to apply.
* @param monitor the progress monitor
* @throws ProgramConflictException
*/
public DiffController(Program p1, Program p2, AddressSetView p1LimitSet,
ProgramDiffFilter diffFilter, ProgramMergeFilter mergeFilter, TaskMonitor monitor)
throws ProgramConflictException {
mergeEngine = new ProgramMergeManager(p1, p2, p1LimitSet, monitor);
mergeEngine.setDiffFilter(diffFilter);
mergeEngine.setMergeFilter(mergeFilter);
this.p1LimitSet = p1LimitSet;
if (p1LimitSet == null) {
p1CurrentAddress = p1.getMinAddress();
}
else {
p1CurrentAddress = p1LimitSet.getMinAddress();
}
p1LastDiffs = new AddressSet();
}
/** Gets the first program being compared by the ProgramDiff.
* @return program1. the program to apply differences to.
*/
Program getProgramOne() {
return mergeEngine.getProgramOne();
}
/** Gets the second program being compared by the ProgramDiff.
* @return program2. the program to get differences from for applying.
*/
Program getProgramTwo() {
return mergeEngine.getProgramTwo();
}
/**
* Gets the address set the current Program Diff is limited to.
* @return the address set the current diff is limited to.
* The addresses in this set are derived from the p1 program.
*/
AddressSetView getLimitedAddressSet() {
return p1LimitSet;
}
/**
* Gets the address set indicating the addresses currently being ignored.
* @return the address set indicating the addresses currently being ignored.
* The addresses in this set are derived from the p1 program.
*/
AddressSetView getIgnoredAddressSet() {
return mergeEngine.getIgnoreAddressSet();
}
/**
* Gets the address set being used to restrict the resulting difference set
* that is reported by getting the differences. This address set is
* effectively a view port into the differences address set.
* @return the address set used to restrict the differences.
* The addresses in this set are derived from the p1 program.
*/
AddressSetView getRestrictedAddressSet() {
return mergeEngine.getRestrictedAddressSet();
}
/**
* Get a copy of the diff filter that the merge is using.
*/
public ProgramDiffFilter getDiffFilter() {
return mergeEngine.getDiffFilter();
}
/**
* Set the filter that indicates which parts of the Program should be
* diffed.
*/
public void setDiffFilter(ProgramDiffFilter filter) {
mergeEngine.setDiffFilter(filter);
}
/**
* Get a copy of the filter that indicates which parts of the Program
* should be merged.
*/
public ProgramMergeFilter getMergeFilter() {
return mergeEngine.getMergeFilter();
}
/**
* Set the filter that indicates which parts of the Program should be
* merged.
*/
public void setMergeFilter(ProgramMergeFilter filter) {
mergeEngine.setMergeFilter(filter);
}
/** Gets the filtered program differences for this merge. Only differences are
* indicated for merge filter categories that are enabled and for addresses
* that have not been marked as ignored.
* <P>Note: This method is potentially time consuming and should normally
* be called from within a background task.
* @param monitor the task monitor for indicating the progress of
* determining differences. This monitor also allows the user to cancel if
* the diff takes too long. If no monitor is desired, use null.
* @return the program differences.
* The addresses in this set are derived from the p1 program.
*/
public AddressSetView getFilteredDifferences(TaskMonitor monitor) throws CancelledException {
AddressSetView diffs1 = mergeEngine.getFilteredDifferences(monitor);
Program program1 = getProgramOne();
Program program2 = getProgramTwo();
monitor.setMessage("Adjusting differences to code unit boundaries...");
AddressSet diffSet2 = DiffUtility.getCompatibleAddressSet(diffs1, program2);
AddressSet diffCuSet2 = DiffUtility.getCodeUnitSet(diffSet2, program2);
monitor.setMessage("Converting Diffs to program 1 set...");
diffs1 = DiffUtility.getCompatibleAddressSet(diffCuSet2, program1);
if (!p1LastDiffs.equals(diffs1)) {
p1LastDiffs = diffs1;
}
return diffs1;
}
/** Restrict the resulting differences to the indicated address set.
* @param p1AddressSet the address set to restrict the getFilteredDifferences() to.
* The addresses in this set should be derived from the p1 program.
* @param monitor the task monitor for canceling the fix up of the
* differences due to the restriction.
*/
public void restrictResults(AddressSetView p1AddressSet, TaskMonitor monitor) {
mergeEngine.restrictResults(p1AddressSet);
differencesChanged(monitor);
}
/** Remove the restriction for the resulting differences to the indicated address set.
* @param monitor the task monitor for canceling the fix up of the
* differences due to the removal of the restriction.
*/
public void removeResultRestrictions(TaskMonitor monitor) {
mergeEngine.removeResultRestrictions();
differencesChanged(monitor);
}
/**
* Apply differences in the address set from program p2
* into the current program p1.
* @param p1AddressSet address set of differences
* The addresses in this set should be derived from the p1 program.
* @param filter merge filter
* @param monitor the task monitor for canceling the fix up of the
* differences due to the removal of the restriction.
* @throws MemoryAccessException
* @throws CancelledException if user cancels via the monitor.
*/
boolean apply(AddressSetView p1AddressSet, TaskMonitor monitor) throws MemoryAccessException,
CancelledException {
boolean applied = mergeEngine.merge(p1AddressSet, monitor);
return applied;
}
/**
* Gets any error and information messages associated with the last apply.
*/
String getApplyMessage() {
return mergeEngine.getErrorMessage() + mergeEngine.getInfoMessage();
}
/**
* Ignore any differences in the specified address set.
* @param p1AddressSet address set set of differences
* The addresses in this set should be derived from the p1 program.
*/
void ignore(AddressSetView p1AddressSet, TaskMonitor monitor) {
mergeEngine.ignore(p1AddressSet);
differencesChanged(monitor);
}
/**
* Gets any warning messages associated with the initial Diff of the two programs.
*/
public String getWarnings() {
return mergeEngine.getWarnings();
}
/**
* Get the address for the diff controller's current location.
* @return the current address.
* This address is derived from the p1 program.
*/
Address getCurrentAddress() {
return p1CurrentAddress;
}
/**
* Go to the given address; update the last address so that
* iterator will be adjusted for next and previous.
* @param p1Address address to go to
* This address should be derived from the p1 program.
*/
private void goTo(Address p1Address) {
p1CurrentAddress = p1Address;
locationChanged(p1CurrentAddress);
}
/**
* set the Diff controller's current address to the specified address.
* @param p1NewAddress the address
* This address should be derived from the p1 program.
*/
void setLocation(Address p1NewAddress) {
if (p1NewAddress.equals(p1CurrentAddress))
return;
p1CurrentAddress = p1NewAddress;
locationChanged(p1CurrentAddress);
}
/**
* Called from the dialog when the cursor should move to the first difference.
* Update the buttons in the dialog according to whether there is
* a next or previous.
*/
void first() {
if (p1LastDiffs.isEmpty()) {
return;
}
goTo(p1LastDiffs.getMinAddress());
}
boolean hasNext() {
return getNextAddress() != null;
}
private Address getNextAddress() {
AddressRangeIterator it = p1LastDiffs.getAddressRanges(p1CurrentAddress, true);
if (!it.hasNext()) {
return null;
}
AddressRange range = it.next();
if (range.contains(p1CurrentAddress)) {
if (it.hasNext()) {
return it.next().getMinAddress();
}
return null;
}
return range.getMinAddress();
}
private Address getPreviousAddress() {
AddressRangeIterator it = p1LastDiffs.getAddressRanges(p1CurrentAddress, false);
if (!it.hasNext()) {
return null;
}
AddressRange range = it.next();
if (range.getMinAddress().equals(p1CurrentAddress)) {
if (it.hasNext()) {
return it.next().getMinAddress();
}
return null;
}
return range.getMinAddress();
}
/**
* Called from the dialog when the "next diff" button is hit.
* Update the buttons in the dialog according to whether there is
* a next or previous.
*/
void next() {
Address nextAddress = getNextAddress();
if (nextAddress != null) {
goTo(nextAddress);
}
}
boolean hasPrevious() {
return getPreviousAddress() != null;
}
/**
* Called from the dialog when the "previous diff" button is hit.
* Update the buttons in the dialog according to whether there is
* a next or previous.
*/
void previous() {
Address previousAddress = getPreviousAddress();
if (previousAddress != null) {
goTo(previousAddress);
}
}
/**
* Refreshes the differences to show what is still different between the two
* programs. After calling this method, any differences that were being
* ignored are still being ignored. The differences are restricted to the
* same address set as before the refresh.
* @param monitor the task monitor for canceling the fix up of the
* recompute of the differences.
* @throws ProgramConflictException
*/
void refresh(boolean keepIgnored, TaskMonitor monitor) throws ProgramConflictException {
AddressSetView ignoreSet = getIgnoredAddressSet();
recomputeDiffs(monitor);
if (keepIgnored) {
mergeEngine.ignore(ignoreSet);
}
differencesChanged(monitor);
}
private void recomputeDiffs(TaskMonitor monitor) throws ProgramConflictException {
recomputeDiffs(getLimitedAddressSet(), monitor);
}
private void recomputeDiffs(AddressSetView newLimitSet, TaskMonitor monitor)
throws ProgramConflictException {
Program p1 = mergeEngine.getProgramOne();
Program p2 = mergeEngine.getProgramTwo();
ProgramDiffFilter diffFilter = mergeEngine.getDiffFilter();
ProgramMergeFilter mergeFilter = mergeEngine.getMergeFilter();
this.p1LimitSet = newLimitSet;
mergeEngine = new ProgramMergeManager(p1, p2, newLimitSet, monitor);
mergeEngine.setDiffFilter(diffFilter);
mergeEngine.setMergeFilter(mergeFilter);
}
public void addDiffControllerListener(DiffControllerListener listener) {
listenerList.add(listener);
}
public void removeDiffControllerListener(DiffControllerListener listener) {
listenerList.remove(listener);
}
public void locationChanged(Address program1Location) {
for (int i = 0; i < listenerList.size(); i++) {
DiffControllerListener listener = listenerList.get(i);
listener.diffLocationChanged(this, program1Location);
}
}
public void differencesChanged(TaskMonitor monitor) {
for (int i = 0; i < listenerList.size(); i++) {
DiffControllerListener listener = listenerList.get(i);
listener.differencesChanged(this);
}
}
}

View file

@ -0,0 +1,31 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 ghidra.app.plugin.core.diff;
import ghidra.program.model.address.Address;
/**
*
*/
public interface DiffControllerListener {
public abstract void diffLocationChanged(DiffController diffControl,
Address location);
public abstract void differencesChanged(DiffController diffControl);
}

View file

@ -0,0 +1,359 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import javax.swing.*;
import javax.swing.text.*;
import docking.ActionContext;
import docking.WindowPosition;
import docking.action.DockingAction;
import docking.action.ToolBarData;
import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.task.SwingUpdateManager;
import resources.Icons;
import resources.ResourceManager;
/**
* The DiffDetailsProvider is used to view the differences for an address or
* code unit address range.
*/
public class DiffDetailsProvider extends ComponentProviderAdapter {
public static final String DIFF_DETAILS_HIDDEN_ACTION = "Diff Details Hidden";
public static final String AUTO_UPDATE_CHECK_BOX = "Auto Update Check Box";
public static final String FILTER_DIFFS_CHECK_BOX = "Filter Diffs Check Box";
public static final String DIFF_DETAILS_TEXT_AREA = "Diff Details Text Area";
public static final String DIFF_DETAILS_PANEL = "Diff Location Details Panel";
public static final ImageIcon ICON = ResourceManager.loadImage("images/xmag.png");
public static final ImageIcon REFRESH_ICON = Icons.REFRESH_ICON;
public static final String TITLE = "Diff Details";
private ProgramDiffPlugin plugin;
private JTextPane textPane;
private StyledDocument doc;
private JCheckBox filterDiffsCB;
private JCheckBox autoUpdateCB;
private boolean filterDiffs = false;
private boolean autoUpdate = false;
private JComponent detailsPanel;
private ArrayList<ActionListener> listenerList = new ArrayList<>();
private ProgramLocation p1DetailsLocation;
private AddressSetView detailsAddrSet;
private boolean isDisplayed = false;
private SwingUpdateManager updateManager;
private ProgramLocation currentLocation;
/**
* @param plugin
*/
public DiffDetailsProvider(ProgramDiffPlugin plugin) {
super(plugin.getTool(), "Diff Location Details", plugin.getName());
setTitle(TITLE);
this.plugin = plugin;
setIcon(ICON);
setTransient();
setWindowMenuGroup("Diff");
setDefaultWindowPosition(WindowPosition.BOTTOM);
setHelpLocation(new HelpLocation("Diff", "Diff_Location_Details"));
detailsPanel = createDiffDetailsPanel();
detailsPanel.setName(DIFF_DETAILS_PANEL);
setAutoUpdate(true);
setFilterDiffs(false);
setUpRefreshDetailsUpdateManager();
}
/**
* @param selected
*/
public void setAutoUpdate(boolean selected) {
autoUpdateCB.setSelected(selected);
autoUpdate = selected;
}
/**
* @param selected
*/
public void setFilterDiffs(boolean selected) {
filterDiffsCB.setSelected(selected);
filterDiffs = selected;
}
public void addActions() {
DockingAction refreshDetailsAction =
new DockingAction("Refresh Diff Details", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
refreshDetails(plugin.getCurrentLocation());
}
};
refreshDetailsAction.setDescription(
"Refresh the details to show any differences at the current location.");
refreshDetailsAction.setEnabled(true);
refreshDetailsAction.setToolBarData(new ToolBarData(REFRESH_ICON, "Diff"));
refreshDetailsAction.setHelpLocation(
new HelpLocation(HelpTopics.DIFF, "Refresh Diff Details"));
plugin.getTool().addLocalAction(this, refreshDetailsAction);
// plugin.getTool().addLocalAction(this, new DiffIgnoreAllAction(this));
}
/**
* Create the automatically update checkbox for diff details.
*/
private void createAutoUpdateCheckBox() {
autoUpdateCB = new JCheckBox("Automatically Update Details", false);
autoUpdateCB.setName(AUTO_UPDATE_CHECK_BOX);
autoUpdateCB.addActionListener(e -> {
autoUpdate = autoUpdateCB.isSelected();
if (autoUpdate) {
refreshDetails(plugin.getCurrentLocation());
}
});
}
/**
* Creates the only show the filtered differences checkbox for diff details
*/
private void createFilterDiffsCheckBox() {
filterDiffsCB = new JCheckBox("Only Show Expected Difference Types", false);
filterDiffsCB.setName(FILTER_DIFFS_CHECK_BOX);
filterDiffsCB.addActionListener(e -> {
filterDiffs = filterDiffsCB.isSelected();
if (autoUpdateCB.isSelected()) {
refreshDetails(plugin.getCurrentLocation());
}
});
}
/**
* @param p1Location
*/
protected void locationChanged(ProgramLocation p1Location) {
if (isDisplayed && autoUpdate) {
refreshDetails(p1Location);
}
}
/**
* Refreshes the displayed Diff Details for the indicated program location address.
* @param p1Location This should be a program1 location.
*/
void refreshDetails(ProgramLocation p1Location) {
if (p1Location == null) {
return;
}
currentLocation = p1Location;
updateManager.update();
}
/**
* Establishes a swing update manager that is used to refresh the DiffDetails.
*/
private void setUpRefreshDetailsUpdateManager() {
updateManager = new SwingUpdateManager(100, 4000, () -> doRefreshDetails(currentLocation));
}
/**
* Refreshes the displayed Diff Details for the indicated program location address.
* @param p1Location This should be a program1 location.
*/
private void doRefreshDetails(ProgramLocation p1Location) {
if (p1Location == null) {
return;
}
// Do nothing if not visible.
if (!isDisplayed) {
return;
}
Program p1 = p1Location.getProgram();
if (p1 != plugin.getFirstProgram() && p1 != plugin.getSecondProgram()) {
return; // If this location isn't for our Diff then ignore it. May be switching program tabs.
}
if (p1DetailsLocation != p1Location) {
p1DetailsLocation = p1Location;
}
AddressSetView newAddrSet = getDetailsAddressSet(p1DetailsLocation);
if (newAddrSet == null) {
setDocumentToErrorMessage("Must have a second program open to determine Diff details.");
return;
}
if (!newAddrSet.equals(detailsAddrSet)) {
detailsAddrSet = newAddrSet;
}
Address p1Address = p1DetailsLocation.getAddress();
try {
if (filterDiffs) {
getFilteredDiffDetails(p1Address);
}
else {
getDiffDetails(p1Address);
}
}
catch (ConcurrentModificationException e) {
setDocumentToErrorMessage(
"Failed to determine Diff Details due to concurrent program changes.\n" +
"This may be caused by background analysis activity.\n" +
" *** Press the Refresh button to update *** ");
// The program is being modified while this is trying to get the details.
// If we want to automatically try again, this would need to re-issue the update
// by calling updateManager.updateLater() here.
}
}
private void setDocumentToErrorMessage(String message) {
try {
doc.remove(0, doc.getLength());
doc.insertString(0, message, new SimpleAttributeSet());
textPane.setCaretPosition(0);
}
catch (BadLocationException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
}
/**
* Gets the Diff Details at the indicated address
* @param p1Address the program1 address
* @throws ConcurrentModificationException if analysis is modifying the program.
*/
private void getDiffDetails(Address p1Address) {
plugin.addDiffDetails(p1Address, doc);
textPane.setCaretPosition(0);
}
private void getFilteredDiffDetails(Address p1Address) {
plugin.addFilteredDiffDetails(p1Address, plugin.getDiffController().getDiffFilter(), doc);
textPane.setCaretPosition(0);
}
/**
* Gets the address set where detailed differences will be determined for details at the
* indicated address. An address set is returned since the indicated address may be in different
* sized code units in each of the two programs.
* @param p1Location the program location in program1 where details are desired.
* @return the address set for code units containing that address within the programs being
* compared to determine differences.
* Otherwise null if a diff of two programs isn't being performed.
*/
private AddressSetView getDetailsAddressSet(ProgramLocation p1Location) {
Address p1Address = p1Location.getAddress();
return plugin.getDetailsAddressSet(p1Address);
}
/**
* Create a panel for the Diff details and auto update checkbox.
*/
private JPanel createDiffDetailsPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setPreferredSize(new Dimension(600, 400));
JScrollPane scrolledDetails = createDetailsPane();
panel.add(scrolledDetails, BorderLayout.CENTER);
createAutoUpdateCheckBox();
createFilterDiffsCheckBox();
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
bottomPanel.add(Box.createHorizontalGlue());
bottomPanel.add(autoUpdateCB);
bottomPanel.add(Box.createHorizontalStrut(10));
bottomPanel.add(filterDiffsCB);
bottomPanel.add(Box.createHorizontalGlue());
panel.add(bottomPanel, BorderLayout.SOUTH);
return panel;
}
private JScrollPane createDetailsPane() {
Font font = new Font("Monospaced", Font.PLAIN, 12);
textPane = new JTextPane();
doc = textPane.getStyledDocument();
textPane.setName(DIFF_DETAILS_TEXT_AREA);
textPane.setEditable(false);
textPane.setMargin(new Insets(5, 5, 5, 5));
textPane.setFont(font);
textPane.setOpaque(true);
textPane.setCaretPosition(0);
JScrollPane scrolledDetails = new JScrollPane(textPane);
JViewport vp = scrolledDetails.getViewport();
vp.add(textPane);
return scrolledDetails;
}
StyledDocument getStyledDocument() {
return doc;
}
@Override
public void componentHidden() {
for (int i = 0; i < listenerList.size(); i++) {
ActionListener listener = listenerList.get(i);
listener.actionPerformed(new ActionEvent(this, 0, DIFF_DETAILS_HIDDEN_ACTION));
}
isDisplayed = false;
p1DetailsLocation = null;
}
@Override
public void componentShown() {
isDisplayed = true;
refreshDetails(plugin.getProgramLocation());
}
@Override
public void closeComponent() {
// overridden to not remove this transient provider
plugin.getTool().showComponentProvider(this, false);
}
@Override
public JComponent getComponent() {
return detailsPanel;
}
Plugin getPlugin() {
return plugin;
}
public void addActionListener(ActionListener listener) {
listenerList.add(listener);
}
public void removeActionListener(ActionListener listener) {
listenerList.remove(listener);
}
}

View file

@ -0,0 +1,224 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.gotoquery.GoToHelper;
import ghidra.app.services.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.symbol.ExternalLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.task.TaskMonitor;
/**
* A service that provides the ability to go to a particular address or location in the
* right-hand listing of the Diff.
*/
public class DiffGoToService implements GoToService {
private GoToService goToService;
private ProgramDiffPlugin diffPlugin;
private GoToHelper helper;
/**
* Creates a service that provides the ability to go to a particular address or location
* in the right-hand listing of the Diff.
* @param goToService basic GoToService for the left-side listing that this will override
* so it can go to addresses and locations in the right-side listing.
* @param diffPlugin the plugin which provides the Diff capability.
*/
public DiffGoToService(GoToService goToService, ProgramDiffPlugin diffPlugin) {
this.goToService = goToService;
this.diffPlugin = diffPlugin;
helper = new GoToHelper(diffPlugin.getTool());
}
@Override
public GoToOverrideService getOverrideService() {
return goToService.getOverrideService();
}
@Override
public boolean goTo(ProgramLocation loc) {
return diffGoTo(loc);
}
@Override
public boolean goTo(Navigatable navigatable, Program program, Address address,
Address refAddress) {
ProgramLocation location = helper.getLocation(program, refAddress, address);
return goTo(navigatable, location, program);
}
@Override
public boolean goTo(ProgramLocation loc, Program program) {
if (program == null || program == diffPlugin.getSecondProgram()) {
return diffGoTo(loc);
}
showProgramFailureStatus();
return false;
}
@Override
public boolean goTo(Navigatable navigatable, ProgramLocation loc, Program program) {
if (loc == null || loc.getAddress() == null) {
return false;
}
if (program == null) {
program = navigatable.getProgram();
}
if (program == null) {
return false;
}
if (program == diffPlugin.getSecondProgram()) {
return diffGoTo(loc);
}
return false;
}
@Override
public boolean goTo(Navigatable navigatable, Address goToAddress) {
if (goToAddress == null) {
return false;
}
if (navigatable == null) {
return diffGoTo(goToAddress);
}
Program program = navigatable.getProgram();
if (program != null) {
Memory memory = program.getMemory();
if (!memory.contains(goToAddress)) {
return false;
}
}
return goTo(goToAddress, program);
}
@Override
public boolean goTo(Address currentAddress, Address goToAddress) {
if (diffGoTo(goToAddress)) {
return true;
}
return goToService.goTo(currentAddress, goToAddress);
}
@Override
public boolean goTo(Address goToAddress) {
return diffGoTo(goToAddress);
}
@Override
public boolean goTo(Address goToAddress, Program program) {
if (program == null || program == diffPlugin.getSecondProgram()) {
return diffGoTo(goToAddress);
}
showProgramFailureStatus();
return false;
}
@Override
public boolean goToExternalLocation(ExternalLocation extLoc, boolean checkNavigationOption) {
showProgramFailureStatus();
return false; // Can only go to locations in the Diff's second program.
}
@Override
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
TaskMonitor monitor) {
// Does this need to do something different here? Maybe if Diff becomes searchable?
return goToService.goToQuery(fromAddr, queryData, listener, monitor);
}
@Override
public boolean goToQuery(Navigatable navigatable, Address fromAddr, QueryData queryData,
GoToServiceListener listener, TaskMonitor monitor) {
// Does this need to do something different here? Maybe if Diff becomes searchable?
return goToService.goToQuery(navigatable, fromAddr, queryData, listener, monitor);
}
@Override
public void setOverrideService(GoToOverrideService override) {
// Do nothing. (May need to change this later if there is reason to override Diff.)
}
@Override
public Navigatable getDefaultNavigatable() {
return goToService.getDefaultNavigatable();
}
private void showProgramFailureStatus() {
diffPlugin.getTool().setStatusInfo(
"Can't navigate from the Diff program to another program.");
}
/**
* Go to the specified program location in the right hand Diff listing.
* @param loc go to this location
* @return true if the listing went to the location.
*/
private boolean diffGoTo(ProgramLocation loc) {
if (loc == null) {
return false;
}
Address addr = loc.getAddress();
if (addr == null) {
return false;
}
saveLocation();
boolean went = diffPlugin.getListingPanel().goTo(loc);
saveLocation();
return went;
}
/**
* Go to the specified address in the right hand Diff listing.
* @param addr go to this address
* @return true if the listing went to the address.
*/
private boolean diffGoTo(Address addr) {
if (addr == null) {
return false;
}
saveLocation();
boolean went = diffPlugin.getListingPanel().goTo(addr);
saveLocation();
return went;
}
/**
* Saving the first program's location (the left listing) in the navigation history.
* The second program's location (the right listing) isn't saved since the navigation is
* relative to a program in the tool's main listing. Also, if the second program's
* locations were saved in the history, their program wouldn't be found and would cause
* errors when restarting the application with the tool and primary program displayed, but
* no Diff program.
*/
private void saveLocation() {
Program firstProgram = diffPlugin.getFirstProgram();
if (firstProgram == null) {
return;
}
NavigationHistoryService historyService =
diffPlugin.getTool().getService(NavigationHistoryService.class);
if (historyService != null) {
historyService.addNewLocation(goToService.getDefaultNavigatable());
}
}
}

View file

@ -0,0 +1,49 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import ghidra.program.util.ProgramMergeFilter;
public class DiffIgnoreAllAction extends DockingAction {
private final static String ACTION_NAME = "Set Ignore for All Apply Settings";
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
private final static String DESCRIPTION = "Change all the difference type apply settings to Ignore.";
private static String[] popupPath = new String[] { ACTION_NAME };
private static String[] menuPath = new String[] { ACTION_NAME };
private DiffApplySettingsProvider provider;
/**
* @param provider the provider using this action
*/
public DiffIgnoreAllAction(DiffApplySettingsProvider provider) {
super("Set All To Ignore", provider.getPlugin().getName());
this.provider = provider;
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
setDescription(DESCRIPTION);
}
@Override
public void actionPerformed(ActionContext context) {
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.IGNORE));
}
}

View file

@ -0,0 +1,48 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import ghidra.program.util.ProgramMergeFilter;
public class DiffMergeAllAction extends DockingAction {
private final static String ACTION_NAME = "Set Merge for All Apply Settings";
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
private final static String DESCRIPTION = "Change all the difference type apply settings to Merge if possible. Otherwise, change to Replace.";
private static String[] popupPath = new String[] { ACTION_NAME };
private static String[] menuPath = new String[] { ACTION_NAME };
private DiffApplySettingsProvider provider;
/**
* @param provider the provider using this action
*/
public DiffMergeAllAction(DiffApplySettingsProvider provider) {
super("Set All To Merge", provider.getPlugin().getName());
this.provider = provider;
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
setDescription(DESCRIPTION);
}
@Override
public void actionPerformed(ActionContext context) {
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.MERGE));
}
}

View file

@ -0,0 +1,181 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import ghidra.app.nav.*;
import ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento;
import ghidra.app.util.HighlightProvider;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
import javax.swing.Icon;
/**
* This is a navigatable for use by the right-hand listing of the Diff.
* It should navigate within the Diff's listing, which would then reposition
* the CodeViewer's listing.
*/
class DiffNavigatable implements Navigatable {
private ProgramDiffPlugin diffPlugin;
private Navigatable navigatable;
private boolean disposed = false;
private WeakSet<NavigatableRemovalListener> navigationListeners =
WeakDataStructureFactory.createCopyOnWriteWeakSet();
/**
* The navigatable for the Diff. The CodeViewerService provides Diff with a listing,
* so where appropriate this navigatable will defer to the CodeViewerService navigatable.
* @param diffPlugin the plugin for the Diff which can be used to obtain needed info.
* @param navigatable navigatable that the CodeViewerService provides.
*/
DiffNavigatable(ProgramDiffPlugin diffPlugin, Navigatable navigatable) {
this.diffPlugin = diffPlugin;
this.navigatable = navigatable;
}
@Override
public boolean goTo(Program program, ProgramLocation location) {
// Defer to the CodeViewer navigatable.
return navigatable.goTo(program, location);
}
@Override
public ProgramLocation getLocation() {
// CodeViewer is designed to handle this based on focus.
return navigatable.getLocation();
}
@Override
public Program getProgram() {
return diffPlugin.getSecondProgram();
}
@Override
public LocationMemento getMemento() {
int cursorOffset = diffPlugin.getListingPanel().getFieldPanel().getCursorOffset();
return new CodeViewerLocationMemento(diffPlugin.getSecondProgram(),
diffPlugin.getCurrentLocation(), cursorOffset);
}
@Override
public void setMemento(LocationMemento memento) {
CodeViewerLocationMemento cvMemento = (CodeViewerLocationMemento) memento;
int cursorOffset = cvMemento.getCursorOffset();
diffPlugin.getListingPanel().getFieldPanel().positionCursor(cursorOffset);
}
@Override
public Icon getNavigatableIcon() {
// Just use the CodeViewer's navigatable icon.
return navigatable.getNavigatableIcon();
}
@Override
public boolean isConnected() {
return true;
}
@Override
public boolean supportsMarkers() {
return isConnected();
}
@Override
public void requestFocus() {
diffPlugin.getListingPanel().getFieldPanel().requestFocus();
}
@Override
public boolean isVisible() {
// Is the CodeViewer visible and the Diff visible?
return navigatable.isVisible() && diffPlugin.isShowingDiff();
}
@Override
public long getInstanceID() {
// CodeViewer provides the listing for Diff.
return navigatable.getInstanceID();
}
@Override
public void setSelection(ProgramSelection selection) {
if (selection == null) {
selection = new ProgramSelection();
}
diffPlugin.setProgram2Selection(selection);
}
@Override
public void setHighlight(ProgramSelection highlight) {
// The right-hand Diff listing doesn't currently support highlight.
}
@Override
public ProgramSelection getSelection() {
// CodeViewer is designed to handle this based on focus.
return navigatable.getSelection();
}
@Override
public ProgramSelection getHighlight() {
// CodeViewer is designed to handle this based on focus.
return navigatable.getHighlight();
}
@Override
public void addNavigatableListener(NavigatableRemovalListener listener) {
navigationListeners.add(listener);
}
@Override
public void removeNavigatableListener(NavigatableRemovalListener listener) {
navigationListeners.remove(listener);
}
public void dispose() {
disposed = true;
for (NavigatableRemovalListener listener : navigationListeners) {
listener.navigatableRemoved(this);
}
}
@Override
public boolean isDisposed() {
return disposed;
}
@Override
public boolean supportsHighlight() {
return true;
}
@Override
public void setHighlightProvider(HighlightProvider highlightProvider, Program program) {
// CodeViewerProvider handles the other listing (the Diff listing) highlights.
navigatable.setHighlightProvider(highlightProvider, program);
}
@Override
public void removeHighlightProvider(HighlightProvider highlightProvider, Program program) {
// CodeViewerProvider handles the other listing (the Diff listing) highlights.
navigatable.removeHighlightProvider(highlightProvider, program);
}
}

View file

@ -0,0 +1,141 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import java.awt.Component;
import java.net.URL;
import ghidra.app.services.ProgramManager;
import ghidra.framework.model.DomainFile;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
/**
* A stubbed {@link ProgramManager} that used the 'second program' at the current program. This
* is used to secondary views in order to install the right program.
*/
public class DiffProgramManager implements ProgramManager {
ProgramDiffPlugin programDiffPlugin;
public DiffProgramManager(ProgramDiffPlugin programDiffPlugin) {
this.programDiffPlugin = programDiffPlugin;
}
@Override
public Program getCurrentProgram() {
return programDiffPlugin.getSecondProgram();
}
@Override
public boolean closeOtherPrograms(boolean ignoreChanges) {
return false;
}
@Override
public boolean closeAllPrograms(boolean ignoreChanges) {
return false;
}
@Override
public boolean closeProgram() {
return false;
}
@Override
public boolean closeProgram(Program program, boolean ignoreChanges) {
return false;
}
@Override
public Program[] getAllOpenPrograms() {
return null;
}
@Override
public Program getProgram(Address addr) {
return null;
}
@Override
public boolean isVisible(Program program) {
return false;
}
@Override
public Program openProgram(URL ghidraURL, int state) {
return null;
}
@Override
public Program openProgram(DomainFile domainFile) {
return null;
}
@Override
public Program openProgram(DomainFile df, int version) {
return null;
}
@Override
public Program openProgram(DomainFile domainFile, Component dialogParent) {
return null;
}
@Override
public Program openProgram(DomainFile domainFile, int version, int state) {
return null;
}
@Override
public void openProgram(Program program) {
// stub
}
@Override
public void openProgram(Program program, boolean current) {
// stub
}
@Override
public void openProgram(Program program, int state) {
// stub
}
@Override
public void releaseProgram(Program program, Object persistentOwner) {
// stub
}
@Override
public void setCurrentProgram(Program p) {
// stub
}
@Override
public boolean setPersistentOwner(Program program, Object owner) {
return false;
}
@Override
public boolean isLocked() {
return false;
}
@Override
public void lockDown(boolean state) {
// Not doing anything
}
}

View file

@ -0,0 +1,48 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import ghidra.program.util.ProgramMergeFilter;
public class DiffReplaceAllAction extends DockingAction {
private final static String ACTION_NAME = "Set Replace for All Apply Settings";
private final static String GROUP_NAME = "DIFF_APPLY_ACTION";
private final static String DESCRIPTION = "Change all the difference type apply settings to Replace.";
private static String[] popupPath = new String[] { ACTION_NAME };
private static String[] menuPath = new String[] { ACTION_NAME };
private DiffApplySettingsProvider provider;
/**
* @param provider the provider using this action
*/
public DiffReplaceAllAction(DiffApplySettingsProvider provider) {
super("Set All To Replace", provider.getPlugin().getName());
this.provider = provider;
setMenuBarData( new MenuData( menuPath, GROUP_NAME ) );
setPopupMenuData( new MenuData( popupPath, GROUP_NAME ) );
setDescription(DESCRIPTION);
}
@Override
public void actionPerformed(ActionContext context) {
provider.setApplyFilter(new ProgramMergeFilter(ProgramMergeFilter.ALL, ProgramMergeFilter.REPLACE));
}
}

View file

@ -0,0 +1,57 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 ghidra.app.plugin.core.diff;
import ghidra.app.services.GoToService;
import ghidra.app.services.ProgramManager;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.plugintool.util.ServiceListener;
public class DiffServiceProvider implements ServiceProvider {
private ServiceProvider serviceProvider;
private ProgramDiffPlugin programDiffPlugin;
private DiffProgramManager diffProgramManager;
private DiffGoToService diffGoToService;
DiffServiceProvider(ServiceProvider serviceProvider, ProgramDiffPlugin programDiffPlugin) {
this.serviceProvider = serviceProvider;
this.programDiffPlugin = programDiffPlugin;
this.diffProgramManager = new DiffProgramManager(this.programDiffPlugin);
GoToService goToService = serviceProvider.getService(GoToService.class);
this.diffGoToService = new DiffGoToService(goToService, programDiffPlugin);
}
public void addServiceListener(ServiceListener listener) {
serviceProvider.addServiceListener(listener);
}
public <T> T getService(Class<T> serviceClass) {
if (serviceClass == ProgramManager.class) {
return serviceClass.cast( diffProgramManager );
}
else if (serviceClass == GoToService.class) {
return serviceClass.cast( diffGoToService );
}
return serviceProvider.getService(serviceClass);
}
public void removeServiceListener(ServiceListener listener) {
serviceProvider.removeServiceListener(listener);
}
}

View file

@ -0,0 +1,29 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
/** A simple listener to allow signalling when a diff tasks starts and ends */
public interface DiffTaskListener {
public static DiffTaskListener NULL_LISTENER = new DiffTaskListener() {
@Override
public void taskInProgress(boolean inProgress) {
// no-op
}
};
public void taskInProgress(boolean inProgress);
}

View file

@ -0,0 +1,532 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramDiffFilter;
import ghidra.program.util.ProgramMemoryComparator;
import ghidra.util.HelpLocation;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import docking.DialogComponentProvider;
import docking.DockingUtils;
import docking.ToolTipManager;
/**
* The ExecuteDiffDialog is used whenever initiating a Program Diff.
* It allows the user to specify the types of differences to determine
* and the address set to diff.
*/
public class ExecuteDiffDialog extends DialogComponentProvider {
public static final String DIFF_ACTION = "Diff";
private static final String TITLE = "Determine Program Differences";
private static final String ADDRESS_AREA_TITLE = "Address Ranges To Diff";
private JCheckBox diffBytesCB;
private JCheckBox diffLabelsCB;
private JCheckBox diffCodeUnitsCB;
private JCheckBox diffReferencesCB;
private JCheckBox diffProgramContextCB;
private JCheckBox diffCommentsCB;
private JCheckBox diffBookmarksCB;
private JCheckBox diffPropertiesCB;
private JCheckBox diffFunctionsCB;
private JButton selectAllButton = new JButton("Select All");
private JButton deselectAllButton = new JButton("Deselect All");
private JCheckBox limitToSelectionCB;
private JTextArea addressText;
private boolean diffBytes;
private boolean diffLabels;
private boolean diffCodeUnits;
private boolean diffReferences;
private boolean diffProgramContext;
private boolean diffComments;
private boolean diffBookmarks;
private boolean diffProperties;
private boolean diffFunctions;
private ProgramDiffFilter diffFilter;
private JPanel diffPanel;
private ArrayList<ActionListener> listenerList = new ArrayList<ActionListener>();
private boolean limitToSelection;
private AddressSetView pgm1MemorySet;
private AddressSetView pgm1SelectionSet;
private AddressSetView pgm1CompatibleSet;
private boolean pgmContextEnabled = true;
/**
* @param frame
*/
public ExecuteDiffDialog() {
super(TITLE, true, true, true, false);
diffPanel = createDiffSettingsPanel();
init();
setHelpLocation(new HelpLocation("Diff", "ExecuteDiffDialog"));
}
public void configure(Program program1, Program program2,
AddressSetView currentProgram1SelectionSet, ProgramDiffFilter diff) {
this.pgm1MemorySet = program1.getMemory();
this.pgm1SelectionSet = currentProgram1SelectionSet;
this.pgm1CompatibleSet = ProgramMemoryComparator.getCombinedAddresses(program1, program2);
setDiffFilter(diff);
boolean hasSelection =
((currentProgram1SelectionSet != null) && !currentProgram1SelectionSet.isEmpty());
setLimitToSelectionEnabled(hasSelection);
limitToSelection(hasSelection);
addressText.setText(getAddressText());
}
private void init() {
addWorkPanel(diffPanel);
addOKButton();
setOkToolTip("Get the differences and highlight them in the second program.");
addCancelButton();
}
/**
* @see ghidra.util.bean.GhidraDialog#okCallback()
*/
@Override
protected void okCallback() {
if (!hasDiffSelection()) {
setStatusText("At least one difference type must be checked.");
Toolkit.getDefaultToolkit().beep();
return;
}
for (int i = 0; i < listenerList.size(); i++) {
ActionListener listener = listenerList.get(i);
listener.actionPerformed(new ActionEvent(this, 0, DIFF_ACTION));
}
close();
}
/**
* @see ghidra.util.bean.GhidraDialog#cancelCallback()
*/
@Override
protected void cancelCallback() {
close();
}
JPanel createDiffSettingsPanel() {
ProgramDiffFilter diff = new ProgramDiffFilter();
JPanel panel = new JPanel(new BorderLayout());
JPanel settingsPanel = new JPanel();
settingsPanel.add(createDiffPanel());
panel.add(settingsPanel, BorderLayout.NORTH);
panel.add(createAddressPanel(), BorderLayout.CENTER);
setDiffFilter(diff);
return panel;
}
/**
* Create a panel for all checkboxes related to applying differences.
*/
private JPanel createDiffPanel() {
JPanel panel = new JPanel();
TitledBorder border = new TitledBorder("Do Differences On");
panel.setBorder(border);
panel.add(createDiffFilterPanel());
return panel;
}
private JPanel createAddressPanel() {
JPanel addressPanel = new JPanel(new BorderLayout());
Border border = BorderFactory.createEtchedBorder();
addressPanel.setBorder(new TitledBorder(border, ADDRESS_AREA_TITLE));
addressPanel.add(createLimitPanel(), BorderLayout.NORTH);
addressText = new JTextArea(5, 30);
addressText.setName("AddressTextArea");
addressText.setEditable(false);
DockingUtils.setTransparent(addressText);
addressText.setText(getAddressText());
JScrollPane scrolledAddresses = new JScrollPane(addressText);
addressPanel.add(scrolledAddresses, BorderLayout.CENTER);
return addressPanel;
}
/**
* Create a panel for the checkboxes to indicate the filter settings.
*/
private JPanel createLimitPanel() {
JPanel panel = new JPanel();
limitToSelectionCB = new JCheckBox("Limit To Selection");
limitToSelectionCB.setName("LimitToSelectionDiffCB");
ToolTipManager.setToolTipText(limitToSelectionCB, "Limits the Diff to the selection.");
limitToSelectionCB.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ev) {
limitToSelection = limitToSelectionCB.isSelected();
updateDiffSetText();
clearStatusText();
}
});
panel.add(limitToSelectionCB);
return panel;
}
/**
* Create a panel for the checkboxes to indicate the filter settings.
*/
private JPanel createDiffFilterPanel() {
JPanel checkBoxPanel = new JPanel();
ToolTipManager.setToolTipText(checkBoxPanel,
"Check the types of differences between the two "
+ "programs that you want detected and highlighted.");
createBytesCheckBox();
createLabelsCheckBox();
createCodeUnitsCheckBox();
createReferencesCheckBox();
createProgramContextCheckBox();
createCommentsCheckBox();
createBookmarksCheckBox();
createPropertiesCheckBox();
createFunctionsCheckBox();
checkBoxPanel.setLayout(new GridLayout(3, 3, 5, 0));
checkBoxPanel.add(diffBytesCB);
checkBoxPanel.add(diffLabelsCB);
checkBoxPanel.add(diffCodeUnitsCB);
checkBoxPanel.add(diffReferencesCB);
checkBoxPanel.add(diffProgramContextCB);
checkBoxPanel.add(diffCommentsCB);
checkBoxPanel.add(diffBookmarksCB);
checkBoxPanel.add(diffPropertiesCB);
checkBoxPanel.add(diffFunctionsCB);
JPanel buttonPanel = new JPanel();
createSelectAllButton();
buttonPanel.add(selectAllButton);
createDeselectAllButton();
buttonPanel.add(deselectAllButton);
JPanel panel = new JPanel(new BorderLayout());
panel.add(checkBoxPanel, BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.SOUTH);
return panel;
}
private void createBytesCheckBox() {
diffBytesCB = new JCheckBox("Bytes", diffBytes);
diffBytesCB.setName("BytesDiffCB");
ToolTipManager.setToolTipText(diffBytesCB, "Highlight byte differences.");
diffBytesCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffBytes = (event.getStateChange() == ItemEvent.SELECTED);
diffFilter.setFilter(ProgramDiffFilter.BYTE_DIFFS, diffBytes);
clearStatusText();
}
});
}
private void createLabelsCheckBox() {
diffLabelsCB = new JCheckBox("Labels", diffLabels);
diffLabelsCB.setName("LabelsDiffCB");
ToolTipManager.setToolTipText(diffLabelsCB, "Highlight label differences.");
diffLabelsCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffLabels = (event.getStateChange() == ItemEvent.SELECTED);
diffFilter.setFilter(ProgramDiffFilter.SYMBOL_DIFFS, diffLabels);
clearStatusText();
}
});
}
private void createCodeUnitsCheckBox() {
diffCodeUnitsCB = new JCheckBox("Code Units", diffCodeUnits);
diffCodeUnitsCB.setName("CodeUnitsDiffCB");
ToolTipManager.setToolTipText(diffCodeUnitsCB, "Highlight the instruction, data, "
+ "and equate differences.");
diffCodeUnitsCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffCodeUnits = (event.getStateChange() == ItemEvent.SELECTED);
diffFilter.setFilter(ProgramDiffFilter.CODE_UNIT_DIFFS, diffCodeUnits);
diffFilter.setFilter(ProgramDiffFilter.EQUATE_DIFFS, diffCodeUnits);
clearStatusText();
}
});
}
private void createReferencesCheckBox() {
diffReferencesCB = new JCheckBox("References", diffReferences);
diffReferencesCB.setName("ReferencesDiffCB");
ToolTipManager.setToolTipText(diffReferencesCB, "Highlight the reference differences.");
diffReferencesCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffReferences = (event.getStateChange() == ItemEvent.SELECTED);
diffFilter.setFilter(ProgramDiffFilter.REFERENCE_DIFFS, diffReferences);
clearStatusText();
}
});
}
private void createProgramContextCheckBox() {
diffProgramContextCB = new JCheckBox("Program Context", diffProgramContext);
diffProgramContextCB.setName("ProgramContextDiffCB");
ToolTipManager.setToolTipText(diffProgramContextCB,
"Highlight the program context register differences.");
diffProgramContextCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffProgramContext = (event.getStateChange() == ItemEvent.SELECTED);
diffFilter.setFilter(ProgramDiffFilter.PROGRAM_CONTEXT_DIFFS, diffProgramContext);
clearStatusText();
}
});
}
private void createCommentsCheckBox() {
diffCommentsCB = new JCheckBox("Comments", diffComments);
diffCommentsCB.setName("CommentsDiffCB");
ToolTipManager.setToolTipText(diffCommentsCB, "Highlight comment differences.");
diffCommentsCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffComments = (event.getStateChange() == ItemEvent.SELECTED);
diffFilter.setFilter(ProgramDiffFilter.COMMENT_DIFFS, diffComments);
clearStatusText();
}
});
}
private void createBookmarksCheckBox() {
diffBookmarksCB = new JCheckBox("Bookmarks", diffBookmarks);
diffBookmarksCB.setName("BookmarksDiffCB");
ToolTipManager.setToolTipText(diffBookmarksCB, "Highlight bookmark differences. "
+ "(for example, bookmark differences)");
diffBookmarksCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffBookmarks = (event.getStateChange() == ItemEvent.SELECTED);
diffFilter.setFilter(ProgramDiffFilter.BOOKMARK_DIFFS, diffBookmarks);
clearStatusText();
}
});
}
private void createPropertiesCheckBox() {
diffPropertiesCB = new JCheckBox("Properties", diffProperties);
diffPropertiesCB.setName("PropertiesDiffCB");
ToolTipManager.setToolTipText(diffPropertiesCB,
"Highlight user defined property differences. "
+ "(for example, Format (space) differences)");
diffPropertiesCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffProperties = (event.getStateChange() == ItemEvent.SELECTED);
diffFilter.setFilter(ProgramDiffFilter.USER_DEFINED_DIFFS, diffProperties);
clearStatusText();
}
});
}
private void createFunctionsCheckBox() {
diffFunctionsCB = new JCheckBox("Functions", diffFunctions);
diffFunctionsCB.setName("FunctionsDiffCB");
ToolTipManager.setToolTipText(diffFunctionsCB, "Highlight function differences.");
diffFunctionsCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
diffFunctions = (event.getStateChange() == ItemEvent.SELECTED);
// Functions check box controls both functions and function tags.
diffFilter.setFilter(ProgramDiffFilter.FUNCTION_DIFFS, diffFunctions);
diffFilter.setFilter(ProgramDiffFilter.FUNCTION_TAG_DIFFS, diffFunctions);
clearStatusText();
}
});
}
private void createSelectAllButton() {
selectAllButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setSelectAll(true);
}
});
selectAllButton.setMnemonic('S');
}
private void createDeselectAllButton() {
deselectAllButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setSelectAll(false);
}
});
deselectAllButton.setMnemonic('D');
}
protected void setSelectAll(boolean selected) {
diffBytesCB.setSelected(selected);
diffLabelsCB.setSelected(selected);
diffCodeUnitsCB.setSelected(selected);
diffReferencesCB.setSelected(selected);
diffProgramContextCB.setSelected(pgmContextEnabled && selected);
diffCommentsCB.setSelected(selected);
diffBookmarksCB.setSelected(selected);
diffPropertiesCB.setSelected(selected);
diffFunctionsCB.setSelected(selected);
}
private void adjustDiffFilter() {
diffBytesCB.setSelected(diffBytes);
diffLabelsCB.setSelected(diffLabels);
diffCodeUnitsCB.setSelected(diffCodeUnits);
diffReferencesCB.setSelected(diffReferences);
diffProgramContextCB.setSelected(pgmContextEnabled && diffProgramContext);
diffCommentsCB.setSelected(diffComments);
diffBookmarksCB.setSelected(diffBookmarks);
diffPropertiesCB.setSelected(diffProperties);
diffFunctionsCB.setSelected(diffFunctions);
}
void setPgmContextEnabled(boolean enable) {
pgmContextEnabled = enable;
diffProgramContextCB.setEnabled(enable);
if (!enable) {
diffProgramContext = false;
}
diffProgramContextCB.setSelected(diffProgramContext);
}
/**
* Get a copy of the diff tool filter settings.
* @return the current diff Filter settings.
*/
ProgramDiffFilter getDiffFilter() {
return new ProgramDiffFilter(diffFilter);
}
/**
* Sets the diff tool filter settings.
* @param filter the new diff Filter settings.
*/
void setDiffFilter(ProgramDiffFilter filter) {
diffFilter = new ProgramDiffFilter(filter);
diffBytes = diffFilter.getFilter(ProgramDiffFilter.BYTE_DIFFS);
diffLabels = diffFilter.getFilter(ProgramDiffFilter.SYMBOL_DIFFS);
diffCodeUnits = diffFilter.getFilter(ProgramDiffFilter.CODE_UNIT_DIFFS);
diffReferences = diffFilter.getFilter(ProgramDiffFilter.REFERENCE_DIFFS);
diffProgramContext =
pgmContextEnabled && diffFilter.getFilter(ProgramDiffFilter.PROGRAM_CONTEXT_DIFFS);
diffComments = diffFilter.getFilter(ProgramDiffFilter.COMMENT_DIFFS);
diffBookmarks = diffFilter.getFilter(ProgramDiffFilter.BOOKMARK_DIFFS);
diffProperties = diffFilter.getFilter(ProgramDiffFilter.USER_DEFINED_DIFFS);
diffFunctions = diffFilter.getFilter(ProgramDiffFilter.FUNCTION_DIFFS);
adjustDiffFilter();
}
/**
* Get the state of the limitToSelection flag.
* @return true indicates limitToSelection box is checked.
*/
boolean isLimitedToSelection() {
return limitToSelectionCB.isSelected();
}
void limitToSelection(boolean limit) {
limitToSelection = limit;
limitToSelectionCB.setSelected(limitToSelection);
updateDiffSetText();
}
void setLimitToSelectionEnabled(boolean enable) {
limitToSelectionCB.setEnabled(enable);
}
private void updateDiffSetText() {
AddressSetView pgm1AddressSet = getAddressSet();
addressText.setText(getAddressText());
setOkEnabled(pgm1AddressSet != null && !pgm1AddressSet.isEmpty());
}
/**
* Get the current address set for the diff depending on whether or not the
* Diff is limited to a selection.
*/
AddressSetView getAddressSet() {
if (isLimitedToSelection()) {
return new AddressSet(pgm1SelectionSet);
}
// if (pgm1CompatibleSet.equals(pgm1MemorySet)) {
// return null;
// }
return new AddressSet(pgm1CompatibleSet);
}
private String getAddressText() {
AddressSetView addrs = getAddressSet();
StringBuffer addrStr = new StringBuffer();
if ((addrs == null) || addrs.equals(pgm1MemorySet)) {
return "Entire Program";
}
for (AddressRange range : addrs) {
addrStr.append(range.toString() + "\n");
}
return addrStr.toString();
}
public void addActionListener(ActionListener listener) {
listenerList.add(listener);
}
public void removeActionListener(ActionListener listener) {
listenerList.remove(listener);
}
/**
* Return true if at least one of the checkboxes for the filter
* has been selected.
*/
boolean hasDiffSelection() {
return (diffBytes || diffLabels || diffCodeUnits || diffProgramContext || diffReferences ||
diffComments || diffBookmarks || diffProperties || diffFunctions);
}
/**
* Return true if all types of differences are being determined.
*/
boolean isMarkingAllDiffs() {
return (diffBytes && diffLabels && diffCodeUnits &&
((!pgmContextEnabled) || diffProgramContext) && diffReferences && diffComments &&
diffBookmarks && diffProperties && diffFunctions);
}
}

View file

@ -0,0 +1,51 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 ghidra.app.plugin.core.diff;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject;
import ghidra.util.task.TaskMonitor;
/**
* Command to apply diffs to current program.
*
*/
class NextDiffCommand extends BackgroundCommand {
private ProgramDiffPlugin plugin;
/**
* Constructor.
* @param plugin
* @param currentLocation
* @param diffControl
*/
NextDiffCommand(ProgramDiffPlugin plugin) {
super("Next Difference", false, false, true);
this.plugin = plugin;
}
/**
* @see ghidra.framework.cmd.BackgroundCommand#applyTo(ghidra.framework.model.DomainObject, ghidra.util.task.TaskMonitor)
*/
@Override
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
monitor.setMessage("NextDiffTask starting...");
plugin.nextDiff();
return true;
}
}

View file

@ -0,0 +1,68 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import javax.swing.JComponent;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.ToolBarData;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
import resources.ResourceManager;
/**
* Action to save the current Diff Apply Settings as the new defaults to be used when new Diffs are started.
*/
class SaveApplySettingsAction extends DockingAction {
private final static String ACTION_NAME = "Save Default Diff Apply Settings";
private final static String GROUP_NAME = "DEFAULTS";
private final static String DESCRIPTION = "Save Current Diff Apply Settings As The Default.";
private DiffApplySettingsProvider settingsProvider;
private DiffApplySettingsOptionManager settingsOptionMgr;
/**
* Creates a new SaveApplySettingsAction.
* @param settingsProvider the component provider where this action will be added.
* @param settingsOptionMgr the options manager to save the default apply settings to.
*/
SaveApplySettingsAction(DiffApplySettingsProvider settingsProvider,
DiffApplySettingsOptionManager settingsOptionMgr) {
super(ACTION_NAME, settingsProvider.getPlugin().getName());
this.settingsProvider = settingsProvider;
this.settingsOptionMgr = settingsOptionMgr;
setToolBarData(new ToolBarData(ResourceManager.loadImage("images/disk.png"), GROUP_NAME));
setEnabled(true);
setDescription(DESCRIPTION);
setHelpLocation(new HelpLocation(HelpTopics.DIFF, ACTION_NAME));
}
@Override
public boolean isEnabledForContext(ActionContext context) {
Object contextObject = context.getContextObject();
JComponent applySettingsComponent = settingsProvider.getComponent();
return contextObject == applySettingsComponent;
}
@Override
public void actionPerformed(ActionContext context) {
settingsOptionMgr.saveDefaultApplyFilter(settingsProvider.getApplyFilter());
settingsProvider.getPlugin().getTool().setStatusInfo(
"Diff Apply Settings have been saved to the tool as the new defaults.");
}
}

View file

@ -0,0 +1,52 @@
/* ###
* 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 ghidra.app.services;
import ghidra.app.plugin.core.diff.ProgramDiffPlugin;
import ghidra.framework.model.DomainFile;
import ghidra.framework.plugintool.ServiceInfo;
import ghidra.program.model.listing.Program;
/**
* Provides a service interface into the Diff Plugin for displaying
* program differences between the current Program and another program.
*
*/
@ServiceInfo(defaultProvider = ProgramDiffPlugin.class, description = "Find differences between two Programs")
public interface DiffService {
/**
* Launch the Diff dialog and display differences between the current program
* and the otherProgram.
* @param otherProgram a domain file for the program to Diff the current program against.
* @return true if the second program is opened and successfully Diffed.
*/
public boolean launchDiff(DomainFile otherProgram);
/**
* Launch the Diff dialog and display differences between the current program
* and the otherProgram.
* @param otherProgram the program to Diff the current program against.
* @return true if the second program is opened and successfully Diffed.
*/
public boolean launchDiff(Program otherProgram);
/**
* Determine if the Diff service is currently displaying a Diff.
*/
public boolean inProgress();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,000 B

View file

@ -0,0 +1,203 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import java.util.List;
import javax.swing.JDialog;
import org.junit.Test;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.symbol.Equate;
import ghidra.program.util.ProgramSelection;
public class DiffApply2Test extends DiffApplyTestAdapter {
@Test
public void testApplyDiffsNextActionFirst() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
byte[] bytes = diffTestP1.getListing().getCodeUnitAt(addr("100")).getBytes();
assertEquals((byte) 0xac, bytes[0]);
AddressSet addrSet = new AddressSet(addr("100"), addr("1ff"));
setDiffSelection(addrSet);
setLocation("100");
applyAndNext();
checkDiffSelection(new AddressSet(addr("00000200"), addr("000002ff")));
assertTrue(diffPlugin.getDiffHighlightSelection().intersect(addrSet).isEmpty());
assertEquals(addr("00000200"), getDiffAddress());
bytes = diffTestP1.getListing().getCodeUnitAt(addr("100")).getBytes();
assertEquals((byte) 0xaf, bytes[0]);
}
@Test
public void testApplyDiffsNextActionMiddle() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
List<Equate> eqs = diffTestP1.getEquateTable().getEquates(addr("1002261"), 0);
assertEquals(0, eqs.size());
AddressSet addrSet = new AddressSet(addr("1002261"), addr("1002262"));
setDiffSelection(addrSet);
setLocation("1002261"); // has Equate Diff
applyAndNext();
checkDiffSelection(new AddressSet(addr("10022d4"), addr("10022e5")));
assertEquals(addr("10022d4"), getDiffAddress());
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
assertEquals(1, eqs.size());
assertEquals(eqs.get(0).getName(), "uno");
assertEquals(eqs.get(0).getValue(), 1);
}
@Test
public void testApplyDiffsNextActionLast() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
AddressSet addrSet = new AddressSet(addr("1005e4f"), addr("1005e53"));
setDiffSelection(addrSet);
setLocation("1005e4f");
assertTrue(!applyDiffsNext.isEnabled());
}
@Test
public void testIgnoreEntireBlock() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
// Cursor in selection
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
AddressSet addrSet = new AddressSet(addr("1002378"), addr("1002396"));
setDiffSelection(addrSet);
setLocation("1002378");
assertNotNull(ignoreDiffs);
assertTrue(ignoreDiffs.isEnabled());
invokeLater(ignoreDiffs);
waitForPostedSwingRunnables();
AddressSet expectedDiffs = origDiffs.subtract(addrSet);
ProgramSelection newSet = diffPlugin.getDiffHighlightSelection();
assertTrue(newSet.intersect(addrSet).isEmpty());
assertTrue(expectedDiffs.equals(newSet));
ProgramSelection sel = runSwing(() -> cb.getCurrentSelection());
assertFalse(sel.isEmpty());
// Cursor outside selection
origDiffs = diffPlugin.getDiffHighlightSelection();
addrSet = new AddressSet(addr("100239d"), addr("100239d"));
setDiffSelection(addrSet);
setLocation("100239d");
assertNotNull(ignoreDiffs);
assertTrue(ignoreDiffs.isEnabled());
invokeLater(ignoreDiffs);
waitForPostedSwingRunnables();
expectedDiffs = origDiffs.subtract(addrSet);
newSet = diffPlugin.getDiffHighlightSelection();
assertTrue(newSet.intersect(addrSet).isEmpty());
assertTrue(expectedDiffs.equals(newSet));
sel = runSwing(() -> cb.getCurrentSelection());
assertFalse(sel.isEmpty());
}
@Test
public void testIgnorePartialBlock() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
// Cursor in selection
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
AddressSet addrSet = new AddressSet(addr("1002a0b"), addr("1002a0b"));
addrSet.addRange(addr("1002a0d"), addr("1002a0d"));
setDiffSelection(addrSet);
setLocation("10029fe");
assertNotNull(ignoreDiffs);
assertTrue(ignoreDiffs.isEnabled());
invokeLater(ignoreDiffs);
waitForPostedSwingRunnables();
AddressSet expectedDiffs = origDiffs.subtract(addrSet);
ProgramSelection newSet = diffPlugin.getDiffHighlightSelection();
assertTrue(newSet.intersect(addrSet).isEmpty());
assertTrue(expectedDiffs.equals(newSet));
ProgramSelection sel = runSwing(() -> cb.getCurrentSelection());
assertFalse(sel.isEmpty());
}
@Test
public void testUndoRedo() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
List<Equate> eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
assertEquals(0, eqs.size());
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
AddressSet addrSet = new AddressSet(addr("1002261"), addr("1002262"));
ProgramSelection newDiffs = new ProgramSelection(origDiffs.subtract(addrSet));
setDiffSelection(addrSet);
setLocation("1002261"); // has Equate Diff
apply();
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
assertEquals(1, eqs.size());
assertEquals(eqs.get(0).getName(), "uno");
assertEquals(eqs.get(0).getValue(), 1);
assertEquals(newDiffs, diffPlugin.getDiffHighlightSelection());
undo(program);
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
assertEquals(0, eqs.size());
assertEquals(newDiffs, diffPlugin.getDiffHighlightSelection()); // For now we don't try to put diff hilights back.
redo(program);
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
assertEquals(1, eqs.size());
assertEquals(eqs.get(0).getName(), "uno");
assertEquals(eqs.get(0).getValue(), 1);
assertEquals(newDiffs, diffPlugin.getDiffHighlightSelection());
}
}

View file

@ -0,0 +1,260 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.JDialog;
import org.junit.Test;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramSelection;
public class DiffApplyIgnoreTest extends DiffApplyTestAdapter {
public DiffApplyIgnoreTest() {
super();
}
@Test
public void testProgramContextIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
assertNotNull(dialog);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(programContextApplyCB);
AddressSet as = new AddressSet(addr("1002378"), addr("1002383"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
@Test
public void testByteIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
assertNotNull(dialog);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(byteApplyCB);
AddressSet as = new AddressSet(addr("1002b45"), addr("1002b45"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
@Test
public void testCodeUnitIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
assertNotNull(dialog);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(codeUnitApplyCB);
AddressSet as = new AddressSet(addr("10024b8"), addr("10024b8"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
@Test
public void testReferenceIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
assertNotNull(dialog);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
memRefIgnore();
extRefIgnore();
stackRefIgnore();
}
private void memRefIgnore() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(refApplyCB);
AddressSet as = new AddressSet(addr("1002a2a"), addr("1002a2a"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
private void extRefIgnore() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(refApplyCB);
AddressSet as = new AddressSet(addr("1001034"), addr("1001037"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
private void stackRefIgnore() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(refApplyCB);
AddressSet as = new AddressSet(addr("10029d1"), addr("10029d1"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
@Test
public void testCommentIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
plateCommentIgnore();
preCommentIgnore();
eolCommentIgnore();
repeatableCommentIgnore();
postCommentIgnore();
}
private void plateCommentIgnore() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(plateCommentApplyCB);
AddressSet as = new AddressSet(addr("100415a"), addr("100415a"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
private void preCommentIgnore() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(preCommentApplyCB);
AddressSet as = new AddressSet(addr("1002395"), addr("1002395"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
private void eolCommentIgnore() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(eolCommentApplyCB);
AddressSet as = new AddressSet(addr("100238f"), addr("100238f"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
private void repeatableCommentIgnore() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(repeatableCommentApplyCB);
AddressSet as = new AddressSet(addr("1002336"), addr("1002336"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
private void postCommentIgnore() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(postCommentApplyCB);
AddressSet as = new AddressSet(addr("100239d"), addr("100239d"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
@Test
public void testLabelIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ignore(labelApplyCB);
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
AddressSet as = new AddressSet(addr("1002a0c"), addr("1002a0c"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
SymbolTable symtab = program.getSymbolTable();
Symbol[] symbols = symtab.getSymbols(addr("1002a0c"));
Comparator<Symbol> c = SymbolUtilities.getSymbolNameComparator();
Arrays.sort(symbols, c);
assertEquals(2, symbols.length);
assertEquals("getResources", symbols[0].getName());
assertEquals("mySymbol", symbols[1].getName());
assertTrue(symbols[0].isPrimary());
assertFalse(symbols[1].isPrimary());
}
@Test
public void testBookmarkIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
assertNotNull(dialog);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(bookmarkApplyCB);
AddressSet as = new AddressSet(addr("1002318"), addr("1002318"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
@Test
public void testPropertyIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
assertNotNull(dialog);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
ignore(propertiesApplyCB);
AddressSet as = new AddressSet(addr("100248c"), addr("100248e"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
@Test
public void testFunctionIgnore() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
assertNotNull(dialog);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
AddressSet as = new AddressSet(addr("100299e"), addr("100299e"));
assertTrue("Original diff set doesn't contain " + as.toString(), origDiffs.contains(as));
ignore(functionApplyCB);
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
}
}

View file

@ -0,0 +1,251 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import java.util.*;
import org.junit.Test;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramSelection;
import ghidra.util.Msg;
import ghidra.util.exception.InvalidInputException;
public class DiffApplyMergeTest extends DiffApplyTestAdapter {
@Override
public void tearDown() {
closeAllWindows();
super.tearDown();
}
@Test
public void testPlateCommentMerge() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
merge(plateCommentApplyCB);
AddressSet as = new AddressSet(addr("100415a"), addr("100415a"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
Listing listing = program.getListing();
CodeUnit cu = listing.getCodeUnitAt(addr("100415a"));
assertEquals("This is my function for testing diff", cu.getComment(CodeUnit.PLATE_COMMENT));
}
@Test
public void testPreCommentMerge() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
merge(preCommentApplyCB);
AddressSet as = new AddressSet(addr("1002395"), addr("1002395"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
Listing listing = program.getListing();
CodeUnit cu = listing.getCodeUnitAt(addr("1002395"));
assertEquals("Pre: Program1\nPre: Program2", cu.getComment(CodeUnit.PRE_COMMENT));
}
@Test
public void testEolCommentMerge() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
merge(eolCommentApplyCB);
AddressSet as = new AddressSet(addr("100238f"), addr("100238f"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
Listing listing = program.getListing();
CodeUnit cu = listing.getCodeUnitAt(addr("100238f"));
assertEquals("EOL: Program1\nEOL: Program2", cu.getComment(CodeUnit.EOL_COMMENT));
}
@Test
public void testRepeatableCommentMerge() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
merge(repeatableCommentApplyCB);
AddressSet as = new AddressSet(addr("1002336"), addr("1002336"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
Listing listing = program.getListing();
CodeUnit cu = listing.getCodeUnitAt(addr("1002336"));
assertEquals("ONE: Repeatable comment.\nTWO: Repeatable comment.",
cu.getComment(CodeUnit.REPEATABLE_COMMENT));
}
@Test
public void testPostCommentMerge() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
merge(postCommentApplyCB);
AddressSet as = new AddressSet(addr("100239d"), addr("100239d"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
Listing listing = program.getListing();
CodeUnit cu = listing.getCodeUnitAt(addr("100239d"));
assertEquals("Post: Program1\nPost: Program2", cu.getComment(CodeUnit.POST_COMMENT));
}
/**
* Tests that we can merge two different function tags that have been added to the
* same function.
*
* Note: The test programs must be modified a bit to facilitate this test. Specifically
* we have to add a function at the same address in both programs, then add tags
* to each program, then add those tags to the newly created functions.
*/
@Test
public void testFunctionTagMerge() {
try {
loadProgram(diffTestP1);
loadProgram(diffTestP2);
FunctionManagerDB funcMgr1 = (FunctionManagerDB) diffTestP1.getFunctionManager();
FunctionManagerDB funcMgr2 = (FunctionManagerDB) diffTestP2.getFunctionManager();
// Create a function in Program 1.
int id = diffTestP1.startTransaction("create1");
funcMgr1.createFunction("testfunc", addr("1002040"),
new AddressSet(addr("1002040"), addr("1002048")), SourceType.DEFAULT);
diffTestP1.endTransaction(id, true);
// Create a function in Program 2.
id = diffTestP2.startTransaction("create2");
funcMgr2.createFunction("testfunc", addr("1002040"),
new AddressSet(addr("1002040"), addr("1002048")), SourceType.DEFAULT);
diffTestP2.endTransaction(id, true);
Function f1 = diffTestP1.getFunctionManager().getFunctionAt(addr("1002040"));
Function f2 = diffTestP2.getFunctionManager().getFunctionAt(addr("1002040"));
// Create a tag and add it to Program 1.
id = diffTestP1.startTransaction("create1");
funcMgr1.getFunctionTagManager().createFunctionTag("TagA", "tag A comment");
f1.addTag("TagA");
diffTestP1.endTransaction(id, true);
// Create a tag and add it to Program 2.
id = diffTestP2.startTransaction("create2");
funcMgr2.getFunctionTagManager().createFunctionTag("TagB", "tag B comment");
f2.addTag("TagB");
diffTestP2.endTransaction(id, true);
// Open the diff display and apply the merge.
openDiff(diffTestP1, diffTestP2);
showApplySettings();
merge(functionTagApplyCB);
AddressSet as = new AddressSet(addr("1002040"), addr("1002040"));
setDiffSelection(as);
apply();
// Check the results. We should have both tags now in the target program
// (Program 1), so check the number of tags and make sure the names are
// correct.
Iterator<FunctionTag> iter = f1.getTags().iterator();
List<String> tagNames = new ArrayList<>();
while (iter.hasNext()) {
FunctionTag tag = iter.next();
tagNames.add(tag.getName());
}
assertEquals(tagNames.size(), 2);
assertTrue(tagNames.contains("TagA"));
assertTrue(tagNames.contains("TagB"));
}
catch (InvalidInputException | OverlappingFunctionException e) {
Msg.error(this, "Error setting up function tag diff test.", e);
}
}
@Test
public void testLabelMerge() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
merge(labelApplyCB);
AddressSet as = new AddressSet(addr("1002a0c"), addr("1002a0c"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
SymbolTable symtab = program.getSymbolTable();
Symbol[] symbols = symtab.getSymbols(addr("1002a0c"));
Comparator<Symbol> c = SymbolUtilities.getSymbolNameComparator();
Arrays.sort(symbols, c);
assertEquals(5, symbols.length);
assertEquals("begin", symbols[0].getName());
assertEquals("fooBar234", symbols[1].getName());
assertEquals("getResources", symbols[2].getName());
assertEquals("mySymbol", symbols[3].getName());
assertEquals("sub21001", symbols[4].getName());
assertFalse(symbols[0].isPrimary());
assertFalse(symbols[1].isPrimary());
assertTrue(symbols[2].isPrimary());
assertFalse(symbols[3].isPrimary());
assertFalse(symbols[4].isPrimary());
}
@Test
public void testLabelMergeSetPrimary() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
mergeSetPrimary(labelApplyCB);
AddressSet as = new AddressSet(addr("1002a0c"), addr("1002a0c"));
setDiffSelection(as);
apply();
assertEquals(origDiffs, diffPlugin.getDiffHighlightSelection());
SymbolTable symtab = program.getSymbolTable();
Symbol[] symbols = symtab.getSymbols(addr("1002a0c"));
Comparator<Symbol> c = SymbolUtilities.getSymbolNameComparator();
Arrays.sort(symbols, c);
assertEquals(5, symbols.length);
assertEquals("begin", symbols[0].getName());
assertEquals("fooBar234", symbols[1].getName());
assertEquals("getResources", symbols[2].getName());
assertEquals("mySymbol", symbols[3].getName());
assertEquals("sub21001", symbols[4].getName());
assertTrue(symbols[0].isPrimary());
assertFalse(symbols[1].isPrimary());
assertFalse(symbols[2].isPrimary());
assertFalse(symbols[3].isPrimary());
assertFalse(symbols[4].isPrimary());
}
}

View file

@ -0,0 +1,333 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.*;
import javax.swing.JDialog;
import org.junit.Test;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionTag;
import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramSelection;
import ghidra.util.Msg;
import ghidra.util.exception.InvalidInputException;
public class DiffApplyReplaceTest extends DiffApplyTestAdapter {
public DiffApplyReplaceTest() {
super();
}
@Test
public void testProgramContextReplace() throws Exception {
openDiff(diffTestP1, diffTestP2);
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(programContextApplyCB);
AddressSet as = new AddressSet(addr("1002378"), addr("1002383"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testByteReplace() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(byteApplyCB);
AddressSet as = new AddressSet(addr("1002b45"), addr("1002b45"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testCodeUnitReplace() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(codeUnitApplyCB);
AddressSet as = new AddressSet(addr("10024b8"), addr("10024bf"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testReferenceReplace() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
memRefReplace();
extRefReplace();
stackRefReplace();
}
private void memRefReplace() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(refApplyCB);
AddressSet as = new AddressSet(addr("1002a2a"), addr("1002a2a"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
private void extRefReplace() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(refApplyCB);
AddressSet as = new AddressSet(addr("1001034"), addr("1001037"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
private void stackRefReplace() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(refApplyCB);
AddressSet as = new AddressSet(addr("10029d1"), addr("10029d1"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testCommentReplace() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
plateCommentReplace();
preCommentReplace();
eolCommentReplace();
repeatableCommentReplace();
postCommentReplace();
}
/**
* Tests that we can replace tags in one function with tags from another.
*
* Note: The test programs must be modified a bit to facilitate this test. Specifically
* we have to add a function at the same address in both programs, then add tags
* to each program, then add those tags to the newly created functions.
*/
@Test
public void testFunctionTagReplace() {
try {
loadProgram(diffTestP1);
loadProgram(diffTestP2);
FunctionManagerDB funcMgr1 = (FunctionManagerDB) diffTestP1.getFunctionManager();
FunctionManagerDB funcMgr2 = (FunctionManagerDB) diffTestP2.getFunctionManager();
// Create a function in Program 1.
int id = diffTestP1.startTransaction("create1");
funcMgr1.createFunction("testfunc", addr("1002040"),
new AddressSet(addr("1002040"), addr("1002048")), SourceType.DEFAULT);
diffTestP1.endTransaction(id, true);
// Create a function in Program 2.
id = diffTestP2.startTransaction("create2");
funcMgr2.createFunction("testfunc", addr("1002040"),
new AddressSet(addr("1002040"), addr("1002048")), SourceType.DEFAULT);
diffTestP2.endTransaction(id, true);
Function f1 = diffTestP1.getFunctionManager().getFunctionAt(addr("1002040"));
Function f2 = diffTestP2.getFunctionManager().getFunctionAt(addr("1002040"));
// Create a tag and add it to Program 1.
id = diffTestP1.startTransaction("create1");
funcMgr1.getFunctionTagManager().createFunctionTag("TagA", "tag A comment");
funcMgr1.getFunctionTagManager().createFunctionTag("TagB", "tag B comment");
f1.addTag("TagA");
f1.addTag("TagB");
diffTestP1.endTransaction(id, true);
// Create a tag and add it to Program 2.
id = diffTestP2.startTransaction("create2");
funcMgr2.getFunctionTagManager().createFunctionTag("TagC", "tag C comment");
funcMgr2.getFunctionTagManager().createFunctionTag("TagD", "tag D comment");
funcMgr2.getFunctionTagManager().createFunctionTag("TagE", "tag E comment");
f2.addTag("TagC");
f2.addTag("TagD");
f2.addTag("TagE");
diffTestP2.endTransaction(id, true);
// Open the diff display and apply the merge.
openDiff(diffTestP1, diffTestP2);
showApplySettings();
replace(functionTagApplyCB);
AddressSet as = new AddressSet(addr("1002040"), addr("1002040"));
setDiffSelection(as);
apply();
// Check the results. We should have both tags now in the target program
// (Program 1), so check the number of tags and make sure the names are
// correct.
Iterator<FunctionTag> iter = f1.getTags().iterator();
List<String> tagNames = new ArrayList<>();
while (iter.hasNext()) {
FunctionTag tag = iter.next();
tagNames.add(tag.getName());
}
assertEquals(tagNames.size(), 3);
assertTrue(tagNames.contains("TagC"));
assertTrue(tagNames.contains("TagD"));
assertTrue(tagNames.contains("TagE"));
}
catch (InvalidInputException | OverlappingFunctionException e) {
Msg.error(this, "Error setting up function tag diff test.", e);
}
}
private void plateCommentReplace() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(plateCommentApplyCB);
AddressSet as = new AddressSet(addr("100415a"), addr("100415a"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
private void preCommentReplace() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(preCommentApplyCB);
AddressSet as = new AddressSet(addr("1002395"), addr("1002395"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
private void eolCommentReplace() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(eolCommentApplyCB);
AddressSet as = new AddressSet(addr("100238f"), addr("100238f"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
private void repeatableCommentReplace() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(repeatableCommentApplyCB);
AddressSet as = new AddressSet(addr("1002336"), addr("1002336"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
private void postCommentReplace() {
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(postCommentApplyCB);
AddressSet as = new AddressSet(addr("100239d"), addr("100239d"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testLabelReplace() {
openDiff(diffTestP1, diffTestP2);
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(labelApplyCB);
AddressSet as = new AddressSet(addr("1002a0c"), addr("1002a0c"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
SymbolTable symtab = program.getSymbolTable();
Symbol[] symbols = symtab.getSymbols(addr("1002a0c"));
Comparator<Symbol> c = SymbolUtilities.getSymbolNameComparator();
Arrays.sort(symbols, c);
assertEquals(3, symbols.length);
assertEquals("begin", symbols[0].getName());
assertEquals("fooBar234", symbols[1].getName());
assertEquals("sub21001", symbols[2].getName());
}
@Test
public void testBookmarkReplace() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(bookmarkApplyCB);
AddressSet as = new AddressSet(addr("1002318"), addr("1002318"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testPropertyReplace() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
replace(propertiesApplyCB);
AddressSet as = new AddressSet(addr("100248c"), addr("100248e"));
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testFunctionReplace() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
showApplySettings();
ProgramSelection origDiffs = diffPlugin.getDiffHighlightSelection();
AddressSet as = new AddressSet(addr("100299e"), addr("100299e"));
assertTrue("Original diff set doesn't contain " + as.toString(), origDiffs.contains(as));
replace(functionApplyCB);
setDiffSelection(as);
apply();
assertEquals(origDiffs.subtract(as), diffPlugin.getDiffHighlightSelection());
}
}

View file

@ -0,0 +1,241 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import ghidra.program.model.address.AddressSet;
import ghidra.program.util.DiffUtility;
public class DiffApplyTest extends DiffApplyTestAdapter {
@Test
public void testShowHideDiffApplySettings() {
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
showApplySettings();
assertTrue(isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
diffPlugin.getDiffApplySettingsProvider().closeComponent();
waitForPostedSwingRunnables();
assertTrue(!isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
invokeLater(diffApplySettings);
assertTrue(isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
diffPlugin.getDiffApplySettingsProvider().closeComponent();
waitForPostedSwingRunnables();
assertTrue(!isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
invokeLater(diffApplySettings);
assertTrue(isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
}
@Test
public void testInitialDiffApplySettings() {
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
showApplySettings();
isReplace(programContextApplyCB);
isReplace(byteApplyCB);
isReplace(codeUnitApplyCB);
isReplace(refApplyCB);
isMerge(plateCommentApplyCB);
isMerge(preCommentApplyCB);
isMerge(eolCommentApplyCB);
isMerge(repeatableCommentApplyCB);
isMerge(postCommentApplyCB);
isMergeSetPrimary(labelApplyCB);
isReplace(functionApplyCB);
isReplace(bookmarkApplyCB);
isReplace(propertiesApplyCB);
}
@Test
public void testIgnoreAllDiffsSettings() {
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
showApplySettings();
invokeLater(ignoreAll);
isIgnore(programContextApplyCB);
isIgnore(byteApplyCB);
isIgnore(codeUnitApplyCB);
isIgnore(refApplyCB);
isIgnore(plateCommentApplyCB);
isIgnore(preCommentApplyCB);
isIgnore(eolCommentApplyCB);
isIgnore(repeatableCommentApplyCB);
isIgnore(postCommentApplyCB);
isIgnore(labelApplyCB);
isIgnore(functionApplyCB);
isIgnore(bookmarkApplyCB);
isIgnore(propertiesApplyCB);
}
@Test
public void testReplaceAllDiffsSettings() {
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
showApplySettings();
invokeLater(replaceAll);
isReplace(programContextApplyCB);
isReplace(byteApplyCB);
isReplace(codeUnitApplyCB);
isReplace(refApplyCB);
isReplace(plateCommentApplyCB);
isReplace(preCommentApplyCB);
isReplace(eolCommentApplyCB);
isReplace(repeatableCommentApplyCB);
isReplace(postCommentApplyCB);
isReplace(labelApplyCB);
isReplace(functionApplyCB);
isReplace(bookmarkApplyCB);
isReplace(propertiesApplyCB);
}
@Test
public void testMergeAllDiffsSettings() {
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
showApplySettings();
invokeLater(ignoreAll);
invokeLater(mergeAll);
isReplace(programContextApplyCB);
isReplace(byteApplyCB);
isReplace(codeUnitApplyCB);
isReplace(refApplyCB);
isMerge(plateCommentApplyCB);
isMerge(preCommentApplyCB);
isMerge(eolCommentApplyCB);
isMerge(repeatableCommentApplyCB);
isMerge(postCommentApplyCB);
isMergeSetPrimary(labelApplyCB);
isReplace(functionApplyCB);
isReplace(bookmarkApplyCB);
isReplace(propertiesApplyCB);
}
@Test
public void testApplyAllDiffsActionWithSimpleLabelMerge() throws Exception {
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
showApplySettings();
merge(labelApplyCB);
invokeLater(selectAllDiffs);
apply();
// FUTURE: Check that actually applied.
AddressSet addrSet = new AddressSet();
// Comment Diffs
addrSet.addRange(addr("1002040"), addr("1002040"));
addrSet.addRange(addr("1002304"), addr("1002304"));
addrSet.addRange(addr("1002306"), addr("1002306"));
addrSet.addRange(addr("100230b"), addr("100230b"));
addrSet.addRange(addr("1002312"), addr("1002312"));
addrSet.addRange(addr("1002336"), addr("1002336"));
addrSet.addRange(addr("1002346"), addr("1002346"));
addrSet.addRange(addr("100238f"), addr("100238f"));
addrSet.addRange(addr("1002395"), addr("1002395"));
addrSet.addRange(addr("100239d"), addr("100239d"));
addrSet.addRange(addr("10030d2"), addr("10030d2"));
addrSet.addRange(addr("100355f"), addr("100355f"));
// Label Diffs
addrSet.addRange(addr("1002a01"), addr("1002a01"));
addrSet.addRange(addr("1002a0c"), addr("1002a0c"));
addrSet.addRange(addr("1002a0d"), addr("1002a0d"));
// onlyInProgram1
addrSet.addRange(addr("00000200"), addr("000002ff"));
// Conflicting Data Diffs
addrSet.add(getPgmConflictDataDiffs());
checkDiffSelection(DiffUtility.getCodeUnitSet(addrSet, program));
}
@Test
public void testApplyAllDiffsAction() throws Exception { // Merges labels and sets primary.
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
showApplySettings();
invokeLater(selectAllDiffs);
apply();
// FUTURE: Check that actually applied.
AddressSet addrSet = new AddressSet();
// Comment Diffs
addrSet.addRange(addr("1002040"), addr("1002040"));
addrSet.addRange(addr("1002304"), addr("1002304"));
addrSet.addRange(addr("1002306"), addr("1002306"));
addrSet.addRange(addr("100230b"), addr("100230b"));
addrSet.addRange(addr("1002312"), addr("1002312"));
addrSet.addRange(addr("1002336"), addr("1002336"));
addrSet.addRange(addr("1002346"), addr("1002346"));
addrSet.addRange(addr("100238f"), addr("100238f"));
addrSet.addRange(addr("1002395"), addr("1002395"));
addrSet.addRange(addr("100239d"), addr("100239d"));
addrSet.addRange(addr("10030d2"), addr("10030d2"));
addrSet.addRange(addr("100355f"), addr("100355f"));
// Label Diffs
addrSet.addRange(addr("1002a01"), addr("1002a01"));
addrSet.addRange(addr("1002a0c"), addr("1002a0c"));
addrSet.addRange(addr("1002a0d"), addr("1002a0d"));
// onlyInProgram1
addrSet.addRange(addr("00000200"), addr("000002ff"));
// Conflicting Data Diffs
addrSet.add(getPgmConflictDataDiffs());
checkDiffSelection(DiffUtility.getCodeUnitSet(addrSet, program));
}
@Test
public void testReplaceAllDiffsAction() throws Exception {
openDiff_CloseWarningDialog(diffTestP1, diffTestP2);
showApplySettings();
invokeLater(replaceAll);
invokeLater(selectAllDiffs);
apply();
checkDiffSelection(getPgmConflictDataDiffs());
}
@Test
public void testReplaceAllDiffsLabelConflict() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
waitForPostedSwingRunnables();
showApplySettings();
invokeLater(replaceAll);
invokeLater(selectAllDiffs);
apply();
checkDiffSelection(getSetupConflictDataDiffs());
}
}

View file

@ -0,0 +1,318 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.*;
import org.junit.Assert;
import docking.action.DockingActionIf;
import ghidra.program.model.address.*;
import ghidra.program.util.ProgramSelection;
public class DiffApplyTestAdapter extends DiffTestAdapter {
DockingActionIf ignoreAll;
DockingActionIf replaceAll;
DockingActionIf mergeAll;
JComponent settingsPanel;
JComboBox<?> programContextApplyCB;
JComboBox<?> byteApplyCB;
JComboBox<?> codeUnitApplyCB;
JComboBox<?> refApplyCB;
JComboBox<?> plateCommentApplyCB;
JComboBox<?> preCommentApplyCB;
JComboBox<?> eolCommentApplyCB;
JComboBox<?> repeatableCommentApplyCB;
JComboBox<?> postCommentApplyCB;
JComboBox<?> labelApplyCB;
JComboBox<?> functionApplyCB;
JComboBox<?> bookmarkApplyCB;
JComboBox<?> propertiesApplyCB;
JComboBox<?> functionTagApplyCB;
public DiffApplyTestAdapter() {
super();
}
/**
* Sets the indicated combo box selection to "Ignore".
* @param comboBox the combo box
*/
void ignore(final JComboBox<?> comboBox) {
runSwing(() -> {
ComboBoxModel<?> model = comboBox.getModel();
for (int i = 0; i < model.getSize(); i++) {
if (model.getElementAt(i).toString().equals("Ignore")) {
comboBox.setSelectedIndex(i);
break;
}
}
isIgnore(comboBox);
});
}
/**
* Sets the indicated combo box selection to "Replace".
* @param comboBox the combo box
*/
void replace(final JComboBox<?> comboBox) {
runSwing(() -> {
ComboBoxModel<?> model = comboBox.getModel();
for (int i = 0; i < model.getSize(); i++) {
if (model.getElementAt(i).toString().equals("Replace")) {
comboBox.setSelectedIndex(i);
break;
}
}
isReplace(comboBox);
});
}
/**
* Sets the indicated combo box selection to "Merge".
* @param comboBox the combo box
*/
void merge(final JComboBox<?> comboBox) {
runSwing(() -> {
ComboBoxModel<?> model = comboBox.getModel();
for (int i = 0; i < model.getSize(); i++) {
if (model.getElementAt(i).toString().equals("Merge")) {
comboBox.setSelectedIndex(i);
break;
}
}
isMerge(comboBox);
});
}
/**
* Sets the indicated combo box selection to "Merge & Set Primary".
* @param comboBox the combo box
*/
void mergeSetPrimary(final JComboBox<?> comboBox) {
runSwing(() -> {
ComboBoxModel<?> model = comboBox.getModel();
for (int i = 0; i < model.getSize(); i++) {
if (model.getElementAt(i).toString().equals("Merge & Set Primary")) {
comboBox.setSelectedIndex(i);
break;
}
}
isMergeSetPrimary(comboBox);
});
}
/**
* @param comboBox the ComboBox for the Diff Apply Setting to check
*/
void isIgnore(JComboBox<?> comboBox) {
assertEquals(comboBox.getName(), "Ignore", comboBox.getSelectedItem().toString());
}
/**
* @param comboBox the ComboBox for the Diff Apply Setting to check
*/
void isReplace(JComboBox<?> comboBox) {
assertEquals(comboBox.getName(), "Replace", comboBox.getSelectedItem().toString());
}
/**
* @param comboBox the ComboBox for the Diff Apply Setting to check
*/
void isMerge(JComboBox<?> comboBox) {
assertEquals(comboBox.getName(), "Merge", comboBox.getSelectedItem().toString());
}
/**
* @param comboBox the ComboBox for the Diff Apply Setting to check
*/
void isMergeSetPrimary(JComboBox<?> comboBox) {
assertEquals(comboBox.getName(), "Merge & Set Primary",
comboBox.getSelectedItem().toString());
}
void waitForApply() {
waitForCondition(() -> getWindow("Apply Differences") == null);
}
void waitForIgnore() {
waitForCondition(() -> getWindow("Ignore Differences") == null);
}
void apply() {
invokeLater(applyDiffs);
waitForSwing();
waitForApply();
waitForSwing();
}
void applyAndNext() {
invokeLater(applyDiffsNext);
waitForSwing();
waitForApply();
waitForSwing();
}
void ignoreAndNext() {
invokeLater(ignoreDiffs);
waitForSwing();
waitForIgnore();
waitForSwing();
}
void showApplySettings() {
invokeLater(diffApplySettings);
assertTrue(isProviderShown(tool.getToolFrame(), "Diff Apply Settings"));
settingsPanel =
(JComponent) findComponentByName(tool.getToolFrame(), "Diff Apply Settings Panel");
assertNotNull(settingsPanel);
getApplySettingsActions();
getApplySettingsComboBoxes();
}
void getApplySettingsActions() {
ignoreAll = getAction(diffPlugin, "Set All To Ignore");
replaceAll = getAction(diffPlugin, "Set All To Replace");
mergeAll = getAction(diffPlugin, "Set All To Merge");
}
void getApplySettingsComboBoxes() {
programContextApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Program Context Diff Apply CB");
byteApplyCB = (JComboBox<?>) findComponentByName(settingsPanel, "Bytes Diff Apply CB");
codeUnitApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Code Units Diff Apply CB");
refApplyCB = (JComboBox<?>) findComponentByName(settingsPanel, "References Diff Apply CB");
plateCommentApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Plate Comments Diff Apply CB");
preCommentApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Pre Comments Diff Apply CB");
eolCommentApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Eol Comments Diff Apply CB");
repeatableCommentApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Repeatable Comments Diff Apply CB");
postCommentApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Post Comments Diff Apply CB");
labelApplyCB = (JComboBox<?>) findComponentByName(settingsPanel, "Labels Diff Apply CB");
functionApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Functions Diff Apply CB");
bookmarkApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Bookmarks Diff Apply CB");
propertiesApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Properties Diff Apply CB");
functionTagApplyCB =
(JComboBox<?>) findComponentByName(settingsPanel, "Function Tags Diff Apply CB");
}
void checkDiffSelection(AddressSetView addrSet) {
ProgramSelection expectedSelection = new ProgramSelection(addrSet);
ProgramSelection currentSelection = cb.getCurrentSelection();
AddressSet missingFromSelection = expectedSelection.subtract(currentSelection);
AddressSet unexpectedlySelected = currentSelection.subtract(expectedSelection);
StringBuffer buf = new StringBuffer();
if (!missingFromSelection.isEmpty()) {
buf.append("\nSelection expected the following addresses but they are missing: \n" +
missingFromSelection.toString());
}
if (!unexpectedlySelected.isEmpty()) {
buf.append("\nSelection unexpectedly contains the following addresses: \n" +
unexpectedlySelected.toString());
}
if (buf.length() > 0) {
String message = buf.toString();
Assert.fail(message);
}
assertEquals(expectedSelection, currentSelection);
}
@SuppressWarnings("unused")
private void checkAddressArrays(String type, Address[] expectedAddresses,
Address[] actualAddresses) {
Arrays.sort(expectedAddresses);
Arrays.sort(actualAddresses);
ArrayList<Address> extraList = new ArrayList<>();
ArrayList<Address> missingList = new ArrayList<>();
int expectedIndex = 0, actualIndex = 0;
int expectedLength = expectedAddresses.length;
int actualLength = actualAddresses.length;
while ((expectedIndex < expectedLength) && (actualIndex < actualLength)) {
Address expectedAddress = expectedAddresses[expectedIndex];
Address actualAddress = actualAddresses[actualIndex];
int compareExpectedToActual = expectedAddress.compareTo(actualAddress);
if (compareExpectedToActual == 0) {
expectedIndex++;
actualIndex++;
}
else if (compareExpectedToActual < 0) {
missingList.add(expectedAddress);
expectedIndex++;
}
else {
extraList.add(actualAddress);
actualIndex++;
}
}
while (expectedIndex < expectedLength) {
missingList.add(expectedAddresses[expectedIndex]);
expectedIndex++;
}
while (actualIndex < actualLength) {
extraList.add(actualAddresses[actualIndex]);
actualIndex++;
}
StringBuffer buf = new StringBuffer();
if (!missingList.isEmpty()) {
buf.append(type + "s are missing at ");
buf.append(missingList.get(0).toString());
int numMissing = missingList.size();
for (int index = 1; index < numMissing; index++) {
buf.append(", " + missingList.get(index).toString());
}
buf.append(".\n");
}
if (!extraList.isEmpty()) {
buf.append("Unexpectedly found " + type + "s at ");
buf.append(extraList.get(0).toString());
int numMissing = extraList.size();
for (int index = 1; index < numMissing; index++) {
buf.append(", " + extraList.get(index).toString());
}
buf.append(".\n");
}
if (buf.length() > 0) {
String message = buf.toString();
// System.out.println(message);
Assert.fail(message);
}
}
void checkProgramSelection(AddressSetView addrSet) {
assertEquals(new ProgramSelection(addrSet), cb.getCurrentSelection());
}
@Override
void setDiffSelection(final AddressSetView addrSet) {
runSwing(() -> diffPlugin.setProgram2Selection(new ProgramSelection(addrSet)), true);
}
}

View file

@ -0,0 +1,205 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.events.ProgramSelectionPluginEvent;
import ghidra.program.model.address.AddressSet;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
public class DiffEnablementTest extends DiffTestAdapter {
public DiffEnablementTest() {
super();
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
// programBuilderDiffTest1.createComment("0x1001010", "Hey", CodeUnit.EOL_COMMENT);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
}
@Test
public void testNoSelectionDiffEnablement() {
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(!applyDiffs.isEnabled());
assertTrue(!applyDiffsNext.isEnabled());
assertTrue(!ignoreDiffs.isEnabled());
assertTrue(nextDiff.isEnabled());
assertTrue(!prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(!setPgm2Selection.isEnabled());
}
@Test
public void testSelectFirstDiffEnablement() {
ProgramSelection sel = new ProgramSelection(addr("100"), addr("110"));
setDiffSelection(sel);
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
addr("100")), program));
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(applyDiffs.isEnabled());
assertTrue(applyDiffsNext.isEnabled());
assertTrue(ignoreDiffs.isEnabled());
assertTrue(nextDiff.isEnabled());
assertTrue(!prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(setPgm2Selection.isEnabled());
}
@Test
public void testSelectLastDiffEnablement() {
ProgramSelection sel = new ProgramSelection(addr("1005e4f"), addr("1004e53"));
setDiffSelection(sel);
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
addr("1005e4f")), program));
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(applyDiffs.isEnabled());
assertTrue(!applyDiffsNext.isEnabled());
assertTrue(ignoreDiffs.isEnabled());
assertTrue(!nextDiff.isEnabled());
assertTrue(prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(setPgm2Selection.isEnabled());
}
@Test
public void testSelectInnerDiffBlockEnablement() {
ProgramSelection sel = new ProgramSelection(addr("10018ce"), addr("10018cf"));
setDiffSelection(sel);
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
addr("1002347")), program));
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(applyDiffs.isEnabled());
assertTrue(applyDiffsNext.isEnabled());
assertTrue(ignoreDiffs.isEnabled());
assertTrue(nextDiff.isEnabled());
assertTrue(prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(setPgm2Selection.isEnabled());
}
@Test
public void testSelectPartialDiffBlockEnablement() {
ProgramSelection sel = new ProgramSelection(addr("100233f"), addr("2345"));
setDiffSelection(sel);
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
addr("1002345")), program));
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(applyDiffs.isEnabled());
assertTrue(applyDiffsNext.isEnabled());
assertTrue(ignoreDiffs.isEnabled());
assertTrue(nextDiff.isEnabled());
assertTrue(prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(setPgm2Selection.isEnabled());
}
@Test
public void testSelectMultipleDiffBlockEnablement() {
AddressSet as = new AddressSet(addr("10018ce"), addr("10018cf"));
as.addRange(addr("1002040"), addr("1002042"));
as.addRange(addr("100233c"), addr("1002347"));
ProgramSelection sel = new ProgramSelection(as);
setDiffSelection(sel);
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
addr("1002040")), program));
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(applyDiffs.isEnabled());
assertTrue(applyDiffsNext.isEnabled());
assertTrue(ignoreDiffs.isEnabled());
assertTrue(nextDiff.isEnabled());
assertTrue(prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(setPgm2Selection.isEnabled());
}
@Test
public void testSelectAllDiffsEnablement() {
ProgramSelection sel = new ProgramSelection(addr("1001000"), addr("100f400"));
setDiffSelection(sel);
tool.firePluginEvent(new ProgramSelectionPluginEvent("test", sel, program));
tool.firePluginEvent(new ProgramLocationPluginEvent("test", new ProgramLocation(program,
addr("1002040")), program));
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(applyDiffs.isEnabled());
assertTrue(applyDiffsNext.isEnabled());
assertTrue(ignoreDiffs.isEnabled());
assertTrue(nextDiff.isEnabled());
assertTrue(prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(setPgm2Selection.isEnabled());
}
}

View file

@ -0,0 +1,110 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.assertEquals;
import javax.swing.JDialog;
import org.junit.Before;
import org.junit.Test;
public class DiffGet2Test extends DiffTestAdapter {
public DiffGet2Test() {
super();
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
// programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
// programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
}
@Test
public void testGetByteDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
byteCB.setSelected(true);
waitForPostedSwingRunnables();
pressButtonByText(getDiffsDialog, "OK");
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
waitForDiff();
assertEquals(getPgmByteDiffs(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testGetCodeUnitDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
codeUnitCB.setSelected(true);
waitForPostedSwingRunnables();
pressButtonByText(getDiffsDialog, "OK");
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
waitForDiff();
assertEquals(getPgmCodeUnitDiffs(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testGetProgramContextDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
programContextCB.setSelected(true);
waitForPostedSwingRunnables();
pressButtonByText(getDiffsDialog, "OK");
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
waitForDiff();
assertEquals(getPgmProgramContextDiffs(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testGetBookmarkDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
bookmarkCB.setSelected(true);
waitForPostedSwingRunnables();
pressButtonByText(getDiffsDialog, "OK");
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
waitForDiff();
assertEquals(getPgmBookmarkDiffs(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testGetCommentDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
commentCB.setSelected(true);
waitForPostedSwingRunnables();
pressButtonByText(getDiffsDialog, "OK");
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
waitForDiff();
assertEquals(getPgmCommentDiffs(), diffPlugin.getDiffHighlightSelection());
}
}

View file

@ -0,0 +1,373 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import java.awt.Window;
import javax.swing.*;
import org.junit.Before;
import org.junit.Test;
import docking.widgets.MultiLineLabel;
import ghidra.app.events.ProgramSelectionPluginEvent;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramSelection;
public class DiffGetTest extends DiffTestAdapter {
@Override
@Before
public void setUp() throws Exception {
super.setUp();
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
}
@Test
public void testDiffAgainstSelf() throws Exception {
getDiffDialog(diffTestP1, diffTestP1);
assertTrue(programContextCB.isSelected());
assertTrue(byteCB.isSelected());
assertTrue(codeUnitCB.isSelected());
assertTrue(refCB.isSelected());
assertTrue(commentCB.isSelected());
assertTrue(labelCB.isSelected());
assertTrue(functionCB.isSelected());
assertTrue(bookmarkCB.isSelected());
assertTrue(propertiesCB.isSelected());
assertTrue(!limitToSelectionCB.isSelected());
assertEquals("Entire Program", limitText.getText());
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals(new ProgramSelection(), diffPlugin.getDiffHighlightSelection());
Window w = waitForWindow("No Differences");
close(w);
}
@Test
public void testDiffNoTypes() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
assertTrue(!programContextCB.isSelected());
assertTrue(!byteCB.isSelected());
assertTrue(!codeUnitCB.isSelected());
assertTrue(!refCB.isSelected());
assertTrue(!commentCB.isSelected());
assertTrue(!labelCB.isSelected());
assertTrue(!functionCB.isSelected());
assertTrue(!bookmarkCB.isSelected());
assertTrue(!propertiesCB.isSelected());
assertTrue(!limitToSelectionCB.isSelected());
assertEquals("Entire Program", limitText.getText());
pressButtonByText(getDiffsDialog, "OK");
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
assertNotNull(getDiffsDialog);
JLabel statusLabel = (JLabel) findComponentByName(getDiffsDialog, "statusLabel");
assertEquals("At least one difference type must be checked.", statusLabel.getText());
assertEquals(new ProgramSelection(), diffPlugin.getDiffHighlightSelection());
close(getDiffsDialog);
}
@Test
public void testGetDefaultDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
assertTrue(programContextCB.isSelected());
assertTrue(byteCB.isSelected());
assertTrue(codeUnitCB.isSelected());
assertTrue(refCB.isSelected());
assertTrue(commentCB.isSelected());
assertTrue(labelCB.isSelected());
assertTrue(functionCB.isSelected());
assertTrue(bookmarkCB.isSelected());
assertTrue(propertiesCB.isSelected());
assertTrue(!limitToSelectionCB.isSelected());
assertEquals("Entire Program", limitText.getText());
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
waitForSwing();
assertEquals(getSetupAllDiffsSet(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testKeepGetDiffsCheckboxState() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
setCheckBoxes(true, new JCheckBox[] { propertiesCB, commentCB });
assertTrue(!programContextCB.isSelected());
assertTrue(!byteCB.isSelected());
assertTrue(!codeUnitCB.isSelected());
assertTrue(!refCB.isSelected());
assertTrue(commentCB.isSelected());
assertTrue(!labelCB.isSelected());
assertTrue(!functionCB.isSelected());
assertTrue(!bookmarkCB.isSelected());
assertTrue(propertiesCB.isSelected());
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
AddressSet as = getSetupCommentDiffs().union(getSetupPropertyDiffs());
assertEquals(new ProgramSelection(as), diffPlugin.getDiffHighlightSelection());
invokeLater(getDiffs);
assertTrue(!programContextCB.isSelected());
assertTrue(!byteCB.isSelected());
assertTrue(!codeUnitCB.isSelected());
assertTrue(!refCB.isSelected());
assertTrue(commentCB.isSelected());
assertTrue(!labelCB.isSelected());
assertTrue(!functionCB.isSelected());
assertTrue(!bookmarkCB.isSelected());
assertTrue(propertiesCB.isSelected());
setCheckBoxes(true, new JCheckBox[] { refCB });
as = as.union(getSetupReferenceDiffs());
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals(new ProgramSelection(as), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testSelectionAllDiffs() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
AddressSet selectionSet = new AddressSet(addr("1001708"), addr("1003001"));
tool.firePluginEvent(
new ProgramSelectionPluginEvent("test", new ProgramSelection(selectionSet), program));
invokeLater(getDiffs);
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
assertNotNull(getDiffsDialog);
getDiffDialogComponents(getDiffsDialog.getComponent());
assertTrue(limitToSelectionCB.isSelected());
assertEquals("[01001708, 01003001]\n", limitText.getText());
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals(getSetupAllDiffsSet().intersect(selectionSet),
diffPlugin.getDiffHighlightSelection());
}
@Test
public void testDeselectAndSelectAllTypesOfDiffs() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
// AddressSet selectionSet = new AddressSet(addrFactory, addr("1001708"), addr("1003001"));
// tool.firePluginEvent(new ProgramSelectionPluginEvent("test",
// new ProgramSelection(selectionSet), program));
invokeLater(getDiffs);
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
assertNotNull(getDiffsDialog);
getDiffDialogComponents(getDiffsDialog.getComponent());
assertEquals(true, programContextCB.isSelected());
assertEquals(true, byteCB.isSelected());
assertEquals(true, codeUnitCB.isSelected());
assertEquals(true, refCB.isSelected());
assertEquals(true, commentCB.isSelected());
assertEquals(true, labelCB.isSelected());
assertEquals(true, functionCB.isSelected());
assertEquals(true, bookmarkCB.isSelected());
assertEquals(true, propertiesCB.isSelected());
assertEquals(false, limitToSelectionCB.isSelected());
pressButtonByText(getDiffsDialog, "Deselect All");
waitForDiff();
assertEquals(false, programContextCB.isSelected());
assertEquals(false, byteCB.isSelected());
assertEquals(false, codeUnitCB.isSelected());
assertEquals(false, refCB.isSelected());
assertEquals(false, commentCB.isSelected());
assertEquals(false, labelCB.isSelected());
assertEquals(false, functionCB.isSelected());
assertEquals(false, bookmarkCB.isSelected());
assertEquals(false, propertiesCB.isSelected());
assertEquals(false, limitToSelectionCB.isSelected());
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals("At least one difference type must be checked.",
getDiffsDialog.getStatusText());
pressButtonByText(getDiffsDialog, "Select All");
waitForDiff();
assertEquals(true, programContextCB.isSelected());
assertEquals(true, byteCB.isSelected());
assertEquals(true, codeUnitCB.isSelected());
assertEquals(true, refCB.isSelected());
assertEquals(true, commentCB.isSelected());
assertEquals(true, labelCB.isSelected());
assertEquals(true, functionCB.isSelected());
assertEquals(true, bookmarkCB.isSelected());
assertEquals(true, propertiesCB.isSelected());
assertEquals(false, limitToSelectionCB.isSelected());
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
waitForSwing(); // Wait for Diff Highlight to get displayed.
assertEquals(getSetupAllDiffsSet(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testSelectionUnchecked() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
AddressSet selectionSet = new AddressSet(addr("1001708"), addr("1003001"));
tool.firePluginEvent(
new ProgramSelectionPluginEvent("test", new ProgramSelection(selectionSet), program));
invokeLater(getDiffs);
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
assertNotNull(getDiffsDialog);
getDiffDialogComponents(getDiffsDialog.getComponent());
assertTrue(limitToSelectionCB.isSelected());
assertEquals("[01001708, 01003001]\n", limitText.getText());
SwingUtilities.invokeLater(() -> limitToSelectionCB.doClick());
waitForSwing();
assertTrue(!limitToSelectionCB.isSelected());
assertEquals("Entire Program", limitText.getText());
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
waitForSwing();
assertEquals(getSetupAllDiffsSet(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testSelectionLabelDiffs() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
AddressSet selectionSet = new AddressSet(addr("1006202"), addr("1006400"));
tool.firePluginEvent(
new ProgramSelectionPluginEvent("test", new ProgramSelection(selectionSet), program));
invokeLater(getDiffs);
getDiffsDialog = waitForDialogComponent(ExecuteDiffDialog.class);
assertNotNull(getDiffsDialog);
getDiffDialogComponents(getDiffsDialog.getComponent());
setAllTypes(false);
setToggleButtonSelected(labelCB, true);
assertTrue(limitToSelectionCB.isSelected());
assertEquals("[01006202, 01006400]\n", limitText.getText());
waitForSwing();
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals(getSetupLabelDiffs().intersect(selectionSet),
diffPlugin.getDiffHighlightSelection());
Window window = waitForWindow("No Differences In Selection");
close(window);
}
@Test
public void testGetReferenceDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
setToggleButtonSelected(refCB, true);
waitForSwing();
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals(getSetupReferenceDiffs(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testGetLabelDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
setToggleButtonSelected(labelCB, true);
waitForSwing();
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals(getSetupLabelDiffs(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testGetFunctionDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
setToggleButtonSelected(functionCB, true);
waitForSwing();
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals(getSetupFunctionDiffs(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testGetPropertyDiffsAction() throws Exception {
getDiffDialog(diffTestP1, diffTestP2);
setAllTypes(false);
setToggleButtonSelected(propertiesCB, true);
waitForSwing();
pressButtonByText(getDiffsDialog, "OK");
waitForDiff();
assertEquals(getSetupPropertyDiffs(), diffPlugin.getDiffHighlightSelection());
}
@Test
public void testDifferentLanguages() throws Exception {
loadProgram(diffTestP1);
pickSecondProgram(getSparcProgram());
assertNull(fp2.getTopLevelAncestor());
Window win = waitForWindow("Can't Open Selected Program");
assertNotNull(win);
String msg = findComponent(win, MultiLineLabel.class).getLabel();
assertTrue(msg.startsWith("Programs languages don't match."));
pressButton(win, "OK");
win = waitForWindow("Select Other Program");
assertNotNull(win);
pressButton(win, "Cancel");
assertFalse(win.isShowing());
assertNull(fp2.getTopLevelAncestor());
}
private Program getSparcProgram() throws Exception {
ProgramBuilder builder = new ProgramBuilder("Sparc", ProgramBuilder._SPARC64);
builder.createMemory("test", "0x100", 0x1000);
return builder.getProgram();
}
}

View file

@ -0,0 +1,106 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import java.util.List;
import javax.swing.JDialog;
import org.junit.Test;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.symbol.Equate;
/**
* Tests the Ignore function of the Diff Tool, such that the current
* difference is ignored and the next difference is selected.
*/
public class DiffIgnoreTest extends DiffApplyTestAdapter {
public DiffIgnoreTest() {
super();
}
/**
* Tests to see if a difference is ignored and the next difference is selected
* @throws Exception
*/
@Test
public void testIgnoreDiffsNextActionFirst() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
byte[] bytes = diffTestP1.getListing().getCodeUnitAt(addr("100")).getBytes();
assertEquals((byte) 0xac, bytes[0]);
AddressSet addrSet = new AddressSet(addr("100"), addr("1ff"));
setDiffSelection(addrSet);
setLocation("100");
ignoreAndNext();
checkDiffSelection(new AddressSet(addr("00000200"), addr("000002ff")));
assertTrue(diffPlugin.getDiffHighlightSelection().intersect(addrSet).isEmpty());
assertEquals(addr("00000200"), getDiffAddress());
bytes = diffTestP1.getListing().getCodeUnitAt(addr("100")).getBytes();
assertEquals((byte) 0xac, bytes[0]);
}
/**
* Tests to see if Equate Tables are properly ignored and the next difference is properly selected
* @throws Exception
*/
@Test
public void testIgnoreDiffsNextActionMiddle() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
List<Equate> eqs = diffTestP1.getEquateTable().getEquates(addr("1002261"), 0);
assertEquals(0, eqs.size());
AddressSet addrSet = new AddressSet(addr("1002261"), addr("1002262"));
setDiffSelection(addrSet);
setLocation("1002261"); // has Equate Diff
ignoreAndNext();
checkDiffSelection(new AddressSet(addr("10022d4"), addr("10022e5")));
assertEquals(addr("10022d4"), getDiffAddress());
eqs = program.getEquateTable().getEquates(addr("1002261"), 0);
assertEquals(0, eqs.size());
}
/**
* Tests to see if the ignore button is disabled after ignoring the last difference
* @throws Exception
*/
@Test
public void testIgnoreDiffsNextActionLast() throws Exception {
openDiff(diffTestP1, diffTestP2);
JDialog dialog = waitForJDialog(tool.getToolFrame(), "Memory Differs", 2000);
pressButtonByText(dialog, "OK");
waitForPostedSwingRunnables();
AddressSet addrSet = new AddressSet(addr("1005e4f"), addr("1005e53"));
setDiffSelection(addrSet);
setLocation("1005e4f");
ignoreAndNext();
assertTrue(!ignoreDiffs.isEnabled());
}
}

View file

@ -0,0 +1,115 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import org.junit.Test;
import ghidra.program.database.ProgramAddressFactory;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.TerminatedStringDataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.DiffUtility;
import ghidra.test.ClassicSampleX86ProgramBuilder;
import ghidra.util.task.TaskMonitorAdapter;
public class DiffOverlayApplyTest extends DiffApplyTestAdapter {
public DiffOverlayApplyTest() {
super();
}
@Test
public void testShowHideDiffApplySettings() throws Exception {
ClassicSampleX86ProgramBuilder builder = new ClassicSampleX86ProgramBuilder();
builder.createMemory(".data", "0x2001000", 1000);
Program p1 = builder.getProgram();
assertTrue(p1.getAddressFactory() instanceof ProgramAddressFactory);
assertEquals(2, p1.getAddressFactory().getNumAddressSpaces()); // ram, OTHER
int id1 = p1.startTransaction("");
Memory memory1 = p1.getMemory();
MemoryBlock dataBlock1 = memory1.getBlock(".data");
MemoryBlock overlayBlock1 =
memory1.createInitializedBlock("OVL1", dataBlock1.getStart(), 0x20L, (byte) 0,
TaskMonitorAdapter.DUMMY_MONITOR, true);
assertEquals(3, p1.getAddressFactory().getNumAddressSpaces()); // ram, OTHER, OVL1
AddressSet addressSet1 = new AddressSet(overlayBlock1.getStart(), overlayBlock1.getEnd());
byte[] bytes1 =
{ 'a', 'p', 'p', 'l', 'e', (byte) 0, 'o', 'r', 'a', 'n', 'g', 'e', (byte) 0 };
memory1.setBytes(overlayBlock1.getStart(), bytes1);
Listing listing1 = p1.getListing();
Address overlayAddress1 = overlayBlock1.getStart();
listing1.createData(overlayAddress1, new TerminatedStringDataType());
overlayAddress1 = overlayAddress1.add(6);
listing1.createData(overlayAddress1, new TerminatedStringDataType());
p1.endTransaction(id1, true);
ClassicSampleX86ProgramBuilder builder2 = new ClassicSampleX86ProgramBuilder();
builder2.createMemory(".data", "0x2001000", 1000);
Program p2 = builder2.getProgram();
assertTrue(p2.getAddressFactory() instanceof ProgramAddressFactory);
assertEquals(2, p2.getAddressFactory().getNumAddressSpaces());
int id2 = p2.startTransaction("");
Memory memory2 = p2.getMemory();
MemoryBlock dataBlock2 = memory2.getBlock(".data");
MemoryBlock overlayBlock2 =
memory2.createInitializedBlock("OVL1", dataBlock2.getStart(), 0x20L, (byte) 0,
TaskMonitorAdapter.DUMMY_MONITOR, true);
assertEquals(3, p2.getAddressFactory().getNumAddressSpaces());
AddressSet addressSet2 = DiffUtility.getCompatibleAddressSet(addressSet1, p2);
byte[] bytes2 =
{ 'd', 'o', 'b', 'e', 'r', 'm', 'a', 'n', (byte) 0, 'p', 'o', 'o', 'd', 'l', 'e',
(byte) 0 };
memory2.setBytes(overlayBlock2.getStart(), bytes2);
Listing listing2 = p2.getListing();
Address overlayAddress2 = overlayBlock2.getStart();
listing2.createData(overlayAddress2, new TerminatedStringDataType());
overlayAddress2 = overlayAddress2.add(9);
listing2.createData(overlayAddress2, new TerminatedStringDataType());
p2.endTransaction(id2, true);
openProgram(p1);
openDiff(p2);
setDiffSelection(addressSet2);
apply();
Listing listing = p1.getListing();
MemoryBlock overlayBlock = p1.getMemory().getBlock("OVL1");
Address overlayAddress = overlayBlock.getStart();
Data dataAt = listing.getDataAt(overlayAddress);
assertNotNull(dataAt);
assertEquals("doberman", dataAt.getValue());
overlayAddress = overlayBlock.getStart().add(9);
dataAt = listing.getDataAt(overlayAddress);
assertNotNull(dataAt);
assertEquals("poodle", dataAt.getValue());
}
}

View file

@ -0,0 +1,235 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.assertNotNull;
import java.awt.Window;
import org.junit.*;
import docking.action.DockingActionIf;
import docking.action.ToggleDockingAction;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
import ghidra.framework.main.FrontEndPlugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.project.tool.GhidraTool;
import ghidra.framework.project.tool.ToolIconURL;
import ghidra.program.database.ProgramDB;
import ghidra.test.ClassicSampleX86ProgramBuilder;
import ghidra.test.TestEnv;
public class DiffSaveSettingsTest extends DiffApplyTestAdapter {
public DiffSaveSettingsTest() {
super();
}
@Override
@Before
public void setUp() throws Exception {
fixupGUI();
env = new TestEnv();
frontEndTool = env.showFrontEndTool();
frontEndPlugin = getPlugin(frontEndTool, FrontEndPlugin.class);
}
private void launchTool() throws Exception {
// Launch our own tool for the Diff so that we can close it and handle "Save Tool?".
runSwing(() -> tool =
(PluginTool) frontEndTool.getProject().getToolServices().launchTool("MyDiffTestTool",
null));
cb = getPlugin(tool, CodeBrowserPlugin.class);
diffPlugin = getPlugin(tool, ProgramDiffPlugin.class);
diffListingPanel = diffPlugin.getListingPanel();
fp1 = cb.getFieldPanel();
fp2 = diffListingPanel.getFieldPanel();
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Program View");
}
private void showNewTool() throws Exception {
// Create our own tool for the Diff so that we can close it and handle "Save Tool?".
runSwing(() -> {
tool = new GhidraTool(frontEndTool.getProject(), "MyDiffTestTool");
tool.setIconURL(new ToolIconURL("preferences-system.png"));
tool.setVisible(true);
});
tool.addPlugin(ProgramManagerPlugin.class.getName());
setUpCodeBrowserTool(tool);
diffListingPanel = diffPlugin.getListingPanel();
fp1 = cb.getFieldPanel();
fp2 = diffListingPanel.getFieldPanel();
openClosePgm2 = (ToggleDockingAction) getAction(diffPlugin, "Open/Close Program View");
}
@Override
@After
public void tearDown() {
Window win = getWindow("Select Other Program");
if (win != null) {
//This window should not be up, so cancel it.
pressButton(win, "Cancel");
}
closeOurTool();
env.dispose();
}
void closeOurTool() {
if (tool == null) {
return;
}
DockingActionIf closeToolAction = getToolAction(tool, "Close Tool");
if (closeToolAction == null) {
return;
}
performAction(closeToolAction, false);
try {
tool.getToolFrame();
}
catch (RuntimeException e1) {
tool = null;
return; // The tool is closed.
}
tool = null;
}
@Test
public void testSaveDiffApplySettings() throws Exception {
// String p3Name = "notepadSetup1ForDiffTest";
// String p4Name = "notepadSetup2ForDiffTest";
ClassicSampleX86ProgramBuilder builder = new ClassicSampleX86ProgramBuilder();
ProgramDB p3 = builder.getProgram();
ProgramDB p4 = builder.getProgram();
showNewTool();
openProgram(p3);
openDiff(p4);
showApplySettings();
isReplace(programContextApplyCB);
isReplace(byteApplyCB);
isReplace(codeUnitApplyCB);
isReplace(refApplyCB);
isMerge(plateCommentApplyCB);
isMerge(preCommentApplyCB);
isMerge(eolCommentApplyCB);
isMerge(repeatableCommentApplyCB);
isMerge(postCommentApplyCB);
isMergeSetPrimary(labelApplyCB);
isReplace(functionApplyCB);
isReplace(bookmarkApplyCB);
isReplace(propertiesApplyCB);
// Change the apply settings.
ignore(programContextApplyCB);
ignore(byteApplyCB);
ignore(codeUnitApplyCB);
ignore(refApplyCB);
replace(plateCommentApplyCB);
replace(preCommentApplyCB);
replace(eolCommentApplyCB);
replace(repeatableCommentApplyCB);
replace(postCommentApplyCB);
merge(labelApplyCB);
ignore(functionApplyCB);
ignore(bookmarkApplyCB);
ignore(propertiesApplyCB);
// Save the settings.
DockingActionIf saveApplySettingsAction =
getAction(diffPlugin, "Save Default Diff Apply Settings");
assertNotNull(saveApplySettingsAction);
performAction(saveApplySettingsAction, true);
// Check the settings.
isIgnore(programContextApplyCB);
isIgnore(byteApplyCB);
isIgnore(codeUnitApplyCB);
isIgnore(refApplyCB);
isReplace(plateCommentApplyCB);
isReplace(preCommentApplyCB);
isReplace(eolCommentApplyCB);
isReplace(repeatableCommentApplyCB);
isReplace(postCommentApplyCB);
isMerge(labelApplyCB);
isIgnore(functionApplyCB);
isIgnore(bookmarkApplyCB);
isIgnore(propertiesApplyCB);
ProgramManagerPlugin pm = getPlugin(tool, ProgramManagerPlugin.class);
DockingActionIf closeAllProgramAction = getAction(pm, "Close All");
assertNotNull(closeAllProgramAction);
performAction(closeAllProgramAction, true);
openProgram(p3);
openDiff(p4);
showApplySettings();
// Check the settings.
isIgnore(programContextApplyCB);
isIgnore(byteApplyCB);
isIgnore(codeUnitApplyCB);
isIgnore(refApplyCB);
isReplace(plateCommentApplyCB);
isReplace(preCommentApplyCB);
isReplace(eolCommentApplyCB);
isReplace(repeatableCommentApplyCB);
isReplace(postCommentApplyCB);
isMerge(labelApplyCB);
isIgnore(functionApplyCB);
isIgnore(bookmarkApplyCB);
isIgnore(propertiesApplyCB);
DockingActionIf closeToolAction = getToolAction(tool, "Close Tool");
performAction(closeToolAction, false);
// Save Tool? (Save)
Window dialog = waitForWindow("Save Tool?");
assertNotNull("Couldn't find 'Save Tool?' dialog.", dialog);
pressButtonByText(dialog, "Save");
launchTool();
// Open another Diff.
openProgram(p3);
openDiff(p4);
showApplySettings();
// Check the settings.
isIgnore(programContextApplyCB);
isIgnore(byteApplyCB);
isIgnore(codeUnitApplyCB);
isIgnore(refApplyCB);
isReplace(plateCommentApplyCB);
isReplace(preCommentApplyCB);
isReplace(eolCommentApplyCB);
isReplace(repeatableCommentApplyCB);
isReplace(postCommentApplyCB);
isMerge(labelApplyCB);
isIgnore(functionApplyCB);
isIgnore(bookmarkApplyCB);
isIgnore(propertiesApplyCB);
closeOurTool();
}
}

View file

@ -0,0 +1,959 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import java.awt.Color;
import java.awt.Window;
import java.math.BigInteger;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import javax.swing.tree.TreePath;
import org.junit.Test;
import docking.ActionContext;
import docking.DialogComponentProvider;
import docking.action.DockingActionIf;
import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.cmd.data.CreateDataCmd;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.events.ProgramSelectionPluginEvent;
import ghidra.app.plugin.core.progmgr.MultiTabPanel;
import ghidra.app.plugin.core.progmgr.MultiTabPlugin;
import ghidra.app.util.viewer.field.OpenCloseField;
import ghidra.app.util.viewer.listingpanel.ListingModel;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.WordDataType;
import ghidra.program.model.listing.*;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
public class DiffTest extends DiffTestAdapter {
@Test
public void testViewDiffsEnablement() throws Exception {
getDiffActions();
assertNotNull(openClosePgm2);
assertTrue(!openClosePgm2.isEnabled());
loadProgram(diffTestP1);
assertTrue(openClosePgm2.isEnabled());
closeProgram();
assertTrue(!openClosePgm2.isEnabled());
}
@Test
public void testOpenDiffProgram() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(fp2.getTopLevelAncestor());
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
checkIfSameSelection(expectedSelection, diffPlugin.getDiffHighlightSelection());
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(!applyDiffs.isEnabled());
assertTrue(!applyDiffsNext.isEnabled());
assertTrue(!ignoreDiffs.isEnabled());
assertTrue(nextDiff.isEnabled());
assertTrue(!prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(!setPgm2Selection.isEnabled());
// Check background color where there is and isn't a difference.
setLocation("1009942");
FieldLocation cursorPosition = fp2.getCursorLocation();
BigInteger index = cursorPosition.getIndex();
Color bg = getBgColor(fp1, index);
Color bg2 = getBgColor(fp2, index);
assertEquals(bg, bg2);
setLocation("100a002");
cursorPosition = fp2.getCursorLocation();
index = cursorPosition.getIndex();
bg = getBgColor(fp1, index);
bg2 = getBgColor(fp2, index);
assertEquals(bg, bg2);
}
@Test
public void testCloseDiffProgram() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(fp2.getTopLevelAncestor());
closeDiff();
assertNull(diffListingPanel.getProgram());
}
@Test
public void testCancelDiff() throws Exception {
// this latch lets us keep the diff from progressing until we are ready
CountDownLatch latch = installDiffBlockingLatch();
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
loadProgram(diffTestP1);
pickSecondProgram(diffTestP2);
waitForTasks();
Window win = waitForWindow("Determine Program Differences");
pressButton(win, "OK");
win = waitForWindow("Checking Program Differences");
pressButton(win, "Cancel");
win = waitForWindow("Cancel?");
pressButton(win, "Yes");
// now that we have pressed cancel, it is safe to let the diff continue
latch.countDown();
waitForCondition(() -> getWindow("Checking Program Differences") == null);
}
@Test
public void testViewDiffsAction() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
loadProgram(diffTestP1);
pickSecondProgram(diffTestP2);
waitForTasks();
Window win = waitForWindow("Determine Program Differences");
pressButton(win, "Cancel");
getDiffActions();
invokeLater(viewDiffs);
Window window = waitForWindow("Diff Already In Progress");
assertNotNull(window);
pressButtonByText(window, "OK");
invokeLater(getDiffs);
win = waitForWindow("Determine Program Differences");
pressButton(win, "OK");
waitForSwing();
waitForCondition(() -> getWindow("Checking Program Differences") == null);
waitForTasks(); // this waits for the task and swing thread to finish posting results
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
checkIfSameSelection(expectedSelection, diffPlugin.getDiffHighlightSelection());
}
@Test
public void testNextDiffAction() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(nextDiff);
assertTrue(nextDiff.isEnabled());
assertEquals(addr("100"), getDiffAddress());
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
invokeLater(nextDiff);
assertEquals(addr("1001034"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("1001034"), addr("1001034")));
}
@Test
public void testNextDiffAction2() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(nextDiff);
assertTrue(nextDiff.isEnabled());
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
new ProgramLocation(program, addr("1004c61")), program));
assertEquals(addr("1004c61"), getDiffAddress());
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
invokeLater(nextDiff);
assertEquals(addr("1005e4f"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("1005e4f"), addr("1005e53")));
}
@Test
public void testPreviousDiffAction() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(nextDiff);
assertTrue(nextDiff.isEnabled());
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
new ProgramLocation(program, addr("100f3ff")), program));
assertEquals(addr("100f3ff"), getDiffAddress());
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
invokeLater(prevDiff);
assertEquals(addr("1005e4f"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("1005e4f"), addr("1005e53")));
}
@Test
public void testPreviousDiffAction2() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(nextDiff);
assertTrue(nextDiff.isEnabled());
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
new ProgramLocation(program, addr("1004c61")), program));
assertEquals(addr("1004c61"), getDiffAddress());
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
invokeLater(prevDiff);
assertEquals(addr("100415a"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("100415a"), addr("100415a")));
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
new ProgramLocation(program, addr("1002055")), program));
assertEquals(addr("1002055"), getDiffAddress());
invokeLater(prevDiff);
assertEquals(addr("100204c"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("100204c"), addr("100204c")));
}
@Test
public void testShowHideDiffDetails() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(diffDetails);
assertTrue(diffDetails.isEnabled());
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
new ProgramLocation(program, addr("1004c61")), program));
assertEquals(addr("1004c61"), getDiffAddress());
assertEquals(cb.getCurrentSelection(), new ProgramSelection());
assertEquals(false, isDiffDetailsDisplayed());
invokeLater(diffDetails); // show
waitForSwing();
assertEquals(true, isDiffDetailsDisplayed());
runSwing(() -> {
DiffDetailsProvider detailsProvider = diffPlugin.getDiffDetailsProvider();
detailsProvider.closeComponent(); // hide
}, false);
waitForSwing();
assertEquals(false, isDiffDetailsDisplayed());
}
boolean isDiffDetailsDisplayed() {
boolean shown = isProviderShown(tool.getToolFrame(), "Diff Details");
if (!shown) {
return false;
}
JPanel detailsPanel = (JPanel) findComponentByName(tool.getToolFrame(),
DiffDetailsProvider.DIFF_DETAILS_PANEL);
return detailsPanel != null;
}
void checkDetails() {
JPanel detailsPanel =
(JPanel) findComponentByName(tool.getToolFrame(), "Diff Details Panel");
assertNotNull(detailsPanel);
JTextArea textArea = (JTextArea) findComponentByName(detailsPanel,
DiffDetailsProvider.DIFF_DETAILS_TEXT_AREA);
assertNotNull(textArea);
JCheckBox autoUpdateCB = (JCheckBox) findComponentByName(detailsPanel,
DiffDetailsProvider.AUTO_UPDATE_CHECK_BOX);
assertNotNull(autoUpdateCB);
}
@Test
public void testDiffDetailsAction() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(diffDetails);
assertTrue(diffDetails.isEnabled());
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
new ProgramLocation(program, addr("100")), program));
assertEquals(addr("100"), getDiffAddress());
invokeLater(diffDetails);
// Check where there are differences
assertEquals(true, isDiffDetailsDisplayed());
JPanel detailsPanel = (JPanel) findComponentByName(tool.getToolFrame(),
DiffDetailsProvider.DIFF_DETAILS_PANEL);
assertNotNull(detailsPanel);
JEditorPane textArea = (JEditorPane) findComponentByName(detailsPanel,
DiffDetailsProvider.DIFF_DETAILS_TEXT_AREA);
assertNotNull(textArea);
String info = textArea.getText();
assertTrue(info.indexOf("Byte Diffs") != -1);
assertTrue(info.indexOf("Bookmark Diffs") == -1);
assertEquals(addr("100"), getDiffAddress());
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
new ProgramLocation(program, addr("1001014")), program));
assertEquals(addr("1001014"), getDiffAddress());
invokeLater(diffDetails);
waitForSwing();
assertEquals(true, isDiffDetailsDisplayed());
// Check where there are no differences
info = textArea.getText();
assertTrue(info.indexOf("No differences") != -1);
assertEquals(addr("1001014"), getDiffAddress());
}
@Test
public void testSelectAllDiffsAction() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(diffDetails);
assertTrue(diffDetails.isEnabled());
invokeLater(selectAllDiffs);
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
checkIfSameSelection(expectedSelection, cb.getCurrentSelection());
}
@Test
public void testSetPgm2SelectionAction() {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
assertNotNull(setPgm2Selection);
assertTrue(!setPgm2Selection.isEnabled());
AddressSet as = new AddressSet();
as.addRange(addr("1001008"), addr("100103f"));
as.addRange(addr("1001965"), addr("1001968"));
as.addRange(addr("1002053"), addr("1002054"));
as.addRange(addr("10022e9"), addr("1002309"));
as.addRange(addr("100a000"), addr("100a006"));
AddressSet diffAs = new AddressSet();
diffAs.addRange(addr("1001034"), addr("1001034"));
diffAs.addRange(addr("10022ee"), addr("10022fc"));
diffAs.addRange(addr("1002304"), addr("1002304"));
diffAs.addRange(addr("1002306"), addr("1002306"));
tool.firePluginEvent(
new ProgramSelectionPluginEvent("test", new ProgramSelection(as), program));
tool.firePluginEvent(new ProgramLocationPluginEvent("test",
new ProgramLocation(program, addr("1001000")), program));
assertTrue(setPgm2Selection.isEnabled());
invokeLater(setPgm2Selection);
assertEquals(new ProgramSelection(diffAs), cb.getCurrentSelection());
}
@Test
public void testChangeToEntireViewWithDiff() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
JTree tree = getProgramTree();
// Replace view with .data
selectTreeNodeByText(tree, ".data");
invokeAndWait(replaceView);
topOfFile(fp1);
assertEquals(addr("1008000"), cb.getCurrentAddress());
bottomOfFile(fp1);
assertEquals(addr("10085ff"), cb.getCurrentAddress());
// Replace with program view
selectTreeNodeByText(tree, "DiffTestPgm1");
invokeAndWait(replaceView);
topOfFile(fp1);
assertEquals(addr("100"), cb.getCurrentAddress());
bottomOfFile(fp1);
assertEquals(addr("100f3ff"), cb.getCurrentAddress());
assertEquals(new ProgramSelection(getSetupAllDiffsSet()),
diffPlugin.getDiffHighlightSelection());
}
@Test
public void testDisplaySingleFragmentWithDiff() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
JTree tree = getProgramTree();
selectTreeNodeByText(tree, ".data");
runSwing(() -> replaceView.actionPerformed(new ActionContext()));
topOfFile(fp1);
assertEquals(addr("1008000"), cb.getCurrentAddress());
bottomOfFile(fp1);
assertEquals(addr("10085ff"), cb.getCurrentAddress());
assertEquals(new ProgramSelection(getSetupAllDiffsSet()),
diffPlugin.getDiffHighlightSelection());
}
@Test
public void testDisplayMultipleFragmentsWithDiff() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
programBuilderDiffTest2.createComment("0x01008000", "My comment", CodeUnit.EOL_COMMENT);
programBuilderDiffTest2.createComment("0x01008607", "My comment", CodeUnit.EOL_COMMENT);
programBuilderDiffTest2.createComment("0x01008a99", "My comment", CodeUnit.EOL_COMMENT);
programBuilderDiffTest2.createComment("0x0100a001", "My comment", CodeUnit.EOL_COMMENT);
openDiff(diffTestP1, diffTestP2);
JTree tree = getProgramTree();
selectTreeNodeByText(tree, ".data");
runSwing(() -> replaceView.actionPerformed(new ActionContext()));
selectTreeNodeByText(tree, ".rsrc");
runSwing(() -> goToView.actionPerformed(new ActionContext()));
topOfFile(fp1);
assertEquals(addr("1008000"), cb.getCurrentAddress());
bottomOfFile(fp1);
assertEquals(addr("100f3ff"), cb.getCurrentAddress());
AddressSet diffSet = new AddressSet(getSetupAllDiffsSet());
diffSet.add(addr("0x01008000"));
diffSet.add(addr("0x01008607"));
diffSet.add(addr("0x01008a99"));
diffSet.add(addr("0x0100a001"));
assertEquals(new ProgramSelection(diffSet), diffPlugin.getDiffHighlightSelection());
AddressSet viewSet = new AddressSet();
viewSet.addRange(addr("1008000"), addr("10085ff")); // .data
viewSet.addRange(addr("100a000"), addr("100f3ff")); // .rsrc
assertEquals(viewSet, cb.getView());
// top of View .rsrc
cb.goTo(new ProgramLocation(program, addr("100a000")));
assertEquals(addr("100a000"), cb.getCurrentAddress());
assertEquals(addr("100a000"), getDiffAddress());
// Previous Diff should add .datau to the view
invokeLater(prevDiff);
waitForSwing();
viewSet.addRange(addr("1008600"), addr("1009943")); // .datau
assertEquals(addr("1008a99"), cb.getCurrentAddress());
assertEquals(addr("1008a99"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("1008a99"), addr("1008a99")));
assertEquals(viewSet, cb.getView());
// top of View .data
topOfFile(fp1);
assertEquals(addr("1008000"), cb.getCurrentAddress());
assertEquals(addr("1008000"), getDiffAddress());
// Previous Diff should add .text to the view
invokeLater(prevDiff);
viewSet.addRange(addr("1001000"), addr("10075ff"));
assertEquals(addr("1005e4f"), cb.getCurrentAddress());
assertEquals(addr("1005e4f"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("1005e4f"), addr("1005e53")));
assertEquals(viewSet, cb.getView());
}
@Test
public void testEmptyViewDiff() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
programBuilderDiffTest2.createComment("0x01008000", "My comment", CodeUnit.EOL_COMMENT);
programBuilderDiffTest2.createComment("0x01008607", "My comment", CodeUnit.EOL_COMMENT);
programBuilderDiffTest2.createComment("0x01009943", "My comment", CodeUnit.EOL_COMMENT);
programBuilderDiffTest2.createComment("0x0100a001", "My comment", CodeUnit.EOL_COMMENT);
openDiff(diffTestP1, diffTestP2);
JTree tree = getProgramTree();
selectTreeNodeByText(tree, "DiffTestPgm1");
performAction(removeView, true);
AddressSet viewSet = new AddressSet();
assertEquals(viewSet, cb.getView());
topOfFile(fp1);
assertNull(cb.getCurrentAddress());
bottomOfFile(fp1);
assertNull(cb.getCurrentAddress());
fp1.getHeight();
assertEquals(0, fp1.getLayoutModel().getNumIndexes().intValue());
assertEquals(0, fp2.getLayoutModel().getNumIndexes().intValue());
AddressSet diffSet = new AddressSet(getSetupAllDiffsSet());
diffSet.add(addr("0x01008000"));
diffSet.add(addr("0x01008607"));
diffSet.add(addr("0x01009943"));
diffSet.add(addr("0x0100a001"));
assertEquals(new ProgramSelection(diffSet), diffPlugin.getDiffHighlightSelection());
assertEquals(addr("100"), diffPlugin.getCurrentAddress());
assertEquals(addr("100"), getDiffAddress());
// Next Diff should add .text to the view
invokeLater(nextDiff);
viewSet.addRange(addr("1001000"), addr("10075ff"));
assertEquals(addr("1001034"), cb.getCurrentAddress());
assertEquals(addr("1001034"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("1001034"), addr("1001034")));
assertEquals(viewSet, cb.getView());
// bottom of View .text
bottomOfFile(fp1);
assertEquals(addr("10075ff"), cb.getCurrentAddress());
assertEquals(addr("10075ff"), getDiffAddress());
// Next Diff should add .data to the view
invokeLater(nextDiff);
waitForSwing();
viewSet.addRange(addr("1008000"), addr("10085ff"));
assertEquals(addr("1008000"), cb.getCurrentAddress());
assertEquals(addr("1008000"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("1008000"), addr("1008000")));
assertEquals(viewSet, cb.getView());
// bottom of View .data
bottomOfFile(fp1);
assertEquals(addr("10085ff"), cb.getCurrentAddress());
assertEquals(addr("10085ff"), getDiffAddress());
// Next Diff should add .datau to the view
invokeLater(nextDiff);
viewSet.addRange(addr("1008600"), addr("1009943"));
assertEquals(addr("1008607"), cb.getCurrentAddress());
assertEquals(addr("1008607"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("1008607"), addr("1008607")));
assertEquals(viewSet, cb.getView());
// bottom of View .datau
bottomOfFile(fp1);
assertEquals(addr("1009943"), cb.getCurrentAddress());
assertEquals(addr("1009943"), getDiffAddress());
// Next Diff should add .rsrc to the view
invokeLater(nextDiff);
viewSet.addRange(addr("100a000"), addr("100f3ff"));
assertEquals(addr("100a001"), cb.getCurrentAddress());
assertEquals(addr("100a001"), getDiffAddress());
assertEquals(cb.getCurrentSelection(),
new ProgramSelection(addr("100a001"), addr("100a001")));
assertEquals(viewSet, cb.getView());
}
@Test
public void testDiffWithChangeTabs() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
ProgramBuilder builder3 = new ProgramBuilder("Program3", ProgramBuilder._X86);
builder3.createMemory(".text", "0x1001000", 0x6600);
builder3.createMemory(".data", "0x1008000", 0x600);
ProgramDB program3 = builder3.getProgram();
ProgramBuilder builder4 = new ProgramBuilder("Program4", ProgramBuilder._X86);
builder4.createMemory(".text", "0x1001000", 0x6600);
builder4.createMemory(".data", "0x1008000", 0x600);
ProgramDB program4 = builder4.getProgram();
tool.removePlugins(new Plugin[] { pt });
tool.addPlugin(MultiTabPlugin.class.getName());
openProgram(program3);
openProgram(program4);
openProgram(diffTestP1);
openDiff(diffTestP2);
assertNotNull(fp2.getTopLevelAncestor());
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
checkIfSameSelection(expectedSelection, diffPlugin.getDiffHighlightSelection());
MultiTabPanel panel = findComponent(tool.getToolFrame(), MultiTabPanel.class);
assertEquals(true, isDiffing());
assertEquals(true, isShowingDiff());
// Check action enablement.
checkDiffAction("View Program Differences", true, true);
checkDiffAction("Open/Close Program View", true, true);
checkDiffAction("Apply Differences", true, false);
checkDiffAction("Apply Differences and Goto Next Difference", true, false);
checkDiffAction("Ignore Selection and Goto Next Difference", true, false);
checkDiffAction("Next Difference", true, true);
checkDiffAction("Previous Difference", true, false);
checkDiffAction("Diff Location Details", true, true);
checkDiffAction("Show Diff Apply Settings", true, true);
checkDiffAction("Get Differences", true, true);
checkDiffAction("Select All Differences", true, true);
checkDiffAction("Set Program1 Selection On Program2", true, false);
// Check background color where there is and isn't a difference.
setLocation("1002995");
assertEquals(addr("1002995"), diffPlugin.getCurrentAddress());
BigInteger index = fp2.getCursorLocation().getIndex();
Color bg = getBgColor(fp1, index);
Color bg2 = getBgColor(fp2, index);
assertEquals(bg, bg2);
checkDiffAction("Next Difference", true, true);
checkDiffAction("Previous Difference", true, true);
setLocation("100299e");
index = fp2.getCursorLocation().getIndex();
assertEquals(addr("100299e"), diffPlugin.getCurrentAddress());
bg = getBgColor(fp1, index);
bg2 = getBgColor(fp2, index);
assertEquals(bg, bg2);
checkDiffAction("Next Difference", true, true);
checkDiffAction("Previous Difference", true, true);
selectTab(panel, program3);
assertEquals(true, isDiffing());
assertEquals(false, isShowingDiff());
// Check action enablement.
checkDiffAction("View Program Differences", true, true);
checkDiffAction("Apply Differences", false, false);
checkDiffAction("Apply Differences and Goto Next Difference", false, false);
checkDiffAction("Ignore Selection and Goto Next Difference", false, false);
checkDiffAction("Next Difference", false, false);
checkDiffAction("Previous Difference", false, false);
checkDiffAction("Diff Location Details", false, false);
checkDiffAction("Show Diff Apply Settings", false, false);
checkDiffAction("Get Differences", false, false);
checkDiffAction("Select All Differences", false, false);
checkDiffAction("Set Program1 Selection On Program2", false, false);
selectTab(panel, program4);
assertEquals(true, isDiffing());
assertEquals(false, isShowingDiff());
// Check action enablement.
checkDiffAction("View Program Differences", true, true);
checkDiffAction("Apply Differences", false, false);
checkDiffAction("Apply Differences and Goto Next Difference", false, false);
checkDiffAction("Ignore Selection and Goto Next Difference", false, false);
checkDiffAction("Next Difference", false, false);
checkDiffAction("Previous Difference", false, false);
checkDiffAction("Diff Location Details", false, false);
checkDiffAction("Show Diff Apply Settings", false, false);
checkDiffAction("Get Differences", false, false);
checkDiffAction("Select All Differences", false, false);
checkDiffAction("Set Program1 Selection On Program2", false, false);
selectTab(panel, diffTestP1);
assertEquals(true, isDiffing());
assertEquals(true, isShowingDiff());
assertEquals(addr("100299e"), diffPlugin.getCurrentAddress());
// Check action enablement.
checkDiffAction("View Program Differences", true, true);
checkDiffAction("Apply Differences", true, false);
checkDiffAction("Apply Differences and Goto Next Difference", true, false);
checkDiffAction("Ignore Selection and Goto Next Difference", true, false);
checkDiffAction("Next Difference", true, true);
checkDiffAction("Previous Difference", true, true);
checkDiffAction("Diff Location Details", true, true);
checkDiffAction("Show Diff Apply Settings", true, true);
checkDiffAction("Get Differences", true, true);
checkDiffAction("Select All Differences", true, true);
checkDiffAction("Set Program1 Selection On Program2", true, false);
// Now close the Diff.
closeDiff();
assertNull(diffListingPanel.getProgram());
assertEquals(false, isDiffing());
assertEquals(false, isShowingDiff());
}
@Test
public void testOpenCloseProgramAction() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
ProgramBuilder builder3 = new ProgramBuilder("Program3", ProgramBuilder._X86);
builder3.createMemory(".text", "0x1001000", 0x6600);
builder3.createMemory(".data", "0x1008000", 0x600);
ProgramDB program3 = builder3.getProgram();
ProgramBuilder builder4 = new ProgramBuilder("Program4", ProgramBuilder._X86);
builder4.createMemory(".text", "0x1001000", 0x6600);
builder4.createMemory(".data", "0x1008000", 0x600);
ProgramDB program4 = builder4.getProgram();
tool.removePlugins(new Plugin[] { pt });
tool.addPlugin(MultiTabPlugin.class.getName());
openProgram(program3);
openProgram(program4);
openProgram(diffTestP1);
openDiff(diffTestP2);
assertNotNull(fp2.getTopLevelAncestor());
ProgramSelection expectedSelection = new ProgramSelection(getSetupAllDiffsSet());
checkIfSameSelection(expectedSelection, diffPlugin.getDiffHighlightSelection());
MultiTabPanel panel = findComponent(tool.getToolFrame(), MultiTabPanel.class);
assertEquals(true, isDiffing());
assertEquals(true, isShowingDiff());
checkDiffAction("Open/Close Program View", true, true);
//
// Different tab--still enabled
//
selectTab(panel, program3);
checkDiffAction("Open/Close Program View", true, true);
clickDiffButton();
assertTrue("Not diffing after clicking the diff button when on a non-diff tab",
isDiffing());
assertTrue("Diff not showing after clicking the diff button when on a non-diff tab",
isShowingDiff());
//
// Diff tab--still enabled
//
checkDiffAction("Open/Close Program View", true, true);
clickDiffButton();
DialogComponentProvider dialogProvider = waitForDialogComponent("Close Diff Session");
assertNotNull("Did not get confirmation dialog", dialogProvider);
pressButtonByText(dialogProvider.getComponent(), "Yes", true);
waitForSwing();
assertFalse("Still diffing after clicking the diff button when on the diff tab",
isDiffing());
assertFalse("Still showing diff after clicking the diff button when on the diff tab",
isShowingDiff());
}
@Test
public void testOpenCloseArrayWithDiffShowing() throws Exception {
programBuilderDiffTest1.createMemory("d4", "0x400", 0x100);
programBuilderDiffTest2.createMemory("d2", "0x200", 0x100);
openDiff(diffTestP1, diffTestP2);
waitForSwing();
ArrayDataType array = new ArrayDataType(new WordDataType(), 12, 2);
CreateDataCmd cmd = new CreateDataCmd(addr("0x00000106"), array);
tool.execute(cmd, diffTestP1);
Data data = diffTestP1.getListing().getDataAt(addr("0x00000106"));
ListingModel listingModel = cb.getListingModel();
cb.goToField(addr("0x00000106"), "+", 0, 0);
assertTrue(cb.getCurrentField() instanceof OpenCloseField);
assertFalse("Array is not closed as expected.", listingModel.isOpen(data));
cb.goToField(addr("0x00000120"), "Address", 0, 0);
assertEquals("00000120", cb.getCurrentFieldText());
cb.goToField(addr("0x00000106"), "Address", 0, 0);
assertEquals("00000106", cb.getCurrentFieldText());
cb.goToField(addr("0x00000106"), "+", 0, 0);
click(cb, 1);
waitForSwing();
assertTrue("Array failed to open.", listingModel.isOpen(data));
cb.goToField(addr("0x00000106"), "+", 0, 0);
click(cb, 1);
waitForSwing();
cb.goToField(addr("0x00000120"), "Address", 0, 0);
cb.goToField(addr("0x00000106"), "Address", 0, 0);
assertEquals("00000106", cb.getCurrentFieldText());
assertFalse("Array failed to close.", listingModel.isOpen(data));
}
//==================================================================================================
// Private Methods
//==================================================================================================
private Color getBgColor(FieldPanel fp, BigInteger index) {
return runSwing(() -> fp.getBackgroundColor(index));
}
private CountDownLatch installDiffBlockingLatch() {
final CountDownLatch continueLatch = new CountDownLatch(1);
diffPlugin.setDiffTaskListener(inProgress -> {
try {
continueLatch.await(5, TimeUnit.SECONDS);
}
catch (InterruptedException e) {
// shouldn't happen
throw new RuntimeException("Unexpectedly interrupted while blocking the diff task!",
e);
}
});
return continueLatch;
}
private void clickDiffButton() {
runSwing(() -> {
openClosePgm2.setSelected(!openClosePgm2.isSelected());
openClosePgm2.actionPerformed(new ActionContext());
}, false);
waitForSwing();
}
/**
* Checks that the specified Diff action is in the indicated state and
* causes a JUnit assert if not.
*
* @param actionName the name of the DIff action.
* @param inTool the action is currently added to the tool.
* @param enabled the action is enabled.
*/
private void checkDiffAction(String actionName, boolean inTool, boolean enabled) {
DockingActionIf tmpAction = getAction(diffPlugin, actionName);
if (inTool) {
if (tmpAction == null) {
assertNotNull("Diff action, " + actionName + ", was not in the tool as expected.",
tmpAction);
}
}
else {
if (tmpAction != null) {
assertNull("Diff action, " + actionName + ", was unexpectedly found in the tool.",
tmpAction);
}
}
if (tmpAction == null) {
return;
}
assertEquals("Diff action, " + actionName + ", was unexpectedly " +
(enabled ? "disabled" : "enabled") + ".", enabled, tmpAction.isEnabled());
}
private boolean isDiffing() {
if (diffPlugin == null) {
return false;
}
if (diffPlugin.getFirstProgram() == null || diffPlugin.getDiffController() == null) {
return false;
}
return true;
}
private boolean isShowingDiff() {
if (diffPlugin == null) {
return false;
}
Program currentProgram = diffPlugin.getCurrentProgram();
Program firstProgram = diffPlugin.getFirstProgram();
if (currentProgram == null || currentProgram != firstProgram) {
return false;
}
return true;
}
private void selectTab(final MultiTabPanel panel, final Program pgm) {
runSwing(() -> invokeInstanceMethod("setSelectedProgram", panel,
new Class[] { Program.class }, new Object[] { pgm }), true);
waitForSwing();
}
private void selectTreeNodeByText(final JTree tree, final String text) throws Exception {
runSwing(() -> {
TreePath path = findTreePathToText(tree, text);
if (path == null) {
throw new RuntimeException("tree path is null.");
}
tree.expandPath(path);
});
waitForSwing();
runSwing(() -> {
TreePath path = findTreePathToText(tree, text);
if (path == null) {
throw new RuntimeException("tree path is null.");
}
tree.getSelectionModel().setSelectionPath(path);
});
}
}

View file

@ -0,0 +1,459 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import static org.junit.Assert.*;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.KeyEvent;
import javax.swing.JComboBox;
import javax.swing.JTree;
import javax.swing.tree.TreePath;
import org.junit.Test;
import docking.ActionContext;
import docking.DialogComponentProvider;
import docking.widgets.MultiLineLabel;
import docking.widgets.fieldpanel.LayoutModel;
import ghidra.app.cmd.label.AddLabelCmd;
import ghidra.app.services.ProgramManager;
import ghidra.framework.cmd.CompoundCmd;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.symbol.SourceType;
public class DualProgramTest extends DiffTestAdapter {
@Test
public void testOpenClosePgm2Enablement() throws Exception {
assertNotNull(openClosePgm2);
assertTrue(!openClosePgm2.isEnabled());
loadProgram(diffTestP1);
assertTrue(openClosePgm2.isEnabled());
closeProgram();
assertTrue(!openClosePgm2.isEnabled());
}
@Test
public void testCancelOpenSecondProgram() throws Exception {
Window win;
Component comp;
restoreProgram(diffTestP2);
loadProgram(diffTestP1);
launchDiffByAction();
win = waitForWindow("Select Other Program");
assertNotNull(win);
comp = getComponentOfType(win, JComboBox.class);
assertNotNull(comp);
JTree tree = findComponent(win, JTree.class);
TreeTestUtils.selectTreeNodeByText(tree, "DiffTestPgm2");
pressButton(win, "Cancel");
waitForSwing();
assertNull(fp2.getTopLevelAncestor());
win = getWindow("Select Other Program");
assertNull(win);
closeProgram();
assertNull(cb.getCurrentLocation());
}
@Test
public void testEscapeOpenSecondProgram() throws Exception {
restoreProgram(diffTestP2);
loadProgram(diffTestP1);
launchDiffByAction();
Window win = waitForWindow("Select Other Program");
assertNotNull(win);
waitForSwing();
Component comp = getComponentOfType(win, JComboBox.class);
assertNotNull(comp);
JTree tree = findComponent(win, JTree.class);
TreeTestUtils.selectTreeNodeByText(tree, "DiffTestPgm2");
triggerActionKey(comp, 0, KeyEvent.VK_ESCAPE);
waitForSwing();
assertNull(fp2.getTopLevelAncestor());
win = getWindow("Select Other Program");
assertNull(win);
closeProgram();
assertNull(cb.getCurrentLocation());
}
@Test
public void testOpenSecondProgram() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
assertNotNull(fp2.getTopLevelAncestor());
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(!applyDiffs.isEnabled());
assertTrue(!applyDiffsNext.isEnabled());
assertTrue(!ignoreDiffs.isEnabled());
assertTrue(!nextDiff.isEnabled());
assertTrue(!prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(!diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(!setPgm2Selection.isEnabled());
}
@Test
public void testCloseSecondProgram() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
assertNotNull(fp2.getTopLevelAncestor());
closeDiff();
assertNull(fp2.getTopLevelAncestor());
}
@Test
public void testNonMatchingProgramType() throws Exception {
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._SPARC64);
ProgramDB otherProgram = otherBuilder.getProgram();
restoreProgram(otherProgram);
loadProgram(diffTestP1);
launchDiffByAction();
Window win = waitForWindow("Select Other Program");
assertNotNull(win);
Component comp = getComponentOfType(win, JComboBox.class);
assertNotNull(comp);
JTree tree = findComponent(win, JTree.class);
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
pressButton(win, "OK");
waitForTasks();
win = waitForWindow("Can't Open Selected Program");
assertNotNull(win);
MultiLineLabel mll = findComponent(win, MultiLineLabel.class);
assertTrue(mll.getLabel().startsWith("Programs languages don't match."));
pressButton(win, "OK");
win = waitForWindow("Select Other Program");
assertNotNull(win);
pressButton(win, "Cancel");
}
@Test
public void testNoMemInCommonContinueNo() throws Exception {
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._X86);
ProgramDB otherProgram = otherBuilder.getProgram();
otherBuilder.createMemory(".stuff", "0x1000600", 0x300);
restoreProgram(otherProgram);
loadProgram(diffTestP1);
launchDiffByAction();
Window win = waitForWindow("Select Other Program");
assertNotNull(win);
Component comp = getComponentOfType(win, JComboBox.class);
assertNotNull(comp);
JTree tree = findComponent(win, JTree.class);
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
pressButton(win, "OK");
waitForTasks();
win = waitForWindow("No Memory In Common");
assertNotNull(win);
MultiLineLabel mll = findComponent(win, MultiLineLabel.class);
assertTrue(
mll.getLabel().startsWith("The two programs have no memory addresses in common."));
pressButton(win, "No");
win = waitForWindow("Select Other Program");
assertNotNull(win);
pressButton(win, "Cancel");
}
@Test
public void testNoMemInCommonContinueYes() throws Exception {
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._X86);
ProgramDB otherProgram = otherBuilder.getProgram();
otherBuilder.createMemory(".stuff", "0x1000600", 0x300);
restoreProgram(otherProgram);
loadProgram(diffTestP1);
launchDiffByAction();
Window win = waitForWindow("Select Other Program");
assertNotNull(win);
Component comp = getComponentOfType(win, JComboBox.class);
assertNotNull(comp);
JTree tree = findComponent(win, JTree.class);
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
pressButton(win, "OK");
waitForTasks();
win = waitForWindow("No Memory In Common");
assertNotNull(win);
MultiLineLabel mll = findComponent(win, MultiLineLabel.class);
assertTrue(
mll.getLabel().startsWith("The two programs have no memory addresses in common."));
pressButton(win, "Yes");
win = waitForWindow("Determine Program Differences");
assertNotNull(win);
pressButton(win, "OK");
assertTrue(!win.isShowing());
waitForTasks();
win = waitForWindow("Memory Differs");
assertNotNull(win);
pressButton(win, "OK");
}
@Test
public void testDiffPgmSameLanguage() throws Exception {
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._X86);
ProgramDB otherProgram = otherBuilder.getProgram();
otherBuilder.createMemory(".stuff", "0x1004000", 0x300);
Window win;
Component comp;
//InfoWindow.showSplashScreen();
showTool(frontEndTool);
env.showTool();
restoreProgram(otherProgram);
loadProgram(diffTestP1);
launchDiffByAction();
waitForSwing();
win = waitForWindow("Select Other Program");
assertNotNull(win);
comp = getComponentOfType(win, JComboBox.class);
assertNotNull(comp);
JTree tree = findComponent(win, JTree.class);
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
pressButton(win, "OK");
waitForTasks();
waitForSwing();
win = getWindow("Select Other Program");
assertNull(win);
assertNotNull(fp2.getTopLevelAncestor());
assertEquals("DiffTestPgm1", diffPlugin.getCurrentProgram().getName());
assertEquals("OtherProgram", diffPlugin.getSecondProgram().getName());
}
@Test
public void testReplacePgm2() throws Exception {
ProgramBuilder otherBuilder = new ProgramBuilder("OtherProgram", ProgramBuilder._X86);
ProgramDB otherProgram = otherBuilder.getProgram();
otherBuilder.createMemory(".stuff", "0x1004000", 0x300);
Window win;
Component comp;
restoreProgram(otherProgram);
restoreProgram(diffTestP2);
loadProgram(diffTestP1);
getDiffActions();
assertNull(fp2.getTopLevelAncestor());
launchDiffByAction();
win = waitForWindow("Select Other Program");
assertNotNull(win);
comp = getComponentOfType(win, JComboBox.class);
assertNotNull(comp);
JTree tree = findComponent(win, JTree.class);
TreeTestUtils.selectTreeNodeByText(tree, "OtherProgram");
pressButton(win, "OK");
waitForTasks();
waitForSwing();
win = getWindow("Select Other Program");
assertNull(win);
assertNotNull(fp2.getTopLevelAncestor());
ProgramManager pm = tool.getService(ProgramManager.class);
assertEquals("DiffTestPgm1", pm.getCurrentProgram().getName());
assertEquals("OtherProgram", diffPlugin.getSecondProgram().getName());
DialogComponentProvider dialog = waitForDialogComponent(DialogComponentProvider.class);
// press the Cancel button
pressButtonByText(dialog, "Cancel");
waitForSwing();
closeDiff();
assertFalse(openClosePgm2.isSelected());
runSwing(() -> {
openClosePgm2.setSelected(true);
openClosePgm2.actionPerformed(new ActionContext());
}, false);
waitForSwing();
waitForCondition(() -> openClosePgm2.isSelected());
assertEquals(true, openClosePgm2.isSelected());
win = waitForWindow("Select Other Program");
assertNotNull(win);
tree = findComponent(win, JTree.class);
TreeTestUtils.selectTreeNodeByText(tree, "DiffTestPgm2");
pressButton(win, "OK");
waitForTasks();
assertTrue(!win.isVisible());
assertNotNull(fp2.getTopLevelAncestor());
assertEquals("DiffTestPgm1", diffPlugin.getCurrentProgram().getName());
assertEquals("DiffTestPgm2", diffPlugin.getSecondProgram().getName());
dialog = waitForDialogComponent(DialogComponentProvider.class);
// press the Cancel button
pressButtonByText(dialog, "Cancel");
}
@Test
public void testOpenSameSecondProgram() throws Exception {
openSecondProgram(diffTestP1, diffTestP1);
assertNotNull(fp2.getTopLevelAncestor());
// Check action enablement.
assertTrue(viewDiffs.isEnabled());
assertTrue(!applyDiffs.isEnabled());
assertTrue(!applyDiffsNext.isEnabled());
assertTrue(!ignoreDiffs.isEnabled());
assertTrue(!nextDiff.isEnabled());
assertTrue(!prevDiff.isEnabled());
assertTrue(diffDetails.isEnabled());
assertTrue(!diffApplySettings.isEnabled());
assertTrue(getDiffs.isEnabled());
assertTrue(selectAllDiffs.isEnabled());
assertTrue(!setPgm2Selection.isEnabled());
// Modify the active program.
setLocation("100f3ff");
CompoundCmd cmd = new CompoundCmd("test");
cmd.add(new AddLabelCmd(addr("100f3ff"), "TestLabel", false, SourceType.USER_DEFINED));
cmd.add(
new AddLabelCmd(addr("100f3ff"), "AnotherTestLabel", false, SourceType.USER_DEFINED));
try {
txId = diffTestP1.startTransaction("Modify Program");
cmd.applyTo(diffTestP1);
}
finally {
diffTestP1.endTransaction(txId, true);
}
// Check that the two programs now differ.
assertTrue(viewDiffs.isEnabled());
LayoutModel lm1 = fp1.getLayoutModel();
LayoutModel lm2 = fp2.getLayoutModel();
int num1 = lm1.getNumIndexes().intValue();
int num2 = lm2.getNumIndexes().intValue();
assertEquals(num1, num2);
}
@Test
public void testEntireViewDiff() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
selectTreeNodeByText(tree, "DiffTestPgm1");
performAction(replaceView, true);
topOfFile(fp1);
assertEquals(addr("00000100"), cb.getCurrentAddress());
bottomOfFile(fp1);
assertEquals(addr("100f3ff"), cb.getCurrentAddress());
}
@Test
public void testSingleFragmentDiff() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
selectTreeNodeByText(tree, ".data");
performAction(replaceView, true);
topOfFile(fp1);
assertEquals(addr("1008000"), cb.getCurrentAddress());
bottomOfFile(fp1);
assertEquals(addr("10085ff"), cb.getCurrentAddress());
}
@Test
public void testMultipleFragmentsDiff() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
selectTreeNodeByText(tree, ".data");
performAction(replaceView, true);
selectTreeNodeByText(tree, ".rsrc");
performAction(goToView, true);
topOfFile(fp1);
assertEquals(addr("1008000"), cb.getCurrentAddress());
bottomOfFile(fp1);
assertEquals(addr("100f3ff"), cb.getCurrentAddress());
}
@Test
public void testEmptyViewDiff() throws Exception {
openSecondProgram(diffTestP1, diffTestP2);
JTree tree = findComponent(tool.getToolFrame(), JTree.class);
selectTreeNodeByText(tree, "DiffTestPgm1");
performAction(removeView, true);
topOfFile(fp1);
assertNull(cb.getCurrentAddress());
bottomOfFile(fp1);
assertNull(cb.getCurrentAddress());
assertEquals(0, fp1.getLayoutModel().getNumIndexes().intValue());
assertEquals(0, fp2.getLayoutModel().getNumIndexes().intValue());
}
/**
* Selects a tree node in the indicated tree with the specified text.
* The matching tree node is determined by comparing the specified text
* with the string returned by the tree node's toString() method.
* <br> Note: This method affects the expansion state of the tree. It
* will expand nodes starting at the root until a match is found or all
* of the tree is checked.
* @param tree the tree
* @param text the tree node's text
*/
public static void selectTreeNodeByText(final JTree tree, final String text) {
runSwing(() -> {
TreePath path = findTreePathToText(tree, text);
if (path == null) {
throw new RuntimeException("tree path is null.");
}
tree.expandPath(path);
}, false);
waitForSwing();
runSwing(() -> {
TreePath path = findTreePathToText(tree, text);
if (path == null) {
throw new RuntimeException("tree path is null.");
}
tree.getSelectionModel().setSelectionPath(path);
}, false);
}
}

View file

@ -0,0 +1,84 @@
/* ###
* 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 ghidra.app.plugin.core.diff;
import java.util.List;
import javax.swing.JTree;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTreeNode;
import generic.test.AbstractGenericTest;
public class TreeTestUtils {
public static TreePath findTreePathToText(JTree tree, String text) {
TreeModel tm = tree.getModel();
GTreeNode rootNode = (GTreeNode) tm.getRoot();
return findPathToText(tree, rootNode, text);
}
protected static TreePath findPathToText(JTree tree, GTreeNode node, String text) {
if (text.equals(node.getName())) {
return node.getTreePath();
}
List<GTreeNode> allChildren = node.getAllChildren();
for (GTreeNode childNode : allChildren) {
TreePath treePath = findPathToText(tree, childNode, text);
if (treePath != null) {
return treePath;
}
}
return null;
}
/**
* Selects a tree node in the indicated tree with the specified text.
* The matching tree node is determined by comparing the specified text
* with the string returned by the tree node's toString() method.
* <br> Note: This method affects the expansion state of the tree. It
* will expand nodes starting at the root until a match is found or all
* of the tree is checked.
* @param tree the tree
* @param text the tree node's text
*/
public static void selectTreeNodeByText(final JTree tree, final String text) {
AbstractGenericTest.runSwing(new Runnable() {
@Override
public void run() {
TreePath path = findTreePathToText(tree, text);
if (path == null) {
throw new RuntimeException("tree path is null.");
}
tree.expandPath(path);
}
});
AbstractGenericTest.waitForSwing();
AbstractGenericTest.runSwing(new Runnable() {
@Override
public void run() {
TreePath path = findTreePathToText(tree, text);
if (path == null) {
throw new RuntimeException("tree path is null.");
}
tree.getSelectionModel().setSelectionPath(path);
}
});
}
}