added domain object listener for function remove events

This commit is contained in:
adamopolous 2019-11-21 14:55:29 -05:00
parent 02df944b0e
commit a682ef31ea
52 changed files with 4184 additions and 2210 deletions

View File

@ -369,10 +369,15 @@ src/main/help/help/topics/FrontEndPlugin/images/program_obj.png||GHIDRA|||Custom
src/main/help/help/topics/FrontEndPlugin/images/up.png||GHIDRA||||END|
src/main/help/help/topics/FrontEndPlugin/images/user.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/help/help/topics/FunctionComparison/FunctionComparison.htm||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/AddFunctionsPanel.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/AddToComparisonIcon.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindow.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/FunctionScope.gif||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/ListingCodeComparisonOptions.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/MultiFunctionComparisonWindow.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/NavNextIcon.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/NavPreviousIcon.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/RemoveFromComparisonIcon.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/binaryData.gif||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/class.png||GHIDRA||||END|
src/main/help/help/topics/FunctionComparison/images/cursor_arrow_flipped.gif||GHIDRA||||END|
@ -946,6 +951,7 @@ src/main/resources/images/arrow_up.png||FAMFAMFAM Icons - CC 2.5|||famfamfam sil
src/main/resources/images/binaryData.gif||GHIDRA||||END|
src/main/resources/images/browser.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
src/main/resources/images/bullet_green.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/resources/images/bullet_star.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
src/main/resources/images/cUnion.png||GHIDRA||||END|
src/main/resources/images/camera-photo.png||Tango Icons - Public Domain|||tango|END|
src/main/resources/images/carry.png||GHIDRA||||END|

View File

@ -11,38 +11,21 @@
<H1><A name="FunctionComparisonPlugin"></A> <A name="Function_Comparison"></A> <A name=
"FunctionComparison"></A> Function Comparison Window</H1>
<P>The Function Comparison window can provide different types of panels for visually comparing
two or more functions. The Listing View of the Function Comparison window and its capabilities
is explained below.</P>
<P>The Function Comparison window provides a way to compare two or more
functions in a simple side-by-side panel. </P>
<H2><A name="Compare Selected Functions"></A>Compare Selected Functions</H2>
<P>To begin, select a function (or multiple functions) from the listing or
the function table. Then right-click and select the <b>Compare Selected
Functions</b> option.</P>
<BLOCKQUOTE>
<P>A Function Comparison window can be displayed from a selection containing two or more
functions in the CodeBrowser listing.</P>
</BLOCKQUOTE>
<BLOCKQUOTE>
<OL>
<LI>In the CodeBrowser Listing make a selection containing two or more functions that you
want to compare.</LI>
<LI>Right mouse click and select the <IMG alt="" src="images/page_white_c.png"> <B>Compare
Selected Functions...</B> option on the right mouse popup menu.</LI>
</OL>
</BLOCKQUOTE>
<P><A name="Dual_Listing"></A>A Function Comparison window will appear.</P>
<P><A name="Dual_Listing"></A>A new function comparison window will appear (subsequent
invocations of this option will create a new tab in the existing window).</P>
<BR><BR>
<CENTER>
<IMG alt="" border="0" src="images/FunctionComparisonWindow.png">
</CENTER><BR>
<BR>
<P>This window can have multiple tabs, one for each type of function comparison view that is
available.</P>
<H2><A name="Listing_View"></A>Listing View</H2>
@ -426,87 +409,40 @@
<H2><A name="Compare Multiple Functions"></A>Comparing Multiple Functions</H2>
<BLOCKQUOTE>
<P>When comparing more than two functions, a Function Comparison window will have additional
components at the top of it. This window displays two functions side by side again, but has
two choice components at the top of the panel, which indicates the current function being
displayed in each side (left and right) of the panel. Each choice component can display a
list indicating the functions that can be displayed in the listing below it. Only one
function at a a time from the list can be displayed below it based on the type of view
currently set. You can simply change the current function for either side of the function
comparison using the choice component in order to get a different function comparison.</P>
<P>There is no limit to the number of functions that can be compared. Users may
add and remove functions from each comparison panel as desired. Simply use the pulldowns
above the listing panels to change what is being compared.</P>
<P>There are two toolbar actions with quick keys that allow you to change to the
next/previous function for the side that has focus as indicated by the pink border.</P>
<P>The following toolbar options are available:</P>
<BR><BR>
<CENTER>
<IMG alt="" border="0" src="images/MultiFunctionComparisonWindow.png">
</CENTER><BR>
<BR>
<blockquote>
<h3><IMG src="images/AddToComparisonIcon.png" border="0"> <a name="Add_To_Comparison"></a>Add To Existing Comparison</h3>
<p>Allows the user to add functions to the current comparison window. When
selected, the following table containing all functions in the current program is
displayed:</p>
<p>
<IMG src="images/AddFunctionsPanel.png" border="0">
</p>
<p>Select the functions to be added and hit the <b>OK</b>
button; the selected functions will be available in both the left and right
sides of the comparison window.
</p>
<h3><IMG src="images/RemoveFromComparisonIcon.png" border="0"> <a name="Remove_From_Comparison"></a>Remove Function From Comparison</h3>
<p>Removes the function in the focused panel from the comparison. This
will remove the function from both the source and target selection pulldowns.</p>
<h3><IMG src="images/NavNextIcon.png" border="0"> <a name="Navigate_Next"></a>Go To Next Function</h3>
<p>Navigates to the next available function in the selection pulldown</p>
<h3><IMG src="images/NavPreviousIcon.png" border="0"> <a name="Navigate_Previous"></a>Go To Previous Function</h3>
<p>Navigates to the previous available function in the selection pulldown</p>
<p><IMG src="../../shared/note.png"> The Remove and Go To actions described
above will operate on the comparison panel that has focus, identified by the
pink border.</p>
</blockquote>
<H3>Multi-Function Comparison Actions</H3>
<BLOCKQUOTE>
<P>There are two additional actions that are available when you are comparing more than
just two functions. The following describes them.</P>
<H4><A name="Compare_The_Next_Function"></A>Compare The Next Function</H4>
<BLOCKQUOTE>
<P>The Function Comparison window has an icon (<IMG alt="" src=
"images/FunctionScope.gif"><IMG alt="" src="images/arrow_down.png">) on the tool bar to
change the function being compared to the next one in the list for the side that
currently has focus.</P>
<OL>
<LI>Make sure the side (left or right) has focus for the function you want to change to
the next one. The side with focus will have the pink border around it. It if doesn't,
you can mouse click in the other side to get the focused side to change or you can use
the Tab key to move the focus until it moves the pink border around the correct
side.</LI>
<LI>Right mouse click and select the <IMG alt="" src="images/FunctionScope.gif"><IMG
alt="" src="images/arrow_down.png"> <B>Compare The Next Function</B> option, OR select
the <IMG alt="" src="images/FunctionScope.gif"><IMG alt="" src="images/arrow_down.png">
button on the tool bar.</LI>
</OL>
<P>The function should change to the next one available in the list from the choice
component. The differences will be recomputed for the new function comparison that is now
displayed. If the currently displayed function is at the end of the list, a message will
display in the tool's status area indicating there isn't a next function.</P>
</BLOCKQUOTE>
<H4><A name="Compare_The_Previous_Function"></A>Compare The Previous Function</H4>
<BLOCKQUOTE>
<P>The Function Comparison window has an icon (<IMG alt="" src=
"images/FunctionScope.gif"><IMG alt="" src="images/arrow_up.png">) on the tool bar to
change the function being compared to the previous one in the list for the side that
currently has focus.</P>
<OL>
<LI>Make sure the side (left or right) has focus for the function you want to change
to the previous one. The side with focus will have the pink border around it. It if
doesn't, you can mouse click in the other side to get the focused side to change or
you can use the Tab key to move the focus until it moves the pink border around the
correct side.</LI>
<LI>Right mouse click and select the <IMG alt="" src="images/FunctionScope.gif"><IMG
alt="" src="images/arrow_up.png"> <B>Compare The Previous Function</B> option, OR
select the <IMG alt="" src="images/FunctionScope.gif"><IMG alt="" src=
"images/arrow_up.png"> button on the tool bar.</LI>
</OL>
<P>The function should change to the previous one available in the list from the choice
component. The differences will be recomputed for the new function comparison that is
now displayed. If the currently displayed function is at the beginning of the list, a
message will display in the tool's status area indicating there isn't a previous
function.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H3>Other Function Comparison Actions</H3>
<P>The following are additional actions that are available in the Function Comparison
@ -582,9 +518,7 @@
</BLOCKQUOTE>
</BLOCKQUOTE><BR>
<BR>
<P class="providedbyplugin">Provided By:&nbsp; <I>FunctionComparisonPlugin</I></P>
<P class="relatedtopic">Related Topics:</P>

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

View File

@ -42,20 +42,24 @@ on the tool bar to make a selection in the Code Browser. To make a selection,</p
<h3><a name="Function_Comparison"></a><a name="Compare_Selected_Functions"></a>Compare Selected Functions</h3>
<blockquote>
<p>The Functions window has an icon (<img src="images/page_white_c.png">)
on the tool bar to compare the functions whose rows are currently selected in the table.
</p>
<p>The Functions window has an icon (<img src="images/page_white_c.png"> which contains a star
<img src="images/bullet_star.png">) on the tool bar that allows users to compare
the functions currently selected in the table.</p>
<p><img border="0" src="../../shared/note.png"> Note that selecting this
will always create a new comparison. If you have an existing comparison
and wish to add functions to it, you must initiate that directly from the
existing comparison window itself.</p>
<p>To create a new comparison:</p>
<ol>
<li>&nbsp;Select the functions you want to compare from the table in the Functions window.
You must select two or more rows in the table.</li>
<li>Right mouse click and select the <img src="images/page_white_c.png">
<b>Compare Selected Functions</b> option, OR select the <img src="images/page_white_c.png">
button on the tool bar.</li>
<li>Select the functions you want to compare from the table in the Functions window.
You must select one or more rows in the table.</li>
<li>Right mouse click and select the <b>Compare Selected Functions</b> option,
OR click the create-comparison icon on the tool bar.</li>
</ol>
<p>This will display a
<a href="help/topics/FunctionComparison/FunctionComparison.htm">Function Comparison</a>
window which allows the functions to be viewed
side by side.</p>
<p>The resulting <a href="help/topics/FunctionComparison/FunctionComparison.htm">Function Comparison</a>
window will allow the functions to be viewed side by side.</p>
</blockquote>
<p>&nbsp;</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,86 +0,0 @@
/* ###
* 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.functioncompare;
import java.util.ArrayList;
import docking.ActionContext;
import docking.action.MenuData;
import ghidra.app.context.*;
import ghidra.program.model.listing.*;
import ghidra.program.util.ProgramSelection;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
/**
* An action that displays a function comparison panel for the two functions that are selected
* in the program.
*/
public class CompareFunctionsAction extends ProgramContextAction {
FunctionComparisonPlugin functionComparisonPlugin;
/**
* Constructs an action for displaying a panel that allows the user to compare functions.
* @param plugin the plugin that owns this action.
*/
CompareFunctionsAction(FunctionComparisonPlugin plugin) {
super("Compare Two Functions", plugin.getName());
functionComparisonPlugin = plugin;
// TODO no icon for now, while this action is at the top-level menu. When we put it in
// its final resting place, we can put the icon back.
// ImageIcon icon = ResourceManager.loadImage("images/page_white_c.png");
setPopupMenuData(new MenuData(new String[] { "Compare Selected Functions..." }, null,
FunctionComparisonPlugin.FUNCTION_MENU_SUBGROUP, MenuData.NO_MNEMONIC,
"Z_End" /* See the FunctionPlugin for this value */));
setHelpLocation(new HelpLocation("FunctionComparison", "Compare_Selected_Functions"));
}
@Override
public boolean isAddToPopup(ActionContext context) {
return (context instanceof ListingActionContext);
}
@Override
protected boolean isEnabledForContext(ProgramActionContext context) {
return (context instanceof ListingActionContext);
}
@Override
public void actionPerformed(ProgramActionContext context) {
if (context instanceof ListingActionContext) {
ListingActionContext listingContext = (ListingActionContext) context;
ProgramSelection selection = listingContext.getSelection();
Program program = listingContext.getProgram();
FunctionManager functionManager = program.getFunctionManager();
ArrayList<Function> functionList = new ArrayList<>();
FunctionIterator functionIter = functionManager.getFunctions(selection, true);
for (Function selectedFunction : functionIter) {
functionList.add(selectedFunction);
}
Function[] functions = functionList.toArray(new Function[functionList.size()]);
if (functions.length < 2) {
String message = "You must select at least two functions in the current program.";
Msg.showError(this, null, "Compare Functions", message);
return;
}
functionComparisonPlugin.showFunctionComparisonProvider(functions);
}
}
}

View File

@ -1,447 +0,0 @@
/* ###
* 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.functioncompare;
import java.awt.*;
import java.awt.event.InputEvent;
import java.util.*;
import javax.swing.*;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.*;
import docking.help.Help;
import docking.help.HelpService;
import docking.widgets.combobox.GComboBox;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
import ghidra.app.util.viewer.util.CodeComparisonPanelActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import resources.MultiIcon;
import resources.ResourceManager;
import resources.icons.TranslateIcon;
/**
* Creates a panel for comparing two or more functions.
* If there are multiple functions to display within either the left or right side of this panel,
* then a combo box will appear above the left and right side of the {@link CodeComparisonPanel}s.
* Each combo box will allow the user to choose which function to display on that side of the panel.
*/
public abstract class FunctionChoiceComparisonPanel extends FunctionComparisonPanel {
private JPanel choicePanel; // null if only 1 left function and 1 right function.
protected WrappedFunction[] leftWrappedFunctions = new WrappedFunction[] {};
protected WrappedFunction[] rightWrappedFunctions = new WrappedFunction[] {};
protected int leftIndex = 0;
protected int rightIndex = 0;
protected JComboBox<WrappedFunction> leftComboBox;
protected JComboBox<WrappedFunction> rightComboBox;
private NextFunctionAction nextFunctionAction;
private PreviousFunctionAction previousFunctionAction;
private static final String FUNCTION_NAVIGATE_GROUP = "A9_FunctionNavigate";
private static final Icon FUNCTION_ICON =
new TranslateIcon(ResourceManager.loadImage("images/FunctionScope.gif"), -5, -2);
private static final Icon NEXT_ICON =
new TranslateIcon(ResourceManager.loadImage("images/arrow_down.png"), 3, 1);
private static final Icon PREVIOUS_ICON =
new TranslateIcon(ResourceManager.loadImage("images/arrow_up.png"), 3, 1);
private static final Icon NEXT_FUNCTION_ICON = new MultiIcon(NEXT_ICON, FUNCTION_ICON);
private static final Icon PREVIOUS_FUNCTION_ICON = new MultiIcon(PREVIOUS_ICON, FUNCTION_ICON);
protected static final HelpService help = Help.getHelpService();
protected static final String HELP_TOPIC = "FunctionComparison";
private MyFunctionComparator myFunctionComparator = new MyFunctionComparator();
/**
* Creates a panel for comparing two or more functions.
*
* @param provider the GUI provider that includes this panel.
* @param tool the tool containing this panel
* @param leftFunction the function displayed in the left side of the panel.
* @param rightFunction the function displayed in the right side of the panel.
*/
protected FunctionChoiceComparisonPanel(ComponentProvider provider, PluginTool tool,
Function leftFunction, Function rightFunction) {
super(provider, tool, leftFunction, rightFunction);
}
/**
* Gets the functions for the left side of the panel.
* <br>
* These functions are sorted ascending on program and function name.
* The primary sort is on program including pathname.
* The secondary sort is on function including namespace.
*
* @return the functions that can be displayed on the left side.
*/
public Function[] getLeftFunctions() {
Function[] leftFunctions = new Function[leftWrappedFunctions.length];
for (int i = 0; i < leftWrappedFunctions.length; i++) {
leftFunctions[i] = leftWrappedFunctions[i].getFunction();
}
return leftFunctions;
}
/**
* Gets the functions for the right side of the panel.
* <br>
* These functions are sorted ascending on program and function name.
* The primary sort is on program including pathname.
* The secondary sort is on function including namespace.
*
* @return the functions that can be displayed on the right side.
*/
public Function[] getRightFunctions() {
Function[] rightFunctions = new Function[rightWrappedFunctions.length];
for (int i = 0; i < rightWrappedFunctions.length; i++) {
rightFunctions[i] = rightWrappedFunctions[i].getFunction();
}
return rightFunctions;
}
/**
* Gets an array of WrappedFunctions for the array of functions passed as a parameter.
* @param functionArray the functions to convert to WrappedFunctions.
* @return the WrappedFunctions.
*/
protected WrappedFunction[] getWrappedFunctions(Function[] functionArray) {
WrappedFunction[] wrappedFunctionArray = new WrappedFunction[functionArray.length];
for (int i = 0; i < functionArray.length; i++) {
wrappedFunctionArray[i] = new WrappedFunction(functionArray[i]);
}
return wrappedFunctionArray;
}
/**
* Adds a panel with combo boxes for choosing the currently displayed function in the
* left and right panel. This also populates the combo boxes with the functions.
*/
protected void addChoicePanel() {
choicePanel = new JPanel(new GridLayout(1, 2));
choicePanel.add(createLeftChoicePanel());
choicePanel.add(createRightChoicePanel());
add(choicePanel, BorderLayout.NORTH);
help.registerHelp(choicePanel, new HelpLocation(HELP_TOPIC, "Compare Multiple Functions"));
}
/**
* Updates the selected left function in the combo box based on the current left index.
*/
protected void adjustSelectedLeftFunction() {
WrappedFunction leftWrappedFunction =
(leftIndex < leftWrappedFunctions.length) ? leftWrappedFunctions[leftIndex] : null;
leftComboBox.setSelectedItem(leftWrappedFunction);
}
/**
* Updates the selected right function in the combo box based on the current right index.
*/
protected void adjustSelectedRightFunction() {
WrappedFunction rightWrappedFunction =
(rightIndex < rightWrappedFunctions.length) ? rightWrappedFunctions[rightIndex] : null;
rightComboBox.setSelectedItem(rightWrappedFunction);
}
private Component createLeftChoicePanel() {
JPanel panel = new JPanel(new BorderLayout());
leftComboBox = new GComboBox<>(leftWrappedFunctions);
adjustSelectedLeftFunction();
leftComboBox.addItemListener(e -> {
WrappedFunction wrappedFunction = (WrappedFunction) leftComboBox.getSelectedItem();
Function function = (wrappedFunction != null) ? wrappedFunction.getFunction() : null;
setLeftFunction(function);
});
panel.add(leftComboBox, BorderLayout.CENTER);
return panel;
}
private Component createRightChoicePanel() {
JPanel panel = new JPanel(new BorderLayout());
rightComboBox = new GComboBox<>(rightWrappedFunctions);
adjustSelectedRightFunction();
rightComboBox.addItemListener(e -> {
WrappedFunction wrappedFunction = (WrappedFunction) rightComboBox.getSelectedItem();
Function function = (wrappedFunction != null) ? wrappedFunction.getFunction() : null;
setRightFunction(function);
});
panel.add(rightComboBox, BorderLayout.CENTER);
return panel;
}
/**
* Creates actions for displaying the next or previous function if we are using combo boxes.
*/
protected void createActions() {
if (choicePanel != null) {
nextFunctionAction = new NextFunctionAction();
previousFunctionAction = new PreviousFunctionAction();
}
}
@Override
public DockingAction[] getCodeComparisonActions() {
DockingAction[] otherActions = super.getCodeComparisonActions();
if (choicePanel == null) {
return otherActions;
}
DockingAction[] myActions =
new DockingAction[] { nextFunctionAction, previousFunctionAction };
DockingAction[] actions = new DockingAction[otherActions.length + myActions.length];
System.arraycopy(otherActions, 0, actions, 0, otherActions.length);
System.arraycopy(myActions, 0, actions, otherActions.length, myActions.length);
return actions;
}
private boolean isValidPanelContext(ActionContext context) {
if (context instanceof CodeComparisonPanelActionContext) {
return choicePanel != null;
}
return false;
}
private void nextFunction() {
CodeComparisonPanel<? extends FieldPanelCoordinator> currentComponent =
getCurrentComponent();
if (currentComponent == null) {
return;
}
boolean leftHasFocus = currentComponent.leftPanelHasFocus();
if (leftHasFocus) {
if (leftIndex < (leftWrappedFunctions.length - 1)) {
leftComboBox.setSelectedIndex(++leftIndex);
}
else {
outputNoNextPreviousMessage(true, leftHasFocus);
return;
}
}
else { // right has focus.
if (rightIndex < (rightWrappedFunctions.length - 1)) {
rightComboBox.setSelectedIndex(++rightIndex);
}
else {
outputNoNextPreviousMessage(true, leftHasFocus);
return;
}
}
}
private void previousFunction() {
CodeComparisonPanel<? extends FieldPanelCoordinator> currentComponent =
getCurrentComponent();
if (currentComponent == null) {
return;
}
boolean leftHasFocus = currentComponent.leftPanelHasFocus();
if (leftHasFocus) {
if (leftIndex > 0) {
leftComboBox.setSelectedIndex(--leftIndex);
}
else {
outputNoNextPreviousMessage(false, leftHasFocus);
return;
}
}
else { // right has focus.
if (rightIndex > 0) {
rightComboBox.setSelectedIndex(--rightIndex);
}
else {
outputNoNextPreviousMessage(false, leftHasFocus);
return;
}
}
}
private void outputNoNextPreviousMessage(boolean forward, boolean isFirstListing) {
tool.setStatusInfo("There isn't another " + (forward ? "next " : "previous ") +
"function for the " + (isFirstListing ? "first" : "second") + " listing.");
}
/**
* Action to display the next function in the currently focused side of the function
* comparison panel if possible.
*/
protected class NextFunctionAction extends DockingAction {
NextFunctionAction() {
super("Compare Next Function", provider.getOwner());
setKeyBindingData(
new KeyBindingData('N', InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK));
setDescription("Compare the next function for the side with focus.");
setPopupMenuData(new MenuData(new String[] { "Compare The Next Function" },
NEXT_FUNCTION_ICON, FUNCTION_NAVIGATE_GROUP));
ToolBarData newToolBarData =
new ToolBarData(NEXT_FUNCTION_ICON, FUNCTION_NAVIGATE_GROUP);
setToolBarData(newToolBarData);
HelpLocation helpLocation = new HelpLocation(HELP_TOPIC, "Compare The Next Function");
setHelpLocation(helpLocation);
setEnabled(true);
}
@Override
public boolean isValidContext(ActionContext context) {
return isValidPanelContext(context);
}
@Override
public void actionPerformed(ActionContext context) {
if (isValidContext(context)) {
nextFunction();
}
}
}
/**
* Action to display the previous function in the currently focused side of the function
* comparison panel if possible.
*/
protected class PreviousFunctionAction extends DockingAction {
PreviousFunctionAction() {
super("Compare Previous Function", provider.getOwner());
setKeyBindingData(
new KeyBindingData('P', InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK));
setDescription("Compare the previous function for the side with focus.");
setPopupMenuData(new MenuData(new String[] { "Compare The Previous Function" },
PREVIOUS_FUNCTION_ICON, FUNCTION_NAVIGATE_GROUP));
ToolBarData newToolBarData =
new ToolBarData(PREVIOUS_FUNCTION_ICON, FUNCTION_NAVIGATE_GROUP);
setToolBarData(newToolBarData);
HelpLocation helpLocation =
new HelpLocation(HELP_TOPIC, "Compare The Previous Function");
setHelpLocation(helpLocation);
setEnabled(true);
}
@Override
public boolean isValidContext(ActionContext context) {
return isValidPanelContext(context);
}
@Override
public void actionPerformed(ActionContext context) {
if (isValidContext(context)) {
previousFunction();
}
}
}
/**
* Gets a sorted array of the functions for the indicated set of functions.
* This sorts the functions first by program and then by function name.
* The primary sort is ascending on the program's pathname.
* The secondary sort is ascending on the function's name which includes its namespace.
*
* @param functions the set of functions
* @return the sorted array of functions
*/
protected Function[] getSortedFunctions(Set<Function> functions) {
Function[] sortedFunctions = functions.toArray(new Function[functions.size()]);
Arrays.sort(sortedFunctions, myFunctionComparator);
return sortedFunctions;
}
/**
* Gets a sorted array of the functions for the indicated array of functions.
* This sorts the functions first by program and then by function name.
* The primary sort is ascending on the program's pathname.
* The secondary sort is ascending on the function's name which includes its namespace.
* <br>
* The original function array is not modified.
*
* @param functions the array of functions
* @return a new sorted array of functions
*/
protected Function[] getSortedFunctions(Function[] functions) {
Function[] sortedFunctions = Arrays.copyOf(functions, functions.length);
Arrays.sort(sortedFunctions, myFunctionComparator);
return sortedFunctions;
}
/**
* A comparator for functions that sorts the functions first by program and then by
* function name.
* The primary sort is ascending on the program's pathname.
* The secondary sort is ascending on the function's name which includes its namespace.
*/
private class MyFunctionComparator implements Comparator<Function> {
@Override
public int compare(Function function1, Function function2) {
if (function1 == null) {
if (function2 == null) {
return 0;
}
return -1;
}
if (function1 == function2) {
return 0;
}
String function1Name = function1.getName(true);
String function2Name = function2.getName(true);
Program program1 = function1.getProgram();
Program program2 = function2.getProgram();
String program1Name = program1.getDomainFile().getPathname();
String program2Name = program2.getDomainFile().getPathname();
int comparePrograms = program1Name.compareTo(program2Name);
if (comparePrograms != 0) {
return comparePrograms;
}
return function1Name.compareTo(function2Name);
}
}
/**
* Class that allows us to display a more informative string in the combo box for each function.
*/
protected static class WrappedFunction {
private Function function;
private WrappedFunction(Function function) {
this.function = function;
}
protected Function getFunction() {
return function;
}
@Override
public String toString() {
return getFunctionTitle();
}
private String getFunctionTitle() {
if (function == null) {
return "none";
}
Program program = function.getProgram();
return function.getName(true) + "()" +
((program != null) ? (" in " + program.getDomainFile().getPathname() + "") : "");
}
}
}

View File

@ -0,0 +1,131 @@
/* ###
* 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.functioncompare;
import java.util.*;
import ghidra.app.services.FunctionComparisonModel;
import ghidra.program.model.listing.Function;
/**
* Defines the structure of a function comparison. The relationship is strictly
* one-to-many; a single <code>source</code> function may be associated with one
* or more <code>target</code> functions.
* <p>
* This is the basic unit for the
* {@link FunctionComparisonModel function comparison data model}
*/
public class FunctionComparison implements Comparable<FunctionComparison> {
private Function source;
/** Use a tree so functions are always kept in sorted order */
private Set<Function> targets = new TreeSet<>(new FunctionComparator());
/**
* Returns the source function
*
* @return the source function
*/
public Function getSource() {
return source;
}
/**
* Returns the set of targets, in sorted order by function name
*
* @return the set of targets
*/
public Set<Function> getTargets() {
return targets;
}
/**
* Sets a given function as the comparison source
*
* @param function the source function
*/
public void setSource(Function function) {
source = function;
}
/**
* Adds a target function to the comparison
*
* @param function the function to add to the target list
*/
public void addTarget(Function function) {
targets.add(function);
}
/**
* Adds a set of functions to the target list
*
* @param functions the functions to add
*/
public void addTargets(Set<Function> functions) {
targets.addAll(functions);
}
/**
* Removes the given function from the target list.
* <p>
* Note that the target list is a {@link Set}, so there will only ever
* be at most one entry that matches the given function
*
* @param function the function to remove
*/
public void removeTarget(Function function) {
targets.remove(function);
}
/**
* Removes all targets from the comparison
*/
public void clearTargets() {
targets.clear();
}
/**
* Ensures that FunctionComparison objects are always ordered according
* to the source function name
*/
@Override
public int compareTo(FunctionComparison o) {
if (o == null) {
return 1;
}
return getSource().getName().compareTo(o.getSource().getName());
}
/**
* Forces an ordering on {@link Function} objects by name. This is
* to ensure that the list of targets is kept in sorted order at all times.
*/
class FunctionComparator implements Comparator<Function> {
@Override
public int compare(Function o1, Function o2) {
if (o1 == o2) {
return 0;
}
if (o2 == null) {
return 1;
}
return o1.getName().compareTo(o2.getName());
}
}
}

View File

@ -0,0 +1,154 @@
/* ###
* 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.functioncompare;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
/**
* Defines the information being displayed in the left or right panels
* of a {@link FunctionComparisonPanel}, which can display either
* {@link Function functions}, {@link Data data}, or specified
* {@link AddressSet address sets}. At any given time, only one of the
* Function or Data attributes may be set; the other will be
* set to null.
*/
class FunctionComparisonData {
protected Program program;
protected Function function;
protected Data data;
protected AddressSetView addressSet = new AddressSet();
/**
* Returns the program for this model
*
* @return the program, or null if not set
*/
public Program getProgram() {
return program;
}
/**
* Sets the program for this model
*
* @param program the program to set
*/
public void setProgram(Program program) {
this.program = program;
}
/**
* Returns the function for this model
*
* @return the function, or null if not set
*/
public Function getFunction() {
return function;
}
/**
* Sets the function for this model
*
* @param function the function to set
*/
public void setFunction(Function function) {
if (function == null) {
clear();
return;
}
this.function = function;
this.data = null;
this.program = function.getProgram();
this.addressSet = function.getBody();
}
/**
* Returns the data for this model
*
* @return the data, or null if not set
*/
public Data getData() {
return data;
}
/**
* Sets the data for this model
*
* @param data the data to set
*/
public void setData(Data data) {
if (data == null) {
clear();
return;
}
this.data = data;
this.function = null;
this.program = data.getProgram();
this.addressSet = new AddressSet(data.getMinAddress(), data.getMaxAddress());
}
/**
* Returns the address set for this model
*
* @return the address set, or null if not set
*/
public AddressSetView getAddressSet() {
return addressSet;
}
/**
* Sets the address for this model
*
* @param addressSet the addressSet to set
*/
public void setAddressSet(AddressSetView addressSet) {
this.addressSet = addressSet;
this.data = null;
this.function = null;
}
/**
* Returns true if the data being managed by this model is of type
* {@link Data}
*
* @return true if this model is set to display {@link Data}
*/
public boolean isData() {
return data != null;
}
/**
* Returns true if the data being managed by this model is of type
* {@link Function}
*
* @return true if this model is set to display a {@link Function}
*/
public boolean isFunction() {
return function != null;
}
/**
* Resets all fields in this model to a nominal state
*/
public void clear() {
this.function = null;
this.data = null;
this.addressSet = new AddressSet();
this.program = null;
}
}

View File

@ -0,0 +1,34 @@
/* ###
* 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.functioncompare;
import java.util.List;
import ghidra.app.services.FunctionComparisonModel;
/**
* Allows subscribers to register for {@link FunctionComparisonModel function
* comparison model} changes
*/
public interface FunctionComparisonModelListener {
/**
* Invoked when the comparison model has changed
*
* @param model the current state of the model
*/
public void modelChanged(List<FunctionComparison> model);
}

View File

@ -15,20 +15,32 @@
*/
package ghidra.app.plugin.core.functioncompare;
import java.util.Set;
import docking.ComponentProviderActivationListener;
import ghidra.app.CorePluginPackage;
import ghidra.app.events.ProgramClosedPluginEvent;
import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.functioncompare.actions.CompareFunctionsAction;
import ghidra.app.plugin.core.functioncompare.actions.CompareFunctionsFromListingAction;
import ghidra.app.services.FunctionComparisonService;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ChangeManager;
import ghidra.program.util.ProgramChangeRecord;
/**
* Plugin that provides the actions that allow the user to compare functions using a
* FunctionComparisonPanel.
* Allows users to create function comparisons that are displayed
* side-by-side in a provider. Comparisons can be initiated via the listing
* or function table and are displayed in a {@link FunctionComparisonProvider}.
* <p>
* The underlying data backing the comparison provider is managed by the
* {@link FunctionComparisonService}.
*/
//@formatter:off
@PluginInfo(
@ -36,36 +48,36 @@ import ghidra.program.model.listing.Program;
packageName = CorePluginPackage.NAME,
category = PluginCategoryNames.DIFF,
shortDescription = "Compare Functions",
description = "This plugin provides actions that allow you to compare two or more functions with each other.",
eventsConsumed = { ProgramClosedPluginEvent.class }
description = "Allows users to compare two or more functions",
servicesProvided = { FunctionComparisonService.class },
eventsConsumed = { ProgramSelectionPluginEvent.class, ProgramActivatedPluginEvent.class,
ProgramClosedPluginEvent.class }
)
//@formatter:on
public class FunctionComparisonPlugin extends ProgramPlugin implements DomainObjectListener {
public class FunctionComparisonPlugin extends ProgramPlugin
implements DomainObjectListener, FunctionComparisonService {
public final static String FUNCTION_MENU_SUBGROUP = "Function";
static final String MENU_PULLRIGHT = "CompareFunctions";
static final String POPUP_MENU_GROUP = "CompareFunction";
private FunctionComparisonProviderManager functionComparisonManager;
/**
* Creates a plugin that provides actions for comparing functions.
* @param tool the tool that owns this plugin.
* Constructor
*
* @param tool the tool that owns this plugin
*/
public FunctionComparisonPlugin(PluginTool tool) {
super(tool, true, true);
functionComparisonManager = new FunctionComparisonProviderManager(this);
tool.setMenuGroup(new String[] { MENU_PULLRIGHT }, POPUP_MENU_GROUP);
}
@Override
protected void init() {
createActions();
}
private void createActions() {
tool.addAction(new CompareFunctionsAction(this));
CompareFunctionsAction compareFunctionsAction = new CompareFunctionsFromListingAction(tool);
tool.addAction(compareFunctionsAction);
}
@Override
@ -85,18 +97,69 @@ public class FunctionComparisonPlugin extends ProgramPlugin implements DomainObj
}
/**
* Displays a panel for comparing the specified functions.
* @param functions the functions that are used to populate both the left and right side
* of the function comparison panel.
* Overridden to listen for two event types:
* <li>Object Restored: In the event of a redo/undo that affects a function
* being shown in the comparison provider, this will allow tell the provider
* to reload</li>
* <li>Object Removed: If a function is deleted, this will tell the provider
* to purge it from the view</li>
*/
void showFunctionComparisonProvider(Function[] functions) {
functionComparisonManager.showFunctionComparisonProvider(functions);
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
for (int i = 0; i < ev.numRecords(); ++i) {
DomainObjectChangeRecord doRecord = ev.getChangeRecord(i);
int eventType = doRecord.getEventType();
switch (eventType) {
case DomainObject.DO_OBJECT_RESTORED:
functionComparisonManager.domainObjectRestored(ev);
break;
case ChangeManager.DOCR_FUNCTION_REMOVED:
ProgramChangeRecord rec = (ProgramChangeRecord) ev.getChangeRecord(i);
Function function = (Function) rec.getObject();
if (function != null) {
removeFunction(function);
}
break;
}
}
}
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
functionComparisonManager.domainObjectRestored(ev);
}
public void addFunctionComparisonProviderListener(
ComponentProviderActivationListener listener) {
functionComparisonManager.addProviderListener(listener);
}
@Override
public void removeFunction(Function function) {
functionComparisonManager.removeFunction(function);
}
@Override
public void removeFunction(Function function, FunctionComparisonProvider provider) {
functionComparisonManager.removeFunction(function, provider);
}
@Override
public FunctionComparisonProvider compareFunctions(Function source, Function target) {
return functionComparisonManager.compareFunctions(source, target);
}
@Override
public void compareFunctions(Set<Function> functions, FunctionComparisonProvider provider) {
functionComparisonManager.compareFunctions(functions, provider);
}
@Override
public FunctionComparisonProvider compareFunctions(Set<Function> functions) {
return functionComparisonManager.compareFunctions(functions);
}
@Override
public void compareFunctions(Function source, Function target,
FunctionComparisonProvider provider) {
functionComparisonManager.compareFunctions(source, target, provider);
}
}

View File

@ -18,140 +18,75 @@ package ghidra.app.plugin.core.functioncompare;
import java.awt.event.MouseEvent;
import java.util.*;
import javax.swing.Icon;
import docking.ActionContext;
import docking.DockingTool;
import docking.action.DockingAction;
import docking.action.DockingActionIf;
import docking.actions.PopupActionProvider;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.services.FunctionComparisonModel;
import ghidra.app.services.FunctionComparisonService;
import ghidra.app.util.viewer.listingpanel.ListingCodeComparisonPanel;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import resources.ResourceManager;
/**
* This is the dockable provider that displays a FunctionComparisonPanel.
* Dockable provider that displays function comparisons Clients create/modify
* these comparisons using the {@link FunctionComparisonService}, which in turn
* creates instances of this provider as-needed.
*/
public class FunctionComparisonProvider extends ComponentProviderAdapter implements PopupActionProvider {
public class FunctionComparisonProvider extends ComponentProviderAdapter
implements PopupActionProvider, FunctionComparisonModelListener {
private static final String HELP_TOPIC = "FunctionComparison";
private static final Icon ICON = ResourceManager.loadImage("images/page_white_c.png");
private FunctionComparisonPanel functionComparisonPanel;
private FunctionComparisonProviderListener listener;
protected static final String HELP_TOPIC = "FunctionComparison";
protected FunctionComparisonPanel functionComparisonPanel;
protected Plugin plugin;
/** Contains all the comparison data to be displayed by this provider */
protected FunctionComparisonModel model;
/**
* Creates a provider for displaying a FunctionComparisonPanel that allows two or more
* functions to be compared. This constructor will load the functions so they are available
* for display in both the left side and right side of the panel. By default the first function
* will be loaded into the left side and the second function will be loaded in the right side.
* @param plugin the plugin that owns this provider.
* @param functions the functions that are used to populate both the left and right side
* of the function comparison panel.
* @param listener the listener to notify when the provider is closing.
* Constructor
*
* @param plugin the active plugin
* @param name the providers name; used to group similar providers into a tab within
* the same window
* @param owner the provider owner, usually a plugin name
*/
public FunctionComparisonProvider(Plugin plugin, Function[] functions,
FunctionComparisonProviderListener listener) {
super(plugin.getTool(), "Function Comparison", plugin.getName());
this.listener = listener;
if (ICON != null) {
setIcon(ICON);
}
functionComparisonPanel = new MultiFunctionComparisonPanel(this, tool, functions);
public FunctionComparisonProvider(Plugin plugin, String name, String owner) {
this(plugin, name, owner, null);
}
/**
* Constructor
*
* @param plugin the active plugin
* @param name the providers name; used to group similar providers into a tab within
* the same window
* @param owner the provider owner, usually a plugin name
* @param contextType the type of context supported by this provider; may be null
*/
public FunctionComparisonProvider(Plugin plugin, String name, String owner,
Class<?> contextType) {
super(plugin.getTool(), name, owner, contextType);
this.plugin = plugin;
model = new FunctionComparisonModel();
model.addFunctionComparisonModelListener(this);
functionComparisonPanel = getComponent();
initFunctionComparisonPanel();
}
/**
* Creates a provider for displaying two or more functions to be compared. This will load the
* functions so the leftFunctions are available for display in the left side and the
* rightFunctions are available for display in the right side of the function comparison panel.
* By default the first function from each array will be the one initially displayed in its
* associated side.
* @param plugin the plugin that owns this provider.
* @param leftFunctions the functions that are used to populate the left side
* @param rightFunctions the functions that are used to populate the right side
* @param listener the listener to notify when the provider is closing.
*/
public FunctionComparisonProvider(Plugin plugin, Function[] leftFunctions,
Function[] rightFunctions, FunctionComparisonProviderListener listener) {
super(plugin.getTool(), "FunctionComparison", plugin.getName());
this.listener = listener;
if (ICON != null) {
setIcon(ICON);
}
functionComparisonPanel =
new MultiFunctionComparisonPanel(this, tool, leftFunctions, rightFunctions);
initFunctionComparisonPanel();
}
/**
* Creates a provider for displaying two or more functions to be compared. This will load
* the functions so the leftFunctions are available for display in the left side. For
* each left side function there is a list of right side functions for comparison.
* rightFunctions are available for display in the right side of the function comparison panel.
* By default the first function from each array will be the one initially displayed in its
* associated side.
* @param plugin the plugin that owns this provider.
* @param functionMap maps each left function to its own set of right functions for comparison.
* @param listener the listener to notify when the provider is closing.
*/
public FunctionComparisonProvider(Plugin plugin,
HashMap<Function, HashSet<Function>> functionMap,
FunctionComparisonProviderListener listener) {
super(plugin.getTool(), "FunctionComparison", plugin.getName());
this.listener = listener;
if (ICON != null) {
setIcon(ICON);
}
functionComparisonPanel = new MappedFunctionComparisonPanel(this, tool, functionMap);
initFunctionComparisonPanel();
}
/**
* Perform initialization for this provider and its panel. This includes setting the
* tab text, getting actions, establishing the popup listener, and specifying help.
*/
private void initFunctionComparisonPanel() {
setTransient();
setTabText(functionComparisonPanel);
addSpecificCodeComparisonActions();
tool.addPopupActionProvider(this);
setHelpLocation(new HelpLocation(HELP_TOPIC, "Function Comparison"));
}
/**
* Creates the text that is displayed on the tab for this provider.
* @param functionCompPanel the function comparison panel for this provider.
*/
private void setTabText(FunctionComparisonPanel functionCompPanel) {
Function leftFunction = functionCompPanel.getLeftFunction();
Function rightFunction = functionCompPanel.getRightFunction();
String tabText = (leftFunction == null && rightFunction == null) ? "No Functions Yet"
: getTabText(leftFunction, rightFunction);
setTabText(tabText);
}
private String getTabText(Function function1, Function function2) {
return ((function1 != null) ? function1.getName() : "none") + " & " +
((function2 != null) ? function2.getName() : "none");
}
private void addSpecificCodeComparisonActions() {
DockingAction[] actions = functionComparisonPanel.getCodeComparisonActions();
for (DockingAction dockingAction : actions) {
addLocalAction(dockingAction);
}
}
@Override
public FunctionComparisonPanel getComponent() {
if (functionComparisonPanel == null) {
functionComparisonPanel = new FunctionComparisonPanel(this, tool, null, null);
}
return functionComparisonPanel;
}
@ -161,6 +96,8 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
buff.append("FunctionComparisonProvider\n");
buff.append("Name: ");
buff.append(getName() + "\n");
buff.append("Tab Text: ");
buff.append(getTabText() + "\n");
Function leftFunction = functionComparisonPanel.getLeftFunction();
String leftName = (leftFunction != null) ? leftFunction.getName() : "No Function";
buff.append("Function 1: " + leftName + "\n");
@ -181,44 +118,15 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
@Override
public void removeFromTool() {
tool.removePopupActionProvider(this);
super.removeFromTool();
}
@Override
public void closeComponent() {
super.closeComponent();
if (listener != null) {
listener.providerClosed(this);
}
}
/**
* Indicates that the specified program has been closed, so this provider can close its
* component, if any of its functions were from that program.
* @param program the program that was closed.
*/
public void programClosed(Program program) {
// For now close the panel if it has any functions with this program.
Function[] functions = functionComparisonPanel.getFunctions();
for (Function function : functions) {
if ((function != null) && function.getProgram() == program) {
closeComponent();
return;
}
}
}
/**
* Indicates that the specified program has been restored, so this can refresh the code
* comparison panel.
* @param program the program that was restored (undo/redo).
*/
public void programRestored(Program program) {
CodeComparisonPanel<? extends FieldPanelCoordinator> comparePanel =
functionComparisonPanel.getCurrentComponent();
comparePanel.programRestored(program);
public void modelChanged(List<FunctionComparison> model) {
this.model.setComparisons(model);
functionComparisonPanel.reload();
setTabText(functionComparisonPanel);
closeIfEmpty();
}
@Override
@ -235,7 +143,72 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
}
/**
* Restores the function comparison provider's components to the indicated saved configuration state.
* Returns the comparison model
*
* @return the comparison model
*/
public FunctionComparisonModel getModel() {
return model;
}
/**
* Replaces the comparison model with the one provided
*
* @param model the comparison model
*/
public void setModel(FunctionComparisonModel model) {
this.model = model;
}
/**
* Removes any functions being displayed by this provider that are from
* the given program. If there are no functions left to display, the
* provider is closed.
*
* @param program the program being closed
*/
public void programClosed(Program program) {
model.removeFunctions(program);
closeIfEmpty();
}
/**
* Removes all functions for the specified program from the comparison
* model
*
* @param program the program whose functions require removal
*/
public void removeFunctions(Program program) {
model.removeFunctions(program);
closeIfEmpty();
}
/**
* Removes the set of functions from the comparison model
*
* @param functions the functions to remove
*/
public void removeFunctions(Set<Function> functions) {
functions.stream().forEach(f -> model.removeFunction(f));
closeIfEmpty();
}
/**
* Indicates that the specified program has been restored, so the
* comparison panel should be refreshed
*
* @param program the program that was restored (undo/redo)
*/
public void programRestored(Program program) {
CodeComparisonPanel<? extends FieldPanelCoordinator> comparePanel =
functionComparisonPanel.getCurrentComponent();
comparePanel.programRestored(program);
}
/**
* Restores the function comparison providers components to the indicated
* saved configuration state
*
* @param saveState the configuration state to restore
*/
public void readConfigState(SaveState saveState) {
@ -243,10 +216,78 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
}
/**
* Saves the current configuration state of the components that compose the function comparison provider.
* Saves the current configuration state of the components that compose
* the function comparison provider
*
* @param saveState the new configuration state
*/
public void writeConfigState(SaveState saveState) {
functionComparisonPanel.writeConfigState(getName(), saveState);
}
/**
* Creates the text that is displayed on the tab for this provider
*
* @param comparisonPanel the function comparison panel for this provider
*/
public void setTabText(FunctionComparisonPanel comparisonPanel) {
Function leftFunction = comparisonPanel.getLeftFunction();
Function rightFunction = comparisonPanel.getRightFunction();
String tabText = (leftFunction == null && rightFunction == null) ? "No Functions Yet"
: getTabText(leftFunction, rightFunction);
setTabText(tabText);
}
/**
* Perform initialization for this provider and its panel
*/
protected void initFunctionComparisonPanel() {
setTransient();
setTabText(functionComparisonPanel);
addSpecificCodeComparisonActions();
tool.addPopupActionProvider(this);
setHelpLocation(new HelpLocation(HELP_TOPIC, "Function Comparison"));
}
/**
* Returns true if the comparison panel is empty
*
* @return true if the panel is empty
*/
boolean isEmpty() {
return functionComparisonPanel.isEmpty();
}
/**
* Closes this provider if there are no comparisons to view
*/
void closeIfEmpty() {
if (isEmpty()) {
closeComponent();
}
}
/**
* Returns the text that should be displayed in the tab, given the two
* left/right functions
*
* @param leftFunction
* @param rightFunction
* @return the tab text
*/
private String getTabText(Function leftFunction, Function rightFunction) {
return ((leftFunction != null) ? leftFunction.getName() : "none") + " & " +
((rightFunction != null) ? rightFunction.getName() : "none");
}
/**
* Gets actions specific to the code comparison panel and adds them to this
* provider
*/
private void addSpecificCodeComparisonActions() {
DockingAction[] actions = functionComparisonPanel.getCodeComparisonActions();
for (DockingAction dockingAction : actions) {
addLocalAction(dockingAction);
}
}
}

View File

@ -16,13 +16,22 @@
package ghidra.app.plugin.core.functioncompare;
/**
* Listener for a FunctionComparisonProvider.
* Allows subscribers to register for function comparison provider changes
* (eg: when the provider is opened/closed)
*/
public interface FunctionComparisonProviderListener {
/**
* Notification method that will get called when the provider is being closed.
* @param provider the provider that is being closed.
* Invoked when the provider is being closed
*
* @param provider the closed provider
*/
public void providerClosed(FunctionComparisonProvider provider);
/**
* Invoked when the provider is being opened
*
* @param provider the opened provider
*/
public void providerOpened(FunctionComparisonProvider provider);
}

View File

@ -15,219 +15,199 @@
*/
package ghidra.app.plugin.core.functioncompare;
import java.util.*;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import ghidra.app.plugin.ProgramPlugin;
import docking.ComponentProviderActivationListener;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
/**
* FunctionComparisonProviderManager allows a plugin to display function comparison panels.
* It responds to program close events and closes any panels that have a function from the
* closed program. It also updates the displays if needed due to an Undo.
* Provides access to all open {@link FunctionComparisonProvider comparison providers}
* and allows users to do the following:
* <li>create new providers</li>
* <li>add comparisons to existing providers</li>
* <li>remove comparisons</li>
* <li>notify subscribers when providers are opened/closed</li>
*/
public class FunctionComparisonProviderManager implements FunctionComparisonProviderListener {
private HashSet<FunctionComparisonProvider> functionComparisonProviders = new HashSet<>();
private ProgramPlugin plugin;
private PluginTool tool;
private Set<FunctionComparisonProvider> providers = new CopyOnWriteArraySet<>();
private Set<ComponentProviderActivationListener> listeners = new HashSet<>();
private Plugin plugin;
/**
* Constructs a FunctionComparisonProviderManager.
* @param plugin the plugin that owns this manager.
* Constructor
*
* @param plugin the parent plugin
*/
public FunctionComparisonProviderManager(ProgramPlugin plugin) {
public FunctionComparisonProviderManager(Plugin plugin) {
this.plugin = plugin;
tool = plugin.getTool();
}
/**
* This will create a new function comparison panel with the specified functions available for
* display in both the left and right side of the panel. Initially the function comparison panel
* will display the first function in the left side and the second function in the right side of
* the panel. If the manager already has a provider to display the specified functions it will
* be brought to the front instead of creating a new panel.
* @param functions the functions that are used to populate both the left and right side
* of the function comparison panel.
* @return the FunctionComparisonProvider that is displaying these functions.
*/
public FunctionComparisonProvider showFunctionComparisonProvider(Function[] functions) {
FunctionComparisonProvider functionComparisonProvider =
findFunctionComparisonProvider(functions);
if (functionComparisonProvider != null) {
// If it is already displayed then bring it to the front.
tool.toFront(functionComparisonProvider);
return functionComparisonProvider;
}
FunctionComparisonProvider provider =
new FunctionComparisonProvider(plugin, functions, this);
functionComparisonProviders.add(provider);
provider.setVisible(true);
return provider;
}
/**
* This creates a new function comparison panel with the specified leftFunctions available for
* display in the left side of the panel and the rightFunctions available for display in the right side.
* Initially the function comparison panel will display the first leftFunction and the first
* rightFunction. If the manager already has a provider to display the specified leftFunctions
* and rightFunctions it will be brought to the front instead of creating a new panel.
* @param leftFunctions the functions that are used to populate the left side
* @param rightFunctions the functions that are used to populate the right side
* @return the FunctionComparisonProvider that is displaying these functions.
*/
public FunctionComparisonProvider showFunctionComparisonProvider(Function[] leftFunctions,
Function[] rightFunctions) {
FunctionComparisonProvider functionComparisonProvider =
findFunctionComparisonProvider(leftFunctions, rightFunctions);
if (functionComparisonProvider != null) {
// If it is already displayed then bring it to the front.
tool.toFront(functionComparisonProvider);
return functionComparisonProvider;
}
FunctionComparisonProvider provider =
new FunctionComparisonProvider(plugin, leftFunctions, rightFunctions, this);
functionComparisonProviders.add(provider);
provider.setVisible(true);
return provider;
}
/**
* This creates a new function comparison panel with the specified left functions
* available for display in the left side of the panel and a list of functions for the
* right side for each function in the left.
* If the manager already has a provider to display the specified function map it will
* be brought to the front instead of creating a new panel.
* @param functionMap map of the functions that are used to populate both the left and
* right side of the function comparison panel.
* @return the FunctionComparisonProvider that is displaying these functions.
*/
public FunctionComparisonProvider showFunctionComparisonProvider(
HashMap<Function, HashSet<Function>> functionMap) {
FunctionComparisonProvider functionComparisonProvider =
findFunctionComparisonProvider(functionMap);
if (functionComparisonProvider != null) {
// If it is already displayed then bring it to the front.
tool.toFront(functionComparisonProvider);
return functionComparisonProvider;
}
FunctionComparisonProvider provider =
new FunctionComparisonProvider(plugin, functionMap, this);
functionComparisonProviders.add(provider);
provider.setVisible(true);
return provider;
}
private FunctionComparisonProvider findFunctionComparisonProvider(Function[] functions) {
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
FunctionComparisonPanel functionComparisonPanel =
functionComparisonProvider.getComponent();
if (functionComparisonPanel instanceof MultiFunctionComparisonPanel) {
MultiFunctionComparisonPanel multiPanel =
(MultiFunctionComparisonPanel) functionComparisonPanel;
if (multiPanel.matchesTheseFunctions(functions, functions)) {
return functionComparisonProvider;
}
}
else { // basic FunctionComparisonPanel
Function[] panelFunctions = functionComparisonPanel.getFunctions();
if (Arrays.equals(panelFunctions, functions)) {
return functionComparisonProvider;
}
}
}
return null;
}
private FunctionComparisonProvider findFunctionComparisonProvider(Function[] functionsL,
Function[] functionsR) {
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
FunctionComparisonPanel functionComparisonPanel =
functionComparisonProvider.getComponent();
if (!(functionComparisonPanel instanceof MultiFunctionComparisonPanel)) {
continue;
}
MultiFunctionComparisonPanel multiPanel =
(MultiFunctionComparisonPanel) functionComparisonPanel;
if (multiPanel.matchesTheseFunctions(functionsL, functionsR)) {
return functionComparisonProvider;
}
}
return null;
}
private FunctionComparisonProvider findFunctionComparisonProvider(
HashMap<Function, HashSet<Function>> functionMap) {
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
FunctionComparisonPanel functionComparisonPanel =
functionComparisonProvider.getComponent();
if (functionComparisonPanel instanceof MappedFunctionComparisonPanel) {
MappedFunctionComparisonPanel mappedPanel =
(MappedFunctionComparisonPanel) functionComparisonPanel;
if (mappedPanel.matchesTheseFunctions(functionMap)) {
return functionComparisonProvider;
}
}
}
return null;
}
@Override
public void providerClosed(FunctionComparisonProvider provider) {
functionComparisonProviders.remove(provider);
providers.remove(provider);
listeners.stream().forEach(l -> l.componentProviderDeactivated(provider));
}
@Override
public void providerOpened(FunctionComparisonProvider provider) {
listeners.stream().forEach(l -> l.componentProviderActivated(provider));
}
/**
* Closes all the function comparison providers that have a function from the indicated program.
* This method should be called when a program is closing.
* @param program the program whose function providers need to close.
* Creates a new comparison between the given set of functions
*
* @param functions the functions to compare
* @return the new comparison provider
*/
public FunctionComparisonProvider compareFunctions(Set<Function> functions) {
if (functions.isEmpty()) {
return null;
}
FunctionComparisonProvider provider = new MultiFunctionComparisonProvider(plugin);
provider.addToTool();
provider.getModel().compareFunctions(functions);
providers.add(provider);
provider.setVisible(true);
return provider;
}
/**
* Creates a new comparison comparison between two functions
*
* @param source the source function
* @param target the target function
* @return the new comparison provider
*/
public FunctionComparisonProvider compareFunctions(Function source,
Function target) {
FunctionComparisonProvider provider = new MultiFunctionComparisonProvider(plugin);
provider.addToTool();
provider.getModel().compareFunctions(source, target);
providers.add(provider);
provider.setVisible(true);
return provider;
}
/**
* Adds a set of functions to an existing comparison provider
*
* @param functions the functions to compare
* @param provider the provider to add the functions to
*/
public void compareFunctions(Set<Function> functions, FunctionComparisonProvider provider) {
if (functions.isEmpty() || provider == null) {
return;
}
providers.add(provider);
provider.setVisible(true);
provider.getModel().compareFunctions(functions);
}
/**
* Adds the given functions to an existing comparison provider
*
* @param source the source function
* @param target the target function
* @param provider the provider to add the functions to
*/
public void compareFunctions(Function source, Function target,
FunctionComparisonProvider provider) {
if (provider == null) {
return;
}
providers.add(provider);
provider.setVisible(true);
provider.getModel().compareFunctions(source, target);
}
/**
* Removes a given function from all comparisons across all providers
*
* @param function the function to remove
*/
public void removeFunction(Function function) {
providers.stream().forEach(p -> p.getModel().removeFunction(function));
}
/**
* Removes a given function from a specified provider
*
* @param function the function to remove
* @param provider the provider to remove the function from
*/
public void removeFunction(Function function, FunctionComparisonProvider provider) {
if (provider == null) {
return;
}
provider.getModel().removeFunction(function);
}
/**
* Registers subscribers who wish to know of provider activation status
*
* @param listener the subscriber to register
*/
public void addProviderListener(ComponentProviderActivationListener listener) {
listeners.add(listener);
}
/**
* Closes all the comparison providers that contain a function from
* the given program
*
* @param program the program whose function providers need to close
*/
public void closeProviders(Program program) {
// Get an array of the providers and loop over it to notify them. This is to prevent
// causing a ConcurrentModificationException. If a provider closes due to the indicated
// program closing, this manager will get notified via the providerClosed method and
// remove that provider from the functionComparisonProviders hashset.
FunctionComparisonProvider[] providers = functionComparisonProviders
.toArray(new FunctionComparisonProvider[functionComparisonProviders.size()]);
for (FunctionComparisonProvider functionComparisonProvider : providers) {
functionComparisonProvider.programClosed(program); // Allow the provider to close itself.
}
providers.stream().forEach(p -> p.programClosed(program));
}
/**
* Cleans up since this manager is being disposed. All function comparison providers will
* close and be cleaned up.
* Removes any comparisons that contain a function from the given program
*
* @param program the program whose functions require removal
*/
public void removeFunctions(Program program) {
providers.stream().forEach(p -> p.removeFunctions(program));
}
/**
* Cleans up all providers, setting them invisible and removing any
* associated ui components (eg: tabs)
*/
public void dispose() {
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
FunctionComparisonPanel functionComparisonPanel =
functionComparisonProvider.getComponent();
functionComparisonPanel.setVisible(false);
functionComparisonPanel.dispose();
for (FunctionComparisonProvider provider : providers) {
FunctionComparisonPanel panel = provider.getComponent();
panel.setVisible(false);
panel.dispose();
}
functionComparisonProviders.clear();
providers.clear();
}
/**
* Called when there is an Undo/Redo. If a program is being restored, this will notify all the
* function comparison providers. This allows them to refresh if they are showing a function
* from the program.
* @param ev the event indicating if this is an Undo/Redo on a program.
* Called when there is an Undo/Redo. If a program is being restored, this
* will notify all the function comparison providers. This allows them to
* refresh if they are showing a function from the program
*
* @param ev the object changed event
*/
public void domainObjectRestored(DomainObjectChangedEvent ev) {
for (DomainObjectChangeRecord domainObjectChangeRecord : ev) {
int eventType = domainObjectChangeRecord.getEventType();
if (eventType == DomainObject.DO_OBJECT_RESTORED) {
Object source = ev.getSource();
if (source instanceof Program) {
Program program = (Program) source;
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
functionComparisonProvider.programRestored(program);
}
}
if (eventType != DomainObject.DO_OBJECT_RESTORED) {
return;
}
Object source = ev.getSource();
if (source instanceof Program) {
Program program = (Program) source;
providers.stream().forEach(p -> p.programRestored(program));
}
}
}

View File

@ -1,167 +0,0 @@
/* ###
* 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.functioncompare;
import java.util.*;
import javax.swing.DefaultComboBoxModel;
import docking.ComponentProvider;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.util.HelpLocation;
/**
* Creates a panel for comparing two or more functions. One or more functions can be displayed
* in the left side of the panel. Each of these left functions is mapped to its own set of functions,
* which can be displayed one at a time in the right side of the panel for comparison.
* If there are multiple functions to display within either the left or right side of this panel,
* then a combo box will appear above the left and right side of the CodeComparisonPanels.
* Each combo box will allow the user to choose which function to display on that side of the panel.
* Changing the selected function in the left side of the panel will possibly change the available
* functions for display in the right side of the panel.
*/
public class MappedFunctionComparisonPanel extends FunctionChoiceComparisonPanel {
private HashMap<Function, HashSet<Function>> functionMap;
/**
* Constructor
*
* @param provider the provider displaying this panel.
* @param tool the tool displaying this panel.
* @param functionMap map of the functions that are used to populate both the left and right side
* of the function comparison panel.
*/
public MappedFunctionComparisonPanel(ComponentProvider provider, PluginTool tool,
HashMap<Function, HashSet<Function>> functionMap) {
super(provider, tool, null, null);
this.functionMap = functionMap;
establishLeftFunctions(0);
establishRightFunctions(0);
if (leftWrappedFunctions.length > 1 || rightWrappedFunctions.length > 1) {
addChoicePanel();
}
reloadLeftFunctions();
reloadRightFunctions();
createActions();
help.registerHelp(this, new HelpLocation(HELP_TOPIC, "Function Comparison"));
}
/**
* Sets the left functions that are used in the left combo box for the left side of the
* function comparison. These functions are in sorted order. It also sets the current
* left index to a valid value that indicates which function in the left list is currently
* selected.
* @param leftFunctionIndex the desired index of the left function that should be
* selected currently. If the specified index isn't valid for the current list of left
* functions then the left index will get set to 0 which indicates the first function.
*/
private void establishLeftFunctions(int leftFunctionIndex) {
Set<Function> leftFunctionSet = functionMap.keySet();
Function[] leftSortedFunctions = getSortedFunctions(leftFunctionSet);
leftWrappedFunctions = getWrappedFunctions(leftSortedFunctions);
leftIndex = (leftFunctionIndex < leftSortedFunctions.length) ? leftFunctionIndex : 0;
setLeftFunction(leftSortedFunctions[leftIndex]);
}
private void adjustRightFunctions(int rightFunctionIndex) {
establishRightFunctions(rightFunctionIndex);
reloadRightFunctions();
}
/**
* Sets the right functions that are used in the right combo box for the right side of the
* function comparison. These functions are in sorted order. It also sets the current
* right index to a valid value that indicates which function in the right list is currently
* selected.
* @param rightFunctionIndex the desired index of the right function that should be
* selected currently. If the specified index isn't valid for the current list of right
* functions then the right index will get set to 0 which indicates the first function.
*/
private void establishRightFunctions(int rightFunctionIndex) {
Set<Function> rightFunctionSet = functionMap.get(getLeftFunction());
Function[] rightSortedFunctions =
(rightFunctionSet != null) ? getSortedFunctions(rightFunctionSet) : new Function[0];
rightWrappedFunctions = getWrappedFunctions(rightSortedFunctions);
rightIndex = (rightFunctionIndex < rightSortedFunctions.length) ? rightFunctionIndex : 0;
}
private void reloadLeftFunctions() {
// Adjust the index if it is out of bounds.
if (leftIndex >= leftWrappedFunctions.length) {
leftIndex = 0;
}
if (leftComboBox != null) {
// Load the functions into the combo box.
leftComboBox.setModel(new DefaultComboBoxModel<>(leftWrappedFunctions));
// Select the function in the combo box.
adjustSelectedLeftFunction();
}
// Set the function in the view.
Function leftFunctionAtIndex = (leftIndex < leftWrappedFunctions.length)
? leftWrappedFunctions[leftIndex].getFunction()
: null;
setLeftFunction(leftFunctionAtIndex);
}
private void reloadRightFunctions() {
// Adjust the index if it is out of bounds.
if (rightIndex >= rightWrappedFunctions.length) {
rightIndex = 0;
}
if (rightComboBox != null) {
// Load the functions into the combo box.
rightComboBox.setModel(new DefaultComboBoxModel<>(rightWrappedFunctions));
// Select the function in the combo box.
adjustSelectedRightFunction();
}
// Set the function in the view.
Function rightFunctionAtIndex = (rightIndex < rightWrappedFunctions.length)
? rightWrappedFunctions[rightIndex].getFunction()
: null;
setRightFunction(rightFunctionAtIndex);
}
/**
* Determines if the map of left functions to lists of associated right functions match
* the map of functions currently displayed for comparison in the left and right side of
* this panel.
*
* @param functionMap the map of left functions to lists of right functions
* @return true if the map matches what is currently displayed by this panel.
*/
boolean matchesTheseFunctions(HashMap<Function, HashSet<Function>> myFunctionMap) {
return functionMap.equals(myFunctionMap);
}
@Override
public void loadFunctions(Function newLeftFunction, Function newRightFunction) {
Function myLeftFunction = getLeftFunction();
super.loadFunctions(newLeftFunction, newRightFunction);
if (myLeftFunction != getLeftFunction()) {
// Left function changed so adjust the function list in the right side.
adjustRightFunctions(0);
}
}
}

View File

@ -15,109 +15,298 @@
*/
package ghidra.app.plugin.core.functioncompare;
import java.util.Arrays;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Iterator;
import java.util.Set;
import docking.ComponentProvider;
import javax.swing.*;
import docking.help.Help;
import docking.help.HelpService;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.services.FunctionComparisonModel;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.util.HelpLocation;
/**
* Creates a panel for comparing two or more functions.
* If there are multiple functions to display within either the left or right side of this panel,
* then a combo box will appear above the left and right side of the CodeComparisonPanels.
* Each combo box will allow the user to choose which function to display on that side of the panel.
* Extends the basic {@link FunctionComparisonPanel one-to-one comparison panel}
* to allow a many-to-many relationship. The panel provides a pair of combo
* boxes above the function display area that allows users to select which
* functions are to be compared.
* <p>
* Throughout this class the terms <code>source</code> and <code>target</code>
* are used when referencing functions. This is because the model that backs
* this panel maintains a relationship between the functions being compared
* such that each source function can only be compared to a specific set
* of target functions. For all practical purposes, the source functions
* appear in the left-side panel and targets appear on the right.
*
*/
public class MultiFunctionComparisonPanel extends FunctionChoiceComparisonPanel {
public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
/** Functions that will show up on the left side of the panel */
private JComboBox<Function> sourceFunctionsCB;
/** Functions that will show up on the right side of the panel */
private JComboBox<Function> targetFunctionsCB;
/** Data models backing the source and target combo boxes */
private DefaultComboBoxModel<Function> sourceFunctionsCBModel;
private DefaultComboBoxModel<Function> targetFunctionsCBModel;
protected static final HelpService help = Help.getHelpService();
public static final String HELP_TOPIC = "FunctionComparison";
/**
* Creates a panel for displaying two or more functions to be compared. This makes the
* functions available for display in both the left side and right side of the panel after
* they are sorted ascending based on the program and function. The primary sort is on
* program including pathname. The secondary sort is on function including namespace.
* By default the first function will be loaded into the left side and the second function
* will be loaded in the right side.
* @param provider the provider displaying this panel.
* @param tool the tool displaying this panel.
* @param functions the functions that are used to populate both the left and right side
* of the function comparison panel.
*/
public MultiFunctionComparisonPanel(ComponentProvider provider, PluginTool tool,
Function[] functions) {
super(provider, tool, null, null);
// For now, sort the functions.
Function[] sortedFunctions = getSortedFunctions(functions);
leftWrappedFunctions = getWrappedFunctions(sortedFunctions);
rightWrappedFunctions = leftWrappedFunctions;
if (leftWrappedFunctions.length >= 2) {
Function leftFunction = (leftIndex < leftWrappedFunctions.length)
? leftWrappedFunctions[leftIndex].getFunction()
: null;
++rightIndex;
Function rightFunction = (rightIndex < rightWrappedFunctions.length)
? rightWrappedFunctions[rightIndex].getFunction()
: null;
loadFunctions(leftFunction, rightFunction);
}
// Don't include the choice panel with its combo boxes unless there are more than 2 functions.
if (leftWrappedFunctions.length > 2) {
addChoicePanel(); // This also populates the combo boxes.
}
createActions();
help.registerHelp(this, new HelpLocation(HELP_TOPIC, "Function Comparison"));
}
/**
* Creates a panel for displaying two or more functions to be compared. This will load the
* functions so the leftFunctions are available for display in the left side and the
* rightFunctions are available for display in the right side of the function comparison panel.
* The functions are sorted ascending based on the program and function. The primary sort
* is on program including pathname. The secondary sort is on function including namespace.
* By default the first function from each array will be the one initially displayed in its
* associated side.
* @param provider the provider displaying this panel.
* @param tool the tool displaying this panel.
* @param leftFunctions the functions that are used to populate the left side
* @param rightFunctions the functions that are used to populate the right side
*/
public MultiFunctionComparisonPanel(ComponentProvider provider, PluginTool tool,
Function[] leftFunctions, Function[] rightFunctions) {
super(provider, tool, null, null);
Function[] sortedLeftFunctions = getSortedFunctions(leftFunctions);
Function[] sortedRightFunctions = getSortedFunctions(rightFunctions);
leftWrappedFunctions = getWrappedFunctions(sortedLeftFunctions);
rightWrappedFunctions =
Arrays.equals(sortedLeftFunctions, sortedRightFunctions) ? leftWrappedFunctions
: getWrappedFunctions(sortedRightFunctions);
if ((leftWrappedFunctions.length >= 1) && (rightWrappedFunctions.length >= 1)) {
Function leftFunction = (leftIndex < leftWrappedFunctions.length)
? leftWrappedFunctions[leftIndex].getFunction()
: null; // Initially leftIndex is 0.
Function rightFunction = (rightIndex < rightWrappedFunctions.length)
? rightWrappedFunctions[rightIndex].getFunction()
: null; // Initially rightIndex is 0.
if (leftFunction == rightFunction && rightWrappedFunctions.length > 1) {
rightFunction = rightWrappedFunctions[++rightIndex].getFunction();
}
loadFunctions(leftFunction, rightFunction);
}
if (leftWrappedFunctions.length > 1 || rightWrappedFunctions.length > 1) {
addChoicePanel(); // This also populates the combo boxes.
}
createActions();
}
/**
* Determines if <code>functionsL</code> and <code>functionsR</code> match the functions
* that can be displayed for comparison in the left and right side of this panel.
* Constructor
*
* @param functionsL the functions to check against those used to populate the left side
* @param functionsR the functions to check against those used to populate the right side
* @return true if functionsL and functionsR match the functions that can be displayed by
* this panel.
* @param provider the comparison provider associated with this panel
* @param tool the active plugin tool
*/
boolean matchesTheseFunctions(Function[] functionsL, Function[] functionsR) {
return Arrays.equals(getLeftFunctions(), getSortedFunctions(functionsL)) &&
Arrays.equals(getRightFunctions(), getSortedFunctions(functionsR));
public MultiFunctionComparisonPanel(FunctionComparisonProvider provider,
PluginTool tool) {
super(provider, tool, null, null);
JPanel choicePanel = new JPanel(new GridLayout(1, 2));
choicePanel.add(createSourcePanel());
choicePanel.add(createTargetPanel());
add(choicePanel, BorderLayout.NORTH);
// For the multi-panels we don't need to show the title of each
// comparison panel because the name of the function/data being shown
// is already visible in the combo box
getComparisonPanels().forEach(p -> p.setShowTitles(false));
}
/**
* Clears the given functions from the comparison panel (both the
* source and target lists) (the default for this method is to only clear
* out the functions visible in the left/right panels)
*/
@Override
public void removeFunctions(Set<Function> functions) {
((MultiFunctionComparisonProvider) provider).removeFunctions(functions);
}
/**
* Clears out the source and targets lists and reloads them to
* ensure that they reflect the current state of the data model. Any
* currently-selected list items will be restored after the lists
* are reloaded.
*/
@Override
public void reload() {
SwingUtilities.invokeLater(() -> {
reloadSourceList();
Function selectedSource = (Function) sourceFunctionsCBModel.getSelectedItem();
reloadTargetList(selectedSource);
loadFunctions(selectedSource, (Function) targetFunctionsCBModel.getSelectedItem());
((FunctionComparisonProvider) provider).setTabText(this);
((FunctionComparisonProvider) provider)
.setTitle(((FunctionComparisonProvider) provider).getTabText());
// Fire a notification to update the UI state; without this the
// actions would not be properly enabled/disabled
tool.contextChanged(provider);
tool.setStatusInfo("function comparisons updated");
});
}
/**
* Returns the combo box (source or target) which has focus
*
* @return the focused component
*/
public JComboBox<Function> getFocusedComponent() {
CodeComparisonPanel<? extends FieldPanelCoordinator> currentComponent =
getCurrentComponent();
boolean sourceHasFocus = currentComponent.leftPanelHasFocus();
return sourceHasFocus ? sourceFunctionsCB : targetFunctionsCB;
}
/**
* Clears out and reloads the source function list. Any selection currently
* made on the list will be reestablished.
*/
private void reloadSourceList() {
// Save off any selected item so we can restore if it later
Function selection = (Function) sourceFunctionsCBModel.getSelectedItem();
// Remove all functions
sourceFunctionsCBModel.removeAllElements();
// Reload the functions
FunctionComparisonModel model = ((FunctionComparisonProvider) provider).getModel();
Iterator<FunctionComparison> compIter = model.getComparisons().iterator();
while (compIter.hasNext()) {
FunctionComparison fc = compIter.next();
sourceFunctionsCBModel.addElement(fc.getSource());
}
restoreSelection(sourceFunctionsCB, selection);
}
/**
* Clears out and reloads the target function list with functions
* associated with the given source function. Any selection currently made
* on the list will be reestablished.
*
* @param source the selected source function
*/
private void reloadTargetList(Function source) {
// Save off any selected item so we can restore if it later
Function selection = (Function) targetFunctionsCBModel.getSelectedItem();
// Remove all functions
targetFunctionsCBModel.removeAllElements();
// Find all target functions associated with the given source function
// and add them to the combo box model
FunctionComparisonModel model = ((FunctionComparisonProvider) provider).getModel();
Iterator<FunctionComparison> compIter = model.getComparisons().iterator();
while (compIter.hasNext()) {
FunctionComparison fc = compIter.next();
if (fc.getSource().equals(source)) {
Set<Function> targets = fc.getTargets();
targetFunctionsCBModel.addAll(targets);
}
}
restoreSelection(targetFunctionsCB, selection);
}
/**
* Sets a given function to be the selected item in a given combo
* box. If the function isn't found, the first item in the box is
* set.
*
* @param cb the combo box
* @param selection the function to set
*/
private void restoreSelection(JComboBox<Function> cb, Function selection) {
ComboBoxModel<Function> model = cb.getModel();
boolean found = false;
for (int i = 0; i < model.getSize(); i++) {
Function f = model.getElementAt(i);
if (f.equals(selection)) {
model.setSelectedItem(f);
found = true;
break;
}
}
if (!found && model.getSize() > 0) {
cb.setSelectedIndex(0);
}
}
/**
* Creates the panel displaying the source combo box
* <p>
* Note: The custom renderer is used so the name of the program associated
* with each function can be displayed in the combo box; this is necessary
* since a combo box may show functions from any number of programs, and
* the default is to simply show the function name<br>
* eg: "init (notepad)"<br>
*
* @return the source panel
*/
private JPanel createSourcePanel() {
JPanel panel = new JPanel(new BorderLayout());
sourceFunctionsCB = new JComboBox<>();
sourceFunctionsCBModel = new DefaultComboBoxModel<>();
sourceFunctionsCB.setModel(sourceFunctionsCBModel);
sourceFunctionsCB.setRenderer(new FunctionListCellRenderer());
sourceFunctionsCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() != ItemEvent.SELECTED) {
return;
}
Function selected = (Function) sourceFunctionsCBModel.getSelectedItem();
loadFunctions(selected, null);
// Each time a source function is selected we need
// to load the targets associated with it
reloadTargetList((Function) sourceFunctionsCBModel.getSelectedItem());
// Fire a notification to update the UI state; without this the
// actions would not be properly enabled/disabled
tool.contextChanged(provider);
}
});
panel.add(sourceFunctionsCB, BorderLayout.CENTER);
return panel;
}
/**
* Creates the panel for the target functions selection components
* <p>
* Note: The custom renderer is used so the name of the program associated
* with each function can be displayed in the combo box; this is necessary
* since a combo box may show functions from any number of programs, and
* the default is to simply show the function name<br>
* eg: "init (notepad)"<br>
*
* @return the target panel
*/
private JPanel createTargetPanel() {
JPanel panel = new JPanel(new BorderLayout());
targetFunctionsCB = new JComboBox<>();
targetFunctionsCBModel = new DefaultComboBoxModel<>();
targetFunctionsCB.setModel(targetFunctionsCBModel);
targetFunctionsCB.setRenderer(new FunctionListCellRenderer());
targetFunctionsCB.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() != ItemEvent.SELECTED) {
return;
}
Function selected = (Function) targetFunctionsCBModel.getSelectedItem();
loadFunctions((Function) sourceFunctionsCBModel.getSelectedItem(), selected);
// Fire a notification to update the UI state; without this the
// actions would not be properly enabled/disabled
tool.contextChanged(provider);
}
});
panel.add(targetFunctionsCB, BorderLayout.CENTER);
return panel;
}
/**
* Cell renderer for combo boxes that changes the default display to show
* both the function name and the program it comes from
*/
private class FunctionListCellRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
if (value == null) {
// It's possible during a close program operation to have this
// renderer called with a null value. If so, we can't get the
// function so just use the default renderer.
return super.getListCellRendererComponent(list, value, index, isSelected,
cellHasFocus);
}
Function f = (Function) value;
String text = f.getName() + " (" + f.getProgram().getName() + ")";
return super.getListCellRendererComponent(list, text, index, isSelected,
cellHasFocus);
}
}
}

View File

@ -0,0 +1,76 @@
/* ###
* 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.functioncompare;
import docking.action.DockingAction;
import ghidra.app.plugin.core.functioncompare.actions.*;
import ghidra.framework.plugintool.Plugin;
/**
* Provider for a {@link MultiFunctionComparisonPanel}. This differs from the
* base comparison provider in that it has additional actions that are
* appropriate for managing multiple comparisons (add, remove, etc...).
*/
public class MultiFunctionComparisonProvider extends FunctionComparisonProvider {
/**
* Constructor
*
* @param plugin the parent plugin
*/
public MultiFunctionComparisonProvider(Plugin plugin) {
super(plugin, "functioncomparisonprovider", plugin.getName());
}
@Override
public FunctionComparisonPanel getComponent() {
if (functionComparisonPanel == null) {
functionComparisonPanel = new MultiFunctionComparisonPanel(this, tool);
}
return functionComparisonPanel;
}
@Override
boolean isEmpty() {
return model.getSourceFunctions().isEmpty();
}
@Override
protected void initFunctionComparisonPanel() {
super.initFunctionComparisonPanel();
DockingAction nextFunctionAction = new NextFunctionAction(this);
DockingAction previousFunctionAction = new PreviousFunctionAction(this);
DockingAction removeFunctionsAction = new RemoveFunctionsAction(this);
DockingAction openFunctionTableAction = getOpenFunctionTableAction();
addLocalAction(nextFunctionAction);
addLocalAction(previousFunctionAction);
addLocalAction(removeFunctionsAction);
addLocalAction(openFunctionTableAction);
}
/**
* Returns an action that opens a table from which users may select
* functions for comparison. By default this returns an action that will
* open a standard function table, but may be overridden as-needed.
*
* @return the docking action
*/
protected DockingAction getOpenFunctionTableAction() {
return new OpenFunctionTableAction(tool, this);
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.functioncompare;
package ghidra.app.plugin.core.functioncompare.actions;
import docking.ActionContext;
import docking.ComponentProvider;
@ -30,10 +30,11 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
/**
* Action that applies the signature of the function in the currently active side of a
* code comparison panel to the function in the other side of the panel.
* <br>Each CodeComparisonPanel can extend this class in order to provide this action
* using its context.
* Applies the signature of the function in the currently active side of a
* code comparison panel to the function in the other side of the panel
* <p>
* Each CodeComparisonPanel can extend this class in order to provide this action
* using its context
*/
public abstract class AbstractApplyFunctionSignatureAction extends DockingAction {
@ -42,9 +43,9 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
private static final String ACTION_NAME = "Apply Function Signature To Other Side";
/**
* Constructor for the action that applies a function signature from one side of a code
* comparison panel to the other.
* @param owner the owner of this action.
* Constructor
*
* @param owner the owner of this action
*/
public AbstractApplyFunctionSignatureAction(String owner) {
super(ACTION_NAME, owner);
@ -97,13 +98,25 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
}
}
/**
* Returns true if the comparison panel opposite the one with focus,
* is read-only
* <p>
* eg: if the right-side panel has focus, and the left-side panel is
* read-only, this will return true
*
* @param codeComparisonPanel the comparison panel
* @return true if the non-focused panel is read-only
*/
protected boolean hasReadOnlyNonFocusedSide(
CodeComparisonPanel<? extends FieldPanelCoordinator> codeComparisonPanel) {
Function leftFunction = codeComparisonPanel.getLeftFunction();
Function rightFunction = codeComparisonPanel.getRightFunction();
if (leftFunction == null || rightFunction == null) {
return false; // Doesn't have a function on both sides.
}
boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
Program leftProgram = leftFunction.getProgram();
Program rightProgram = rightFunction.getProgram();
@ -111,11 +124,22 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
(leftHasFocus && rightProgram.getDomainFile().isReadOnly());
}
/**
* Attempts to change the signature of a function to that of another
* function
*
* @param provider the parent component provider
* @param destinationFunction the function to change
* @param sourceFunction the function to copy
* @return true if the operation was successful
*/
protected boolean updateFunction(ComponentProvider provider, Function destinationFunction,
Function sourceFunction) {
Program program = destinationFunction.getProgram();
int txID = program.startTransaction(ACTION_NAME);
boolean commit = false;
try {
FunctionUtility.updateFunction(destinationFunction, sourceFunction);
commit = true;
@ -129,6 +153,7 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
finally {
program.endTransaction(txID, commit);
}
return commit;
}
}

View File

@ -0,0 +1,105 @@
/* ###
* 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.functioncompare.actions;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import docking.ActionContext;
import docking.action.*;
import ghidra.app.services.FunctionComparisonService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.util.HelpLocation;
import resources.MultiIcon;
import resources.ResourceManager;
import resources.icons.ScaledImageIconWrapper;
import resources.icons.TranslateIcon;
/**
* Creates a new comparison between a set of functions, launching a new
* comparison provider in the process
* <p>
* This class is abstract to force implementors to supply the source of the
* functions (may be the listing, a table, etc...)
*
* @see {@link #getSelectedFunctions(ActionContext) getSelectedFunctions}
*/
public abstract class CompareFunctionsAction extends DockingAction {
protected FunctionComparisonService comparisonService;
private static final ImageIcon COMPARISON_ICON =
ResourceManager.loadImage("images/page_white_c.png");
private static final Icon NEW_ICON = ResourceManager.loadImage("images/bullet_star.png");
private static final Icon SCALED_NEW_ICON = new ScaledImageIconWrapper(NEW_ICON, 16, 16);
private static final Icon TRANSLATED_NEW_ICON = new TranslateIcon(SCALED_NEW_ICON, 4, -4);
private static final Icon CREATE_NEW_COMPARISON_ICON =
new MultiIcon(COMPARISON_ICON, TRANSLATED_NEW_ICON);
private static final String CREATE_COMPARISON_GROUP = "A9_CreateComparison";
/**
* Constructor
*
* @param tool the plugin tool
*/
public CompareFunctionsAction(PluginTool tool) {
super("Compare Functions", tool.getName());
this.comparisonService = tool.getService(FunctionComparisonService.class);
setActionAttributes();
}
@Override
public void actionPerformed(ActionContext context) {
Set<Function> functions = getSelectedFunctions(context);
comparisonService.compareFunctions(functions);
}
@Override
public boolean isEnabledForContext(ActionContext actionContext) {
Set<Function> functions = getSelectedFunctions(actionContext);
return !functions.isEmpty();
}
/**
* Returns the icon to use for the action
*
* @return the icon
*/
protected Icon getToolBarIcon() {
return CREATE_NEW_COMPARISON_ICON;
}
/**
* Returns the set of functions that will be sent to the comparison service
*
* @param actionContext the current action context
* @return set of functions to be compared
*/
protected abstract Set<Function> getSelectedFunctions(ActionContext actionContext);
private void setActionAttributes() {
setDescription("Create Function Comparison");
setPopupMenuData(new MenuData(new String[] { "Compare Selected Functions" },
getToolBarIcon(), CREATE_COMPARISON_GROUP));
ToolBarData newToolBarData =
new ToolBarData(getToolBarIcon(), CREATE_COMPARISON_GROUP);
setToolBarData(newToolBarData);
setHelpLocation(new HelpLocation("FunctionComparison", "Function_Comparison"));
}
}

View File

@ -0,0 +1,99 @@
/* ###
* 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.functioncompare.actions;
import java.util.*;
import docking.ActionContext;
import ghidra.app.plugin.core.functionwindow.FunctionRowObject;
import ghidra.app.plugin.core.functionwindow.FunctionTableModel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.*;
import ghidra.util.table.GhidraTable;
/**
* Creates a comparison between a set of functions extracted from selections in
* a ghidra table. By default this table is assumed to be constructed using a
* {@link FunctionTableModel}. If the {@link ActionContext context} for
* this action does NOT meet those parameters this action will not even be
* enabled.
* <p>
* If this action is to be used with a different type of table, simply
* extend this class and override {@link #getSelectedFunctions(ActionContext) getSelectedFunctions}
* and {@link #isModelSupported(ActionContext) isModelSupported} as-needed.
*/
public class CompareFunctionsFromFunctionTableAction extends CompareFunctionsAction {
/**
* Constructor
*
* @param tool the plugin tool
*/
public CompareFunctionsFromFunctionTableAction(PluginTool tool) {
super(tool);
}
@Override
public boolean isAddToPopup(ActionContext context) {
return isModelSupported(context);
}
@Override
public boolean isValidContext(ActionContext context) {
return isModelSupported(context);
}
@Override
protected Set<Function> getSelectedFunctions(ActionContext actionContext) {
Set<Function> functions = new HashSet<>();
GhidraTable table = (GhidraTable) actionContext.getContextObject();
int[] selectedRows = table.getSelectedRows();
if (selectedRows.length == 0) {
return Collections.emptySet();
}
FunctionTableModel model = (FunctionTableModel) table.getModel();
Program program = model.getProgram();
FunctionManager functionManager = program.getFunctionManager();
List<FunctionRowObject> functionRowObjects = model.getRowObjects(selectedRows);
for (FunctionRowObject functionRowObject : functionRowObjects) {
long key = functionRowObject.getKey();
Function rowFunction = functionManager.getFunction(key);
functions.add(rowFunction);
}
return functions;
}
/**
* Helper method to determine if the current context is one that this
* action supports (eg: is this action being applied to a table that
* contains function information?).
* <p>
* By default this method verifies that the table in question is a
* {@link FunctionTableModel}. If another table is being used, override this
* method.
*
* @param context the action context
* @return true if the context is a function table model
*/
protected boolean isModelSupported(ActionContext context) {
if (!(context.getContextObject() instanceof GhidraTable)) {
return false;
}
GhidraTable table = (GhidraTable) context.getContextObject();
return table.getModel() instanceof FunctionTableModel;
}
}

View File

@ -0,0 +1,65 @@
/* ###
* 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.functioncompare.actions;
import java.util.HashSet;
import java.util.Set;
import docking.ActionContext;
import ghidra.app.context.ListingActionContext;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.*;
import ghidra.program.util.ProgramSelection;
/**
* Creates a comparison between a set of functions extracted from selections
* in the listing
*/
public class CompareFunctionsFromListingAction extends CompareFunctionsAction {
/**
* Constructor
*
* @param tool the plugin tool
*/
public CompareFunctionsFromListingAction(PluginTool tool) {
super(tool);
}
@Override
public boolean isAddToPopup(ActionContext actionContext) {
return actionContext instanceof ListingActionContext;
}
@Override
public boolean isValidContext(ActionContext context) {
return context instanceof ListingActionContext;
}
@Override
protected Set<Function> getSelectedFunctions(ActionContext actionContext) {
ListingActionContext listingContext = (ListingActionContext) actionContext;
ProgramSelection selection = listingContext.getSelection();
Program program = listingContext.getProgram();
FunctionManager functionManager = program.getFunctionManager();
Set<Function> functions = new HashSet<>();
FunctionIterator functionIter = functionManager.getFunctions(selection, true);
for (Function selectedFunction : functionIter) {
functions.add(selectedFunction);
}
return functions;
}
}

View File

@ -0,0 +1,98 @@
/* ###
* 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.functioncompare.actions;
import java.awt.Component;
import java.awt.event.InputEvent;
import javax.swing.Icon;
import javax.swing.JComboBox;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.*;
import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonPanel;
import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonProvider;
import ghidra.program.model.listing.Function;
import ghidra.util.HelpLocation;
import resources.MultiIcon;
import resources.ResourceManager;
import resources.icons.TranslateIcon;
/**
* Displays the next available function in the function comparison panel. If
* already at the end of the list, the action will not be enabled.
*/
public class NextFunctionAction extends DockingAction {
private static final String FUNCTION_NAVIGATE_GROUP = "A9_FunctionNavigate";
private static final Icon NEXT_ICON =
new TranslateIcon(ResourceManager.loadImage("images/arrow_down.png"), 3, 1);
private static final Icon FUNCTION_ICON =
new TranslateIcon(ResourceManager.loadImage("images/FunctionScope.gif"), -5, -2);
private static final Icon NEXT_FUNCTION_ICON = new MultiIcon(NEXT_ICON, FUNCTION_ICON);
/**
* Constructor
*
* @param provider the comparison provider for this action
*/
public NextFunctionAction(MultiFunctionComparisonProvider provider) {
super("Compare Next Function", provider.getOwner());
setKeyBindingData(
new KeyBindingData('N', InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
setDescription("Compare the next function for the side with focus.");
setPopupMenuData(
new MenuData(new String[] { "Compare The Next Function" }, NEXT_FUNCTION_ICON,
FUNCTION_NAVIGATE_GROUP));
ToolBarData newToolBarData =
new ToolBarData(NEXT_FUNCTION_ICON, FUNCTION_NAVIGATE_GROUP);
setToolBarData(newToolBarData);
HelpLocation helpLocation =
new HelpLocation(MultiFunctionComparisonPanel.HELP_TOPIC, "Navigate_Next");
setHelpLocation(helpLocation);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!(context.getComponentProvider() instanceof MultiFunctionComparisonProvider)) {
return false;
}
MultiFunctionComparisonProvider provider =
(MultiFunctionComparisonProvider) context.getComponentProvider();
Component comp = provider.getComponent();
if (!(comp instanceof MultiFunctionComparisonPanel)) {
return false;
}
MultiFunctionComparisonPanel panel = (MultiFunctionComparisonPanel) comp;
JComboBox<Function> focusedComponent = panel.getFocusedComponent();
return focusedComponent.getSelectedIndex() < (focusedComponent.getModel().getSize() - 1);
}
@Override
public void actionPerformed(ActionContext context) {
ComponentProvider provider = context.getComponentProvider();
MultiFunctionComparisonPanel panel = (MultiFunctionComparisonPanel) provider.getComponent();
JComboBox<Function> focusedComponent = panel.getFocusedComponent();
focusedComponent.setSelectedIndex(focusedComponent.getSelectedIndex() + 1);
provider.contextChanged();
}
}

View File

@ -0,0 +1,121 @@
/* ###
* 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.functioncompare.actions;
import java.util.*;
import java.util.stream.Collectors;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import docking.ActionContext;
import docking.action.*;
import docking.widgets.dialogs.TableChooserDialog;
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProvider;
import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonPanel;
import ghidra.app.plugin.core.functionwindow.FunctionRowObject;
import ghidra.app.plugin.core.functionwindow.FunctionTableModel;
import ghidra.app.services.FunctionComparisonService;
import ghidra.app.services.ProgramManager;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities;
import resources.MultiIcon;
import resources.ResourceManager;
import resources.icons.ScaledImageIconWrapper;
import resources.icons.TranslateIcon;
import util.CollectionUtils;
/**
* Opens a table chooser allowing the user to select functions from the current
* program. The table displayed uses a {@link FunctionTableModel}.
*
* @see FunctionComparisonService
*/
public class OpenFunctionTableAction extends DockingAction {
private static final Icon ADD_ICON = ResourceManager.loadImage("images/Plus.png");
private static final Icon SCALED_ADD_ICON = new ScaledImageIconWrapper(ADD_ICON, 10, 10);
private static final ImageIcon COMPARISON_ICON =
ResourceManager.loadImage("images/page_white_c.png");
private static final Icon TRANSLATED_ADD_ICON = new TranslateIcon(SCALED_ADD_ICON, 8, 1);
private static final String ADD_COMPARISON_GROUP = "A9_AddToComparison";
private static final Icon ADD_TO_COMPARISON_ICON =
new MultiIcon(COMPARISON_ICON, TRANSLATED_ADD_ICON);
protected PluginTool tool;
protected ProgramManager programManagerService;
protected FunctionComparisonService comparisonService;
/**
* Constructor
*
* @param tool the plugin tool
* @param provider the function comparison provider
*/
public OpenFunctionTableAction(PluginTool tool, FunctionComparisonProvider provider) {
super("Add Functions To Comparison", provider.getOwner());
this.tool = tool;
this.programManagerService = tool.getService(ProgramManager.class);
this.comparisonService = tool.getService(FunctionComparisonService.class);
setDescription("Add functions to comparison");
setPopupMenuData(new MenuData(new String[] { "Add functions" },
ADD_TO_COMPARISON_ICON, ADD_COMPARISON_GROUP));
ToolBarData newToolBarData =
new ToolBarData(ADD_TO_COMPARISON_ICON, ADD_COMPARISON_GROUP);
setToolBarData(newToolBarData);
HelpLocation helpLocation = new HelpLocation(MultiFunctionComparisonPanel.HELP_TOPIC,
"Add_To_Comparison");
setHelpLocation(helpLocation);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return context.getComponentProvider() instanceof FunctionComparisonProvider;
}
@Override
public void actionPerformed(ActionContext context) {
Runnable runnable = () -> {
FunctionComparisonProvider provider =
(FunctionComparisonProvider) context.getComponentProvider();
Program currentProgram = programManagerService.getCurrentProgram();
FunctionTableModel model = new FunctionTableModel(tool, currentProgram);
model.reload(programManagerService.getCurrentProgram());
TableChooserDialog<FunctionRowObject> diag =
new TableChooserDialog<>("Select Functions: " + currentProgram.getName(),
model, true);
tool.showDialog(diag);
List<FunctionRowObject> rows = diag.getSelectionItems();
if (CollectionUtils.isBlank(rows)) {
return; // the table chooser can return null if the operation was cancelled
}
Set<Function> functions =
rows.stream().map(row -> row.getFunction()).collect(Collectors.toSet());
comparisonService.compareFunctions(new HashSet<>(functions), provider);
};
SystemUtilities.runSwingLater(runnable);
}
}

View File

@ -0,0 +1,98 @@
/* ###
* 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.functioncompare.actions;
import java.awt.Component;
import java.awt.event.InputEvent;
import javax.swing.Icon;
import javax.swing.JComboBox;
import docking.ActionContext;
import docking.ComponentProvider;
import docking.action.*;
import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonPanel;
import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonProvider;
import ghidra.program.model.listing.Function;
import ghidra.util.HelpLocation;
import resources.MultiIcon;
import resources.ResourceManager;
import resources.icons.TranslateIcon;
/**
* Displays the previous function in the function comparison panel. If
* already at the beginning of the list, the action will not be enabled.
*/
public class PreviousFunctionAction extends DockingAction {
private static final String FUNCTION_NAVIGATE_GROUP = "A9_FunctionNavigate";
private static final Icon PREVIOUS_ICON =
new TranslateIcon(ResourceManager.loadImage("images/arrow_up.png"), 3, 1);
private static final Icon FUNCTION_ICON =
new TranslateIcon(ResourceManager.loadImage("images/FunctionScope.gif"), -5, -2);
private static final Icon PREVIOUS_FUNCTION_ICON = new MultiIcon(PREVIOUS_ICON, FUNCTION_ICON);
/**
* Constructor
*
* @param provider the function comparison provider
*/
public PreviousFunctionAction(MultiFunctionComparisonProvider provider) {
super("Compare Previous Function", provider.getOwner());
setKeyBindingData(
new KeyBindingData('P', InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
setDescription("Compare the previous function for the side with focus.");
setPopupMenuData(new MenuData(new String[] { "Compare The Previous Function" },
PREVIOUS_FUNCTION_ICON, FUNCTION_NAVIGATE_GROUP));
ToolBarData newToolBarData =
new ToolBarData(PREVIOUS_FUNCTION_ICON, FUNCTION_NAVIGATE_GROUP);
setToolBarData(newToolBarData);
HelpLocation helpLocation =
new HelpLocation(MultiFunctionComparisonPanel.HELP_TOPIC,
"Navigate Previous");
setHelpLocation(helpLocation);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!(context.getComponentProvider() instanceof MultiFunctionComparisonProvider)) {
return false;
}
MultiFunctionComparisonProvider provider =
(MultiFunctionComparisonProvider) context.getComponentProvider();
Component comp = provider.getComponent();
if (!(comp instanceof MultiFunctionComparisonPanel)) {
return false;
}
MultiFunctionComparisonPanel panel = (MultiFunctionComparisonPanel) comp;
JComboBox<Function> focusedComponent = panel.getFocusedComponent();
return focusedComponent.getSelectedIndex() > 0;
}
@Override
public void actionPerformed(ActionContext context) {
ComponentProvider provider = context.getComponentProvider();
MultiFunctionComparisonPanel panel = (MultiFunctionComparisonPanel) provider.getComponent();
JComboBox<Function> focusedComponent = panel.getFocusedComponent();
focusedComponent.setSelectedIndex(focusedComponent.getSelectedIndex() - 1);
provider.contextChanged();
}
}

View File

@ -0,0 +1,100 @@
/* ###
* 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.functioncompare.actions;
import java.awt.Component;
import java.awt.event.InputEvent;
import java.util.Arrays;
import java.util.HashSet;
import javax.swing.Icon;
import javax.swing.JComboBox;
import docking.ActionContext;
import docking.action.*;
import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonPanel;
import ghidra.app.plugin.core.functioncompare.MultiFunctionComparisonProvider;
import ghidra.program.model.listing.Function;
import ghidra.util.HelpLocation;
import resources.MultiIcon;
import resources.ResourceManager;
import resources.icons.TranslateIcon;
/**
* Removes the currently-selected function from the comparison panel. If no
* functions are enabled, the action will be disabled.
*/
public class RemoveFunctionsAction extends DockingAction {
private static final Icon FUNCTION_ICON =
new TranslateIcon(ResourceManager.loadImage("images/FunctionScope.gif"), -5, -2);
private static final Icon REMOVE_ICON =
new TranslateIcon(ResourceManager.loadImage("images/edit-delete.png"), 3, 3);
private static final String REMOVE_FUNCTION_GROUP = "A9_RemoveFunctions";
private static final Icon REMOVE_FUNCTION_ICON = new MultiIcon(REMOVE_ICON, FUNCTION_ICON);
/**
* Constructor
*
* @param provider the function comparison provider
*/
public RemoveFunctionsAction(MultiFunctionComparisonProvider provider) {
super("Remove Functions", provider.getOwner());
setKeyBindingData(
new KeyBindingData('R', InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
setDescription("Removes function in the focused comparison panel");
setPopupMenuData(new MenuData(new String[] { "Remove Function" },
REMOVE_FUNCTION_ICON, REMOVE_FUNCTION_GROUP));
ToolBarData newToolBarData =
new ToolBarData(REMOVE_FUNCTION_ICON, REMOVE_FUNCTION_GROUP);
setToolBarData(newToolBarData);
HelpLocation helpLocation =
new HelpLocation(MultiFunctionComparisonPanel.HELP_TOPIC, "Remove_From_Comparison");
setHelpLocation(helpLocation);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (!(context.getComponentProvider() instanceof MultiFunctionComparisonProvider)) {
return false;
}
MultiFunctionComparisonProvider provider =
(MultiFunctionComparisonProvider) context.getComponentProvider();
Component comp = provider.getComponent();
if (!(comp instanceof MultiFunctionComparisonPanel)) {
return false;
}
MultiFunctionComparisonPanel panel = (MultiFunctionComparisonPanel) comp;
JComboBox<Function> focusedComponent = panel.getFocusedComponent();
return focusedComponent.getSelectedIndex() != -1;
}
@Override
public void actionPerformed(ActionContext context) {
MultiFunctionComparisonProvider provider =
(MultiFunctionComparisonProvider) context.getComponentProvider();
JComboBox<Function> focusedComponent =
((MultiFunctionComparisonPanel) provider.getComponent()).getFocusedComponent();
Function selectedFunction = (Function) focusedComponent.getSelectedItem();
provider.removeFunctions(new HashSet<>(Arrays.asList(selectedFunction)));
provider.contextChanged();
}
}

View File

@ -17,15 +17,15 @@ package ghidra.app.plugin.core.functionwindow;
import ghidra.program.model.listing.Function;
class FunctionRowObject implements Comparable<FunctionRowObject> {
public class FunctionRowObject implements Comparable<FunctionRowObject> {
private final Function function;
FunctionRowObject(Function function) {
public FunctionRowObject(Function function) {
this.function = function;
}
Function getFunction() {
public Function getFunction() {
return function;
}
@ -54,7 +54,7 @@ class FunctionRowObject implements Comparable<FunctionRowObject> {
return true;
}
long getKey() {
public long getKey() {
return function.getID();
}

View File

@ -29,7 +29,7 @@ import ghidra.util.table.AddressBasedTableModel;
import ghidra.util.table.field.*;
import ghidra.util.task.TaskMonitor;
class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
public class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
static final int LOCATION_COL_WIDTH = 50;
@ -39,7 +39,7 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
private FunctionManager functionMgr;
FunctionTableModel(PluginTool tool, Program program) {
public FunctionTableModel(PluginTool tool, Program program) {
super("Functions", tool, program, null);
}
@ -66,7 +66,7 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
return descriptor;
}
void reload(Program newProgram) {
public void reload(Program newProgram) {
this.setProgram(newProgram);
if (newProgram != null) {
functionMgr = newProgram.getFunctionManager();

View File

@ -15,33 +15,34 @@
*/
package ghidra.app.plugin.core.functionwindow;
import java.util.List;
import javax.swing.KeyStroke;
import javax.swing.ImageIcon;
import docking.ActionContext;
import docking.action.*;
import docking.ComponentProvider;
import docking.ComponentProviderActivationListener;
import docking.action.DockingAction;
import docking.action.KeyBindingData;
import ghidra.app.CorePluginPackage;
import ghidra.app.events.ProgramClosedPluginEvent;
import ghidra.app.events.ProgramSelectionPluginEvent;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProvider;
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProviderManager;
import ghidra.app.plugin.core.functioncompare.actions.CompareFunctionsFromFunctionTableAction;
import ghidra.app.services.FunctionComparisonService;
import ghidra.framework.model.*;
import ghidra.framework.options.OptionsChangeListener;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.util.*;
import ghidra.util.Msg;
import ghidra.util.table.GhidraTable;
import ghidra.program.util.ChangeManager;
import ghidra.program.util.ProgramChangeRecord;
import ghidra.util.table.SelectionNavigationAction;
import ghidra.util.table.actions.MakeProgramSelectionAction;
import ghidra.util.task.SwingUpdateManager;
import resources.ResourceManager;
//@formatter:off
@PluginInfo(
@ -50,24 +51,28 @@ import resources.ResourceManager;
category = PluginCategoryNames.CODE_VIEWER,
shortDescription = "Function Viewer",
description = "Provides a window that displays the list of functions in the program.",
servicesRequired = { FunctionComparisonService.class },
eventsConsumed = { ProgramClosedPluginEvent.class }
)
//@formatter:on
public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectListener {
public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectListener,
OptionsChangeListener, ComponentProviderActivationListener {
private DockingAction selectAction;
private DockingAction compareAction;
private DockingAction compareFunctionsAction;
private FunctionWindowProvider provider;
private SwingUpdateManager swingMgr;
private FunctionComparisonProviderManager functionComparisonManager;
private FunctionComparisonService functionComparisonService;
public FunctionWindowPlugin(PluginTool tool) {
super(tool, true, false);
functionComparisonManager = new FunctionComparisonProviderManager(this);
swingMgr = new SwingUpdateManager(1000, () -> provider.reload());
swingMgr = new SwingUpdateManager(1000, new Runnable() {
@Override
public void run() {
provider.reload();
}
});
}
@Override
@ -75,7 +80,21 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
super.init();
provider = new FunctionWindowProvider(this);
functionComparisonService = tool.getService(FunctionComparisonService.class);
createActions();
/**
* Kicks the tool actions to set the proper enablement when selection changes
* on the function table
*/
provider.getTable().getSelectionModel().addListSelectionListener(x -> {
tool.contextChanged(provider);
});
// Listen for providers being opened/closed to we can disable the
// add-to-comparison action if there are no comparison windows
// open.
functionComparisonService.addFunctionComparisonProviderListener(this);
}
@Override
@ -84,15 +103,14 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
currentProgram.removeListener(this);
}
swingMgr.dispose();
provider.dispose();
if (provider != null) {
provider.dispose();
}
super.dispose();
}
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
functionComparisonManager.domainObjectRestored(ev);
}
if (!provider.isVisible()) {
return;
@ -181,82 +199,64 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
}
private void createActions() {
addSelectAction();
addCompareAction();
DockingAction action = new SelectionNavigationAction(this, provider.getTable());
tool.addLocalAction(provider, action);
}
private void addSelectAction() {
selectAction = new MakeProgramSelectionAction(this, provider.getTable());
tool.addLocalAction(provider, selectAction);
compareFunctionsAction = new CompareFunctionsFromFunctionTableAction(tool);
tool.addLocalAction(provider, compareFunctionsAction);
}
private void addCompareAction() {
compareAction = new DockingAction("Compare Selected Functions", getName()) {
@Override
public void actionPerformed(ActionContext context) {
compareSelectedFunctions();
}
};
// private void installDummyAction(DockingAction action) {
// DummyKeyBindingsOptionsAction dummyAction =
// new DummyKeyBindingsOptionsAction(action.getName(), null);
// tool.addAction(dummyAction);
//
// ToolOptions options = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
// options.addOptionsChangeListener(this);
//
// KeyStroke keyStroke = options.getKeyStroke(dummyAction.getFullName(), null);
// if (keyStroke != null) {
// action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
// }
// }
ImageIcon icon = ResourceManager.loadImage("images/page_white_c.png");
compareAction.setPopupMenuData(new MenuData(new String[] { "Compare Functions" }, icon));
compareAction.setDescription("Compares the currently selected function(s) in the table.");
compareAction.setToolBarData(new ToolBarData(icon));
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) {
tool.addLocalAction(provider, compareAction);
if (optionName.startsWith(selectAction.getName())) {
KeyStroke keyStroke = (KeyStroke) newValue;
selectAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
}
if (optionName.startsWith(compareFunctionsAction.getName())) {
KeyStroke keyStroke = (KeyStroke) newValue;
compareFunctionsAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
}
}
void setActionsEnabled(boolean enabled) {
selectAction.setEnabled(enabled);
compareAction.setEnabled(enabled);
compareFunctionsAction.setEnabled(enabled);
}
void showFunctions() {
provider.showFunctions();
}
private void selectFunctions(ProgramSelection selection) {
ProgramSelectionPluginEvent pspe =
new ProgramSelectionPluginEvent("Selection", selection, currentProgram);
firePluginEvent(pspe);
}
private FunctionComparisonProvider compareSelectedFunctions() {
Function[] functions = getSelectedFunctions();
if (functions.length < 2) {
Msg.showError(this, provider.getComponent(), "Compare Selected Functions",
"Select two or more rows in the table indicating functions to compare.");
return null;
@Override
public void componentProviderActivated(ComponentProvider componentProvider) {
if (componentProvider instanceof FunctionComparisonProvider) {
tool.contextChanged(provider);
}
return functionComparisonManager.showFunctionComparisonProvider(functions);
}
/**
* Gets the functions that are currently selected in the table.
* @return the selected functions
*/
private Function[] getSelectedFunctions() {
GhidraTable table = provider.getTable();
int[] selectedRows = table.getSelectedRows();
Function[] functions = new Function[selectedRows.length];
FunctionTableModel model = provider.getModel();
Program program = model.getProgram();
FunctionManager functionManager = program.getFunctionManager();
List<FunctionRowObject> functionRowObjects = model.getRowObjects(selectedRows);
int index = 0;
for (FunctionRowObject functionRowObject : functionRowObjects) {
long key = functionRowObject.getKey();
functions[index++] = functionManager.getFunction(key);
}
return functions;
}
@Override
protected void programClosed(Program program) {
functionComparisonManager.closeProviders(program);
public void componentProviderDeactivated(ComponentProvider componentProvider) {
if (componentProvider instanceof FunctionComparisonProvider) {
tool.contextChanged(provider);
}
}
}

View File

@ -33,7 +33,7 @@ import ghidra.util.table.*;
import resources.ResourceManager;
/**
* Provider for the equates table.
* Provider that displays all functions in the selected program
*/
public class FunctionWindowProvider extends ComponentProviderAdapter {
@ -45,9 +45,13 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
private JComponent mainPanel;
private GhidraTableFilterPanel<FunctionRowObject> tableFilterPanel;
private GhidraThreadedTablePanel<FunctionRowObject> threadedTablePanel;
/**
* Constructor
*
* @param plugin the function window plugin
*/
FunctionWindowProvider(FunctionWindowPlugin plugin) {
super(plugin.getTool(), "Functions Window", plugin.getName());
setTitle("Functions");
@ -124,8 +128,9 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
functionTable.setPreferredScrollableViewportSize(new Dimension(350, 150));
functionTable.setRowSelectionAllowed(true);
functionTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
functionTable.getSelectionModel().addListSelectionListener(
e -> plugin.setActionsEnabled(functionTable.getSelectedRowCount() > 0));
functionTable.getSelectionModel()
.addListSelectionListener(
e -> plugin.setActionsEnabled(functionTable.getSelectedRowCount() > 0));
functionModel.addTableModelListener(e -> {
int rowCount = functionModel.getRowCount();
@ -158,8 +163,10 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
}
private void setFunctionTableRenderer() {
functionTable.getColumnModel().getColumn(FunctionTableModel.LOCATION_COL).setPreferredWidth(
FunctionTableModel.LOCATION_COL_WIDTH);
functionTable.getColumnModel()
.getColumn(FunctionTableModel.LOCATION_COL)
.setPreferredWidth(
FunctionTableModel.LOCATION_COL_WIDTH);
}
void update(Function function) {
@ -203,5 +210,4 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
public boolean isTransient() {
return false;
}
}

View File

@ -0,0 +1,355 @@
/* ###
* 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 java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import ghidra.app.plugin.core.functioncompare.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskLauncher;
/**
* A collection of {@link FunctionComparison function comparison}
* objects that describe how functions may be compared. Each comparison object
* is a mapping of a function (source) to a list of functions (targets).
* <p>
* This model is intended to be used by the {@link FunctionComparisonProvider}
* as the basis for its display. It should never be created manually, and should
* only be accessed via the {@link FunctionComparisonService}.
* <p>
* Note: Subscribers may register to be informed of changes to this model via the
* {@link FunctionComparisonModelListener comparison model listener} interface.
*/
public class FunctionComparisonModel {
private List<FunctionComparison> comparisons = new ArrayList<>();
private List<FunctionComparisonModelListener> listeners = new ArrayList<>();
/**
* Adds the given subscriber to the list of those to be notified of model
* changes
*
* @param listener the model change subscriber
*/
public void addFunctionComparisonModelListener(FunctionComparisonModelListener listener) {
listeners.add(listener);
}
/**
* Returns a list of all comparisons in the model, in sorted order by
* source function name
*
* @return a list of all comparisons in the model
*/
public List<FunctionComparison> getComparisons() {
List<FunctionComparison> toReturn = new ArrayList<>();
toReturn.addAll(comparisons);
Collections.sort(toReturn);
return toReturn;
}
/**
* Replaces the current model with the comparisons provided
*
* @param comparisons the new comparison model
*/
public void setComparisons(List<FunctionComparison> comparisons) {
this.comparisons = comparisons;
}
/**
* Adds a single comparison to the model
*
* @param comparison the comparison to add
*/
public void addComparison(FunctionComparison comparison) {
comparisons.add(comparison);
}
/**
* Returns a list of all targets in the model (across all comparisons) for
* a given source function
*
* @param source the source function
* @return list of associated target functions
*/
public Set<Function> getTargets(Function source) {
Set<Function> targets = new HashSet<>();
for (FunctionComparison fc : comparisons) {
if (fc.getSource().equals(source)) {
targets.addAll(fc.getTargets());
}
}
return targets;
}
/**
* Updates the model with a set of functions to compare. This will add the
* functions to any existing {@link FunctionComparison comparisons} in the
* model and create new comparisons for functions not represented.
* <p>
* Note: It is assumed that when using this method, all functions can be
* compared with all other functions; meaning each function will be added as
* both a source AND a target. To specify a specific source/target
* relationship, see {@link #compareFunctions(Function, Function)}.
*
* @param functions the set of functions to compare
*/
public void compareFunctions(Set<Function> functions) {
if (CollectionUtils.isEmpty(functions)) {
return; // not an error, just return
}
addToExistingComparisons(functions);
createNewComparisons(functions);
fireModelChanged();
}
/**
* Compares two functions. If a comparison already exists in the model for
* the given source, the target will simply be added to it; otherwise a
* new comparison will be created.
*
* @param source the source function
* @param target the target function
*/
public void compareFunctions(Function source, Function target) {
FunctionComparison fc = getOrCreateComparison(source);
fc.addTarget(target);
fireModelChanged();
}
/**
* Removes the given function from all comparisons in the model, whether
* stored as a source or target
*
* @param function the function to remove
*/
public void removeFunction(Function function) {
List<FunctionComparison> comparisonsToRemove = new ArrayList<>();
Iterator<FunctionComparison> iter = comparisons.iterator();
while (iter.hasNext()) {
// First remove any comparisons that have the function as its
// source
FunctionComparison fc = iter.next();
if (fc.getSource().equals(function)) {
comparisonsToRemove.add(fc);
continue;
}
// Now remove the function from the target list (if it's there)
fc.getTargets().remove(function);
}
comparisons.removeAll(comparisonsToRemove);
fireModelChanged();
}
/**
* Removes all functions in the model that come from the given
* program
*
* @param program the program to remove functions from
*/
public void removeFunctions(Program program) {
Set<Function> sources = getSourceFunctions();
Set<Function> targets = getTargetFunctions();
Set<Function> sourcesToRemove = sources.stream()
.filter(f -> f.getProgram().equals(program))
.collect(Collectors.toSet());
Set<Function> targetsToRemove = targets.stream()
.filter(f -> f.getProgram().equals(program))
.collect(Collectors.toSet());
sourcesToRemove.stream().forEach(f -> removeFunction(f));
targetsToRemove.stream().forEach(f -> removeFunction(f));
}
/**
* Returns all source functions in the model
*
* @return a set of all source functions
*/
public Set<Function> getSourceFunctions() {
Set<Function> items = new HashSet<>();
for (FunctionComparison fc : comparisons) {
items.add(fc.getSource());
}
return items;
}
/**
* Returns all target functions in the model
*
* @return a set of all target functions
*/
public Set<Function> getTargetFunctions() {
Set<Function> items = new HashSet<>();
Iterator<FunctionComparison> iter = comparisons.iterator();
while (iter.hasNext()) {
FunctionComparison fc = iter.next();
items.addAll(fc.getTargets());
}
return items;
}
/**
* Returns a set of all target functions for a given source
*
* @param source the source function to search for
* @return the set of associated target functions
*/
public Set<Function> getTargetFunctions(Function source) {
Set<Function> items = new HashSet<>();
Iterator<FunctionComparison> iter = comparisons.iterator();
while (iter.hasNext()) {
FunctionComparison fc = iter.next();
if (!fc.getSource().equals(source)) {
continue;
}
items.addAll(fc.getTargets());
}
return items;
}
/**
* Creates a {@link FunctionComparison comparison} for each function
* given, such that each comparison will have every other function as its
* targets. For example, given three functions, f1, f2, and f3, this is what the
* model will look like after this call:
* <li>comparison 1:</li>
* <ul>
* <li> source: f1</li>
* <li> targets: f2, f3</li>
* </ul>
* <li>comparison 2:</li>
* <ul>
* <li> source: f2</li>
* <li> targets: f1, f3</li>
* </ul>
* <li>comparison 3:</li>
* <ul>
* <li> source: f3</li>
* <li> targets: f1, f2</li>
* </ul>
*
* If this model already contains a comparison for a given function
* (meaning the model contains a comparison with the function as the
* source) then that function is skipped.
* <p>
* Note that this could be a long-running process if many (thousands)
* functions are chosen to compare, hence the monitored task. In practice
* this should never be the case, as users will likely not be
* comparing more than a handful of functions at any given time.
*
* @param functions the set of functions to create comparisons for
*/
private void createNewComparisons(Set<Function> functions) {
TaskLauncher.launchModal("Creating Comparisons", (monitor) -> {
// Remove any functions that already have an comparison in the
// model; these will be ignored
functions.removeIf(f -> comparisons.stream()
.anyMatch(fc -> f.equals(fc.getSource())));
monitor.setIndeterminate(false);
monitor.setMessage("Creating new comparisons");
monitor.initialize(functions.size());
// Save off all the existing targets in the model; these have to be
// added to any new comparisons
Set<Function> existingTargets = getTargetFunctions();
// Now loop over the given functions and create new comparisons
for (Function f : functions) {
try {
monitor.checkCanceled();
}
catch (CancelledException e) {
Msg.info(this, "Function comparison operation cancelled");
return;
}
FunctionComparison fc = new FunctionComparison();
fc.setSource(f);
fc.addTargets(functions);
fc.addTargets(existingTargets);
comparisons.add(fc);
monitor.incrementProgress(1);
}
});
}
/**
* Searches the model for a comparison that has the given function as its
* source; if not found, a new comparison is created
*
* @param source the source function to search for
* @return a function comparison object for the given source
*/
private FunctionComparison getOrCreateComparison(Function source) {
for (FunctionComparison fc : comparisons) {
if (fc.getSource().equals(source)) {
return fc;
}
}
FunctionComparison fc = new FunctionComparison();
fc.setSource(source);
comparisons.add(fc);
return fc;
}
/**
* Adds a given set of functions to every target list in every
* comparison in the model
*
* @param functions the functions to add
*/
private void addToExistingComparisons(Set<Function> functions) {
for (FunctionComparison fc : comparisons) {
fc.getTargets().addAll(functions);
}
}
/**
* Sends model-change notifications to all subscribers. The updated model
* is sent in the callback.
*/
private void fireModelChanged() {
listeners.forEach(l -> l.modelChanged(comparisons));
}
}

View File

@ -0,0 +1,97 @@
/* ###
* 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 java.util.Set;
import docking.ComponentProviderActivationListener;
import ghidra.app.plugin.core.functioncompare.FunctionComparisonPlugin;
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProvider;
import ghidra.framework.plugintool.ServiceInfo;
import ghidra.program.model.listing.Function;
/**
* Allows users to create comparisons between functions which will be displayed
* side-by-side in a {@link FunctionComparisonProvider}
*/
@ServiceInfo(defaultProvider = FunctionComparisonPlugin.class)
public interface FunctionComparisonService {
/**
* Creates a comparison between a set of functions, where each function
* in the list can be compared against any other function in the list
*
* @param functions the functions to compare
* @return the new comparison provider
*/
public FunctionComparisonProvider compareFunctions(Set<Function> functions);
/**
* Creates a comparison between two functions
*
* @param source a function in the comparison
* @param target a function in the comparison
* @return the comparison provider
*/
public FunctionComparisonProvider compareFunctions(Function source,
Function target);
/**
* Creates a comparison between a set of functions, adding them to the
* given comparison provider
*
* @param functions the functions to compare
* @param provider the provider to add the comparisons to
*/
public void compareFunctions(Set<Function> functions,
FunctionComparisonProvider provider);
/**
* Creates a comparison between two functions and adds it to a given
* comparison provider
*
* @param source a function in the comparison
* @param target a function in the comparison
* @param provider the provider to add the comparison to
*/
public void compareFunctions(Function source, Function target,
FunctionComparisonProvider provider);
/**
* Removes a given function from all comparisons across all comparison
* providers
*
* @param function the function to remove
*/
public void removeFunction(Function function);
/**
* Removes a given function from all comparisons in the given comparison
* provider
*
* @param function the function to remove
* @param provider the comparison provider to remove functions from
*/
public void removeFunction(Function function, FunctionComparisonProvider provider);
/**
* Adds the given listener to the list of subscribers who wish to be
* notified of provider activation events (eg: provider open/close)
*
* @param listener the listener to be added
*/
public void addFunctionComparisonProviderListener(ComponentProviderActivationListener listener);
}

View File

@ -17,7 +17,7 @@ package ghidra.app.util.viewer.listingpanel;
import docking.ActionContext;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.plugin.core.functioncompare.AbstractApplyFunctionSignatureAction;
import ghidra.app.plugin.core.functioncompare.actions.AbstractApplyFunctionSignatureAction;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
/**

View File

@ -361,15 +361,23 @@ public class ListingCodeComparisonPanel
}
private void updateLeftListingTitle() {
titlePanels[LEFT].setTitleName(getLeftProgramName());
}
private String getLeftProgramName() {
String leftProgramName =
(programs[LEFT] != null) ? programs[LEFT].getDomainFile().toString() : "none";
titlePanels[LEFT].setTitleName(leftProgramName);
return leftProgramName;
}
private void updateRightListingTitle() {
titlePanels[RIGHT].setTitleName(getRightProgramName());
}
private String getRightProgramName() {
String rightProgramName =
(programs[RIGHT] != null) ? programs[RIGHT].getDomainFile().toString() : "none";
titlePanels[RIGHT].setTitleName(rightProgramName);
return rightProgramName;
}
private void initializeListingFieldNavigation() {
@ -466,18 +474,10 @@ public class ListingCodeComparisonPanel
public void updateActionEnablement() {
boolean isShowing = isShowing();
boolean listingDiffActionEnablement = isShowing && listingDiff.hasCorrelation();
toggleHoverAction.setEnabled(isShowing);
nextPreviousAreaMarkerAction.setEnabled(listingDiffActionEnablement);
nextDiffAction.setEnabled(listingDiffActionEnablement);
previousDiffAction.setEnabled(listingDiffActionEnablement);
optionsAction.setEnabled(listingDiffActionEnablement);
// Diff actions
tool.contextChanged(tool.getActiveComponentProvider());
diffActionManager.updateActionEnablement(listingDiffActionEnablement);
// applyFunctionSignature enablement is handled by context.
// For now don't do anything here with the header or orientation actions.
}
class ToggleHeaderAction extends ToggleDockingAction {
@ -537,6 +537,11 @@ public class ListingCodeComparisonPanel
setHover(true);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return isShowing();
}
@Override
public void actionPerformed(ActionContext context) {
setHover(isSelected());
@ -684,6 +689,11 @@ public class ListingCodeComparisonPanel
previousDiffAction.setMenuString();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return isShowing() && listingDiff.hasCorrelation();
}
@Override
public void actionStateChanged(ActionState<String> newActionState, EventTrigger trigger) {
adjustNextPreviousAreaType();
@ -718,6 +728,11 @@ public class ListingCodeComparisonPanel
return isValidPanelContext(context);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return isShowing() && listingDiff.hasCorrelation();
}
@Override
public void actionPerformed(ActionContext context) {
if (isValidContext(context)) {
@ -757,6 +772,11 @@ public class ListingCodeComparisonPanel
return isValidPanelContext(context);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return isShowing() && listingDiff.hasCorrelation();
}
@Override
public void actionPerformed(ActionContext context) {
if (isValidContext(context)) {
@ -784,6 +804,11 @@ public class ListingCodeComparisonPanel
setEnabled(true);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return isShowing() && listingDiff.hasCorrelation();
}
@Override
public boolean isValidContext(ActionContext context) {
return isValidPanelContext(context);
@ -1518,8 +1543,9 @@ public class ListingCodeComparisonPanel
comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
if (programs[LEFT] != null) {
AddressIndexMap indexMap = listingPanels[LEFT].getAddressIndexMap();
listingPanels[LEFT].getFieldPanel().setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMap));
listingPanels[LEFT].getFieldPanel()
.setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMap));
markerManagers[LEFT].setProgram(programs[LEFT]);
unmatchedCodeMarkers[LEFT] =
markerManagers[LEFT].createAreaMarker("Listing1 Unmatched Code",
@ -1532,8 +1558,10 @@ public class ListingCodeComparisonPanel
}
if (programs[RIGHT] != null) {
AddressIndexMap rightIndexMap = listingPanels[RIGHT].getAddressIndexMap();
listingPanels[RIGHT].getFieldPanel().setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], rightIndexMap));
listingPanels[RIGHT].getFieldPanel()
.setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT],
rightIndexMap));
markerManagers[RIGHT].setProgram(programs[RIGHT]);
unmatchedCodeMarkers[RIGHT] =
markerManagers[RIGHT].createAreaMarker("Listing2 Unmatched Code",
@ -1633,8 +1661,9 @@ public class ListingCodeComparisonPanel
indexMaps[LEFT] = new AddressIndexMap(addressSets[LEFT]);
markerManagers[LEFT].getOverviewProvider().setAddressIndexMap(indexMaps[LEFT]);
listingPanels[LEFT].getFieldPanel().setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMaps[LEFT]));
listingPanels[LEFT].getFieldPanel()
.setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMaps[LEFT]));
}
private void updateRightAddressSet(Function rightFunction) {
@ -1649,8 +1678,9 @@ public class ListingCodeComparisonPanel
indexMaps[RIGHT] = new AddressIndexMap(addressSets[RIGHT]);
markerManagers[RIGHT].getOverviewProvider().setAddressIndexMap(indexMaps[RIGHT]);
listingPanels[RIGHT].getFieldPanel().setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], indexMaps[RIGHT]));
listingPanels[RIGHT].getFieldPanel()
.setBackgroundColorModel(
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], indexMaps[RIGHT]));
}
@Override
@ -1992,6 +2022,11 @@ public class ListingCodeComparisonPanel
setDualPanelFocus(i);
}
}
// Kick the tool so action buttons will be updated
if (tool.getActiveComponentProvider() != null) {
tool.getActiveComponentProvider().contextChanged();
}
}
private void setDualPanelFocus(int leftOrRight) {

View File

@ -127,10 +127,12 @@ public class ListingComparisonFieldPanelCoordinator extends LayoutLockedFieldPan
ListingPanel rightListingPanel = dualListingPanel.getRightPanel();
AddressIndexMap leftAddressIndexMap = leftListingPanel.getAddressIndexMap();
AddressIndexMap rightAddressIndexMap = rightListingPanel.getAddressIndexMap();
BigInteger leftIndex =
(leftAddress != null) ? leftAddressIndexMap.getIndex(leftAddress) : null;
BigInteger rightIndex =
(rightAddress != null) ? rightAddressIndexMap.getIndex(rightAddress) : null;
BigInteger[] lineNumbers =
new BigInteger[] { (leftIndex != null) ? leftIndex : BigInteger.ZERO,
(rightIndex != null) ? rightIndex : BigInteger.ZERO };

View File

@ -31,15 +31,17 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.classfinder.ExtensionPoint;
/**
* The CodeComparisonPanel class should be extended by any class that is to be discovered by
* the {@link FunctionComparisonPanel} class and included as a form of comparing two sections
* of code within the same or different programs.
* <br><br>
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN "CodeComparisonPanel".
* If not, the ClassSearcher will not find them.
* The CodeComparisonPanel class should be extended by any class that is to be
* discovered by the {@link FunctionComparisonPanel} class and included as a
* form of comparing two sections of code within the same or different programs
* <p>
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN <
* code>CodeComparisonPanel</code> so they are discoverable by the
* {@link ClassSearcher}
*/
public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> extends JPanel
implements ExtensionPoint, FocusListener {
@ -68,15 +70,18 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
protected Function[] functions = new Function[2];
protected Data[] data = new Data[2];
/** If true, the title of each comparison panel will be shown */
private boolean showTitles = true;
private boolean syncScrolling = false;
private T fieldPanelCoordinator;
/**
* Base constructor
* @param owner the name of the owner of this component (typically the name of the plugin that
* owns this panel.)
* @param tool the tool that contains the component.
* Constructor
*
* @param owner the name of the owner of this component
* @param tool the tool that contains the component
*/
protected CodeComparisonPanel(String owner, PluginTool tool) {
this.owner = owner;
@ -84,34 +89,38 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
}
/**
* The GUI component for this CodeComparisonPanel. A CodeComparisonPanel provides a
* dual display with a left and right side for comparing some part of the code for two programs.
* @return the component.
* The GUI component for this CodeComparisonPanel
*
* @return the component
*/
public abstract JComponent getComponent();
/**
* The title for this code comparison component.
* @return the title.
* The title for this code comparison panel
*
* @return the title
*/
public abstract String getTitle();
/**
* Specifies the two programs to be compared by this panel.
* @param leftProgram the program for the left side.
* @param rightProgram the program for the right side.
* Specifies the two programs to be compared by this panel
*
* @param leftProgram the program for the left side
* @param rightProgram the program for the right side
*/
protected abstract void setPrograms(Program leftProgram, Program rightProgram);
/**
* Displays a comparison of two program's functions.
* Displays a comparison of two program's functions
*
* @param leftFunction the function to show in the left side of the code comparison view
* @param rightFunction the function to show in the right side of the code comparison view
*/
public abstract void loadFunctions(Function leftFunction, Function rightFunction);
/**
* Displays a comparison of two program's data items.
* Displays a comparison of two program's data items
*
* @param leftData the data item to show in the left side of the code comparison view
* @param rightData the data item to show in the right side of the code comparison view
*/
@ -119,7 +128,8 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
/**
* Displays program information for a particular set of addresses in the two programs
* being compared.
* being compared
*
* @param leftProgram the program in the left side of the code comparison view
* @param rightProgram the program in the right side of the code comparison view
* @param leftAddresses the addresses of the program info to show in the left side
@ -129,19 +139,21 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
AddressSetView leftAddresses, AddressSetView rightAddresses);
/**
* Cleans up resources when this panel is no longer needed.
* Cleans up resources when this panel is no longer needed
*/
public abstract void dispose();
/**
* Enable/disable navigation in this panel using the mouse.
* @param enabled false disables mouse navigation.
* Enable/disable navigation in this panel using the mouse
*
* @param enabled false disables mouse navigation
*/
public abstract void setMouseNavigationEnabled(boolean enabled);
/**
* Get the actions for this CodeComparisonPanel.
* @return an array containing the actions
* Returns the actions for this panel
*
* @return an array of docking actions
*/
public DockingAction[] getActions() {
// No actions currently that appear for each CodeComparisonPanel.
@ -151,6 +163,14 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
return actions;
}
public boolean getShowTitles() {
return showTitles;
}
public void setShowTitles(boolean showTitles) {
this.showTitles = showTitles;
}
/**
* Determines if this panel is intended to take the place of another and if so it returns
* the class of the panel to be superseded.

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

View File

@ -0,0 +1,430 @@
/* ###
* 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.functioncompare;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Date;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.app.services.FunctionComparisonModel;
import ghidra.app.services.FunctionComparisonService;
import ghidra.framework.plugintool.DummyPluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.data.ByteDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
/**
* Tests the function comparison API and data model. Each test verifies that
* the underlying data model looks correct following a particular API method
* call. There are a few tests that also exercise various features of the data
* model directly.
* <li>The API methods being tested: {@link FunctionComparisonService}</li>
* <li>The model being used for verification: {@link FunctionComparison}</li>
*/
public class CompareFunctionsTest extends AbstractGhidraHeadedIntegrationTest {
private Program program1;
private Program program2;
private Function foo;
private Function bar;
private Function junk;
private Function stuff;
private Function one;
private Function two;
private Function three;
private Function four;
private Function five;
private FunctionComparisonPlugin plugin;
private FunctionComparisonProvider provider;
private FunctionComparisonProvider provider0;
private FunctionComparisonModel model;
@Before
public void setUp() throws Exception {
DummyPluginTool tool = new DummyPluginTool();
plugin = new FunctionComparisonPlugin(tool);
assertNotNull(plugin);
buildTestProgram1();
buildTestProgram2();
model = createTestModel();
}
/**
*
* Tests for {@link FunctionComparisonService#compareFunctions(Set)}
*
*/
@Test
public void testSetNoFunctions() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet();
FunctionComparisonProvider provider = plugin.compareFunctions(functions);
assert (provider == null);
}
@Test
public void testSetOneFunction() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo);
provider = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo);
}
@Test
public void testSetDuplicateFunctionDifferentProviders() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo);
provider = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo);
provider0 = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, foo, foo);
}
@Test
public void testSetDuplicateFunctionSameProvider() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo);
provider = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo);
plugin.compareFunctions(functions, provider);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo);
}
@Test
public void testSetMultipleFunctions() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, junk, stuff);
provider = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, junk, stuff);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, junk, stuff);
CompareFunctionsTestUtility.checkTargetFunctions(provider, junk, foo, junk, stuff);
CompareFunctionsTestUtility.checkTargetFunctions(provider, stuff, foo, junk, stuff);
}
@Test
public void testSetMultipleFunctionsMultipleSets() throws Exception {
Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(one, two);
Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(three, four, five);
provider = plugin.compareFunctions(functions1);
provider0 = plugin.compareFunctions(functions2);
CompareFunctionsTestUtility.checkSourceFunctions(provider, one, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider, one, one, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider, two, one, two);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, three, four, five);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, three, three, four, five);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, four, three, four, five);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, five, three, four, five);
}
@Test
public void testSetCombineTwoSets() throws Exception {
Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(foo, two);
Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(bar, three, four);
provider = plugin.compareFunctions(functions1);
plugin.compareFunctions(functions2, provider);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, two, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, two, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider, two, foo, two, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, two, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider, three, foo, two, bar, three,
four);
CompareFunctionsTestUtility.checkTargetFunctions(provider, four, foo, two, bar, three,
four);
}
/**
*
* Tests for {@link FunctionComparisonService#compareFunctions(Set, FunctionComparisonProvider)}
*
*/
@Test
public void testSetAddToSpecificProvider() throws Exception {
Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(foo, two);
Set<Function> functions2 = CompareFunctionsTestUtility.getFunctionsAsSet(bar, three);
Set<Function> functions3 = CompareFunctionsTestUtility.getFunctionsAsSet(four);
provider = plugin.compareFunctions(functions1);
provider0 = plugin.compareFunctions(functions2);
plugin.compareFunctions(functions3, provider0);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, two);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider, two, foo, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, bar, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, three, bar, three, four);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, four, bar, three, four);
}
/**
*
* Tests for {@link FunctionComparisonService#removeFunction(Function)}
*
*/
@Test
public void testRemoveFunction() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
plugin.removeFunction(foo);
CompareFunctionsTestUtility.checkSourceFunctions(provider, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, bar);
}
@Test
public void testRemoveFunctionTargetOnly() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions);
plugin.compareFunctions(foo, two, provider); // add a target to foo, which is not also a source
// Verify the structure with the new target
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar, two);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
plugin.removeFunction(two);
// Verify the new target is gone
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
}
@Test
public void testRemoveFunctionMultipleProviders() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions);
provider0 = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, foo, bar);
plugin.removeFunction(foo);
CompareFunctionsTestUtility.checkSourceFunctions(provider, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, bar);
}
@Test
public void testRemoveNonexistentFunction() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
plugin.removeFunction(two); // nothing should happen
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
}
/**
*
* Tests for {@link FunctionComparisonService#removeFunction(Function, FunctionComparisonProvider)}
*
*/
@Test
public void testRemoveFunctionFromSpecificProvider() throws Exception {
Set<Function> functions1 = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions1);
provider0 = plugin.compareFunctions(functions1);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, bar, foo, bar);
plugin.removeFunction(foo, provider);
CompareFunctionsTestUtility.checkSourceFunctions(provider, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider0, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider0, bar, foo, bar);
}
/**
*
* Tests for {@link FunctionComparisonService#compareFunctions(Function, Function)}
*
*/
@Test
public void testDualCompare() {
provider = plugin.compareFunctions(foo, bar);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, bar);
}
/**
*
* Tests for {@link FunctionComparisonService#compareFunctions(Function, Function, FunctionComparisonProvider)}
*
*/
@Test
public void testDualCompareAddToExisting() {
provider = plugin.compareFunctions(foo, bar);
plugin.compareFunctions(foo, two, provider);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, bar, two);
}
/**
*
* Data Model tests
*
*/
@Test
public void testGetTargets() {
Set<Function> targets = model.getTargetFunctions();
assertTrue(targets.size() == 6);
assertTrue(targets.contains(bar));
assertTrue(targets.contains(two));
assertTrue(targets.contains(three));
assertTrue(targets.contains(four));
assertTrue(targets.contains(five));
assertTrue(targets.contains(stuff));
}
@Test
public void testGetTargetsForSource() {
Set<Function> targets = model.getTargetFunctions(bar);
assertTrue(targets.size() == 3);
assertTrue(targets.contains(three));
assertTrue(targets.contains(four));
assertTrue(targets.contains(five));
}
@Test
public void getSources() {
Set<Function> sources = model.getSourceFunctions();
assertTrue(sources.size() == 3);
assertTrue(sources.contains(foo));
assertTrue(sources.contains(bar));
assertTrue(sources.contains(junk));
}
@Test
public void testRemoveFunctionFromModel() {
model.removeFunction(bar);
Set<Function> sources = model.getSourceFunctions();
assertTrue(sources.size() == 2);
assertTrue(sources.contains(foo));
assertTrue(sources.contains(junk));
Set<Function> targets = model.getTargetFunctions(foo);
assertTrue(targets.size() == 1);
assertTrue(targets.contains(two));
targets = model.getTargetFunctions(junk);
assertTrue(targets.size() == 1);
assertTrue(targets.contains(stuff));
}
private ProgramBuilder buildTestProgram1() throws Exception {
ProgramBuilder builder = new ProgramBuilder("TestPgm1", ProgramBuilder._TOY_BE);
builder.createMemory(".text", "0x1001000", 0x6600);
builder.setProperty(Program.DATE_CREATED, new Date(100000000)); // arbitrary, but consistent
// functions
DataType dt = new ByteDataType();
Parameter p = new ParameterImpl(null, dt, builder.getProgram());
foo = builder.createEmptyFunction("Foo", "10018cf", 10, null, p);
bar = builder.createEmptyFunction("Bar", "100299e", 130, null, p, p, p);
junk = builder.createEmptyFunction("Junk", "1002cf5", 15, null, p, p, p, p, p);
stuff = builder.createEmptyFunction("Stuff", "1003100", 20, null, p, p);
program1 = builder.getProgram();
AbstractGenericTest.setInstanceField("recordChanges", program1, Boolean.TRUE);
return builder;
}
private ProgramBuilder buildTestProgram2() throws Exception {
ProgramBuilder builder = new ProgramBuilder("TestPgm2", ProgramBuilder._TOY64_BE);
builder.createMemory(".text", "0x1001000", 0x6600);
builder.setProperty(Program.DATE_CREATED, new Date(100000000)); // arbitrary, but consistent
// functions
DataType dt = new ByteDataType();
Parameter p = new ParameterImpl(null, dt, builder.getProgram());
one = builder.createEmptyFunction("One", "10017c5", 10, null, p);
two = builder.createEmptyFunction("Two", "1001822", 130, null, p, p, p);
three = builder.createEmptyFunction("Three", "1001944", 15, null, p, p, p, p, p);
four = builder.createEmptyFunction("Four", "1002100", 20, null, p, p);
five = builder.createEmptyFunction("Five", "1002200", 20, null, p, p);
program2 = builder.getProgram();
AbstractGenericTest.setInstanceField("recordChanges", program2, Boolean.TRUE);
return builder;
}
private FunctionComparisonModel createTestModel() {
FunctionComparisonModel model = new FunctionComparisonModel();
FunctionComparison c1 = new FunctionComparison();
c1.setSource(foo);
c1.addTarget(bar);
c1.addTarget(two);
model.addComparison(c1);
FunctionComparison c2 = new FunctionComparison();
c2.setSource(bar);
c2.addTarget(three);
c2.addTarget(four);
c2.addTarget(five);
model.addComparison(c2);
FunctionComparison c3 = new FunctionComparison();
c3.setSource(junk);
c3.addTarget(stuff);
model.addComparison(c3);
return model;
}
}

View File

@ -0,0 +1,234 @@
/* ###
* 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.functioncompare;
import static org.junit.Assert.assertNotNull;
import java.awt.Window;
import java.util.Date;
import java.util.Set;
import javax.swing.JPanel;
import org.junit.*;
import docking.action.DockingActionIf;
import docking.widgets.dialogs.TableChooserDialog;
import docking.widgets.table.GFilterTable;
import generic.test.AbstractGenericTest;
import ghidra.app.plugin.core.functionwindow.FunctionRowObject;
import ghidra.app.plugin.core.functionwindow.FunctionTableModel;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.data.ByteDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
/**
* Tests for the {@link FunctionComparisonPlugin function comparison plugin}
* that involve the GUI
*/
public class CompareFunctionsTestSlow extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env;
private Program program1;
private Program program2;
private Function foo;
private Function bar;
private Function bat;
private FunctionComparisonPlugin plugin;
private FunctionComparisonProvider provider;
@Before
public void setUp() throws Exception {
env = new TestEnv();
plugin = env.addPlugin(FunctionComparisonPlugin.class);
assertNotNull(plugin);
buildTestProgram1();
buildTestProgram2();
showTool(plugin.getTool());
env.open(program1);
}
@After
public void tearDown() throws Exception {
env.dispose();
}
@Test
public void testRemoveLastItem() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo);
provider = plugin.compareFunctions(functions);
provider = waitForComponentProvider(FunctionComparisonProvider.class);
plugin.removeFunction(foo, provider);
assert (!provider.isVisible());
}
@Test
public void testCloseProgram() throws Exception {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions);
CompareFunctionsTestUtility.checkSourceFunctions(provider, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, foo, foo, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, foo, bar);
plugin.programClosed(program1);
CompareFunctionsTestUtility.checkSourceFunctions(provider, bar);
CompareFunctionsTestUtility.checkTargetFunctions(provider, bar, bar);
plugin.programClosed(program2);
CompareFunctionsTestUtility.checkSourceFunctions(provider);
}
@Test
public void testNextPreviousAction() {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions);
provider.setVisible(true);
waitForSwing();
// Must do this or there will be no "active" provider in the actions
// initiated below
clickComponentProvider(provider);
DockingActionIf nextAction = getAction(plugin, "Compare Next Function");
DockingActionIf prevAction = getAction(plugin, "Compare Previous Function");
assert (nextAction.isEnabled());
assert (!prevAction.isEnabled());
performAction(nextAction);
assert (!nextAction.isEnabled());
assert (prevAction.isEnabled());
}
@Test
public void testNextPreviousActionSwitchPanelFocus() {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions);
provider.setVisible(true);
waitForSwing();
// Must do this or there will be no "active" provider in the actions
// initiated below
clickComponentProvider(provider);
DockingActionIf nextAction = getAction(plugin, "Compare Next Function");
DockingActionIf prevAction = getAction(plugin, "Compare Previous Function");
assert (nextAction.isEnabled());
assert (!prevAction.isEnabled());
performAction(nextAction);
assert (!nextAction.isEnabled());
assert (prevAction.isEnabled());
JPanel rightPanel =
provider.getComponent().getDualListingPanel().getRightPanel().getFieldPanel();
clickMouse(rightPanel, 1, 30, 30, 1, 0);
waitForSwing();
provider.getComponent().updateActionEnablement();
assert (nextAction.isEnabled());
assert (!prevAction.isEnabled());
}
@Test
public void testOpenFunctionTableActionForAdd() {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo, bar);
provider = plugin.compareFunctions(functions);
provider.setVisible(true);
DockingActionIf openTableAction = getAction(plugin, "Add Functions To Comparison");
performAction(openTableAction);
Window selectWindow = waitForWindowByTitleContaining("Select Functions");
assert (selectWindow != null);
}
@SuppressWarnings("unchecked")
@Test
public void testAddFunctionToExistingCompare() {
Set<Function> functions = CompareFunctionsTestUtility.getFunctionsAsSet(foo);
provider = plugin.compareFunctions(functions);
provider.setVisible(true);
waitForSwing();
// Must do this or there will be no "active" provider in the actions
// initiated below
clickComponentProvider(provider);
assert (provider.getModel().getSourceFunctions().size() == 1);
DockingActionIf openTableAction = getAction(plugin, "Add Functions To Comparison");
performAction(openTableAction);
TableChooserDialog<FunctionTableModel> chooser =
waitForDialogComponent(TableChooserDialog.class);
assert (chooser != null);
GFilterTable<FunctionRowObject> table =
(GFilterTable<FunctionRowObject>) getInstanceField("gFilterTable", chooser);
assert (table.getModel().getRowCount() == 2);
clickTableCell(table.getTable(), 1, 0, 1);
pressButtonByText(chooser, "OK");
waitForSwing();
assert (provider.getModel().getSourceFunctions().size() == 2);
}
/**
* Builds a program with 2 functions
*/
private ProgramBuilder buildTestProgram1() throws Exception {
ProgramBuilder builder = new ProgramBuilder("TestPgm1", ProgramBuilder._TOY_BE);
builder.createMemory(".text", "0x1001000", 0x6600);
builder.setProperty(Program.DATE_CREATED, new Date(100000000)); // arbitrary, but consistent
// functions
DataType dt = new ByteDataType();
Parameter p = new ParameterImpl(null, dt, builder.getProgram());
foo = builder.createEmptyFunction("Foo", "10018cf", 10, null, p);
bat = builder.createEmptyFunction("Bar", "100299e", 130, null, p, p, p);
program1 = builder.getProgram();
AbstractGenericTest.setInstanceField("recordChanges", program1, Boolean.TRUE);
return builder;
}
/**
* Builds a program with 1 function
*/
private ProgramBuilder buildTestProgram2() throws Exception {
ProgramBuilder builder = new ProgramBuilder("TestPgm1", ProgramBuilder._TOY_BE);
builder.createMemory(".text", "0x1001000", 0x6600);
builder.setProperty(Program.DATE_CREATED, new Date(100000000)); // arbitrary, but consistent
// functions
DataType dt = new ByteDataType();
Parameter p = new ParameterImpl(null, dt, builder.getProgram());
bar = builder.createEmptyFunction("Bar", "10018cf", 10, null, p);
program2 = builder.getProgram();
AbstractGenericTest.setInstanceField("recordChanges", program2, Boolean.TRUE);
return builder;
}
}

View File

@ -0,0 +1,88 @@
/* ###
* 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.functioncompare;
import java.util.*;
import ghidra.program.model.listing.Function;
/**
* Helper methods for use with function comparison tests
*
* @see {@link CompareFunctionsTest}
* @see {@link CompareFunctionsTestSlow}
*/
public class CompareFunctionsTestUtility {
/**
* Asserts that a given list of functions represents all of the source
* functions in a comparison model
*
* @param provider the function comparison provider
* @param functions the source functions
*/
public static void checkSourceFunctions(FunctionComparisonProvider provider,
Function... functions) {
Set<Function> funcs = new HashSet<>(Arrays.asList(functions));
Set<Function> fcs = provider.getModel().getSourceFunctions();
assert (fcs.size() == funcs.size());
assert (fcs.containsAll(funcs));
}
/**
* Asserts that a given function (source) is mapped to a collection of
* functions (targets) in a comparison model
*
* @param provider the function comparison provider
* @param source the source function
* @param targets the target functions
*/
public static void checkTargetFunctions(FunctionComparisonProvider provider,
Function source, Function... targets) {
Set<Function> targetsAsList = new HashSet<>(Arrays.asList(targets));
Set<Function> tgts = provider.getModel().getTargetFunctions(source);
assert (tgts.size() == targetsAsList.size());
assert (tgts.containsAll(targetsAsList));
}
/**
* Returns the given functions as a {@link Set}
*
* @param functions the functions to return as a set
* @return a set of functions
*/
public static Set<Function> getFunctionsAsSet(Function... functions) {
Set<Function> set = new HashSet<>();
set.addAll(Arrays.asList(functions));
return set;
}
/**
* Returns the given functions as a {@link Map} of a function (source) to
* a set of functions (targets)
*
* @param source the key of the map
* @param targets the value of the map
* @return a map of a function to a set of functions
*/
public static Map<Function, Set<Function>> getFunctionsAsMap(Function source,
Function... targets) {
Set<Function> targetSet = getFunctionsAsSet(targets);
Map<Function, Set<Function>> map = new HashMap<>();
map.put(source, targetSet);
return map;
}
}

View File

@ -17,7 +17,7 @@ package ghidra.app.decompiler.component;
import docking.ActionContext;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import ghidra.app.plugin.core.functioncompare.AbstractApplyFunctionSignatureAction;
import ghidra.app.plugin.core.functioncompare.actions.AbstractApplyFunctionSignatureAction;
import ghidra.app.util.viewer.util.CodeComparisonPanel;
/**

View File

@ -41,7 +41,7 @@ import ghidra.program.util.ProgramLocation;
import ghidra.util.HTMLUtilities;
/**
* Panel that displays two decompilers for comparison.
* Panel that displays two decompilers for comparison
*/
public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFieldPanelCoordinator>
extends CodeComparisonPanel<DualDecompilerFieldPanelCoordinator> {
@ -68,7 +68,8 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
private ProgramLocationListener rightDecompilerLocationListener;
/**
* Creates a comparison panel with two decompilers.
* Creates a comparison panel with two decompilers
*
* @param owner the owner of this panel
* @param tool the tool displaying this panel
*/
@ -189,7 +190,13 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
loadLeftFunction(leftFunction);
loadRightFunction(rightFunction);
setTitles(leftFunction, rightFunction);
if (getShowTitles()) {
setTitles(leftFunction, rightFunction);
}
else {
setTitles("", "");
}
if (dualDecompilerCoordinator != null) {
dualDecompilerCoordinator.leftLocationChanged((ProgramLocation) null);
}
@ -429,6 +436,9 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
setDualPanelFocus(i);
}
}
// Kick the tool so action buttons will be updated
tool.getActiveComponentProvider().contextChanged();
}
private void setDualPanelFocus(int leftOrRight) {

View File

@ -18,6 +18,7 @@ package docking.widgets;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
@ -30,6 +31,7 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
private GTable gTable;
private T selectedValue;
private List<T> selectedValues = new ArrayList<>();
private GTableFilterPanel<T> filterPanel;
private RowObjectTableModel<T> model;
@ -55,10 +57,15 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
@Override
protected void okCallback() {
int selectedRow = gTable.getSelectedRow();
if (selectedRow >= 0) {
int modelRow = filterPanel.getModelRow(selectedRow);
selectedValue = model.getRowObject(modelRow);
int[] selectedRows = gTable.getSelectedRows();
if (selectedRows.length > 0) {
selectedValues.clear();
for (int selectedRow : selectedRows) {
int modelRow = filterPanel.getModelRow(selectedRow);
T rowObject = model.getRowObject(modelRow);
selectedValues.add(rowObject);
}
selectedValue = selectedValues.isEmpty() ? null : selectedValues.get(0);
close();
}
}
@ -101,15 +108,26 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
return selectedValue;
}
public List<T> getSelectedItems() {
return selectedValues;
}
public T show(Component parent) {
setMultiSelectionMode(false);
DockingWindowManager.showDialog(parent, this);
return getSelectedItem();
}
public List<T> showSelectMultiple(Component parent) {
setMultiSelectionMode(true);
DockingWindowManager.showDialog(parent, this);
return getSelectedItems();
}
public void setMultiSelectionMode(boolean enable) {
if (enable) {
gTable.getSelectionModel().setSelectionMode(
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
gTable.getSelectionModel()
.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
}
else {
gTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

View File

@ -0,0 +1,128 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets.dialogs;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import docking.DialogComponentProvider;
import docking.widgets.table.*;
/**
* Dialog for displaying table data in a dialog for the purpose of the user selecting one or
* more items from the table.
*
* @param <T> The type of row object in the table.
*/
public class TableChooserDialog<T> extends DialogComponentProvider {
private RowObjectTableModel<T> model;
private GFilterTable<T> gFilterTable;
private List<T> selectedItems;
/**
* Create a new Dialog for displaying and choosing table row items
*
* @param title The title for the dialog
* @param model a {@link RowObjectTableModel} that has the tRable data
* @param allowMultipleSelection if true, the dialog allows the user to select more
* than one row; otherwise, only single selection is allowed
*/
public TableChooserDialog(String title, RowObjectTableModel<T> model,
boolean allowMultipleSelection) {
super(title);
this.model = model;
addWorkPanel(buildTable(allowMultipleSelection));
addOKButton();
addCancelButton();
}
/**
* Returns the list of selected items or null if the dialog was cancelled.
* @return the list of selected items or null if the dialog was cancelled.
*/
public List<T> getSelectionItems() {
return selectedItems;
}
private void initializeTable(boolean allowMultipleSelection) {
GTable table = gFilterTable.getTable();
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
int selectionMode = allowMultipleSelection ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
: ListSelectionModel.SINGLE_SELECTION;
table.getSelectionModel().setSelectionMode(selectionMode);
}
protected void processMouseClicked(MouseEvent e) {
if (e.getClickCount() != 2) {
return;
}
int rowAtPoint = gFilterTable.getTable().rowAtPoint(e.getPoint());
if (rowAtPoint < 0) {
return;
}
T selectedRowObject = gFilterTable.getSelectedRowObject();
selectedItems = Arrays.asList(selectedRowObject);
close();
}
@Override
protected void okCallback() {
selectedItems = gFilterTable.getSelectedRowObjects();
close();
}
@Override
protected void cancelCallback() {
selectedItems = null;
close();
}
@Override
protected void dialogShown() {
gFilterTable.focusFilter();
}
private JComponent buildTable(boolean allowMultipleSelection) {
gFilterTable = new GFilterTable<>(model);
initializeTable(allowMultipleSelection);
gFilterTable.getTable().addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (!e.isShiftDown()) {
processMouseClicked(e);
}
updateOkEnabled();
}
});
setOkEnabled(false);
return gFilterTable;
}
protected void updateOkEnabled() {
setOkEnabled(gFilterTable.getSelectedRowObject() != null);
}
}

View File

@ -15,13 +15,12 @@
*/
package docking.widgets.fieldpanel.internal;
import ghidra.util.exception.AssertException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import docking.widgets.fieldpanel.FieldPanel;
import ghidra.util.exception.AssertException;
/**
* A LineLockedFieldPanelCoordinator coordinates the scrolling of a set of field panels by sharing
@ -63,12 +62,12 @@ public class LineLockedFieldPanelCoordinator extends FieldPanelCoordinator {
public void setLockedLines(BigInteger[] lockedLineNumbers) {
if (lockedLineNumbers.length != this.lockedLineNumbers.length) {
throw new AssertException("The number of lines(" + lockedLineNumbers.length +
") must exactly match the number of panels(" + this.lockedLineNumbers.length + ").");
") must exactly match the number of panels(" + this.lockedLineNumbers.length +
").");
}
for (int i = 0; i < lockedLineNumbers.length; i++) {
if (lockedLineNumbers[i] == null) {
throw new AssertException("lockedLineNumber for field panel [" + i +
"] was unexpectedly null.");
lockedLineNumbers[i] = BigInteger.ZERO;
}
}
for (int i = 0; i < lockedLineNumbers.length; i++) {
@ -99,7 +98,7 @@ public class LineLockedFieldPanelCoordinator extends FieldPanelCoordinator {
*/
@Override
public void remove(FieldPanel fp) {
List<BigInteger> lineNumberList = new ArrayList<BigInteger>(panels.length);
List<BigInteger> lineNumberList = new ArrayList<>(panels.length);
// Adjust our locked line number array.
int length = panels.length;
for (int i = 0; i < length; i++) {

View File

@ -46,7 +46,8 @@ public abstract class ComponentProviderAdapter extends ComponentProvider {
* @param tool the plugin tool.
* @param name The providers name. This is used to group similar providers into a tab within
* the same window.
* @param owner The owner of this provider, usually a plugin name.
* @param owner The owner of this provider, usually a plugin name
* @param contextType the type of context supported by this provider; may be null
*/
public ComponentProviderAdapter(PluginTool tool, String name, String owner,
Class<?> contextType) {

View File

@ -15,12 +15,15 @@
*/
package help.screenshot;
import java.util.HashMap;
import java.awt.Rectangle;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import docking.ComponentProvider;
import docking.DialogComponentProvider;
import docking.action.DockingActionIf;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.plugin.core.functioncompare.*;
import ghidra.app.util.viewer.listingpanel.ListingCodeComparisonPanel;
@ -30,8 +33,8 @@ import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
@ -52,19 +55,27 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
public void setUp() throws Exception {
super.setUp();
plugin = getPlugin(tool, FunctionComparisonPlugin.class);
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
}
public void tearDown() throws Exception {
super.tearDown();
}
@Test
public void testFunctionComparisonWindow() {
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
positionListingTop(0x004118f0);
int txId1 = sourceProgram.startTransaction("Modify Program1");
int txId2 = destinationProgram.startTransaction("Modify Program2");
try {
sourceProgram.setName("TestProgram");
destinationProgram.setName("OtherProgram");
sourceProgram.getDomainFile().setName("FirstProgram");
destinationProgram.getDomainFile().setName("SecondProgram");
sourceProgram.setName("FirstProgram");
destinationProgram.setName("SecondProgram");
Listing sourceListing = sourceProgram.getListing();
Listing destListing = destinationProgram.getListing();
Memory sourceMemory = sourceProgram.getMemory();
@ -81,100 +92,10 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
f2.setName("FunctionB", SourceType.USER_DEFINED);
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
Function[] functions = new Function[] { f1, f2 };
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
FunctionComparisonProvider functionComparisonProvider =
providerMgr.showFunctionComparisonProvider(functions);
FunctionComparisonPanel functionComparisonPanel =
functionComparisonProvider.getComponent();
runSwing(() -> {
functionComparisonPanel.setCurrentTabbedComponent("Listing View");
ListingCodeComparisonPanel dualListing =
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
ListingPanel leftPanel = dualListing.getLeftPanel();
dualListing.setLeftTitle("FunctionA() in /TestProgram");
dualListing.setRightTitle("FunctionB() in /OtherProgram");
leftPanel.goTo(addr(0x004119aa));
});
waitForSwing();
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 550);
}
catch (DuplicateNameException e) {
e.printStackTrace();
}
catch (InvalidInputException e) {
e.printStackTrace();
}
catch (MemoryAccessException e) {
e.printStackTrace();
}
finally {
destinationProgram.endTransaction(txId2, false);
sourceProgram.endTransaction(txId1, false);
}
}
@Test
public void testFunctionComparisonWindowFromMap() throws CircularDependencyException {
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
positionListingTop(0x004118f0);
int txId1 = sourceProgram.startTransaction("Modify Program1");
int txId2 = destinationProgram.startTransaction("Modify Program2");
try {
sourceProgram.setName("TestProgram");
destinationProgram.setName("OtherProgram");
Listing sourceListing = sourceProgram.getListing();
Listing destListing = destinationProgram.getListing();
Memory sourceMemory = sourceProgram.getMemory();
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
f1.setName("Function1", SourceType.USER_DEFINED);
Namespace parentNamespace = sourceProgram.getSymbolTable().createNameSpace(
program.getGlobalNamespace(), "Namespace1", SourceType.USER_DEFINED);
f1.setParentNamespace(parentNamespace);
sourceListing.setComment(addr(0x004118f0), CodeUnit.PLATE_COMMENT, null);
sourceListing.clearCodeUnits(addr(0x004119b1), addr(0x004119b4), false);
sourceMemory.setByte(addr(0x004119b2), (byte) 0x55);
sourceMemory.setByte(addr(0x004119b4), (byte) 0x52);
disassemble(sourceProgram, 0x004119b1, 4, false);
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
f2.setName("Function2", SourceType.USER_DEFINED);
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
Function fA = getFunction(sourceProgram, addr(0x00411a30));
fA.setName("FunctionA", SourceType.USER_DEFINED);
sourceListing.setComment(addr(0x00411a30), CodeUnit.PLATE_COMMENT, null);
Function fB = getFunction(destinationProgram, addr(0x00411a10));
fB.setName("FunctionB", SourceType.USER_DEFINED);
destListing.setComment(addr(0x00411a10), CodeUnit.PLATE_COMMENT, null);
Function fC = getFunction(sourceProgram, addr(0x00411ab0));
fC.setName("FunctionC", SourceType.USER_DEFINED);
sourceListing.setComment(addr(0x00411ab0), CodeUnit.PLATE_COMMENT, null);
Function fD = getFunction(destinationProgram, addr(0x00411a90));
fD.setName("FunctionD", SourceType.USER_DEFINED);
destListing.setComment(addr(0x00411a90), CodeUnit.PLATE_COMMENT, null);
HashMap<Function, HashSet<Function>> functionMap = new HashMap<>();
HashSet<Function> functionSet = new HashSet<>();
functionSet.add(fA);
functionSet.add(fB);
functionMap.put(f1, functionSet);
functionSet = new HashSet<>();
functionSet.add(fC);
functionSet.add(fD);
functionMap.put(f2, functionSet);
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
FunctionComparisonProvider functionComparisonProvider =
providerMgr.showFunctionComparisonProvider(functionMap);
providerMgr.compareFunctions(f1, f2);
FunctionComparisonPanel functionComparisonPanel =
functionComparisonProvider.getComponent();
runSwing(() -> {
@ -183,12 +104,12 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
ListingPanel leftPanel = dualListing.getLeftPanel();
leftPanel.goTo(addr(0x004119aa));
});
waitForSwing();
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 550);
}
catch (DuplicateNameException | InvalidInputException | MemoryAccessException e) {
catch (DuplicateNameException | InvalidInputException | MemoryAccessException
| InvalidNameException | IOException e) {
e.printStackTrace();
}
finally {
@ -198,136 +119,102 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
}
@Test
public void testListingCodeComparisonOptions() {
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
public void testAddToComparisonIcon() {
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
positionListingTop(0x004118f0);
int txId1 = sourceProgram.startTransaction("Modify Program1");
int txId2 = destinationProgram.startTransaction("Modify Program2");
try {
sourceProgram.setName("TestProgram");
destinationProgram.setName("OtherProgram");
Listing sourceListing = sourceProgram.getListing();
Listing destListing = destinationProgram.getListing();
Memory sourceMemory = sourceProgram.getMemory();
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
FunctionComparisonProvider functionComparisonProvider =
providerMgr.compareFunctions(f1, f2);
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
f1.setName("FunctionA", SourceType.USER_DEFINED);
sourceListing.setComment(addr(0x004118f0), CodeUnit.PLATE_COMMENT, null);
sourceListing.clearCodeUnits(addr(0x004119b1), addr(0x004119b4), false);
sourceMemory.setByte(addr(0x004119b2), (byte) 0x55);
sourceMemory.setByte(addr(0x004119b4), (byte) 0x52);
disassemble(sourceProgram, 0x004119b1, 4, false);
waitForSwing();
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
f2.setName("FunctionB", SourceType.USER_DEFINED);
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
Function[] functions = new Function[] { f1, f2 };
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
FunctionComparisonProvider functionComparisonProvider =
providerMgr.showFunctionComparisonProvider(functions);
FunctionComparisonPanel functionComparisonPanel =
functionComparisonProvider.getComponent();
runSwing(() -> {
functionComparisonPanel.setCurrentTabbedComponent("Listing View");
});
waitForSwing();
ComponentProvider provider = getProvider("Function Comparison");
performAction("Listing Code Comparison Options", "Function Comparison", provider,
false);
captureDialog(600, 300);
pressButtonByText(getDialog(), "Cancel");
waitForSwing();
}
catch (DuplicateNameException e) {
e.printStackTrace();
}
catch (InvalidInputException e) {
e.printStackTrace();
}
catch (MemoryAccessException e) {
e.printStackTrace();
}
finally {
destinationProgram.endTransaction(txId2, false);
sourceProgram.endTransaction(txId1, false);
}
captureProvider(functionComparisonProvider);
crop(new Rectangle(261, 2, 24, 24));
}
@Test
public void testMultiFunctionComparisonWindow() {
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
public void testRemoveFromComparisonIcon() {
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
positionListingTop(0x004118f0);
int txId1 = sourceProgram.startTransaction("Modify Program1");
int txId2 = destinationProgram.startTransaction("Modify Program2");
try {
sourceProgram.setName("TestProgram");
destinationProgram.setName("OtherProgram");
Listing sourceListing = sourceProgram.getListing();
Listing destListing = destinationProgram.getListing();
Memory sourceMemory = sourceProgram.getMemory();
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
FunctionComparisonProvider functionComparisonProvider =
providerMgr.compareFunctions(f1, f2);
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
f1.setName("FunctionA", SourceType.USER_DEFINED);
sourceListing.setComment(addr(0x004118f0), CodeUnit.PLATE_COMMENT, null);
sourceListing.clearCodeUnits(addr(0x004119b1), addr(0x004119b4), false);
sourceMemory.setByte(addr(0x004119b2), (byte) 0x55);
sourceMemory.setByte(addr(0x004119b4), (byte) 0x52);
disassemble(sourceProgram, 0x004119b1, 4, false);
waitForSwing();
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
f2.setName("FunctionB", SourceType.USER_DEFINED);
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
Function f3 = getFunction(sourceProgram, addr(0x004117c0));
f3.setName("FunctionC", SourceType.USER_DEFINED);
sourceListing.setComment(addr(0x004117c0), CodeUnit.PLATE_COMMENT, null);
Function f4 = getFunction(destinationProgram, addr(0x004117b0));
f4.setName("FunctionD", SourceType.USER_DEFINED);
destListing.setComment(addr(0x004117b0), CodeUnit.PLATE_COMMENT, null);
Function[] functions = new Function[] { f1, f2, f3, f4 };
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
FunctionComparisonProvider functionComparisonProvider =
providerMgr.showFunctionComparisonProvider(functions);
FunctionComparisonPanel functionComparisonPanel =
functionComparisonProvider.getComponent();
runSwing(() -> {
functionComparisonPanel.setCurrentTabbedComponent("Listing View");
ListingCodeComparisonPanel dualListing =
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
ListingPanel leftPanel = dualListing.getLeftPanel();
leftPanel.goTo(addr(0x004119a5));
});
waitForSwing();
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 598);
}
catch (DuplicateNameException | InvalidInputException | MemoryAccessException e) {
e.printStackTrace();
}
finally {
destinationProgram.endTransaction(txId2, false);
sourceProgram.endTransaction(txId1, false);
}
captureProvider(functionComparisonProvider);
crop(new Rectangle(351, 2, 24, 24));
}
Function getFunction(Program program1, Address entryPoint) {
@Test
public void testNavNextIcon() {
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
Set<Function> functions = new HashSet<>();
functions.add(f1);
functions.add(f2);
FunctionComparisonProvider functionComparisonProvider =
providerMgr.compareFunctions(functions);
waitForSwing();
captureProvider(functionComparisonProvider);
crop(new Rectangle(293, 2, 24, 24));
}
@Test
public void testNavPreviousIcon() {
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
Set<Function> functions = new HashSet<>();
functions.add(f1);
functions.add(f2);
FunctionComparisonProvider functionComparisonProvider =
providerMgr.compareFunctions(functions);
MultiFunctionComparisonPanel panel =
(MultiFunctionComparisonPanel) functionComparisonProvider.getComponent();
panel.getFocusedComponent().setSelectedIndex(1);
waitForSwing();
captureProvider(functionComparisonProvider);
crop(new Rectangle(315, 2, 24, 24));
}
@Test
public void testAddFunctionsPanel() {
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
FunctionComparisonProviderManager providerMgr =
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
Set<Function> functions = new HashSet<>();
functions.add(f1);
functions.add(f2);
providerMgr.compareFunctions(functions);
DockingActionIf openTableAction = getAction(plugin, "Add Functions To Comparison");
performAction(openTableAction);
DialogComponentProvider table = waitForDialogComponent("Select Functions");
captureDialog(table);
}
private Function getFunction(Program program1, Address entryPoint) {
FunctionManager functionManager = program1.getFunctionManager();
return functionManager.getFunctionAt(entryPoint);
}
public void disassemble(Program pgm1, long addressAsLong, int length, boolean followFlows) {
private void disassemble(Program pgm1, long addressAsLong, int length, boolean followFlows) {
Address address = addr(addressAsLong);
DisassembleCommand cmd = new DisassembleCommand(address,
new AddressSet(address, address.add(length - 1)), followFlows);