added domain object listener for function remove events
|
@ -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/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/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/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/FunctionComparisonWindow.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FunctionComparison/images/FunctionScope.gif||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/ListingCodeComparisonOptions.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FunctionComparison/images/MultiFunctionComparisonWindow.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/binaryData.gif||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FunctionComparison/images/class.png||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|
|
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/binaryData.gif||GHIDRA||||END|
|
||||||
src/main/resources/images/browser.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|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_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/cUnion.png||GHIDRA||||END|
|
||||||
src/main/resources/images/camera-photo.png||Tango Icons - Public Domain|||tango|END|
|
src/main/resources/images/camera-photo.png||Tango Icons - Public Domain|||tango|END|
|
||||||
src/main/resources/images/carry.png||GHIDRA||||END|
|
src/main/resources/images/carry.png||GHIDRA||||END|
|
||||||
|
|
|
@ -11,28 +11,15 @@
|
||||||
<H1><A name="FunctionComparisonPlugin"></A> <A name="Function_Comparison"></A> <A name=
|
<H1><A name="FunctionComparisonPlugin"></A> <A name="Function_Comparison"></A> <A name=
|
||||||
"FunctionComparison"></A> Function Comparison Window</H1>
|
"FunctionComparison"></A> Function Comparison Window</H1>
|
||||||
|
|
||||||
<P>The Function Comparison window can provide different types of panels for visually comparing
|
<P>The Function Comparison window provides a way to compare two or more
|
||||||
two or more functions. The Listing View of the Function Comparison window and its capabilities
|
functions in a simple side-by-side panel. </P>
|
||||||
is explained below.</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 name="Dual_Listing"></A>A new function comparison window will appear (subsequent
|
||||||
<P>A Function Comparison window can be displayed from a selection containing two or more
|
invocations of this option will create a new tab in the existing window).</P>
|
||||||
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>
|
|
||||||
<BR><BR>
|
<BR><BR>
|
||||||
|
|
||||||
<CENTER>
|
<CENTER>
|
||||||
|
@ -40,10 +27,6 @@
|
||||||
</CENTER><BR>
|
</CENTER><BR>
|
||||||
<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>
|
<H2><A name="Listing_View"></A>Listing View</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
|
@ -426,86 +409,39 @@
|
||||||
<H2><A name="Compare Multiple Functions"></A>Comparing Multiple Functions</H2>
|
<H2><A name="Compare Multiple Functions"></A>Comparing Multiple Functions</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>When comparing more than two functions, a Function Comparison window will have additional
|
<P>There is no limit to the number of functions that can be compared. Users may
|
||||||
components at the top of it. This window displays two functions side by side again, but has
|
add and remove functions from each comparison panel as desired. Simply use the pulldowns
|
||||||
two choice components at the top of the panel, which indicates the current function being
|
above the listing panels to change what is being compared.</P>
|
||||||
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 are two toolbar actions with quick keys that allow you to change to the
|
<P>The following toolbar options are available:</P>
|
||||||
next/previous function for the side that has focus as indicated by the pink border.</P>
|
|
||||||
|
|
||||||
<BR><BR>
|
<blockquote>
|
||||||
<CENTER>
|
<h3><IMG src="images/AddToComparisonIcon.png" border="0"> <a name="Add_To_Comparison"></a>Add To Existing Comparison</h3>
|
||||||
<IMG alt="" border="0" src="images/MultiFunctionComparisonWindow.png">
|
<p>Allows the user to add functions to the current comparison window. When
|
||||||
</CENTER><BR>
|
selected, the following table containing all functions in the current program is
|
||||||
<BR>
|
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>Multi-Function Comparison Actions</H3>
|
<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>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<h3><IMG src="images/NavPreviousIcon.png" border="0"> <a name="Navigate_Previous"></a>Go To Previous Function</h3>
|
||||||
<P>There are two additional actions that are available when you are comparing more than
|
<p>Navigates to the previous available function in the selection pulldown</p>
|
||||||
just two functions. The following describes them.</P>
|
|
||||||
|
|
||||||
<H4><A name="Compare_The_Next_Function"></A>Compare The Next Function</H4>
|
<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
|
||||||
<BLOCKQUOTE>
|
pink border.</p>
|
||||||
<P>The Function Comparison window has an icon (<IMG alt="" src=
|
</blockquote>
|
||||||
"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>
|
<H3>Other Function Comparison Actions</H3>
|
||||||
|
|
||||||
|
@ -582,8 +518,6 @@
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
</BLOCKQUOTE><BR>
|
</BLOCKQUOTE><BR>
|
||||||
<BR>
|
|
||||||
|
|
||||||
|
|
||||||
<P class="providedbyplugin">Provided By: <I>FunctionComparisonPlugin</I></P>
|
<P class="providedbyplugin">Provided By: <I>FunctionComparisonPlugin</I></P>
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 609 B |
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 476 B |
After Width: | Height: | Size: 473 B |
After Width: | Height: | Size: 608 B |
|
@ -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>
|
<h3><a name="Function_Comparison"></a><a name="Compare_Selected_Functions"></a>Compare Selected Functions</h3>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>The Functions window has an icon (<img src="images/page_white_c.png">)
|
<p>The Functions window has an icon (<img src="images/page_white_c.png"> which contains a star
|
||||||
on the tool bar to compare the functions whose rows are currently selected in the table.
|
<img src="images/bullet_star.png">) on the tool bar that allows users to compare
|
||||||
</p>
|
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>
|
<ol>
|
||||||
<li> Select the functions you want to compare from the table in the Functions window.
|
<li>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>
|
You must select one or more rows in the table.</li>
|
||||||
<li>Right mouse click and select the <img src="images/page_white_c.png">
|
<li>Right mouse click and select the <b>Compare Selected Functions</b> option,
|
||||||
<b>Compare Selected Functions</b> option, OR select the <img src="images/page_white_c.png">
|
OR click the create-comparison icon on the tool bar.</li>
|
||||||
button on the tool bar.</li>
|
|
||||||
</ol>
|
</ol>
|
||||||
<p>This will display a
|
|
||||||
<a href="help/topics/FunctionComparison/FunctionComparison.htm">Function Comparison</a>
|
<p>The resulting <a href="help/topics/FunctionComparison/FunctionComparison.htm">Function Comparison</a>
|
||||||
window which allows the functions to be viewed
|
window will allow the functions to be viewed side by side.</p>
|
||||||
side by side.</p>
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|
||||||
<p> </p>
|
<p> </p>
|
||||||
|
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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() + "") : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -15,20 +15,32 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functioncompare;
|
package ghidra.app.plugin.core.functioncompare;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import docking.ComponentProviderActivationListener;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.events.ProgramClosedPluginEvent;
|
import ghidra.app.events.*;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.plugin.ProgramPlugin;
|
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.model.*;
|
||||||
import ghidra.framework.plugintool.PluginInfo;
|
import ghidra.framework.plugintool.PluginInfo;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
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
|
* Allows users to create function comparisons that are displayed
|
||||||
* FunctionComparisonPanel.
|
* 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
|
//@formatter:off
|
||||||
@PluginInfo(
|
@PluginInfo(
|
||||||
|
@ -36,36 +48,36 @@ import ghidra.program.model.listing.Program;
|
||||||
packageName = CorePluginPackage.NAME,
|
packageName = CorePluginPackage.NAME,
|
||||||
category = PluginCategoryNames.DIFF,
|
category = PluginCategoryNames.DIFF,
|
||||||
shortDescription = "Compare Functions",
|
shortDescription = "Compare Functions",
|
||||||
description = "This plugin provides actions that allow you to compare two or more functions with each other.",
|
description = "Allows users to compare two or more functions",
|
||||||
eventsConsumed = { ProgramClosedPluginEvent.class }
|
servicesProvided = { FunctionComparisonService.class },
|
||||||
|
eventsConsumed = { ProgramSelectionPluginEvent.class, ProgramActivatedPluginEvent.class,
|
||||||
|
ProgramClosedPluginEvent.class }
|
||||||
)
|
)
|
||||||
//@formatter:on
|
//@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";
|
public final static String FUNCTION_MENU_SUBGROUP = "Function";
|
||||||
static final String MENU_PULLRIGHT = "CompareFunctions";
|
static final String MENU_PULLRIGHT = "CompareFunctions";
|
||||||
static final String POPUP_MENU_GROUP = "CompareFunction";
|
static final String POPUP_MENU_GROUP = "CompareFunction";
|
||||||
|
|
||||||
private FunctionComparisonProviderManager functionComparisonManager;
|
private FunctionComparisonProviderManager functionComparisonManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a plugin that provides actions for comparing functions.
|
* Constructor
|
||||||
* @param tool the tool that owns this plugin.
|
*
|
||||||
|
* @param tool the tool that owns this plugin
|
||||||
*/
|
*/
|
||||||
public FunctionComparisonPlugin(PluginTool tool) {
|
public FunctionComparisonPlugin(PluginTool tool) {
|
||||||
super(tool, true, true);
|
super(tool, true, true);
|
||||||
|
|
||||||
functionComparisonManager = new FunctionComparisonProviderManager(this);
|
functionComparisonManager = new FunctionComparisonProviderManager(this);
|
||||||
|
|
||||||
tool.setMenuGroup(new String[] { MENU_PULLRIGHT }, POPUP_MENU_GROUP);
|
tool.setMenuGroup(new String[] { MENU_PULLRIGHT }, POPUP_MENU_GROUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init() {
|
protected void init() {
|
||||||
createActions();
|
CompareFunctionsAction compareFunctionsAction = new CompareFunctionsFromListingAction(tool);
|
||||||
}
|
tool.addAction(compareFunctionsAction);
|
||||||
|
|
||||||
private void createActions() {
|
|
||||||
tool.addAction(new CompareFunctionsAction(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -85,18 +97,69 @@ public class FunctionComparisonPlugin extends ProgramPlugin implements DomainObj
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a panel for comparing the specified functions.
|
* Overridden to listen for two event types:
|
||||||
* @param functions the functions that are used to populate both the left and right side
|
* <li>Object Restored: In the event of a redo/undo that affects a function
|
||||||
* of the function comparison panel.
|
* 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) {
|
@Override
|
||||||
functionComparisonManager.showFunctionComparisonProvider(functions);
|
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
|
@Override
|
||||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
public void addFunctionComparisonProviderListener(
|
||||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
|
ComponentProviderActivationListener listener) {
|
||||||
functionComparisonManager.domainObjectRestored(ev);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,140 +18,75 @@ package ghidra.app.plugin.core.functioncompare;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.DockingTool;
|
import docking.DockingTool;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.actions.PopupActionProvider;
|
import docking.actions.PopupActionProvider;
|
||||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
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.ListingCodeComparisonPanel;
|
||||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||||
import ghidra.app.util.viewer.util.CodeComparisonPanel;
|
import ghidra.app.util.viewer.util.CodeComparisonPanel;
|
||||||
import ghidra.framework.options.SaveState;
|
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.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.HelpLocation;
|
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";
|
protected static final String HELP_TOPIC = "FunctionComparison";
|
||||||
private static final Icon ICON = ResourceManager.loadImage("images/page_white_c.png");
|
protected FunctionComparisonPanel functionComparisonPanel;
|
||||||
private FunctionComparisonPanel functionComparisonPanel;
|
protected Plugin plugin;
|
||||||
private FunctionComparisonProviderListener listener;
|
|
||||||
|
/** 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
|
* Constructor
|
||||||
* 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
|
* @param plugin the active plugin
|
||||||
* will be loaded into the left side and the second function will be loaded in the right side.
|
* @param name the providers name; used to group similar providers into a tab within
|
||||||
* @param plugin the plugin that owns this provider.
|
* the same window
|
||||||
* @param functions the functions that are used to populate both the left and right side
|
* @param owner the provider owner, usually a plugin name
|
||||||
* of the function comparison panel.
|
|
||||||
* @param listener the listener to notify when the provider is closing.
|
|
||||||
*/
|
*/
|
||||||
public FunctionComparisonProvider(Plugin plugin, Function[] functions,
|
public FunctionComparisonProvider(Plugin plugin, String name, String owner) {
|
||||||
FunctionComparisonProviderListener listener) {
|
this(plugin, name, owner, null);
|
||||||
super(plugin.getTool(), "Function Comparison", plugin.getName());
|
|
||||||
this.listener = listener;
|
|
||||||
if (ICON != null) {
|
|
||||||
setIcon(ICON);
|
|
||||||
}
|
}
|
||||||
functionComparisonPanel = new MultiFunctionComparisonPanel(this, tool, functions);
|
|
||||||
|
/**
|
||||||
|
* 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();
|
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
|
@Override
|
||||||
public FunctionComparisonPanel getComponent() {
|
public FunctionComparisonPanel getComponent() {
|
||||||
|
if (functionComparisonPanel == null) {
|
||||||
|
functionComparisonPanel = new FunctionComparisonPanel(this, tool, null, null);
|
||||||
|
}
|
||||||
return functionComparisonPanel;
|
return functionComparisonPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +96,8 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
|
||||||
buff.append("FunctionComparisonProvider\n");
|
buff.append("FunctionComparisonProvider\n");
|
||||||
buff.append("Name: ");
|
buff.append("Name: ");
|
||||||
buff.append(getName() + "\n");
|
buff.append(getName() + "\n");
|
||||||
|
buff.append("Tab Text: ");
|
||||||
|
buff.append(getTabText() + "\n");
|
||||||
Function leftFunction = functionComparisonPanel.getLeftFunction();
|
Function leftFunction = functionComparisonPanel.getLeftFunction();
|
||||||
String leftName = (leftFunction != null) ? leftFunction.getName() : "No Function";
|
String leftName = (leftFunction != null) ? leftFunction.getName() : "No Function";
|
||||||
buff.append("Function 1: " + leftName + "\n");
|
buff.append("Function 1: " + leftName + "\n");
|
||||||
|
@ -181,44 +118,15 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
|
||||||
@Override
|
@Override
|
||||||
public void removeFromTool() {
|
public void removeFromTool() {
|
||||||
tool.removePopupActionProvider(this);
|
tool.removePopupActionProvider(this);
|
||||||
|
|
||||||
super.removeFromTool();
|
super.removeFromTool();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeComponent() {
|
public void modelChanged(List<FunctionComparison> model) {
|
||||||
super.closeComponent();
|
this.model.setComparisons(model);
|
||||||
|
functionComparisonPanel.reload();
|
||||||
if (listener != null) {
|
setTabText(functionComparisonPanel);
|
||||||
listener.providerClosed(this);
|
closeIfEmpty();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
* @param saveState the configuration state to restore
|
||||||
*/
|
*/
|
||||||
public void readConfigState(SaveState saveState) {
|
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
|
* @param saveState the new configuration state
|
||||||
*/
|
*/
|
||||||
public void writeConfigState(SaveState saveState) {
|
public void writeConfigState(SaveState saveState) {
|
||||||
functionComparisonPanel.writeConfigState(getName(), 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,22 @@
|
||||||
package ghidra.app.plugin.core.functioncompare;
|
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 {
|
public interface FunctionComparisonProviderListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification method that will get called when the provider is being closed.
|
* Invoked when the provider is being closed
|
||||||
* @param provider the provider that is being closed.
|
*
|
||||||
|
* @param provider the closed provider
|
||||||
*/
|
*/
|
||||||
public void providerClosed(FunctionComparisonProvider provider);
|
public void providerClosed(FunctionComparisonProvider provider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the provider is being opened
|
||||||
|
*
|
||||||
|
* @param provider the opened provider
|
||||||
|
*/
|
||||||
|
public void providerOpened(FunctionComparisonProvider provider);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,219 +15,199 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functioncompare;
|
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.model.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.Plugin;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FunctionComparisonProviderManager allows a plugin to display function comparison panels.
|
* Provides access to all open {@link FunctionComparisonProvider comparison providers}
|
||||||
* It responds to program close events and closes any panels that have a function from the
|
* and allows users to do the following:
|
||||||
* closed program. It also updates the displays if needed due to an Undo.
|
* <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 {
|
public class FunctionComparisonProviderManager implements FunctionComparisonProviderListener {
|
||||||
|
|
||||||
private HashSet<FunctionComparisonProvider> functionComparisonProviders = new HashSet<>();
|
private Set<FunctionComparisonProvider> providers = new CopyOnWriteArraySet<>();
|
||||||
private ProgramPlugin plugin;
|
private Set<ComponentProviderActivationListener> listeners = new HashSet<>();
|
||||||
private PluginTool tool;
|
private Plugin plugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a FunctionComparisonProviderManager.
|
* Constructor
|
||||||
* @param plugin the plugin that owns this manager.
|
*
|
||||||
|
* @param plugin the parent plugin
|
||||||
*/
|
*/
|
||||||
public FunctionComparisonProviderManager(ProgramPlugin plugin) {
|
public FunctionComparisonProviderManager(Plugin plugin) {
|
||||||
this.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
|
@Override
|
||||||
public void providerClosed(FunctionComparisonProvider provider) {
|
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.
|
* Creates a new comparison between the given set of functions
|
||||||
* This method should be called when a program is closing.
|
*
|
||||||
* @param program the program whose function providers need to close.
|
* @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) {
|
public void closeProviders(Program program) {
|
||||||
// Get an array of the providers and loop over it to notify them. This is to prevent
|
providers.stream().forEach(p -> p.programClosed(program));
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up since this manager is being disposed. All function comparison providers will
|
* Removes any comparisons that contain a function from the given program
|
||||||
* close and be cleaned up.
|
*
|
||||||
|
* @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() {
|
public void dispose() {
|
||||||
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
|
for (FunctionComparisonProvider provider : providers) {
|
||||||
FunctionComparisonPanel functionComparisonPanel =
|
FunctionComparisonPanel panel = provider.getComponent();
|
||||||
functionComparisonProvider.getComponent();
|
panel.setVisible(false);
|
||||||
functionComparisonPanel.setVisible(false);
|
panel.dispose();
|
||||||
functionComparisonPanel.dispose();
|
|
||||||
}
|
}
|
||||||
functionComparisonProviders.clear();
|
providers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when there is an Undo/Redo. If a program is being restored, this will notify all the
|
* Called when there is an Undo/Redo. If a program is being restored, this
|
||||||
* function comparison providers. This allows them to refresh if they are showing a function
|
* will notify all the function comparison providers. This allows them to
|
||||||
* from the program.
|
* refresh if they are showing a function from the program
|
||||||
* @param ev the event indicating if this is an Undo/Redo on a program.
|
*
|
||||||
|
* @param ev the object changed event
|
||||||
*/
|
*/
|
||||||
public void domainObjectRestored(DomainObjectChangedEvent ev) {
|
public void domainObjectRestored(DomainObjectChangedEvent ev) {
|
||||||
for (DomainObjectChangeRecord domainObjectChangeRecord : ev) {
|
for (DomainObjectChangeRecord domainObjectChangeRecord : ev) {
|
||||||
int eventType = domainObjectChangeRecord.getEventType();
|
int eventType = domainObjectChangeRecord.getEventType();
|
||||||
if (eventType == DomainObject.DO_OBJECT_RESTORED) {
|
if (eventType != DomainObject.DO_OBJECT_RESTORED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Object source = ev.getSource();
|
Object source = ev.getSource();
|
||||||
if (source instanceof Program) {
|
if (source instanceof Program) {
|
||||||
Program program = (Program) source;
|
Program program = (Program) source;
|
||||||
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
|
providers.stream().forEach(p -> p.programRestored(program));
|
||||||
functionComparisonProvider.programRestored(program);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,109 +15,298 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functioncompare;
|
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.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.util.HelpLocation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a panel for comparing two or more functions.
|
* Extends the basic {@link FunctionComparisonPanel one-to-one comparison panel}
|
||||||
* If there are multiple functions to display within either the left or right side of this panel,
|
* to allow a many-to-many relationship. The panel provides a pair of combo
|
||||||
* then a combo box will appear above the left and right side of the CodeComparisonPanels.
|
* boxes above the function display area that allows users to select which
|
||||||
* Each combo box will allow the user to choose which function to display on that side of the panel.
|
* functions are to be compared.
|
||||||
*/
|
* <p>
|
||||||
public class MultiFunctionComparisonPanel extends FunctionChoiceComparisonPanel {
|
* 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
|
||||||
* Creates a panel for displaying two or more functions to be compared. This makes the
|
* such that each source function can only be compared to a specific set
|
||||||
* functions available for display in both the left side and right side of the panel after
|
* of target functions. For all practical purposes, the source functions
|
||||||
* they are sorted ascending based on the program and function. The primary sort is on
|
* appear in the left-side panel and targets appear on the right.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* @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.
|
|
||||||
*/
|
*/
|
||||||
boolean matchesTheseFunctions(Function[] functionsL, Function[] functionsR) {
|
public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
|
||||||
return Arrays.equals(getLeftFunctions(), getSortedFunctions(functionsL)) &&
|
|
||||||
Arrays.equals(getRightFunctions(), getSortedFunctions(functionsR));
|
/** 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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param provider the comparison provider associated with this panel
|
||||||
|
* @param tool the active plugin tool
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functioncompare;
|
package ghidra.app.plugin.core.functioncompare.actions;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.ComponentProvider;
|
import docking.ComponentProvider;
|
||||||
|
@ -30,10 +30,11 @@ import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.exception.InvalidInputException;
|
import ghidra.util.exception.InvalidInputException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action that applies the signature of the function in the currently active side of a
|
* 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.
|
* 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
|
* <p>
|
||||||
* using its context.
|
* Each CodeComparisonPanel can extend this class in order to provide this action
|
||||||
|
* using its context
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractApplyFunctionSignatureAction extends DockingAction {
|
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";
|
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
|
* Constructor
|
||||||
* comparison panel to the other.
|
*
|
||||||
* @param owner the owner of this action.
|
* @param owner the owner of this action
|
||||||
*/
|
*/
|
||||||
public AbstractApplyFunctionSignatureAction(String owner) {
|
public AbstractApplyFunctionSignatureAction(String owner) {
|
||||||
super(ACTION_NAME, 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(
|
protected boolean hasReadOnlyNonFocusedSide(
|
||||||
CodeComparisonPanel<? extends FieldPanelCoordinator> codeComparisonPanel) {
|
CodeComparisonPanel<? extends FieldPanelCoordinator> codeComparisonPanel) {
|
||||||
Function leftFunction = codeComparisonPanel.getLeftFunction();
|
Function leftFunction = codeComparisonPanel.getLeftFunction();
|
||||||
Function rightFunction = codeComparisonPanel.getRightFunction();
|
Function rightFunction = codeComparisonPanel.getRightFunction();
|
||||||
|
|
||||||
if (leftFunction == null || rightFunction == null) {
|
if (leftFunction == null || rightFunction == null) {
|
||||||
return false; // Doesn't have a function on both sides.
|
return false; // Doesn't have a function on both sides.
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
|
boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
|
||||||
Program leftProgram = leftFunction.getProgram();
|
Program leftProgram = leftFunction.getProgram();
|
||||||
Program rightProgram = rightFunction.getProgram();
|
Program rightProgram = rightFunction.getProgram();
|
||||||
|
@ -111,11 +124,22 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
|
||||||
(leftHasFocus && rightProgram.getDomainFile().isReadOnly());
|
(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,
|
protected boolean updateFunction(ComponentProvider provider, Function destinationFunction,
|
||||||
Function sourceFunction) {
|
Function sourceFunction) {
|
||||||
|
|
||||||
Program program = destinationFunction.getProgram();
|
Program program = destinationFunction.getProgram();
|
||||||
int txID = program.startTransaction(ACTION_NAME);
|
int txID = program.startTransaction(ACTION_NAME);
|
||||||
boolean commit = false;
|
boolean commit = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FunctionUtility.updateFunction(destinationFunction, sourceFunction);
|
FunctionUtility.updateFunction(destinationFunction, sourceFunction);
|
||||||
commit = true;
|
commit = true;
|
||||||
|
@ -129,6 +153,7 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(txID, commit);
|
program.endTransaction(txID, commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return commit;
|
return commit;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,15 +17,15 @@ package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
|
|
||||||
class FunctionRowObject implements Comparable<FunctionRowObject> {
|
public class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||||
|
|
||||||
private final Function function;
|
private final Function function;
|
||||||
|
|
||||||
FunctionRowObject(Function function) {
|
public FunctionRowObject(Function function) {
|
||||||
this.function = function;
|
this.function = function;
|
||||||
}
|
}
|
||||||
|
|
||||||
Function getFunction() {
|
public Function getFunction() {
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getKey() {
|
public long getKey() {
|
||||||
return function.getID();
|
return function.getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import ghidra.util.table.AddressBasedTableModel;
|
||||||
import ghidra.util.table.field.*;
|
import ghidra.util.table.field.*;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
public class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||||
|
|
||||||
static final int LOCATION_COL_WIDTH = 50;
|
static final int LOCATION_COL_WIDTH = 50;
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||||
|
|
||||||
private FunctionManager functionMgr;
|
private FunctionManager functionMgr;
|
||||||
|
|
||||||
FunctionTableModel(PluginTool tool, Program program) {
|
public FunctionTableModel(PluginTool tool, Program program) {
|
||||||
super("Functions", tool, program, null);
|
super("Functions", tool, program, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload(Program newProgram) {
|
public void reload(Program newProgram) {
|
||||||
this.setProgram(newProgram);
|
this.setProgram(newProgram);
|
||||||
if (newProgram != null) {
|
if (newProgram != null) {
|
||||||
functionMgr = newProgram.getFunctionManager();
|
functionMgr = newProgram.getFunctionManager();
|
||||||
|
|
|
@ -15,33 +15,34 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functionwindow;
|
package ghidra.app.plugin.core.functionwindow;
|
||||||
|
|
||||||
import java.util.List;
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import docking.ComponentProvider;
|
||||||
|
import docking.ComponentProviderActivationListener;
|
||||||
import docking.ActionContext;
|
import docking.action.DockingAction;
|
||||||
import docking.action.*;
|
import docking.action.KeyBindingData;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.events.ProgramClosedPluginEvent;
|
import ghidra.app.events.ProgramClosedPluginEvent;
|
||||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.plugin.ProgramPlugin;
|
import ghidra.app.plugin.ProgramPlugin;
|
||||||
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProvider;
|
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.model.*;
|
||||||
|
import ghidra.framework.options.OptionsChangeListener;
|
||||||
|
import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.framework.plugintool.PluginInfo;
|
import ghidra.framework.plugintool.PluginInfo;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
import ghidra.program.model.address.Address;
|
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.model.symbol.Symbol;
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.ChangeManager;
|
||||||
import ghidra.util.Msg;
|
import ghidra.program.util.ProgramChangeRecord;
|
||||||
import ghidra.util.table.GhidraTable;
|
|
||||||
import ghidra.util.table.SelectionNavigationAction;
|
import ghidra.util.table.SelectionNavigationAction;
|
||||||
import ghidra.util.table.actions.MakeProgramSelectionAction;
|
import ghidra.util.table.actions.MakeProgramSelectionAction;
|
||||||
import ghidra.util.task.SwingUpdateManager;
|
import ghidra.util.task.SwingUpdateManager;
|
||||||
import resources.ResourceManager;
|
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@PluginInfo(
|
@PluginInfo(
|
||||||
|
@ -50,24 +51,28 @@ import resources.ResourceManager;
|
||||||
category = PluginCategoryNames.CODE_VIEWER,
|
category = PluginCategoryNames.CODE_VIEWER,
|
||||||
shortDescription = "Function Viewer",
|
shortDescription = "Function Viewer",
|
||||||
description = "Provides a window that displays the list of functions in the program.",
|
description = "Provides a window that displays the list of functions in the program.",
|
||||||
|
servicesRequired = { FunctionComparisonService.class },
|
||||||
eventsConsumed = { ProgramClosedPluginEvent.class }
|
eventsConsumed = { ProgramClosedPluginEvent.class }
|
||||||
)
|
)
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectListener {
|
public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectListener,
|
||||||
|
OptionsChangeListener, ComponentProviderActivationListener {
|
||||||
|
|
||||||
private DockingAction selectAction;
|
private DockingAction selectAction;
|
||||||
private DockingAction compareAction;
|
private DockingAction compareFunctionsAction;
|
||||||
private FunctionWindowProvider provider;
|
private FunctionWindowProvider provider;
|
||||||
private SwingUpdateManager swingMgr;
|
private SwingUpdateManager swingMgr;
|
||||||
private FunctionComparisonProviderManager functionComparisonManager;
|
private FunctionComparisonService functionComparisonService;
|
||||||
|
|
||||||
public FunctionWindowPlugin(PluginTool tool) {
|
public FunctionWindowPlugin(PluginTool tool) {
|
||||||
super(tool, true, false);
|
super(tool, true, false);
|
||||||
|
|
||||||
functionComparisonManager = new FunctionComparisonProviderManager(this);
|
swingMgr = new SwingUpdateManager(1000, new Runnable() {
|
||||||
|
@Override
|
||||||
swingMgr = new SwingUpdateManager(1000, () -> provider.reload());
|
public void run() {
|
||||||
|
provider.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,7 +80,21 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
|
||||||
super.init();
|
super.init();
|
||||||
|
|
||||||
provider = new FunctionWindowProvider(this);
|
provider = new FunctionWindowProvider(this);
|
||||||
|
functionComparisonService = tool.getService(FunctionComparisonService.class);
|
||||||
createActions();
|
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
|
@Override
|
||||||
|
@ -84,15 +103,14 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
|
||||||
currentProgram.removeListener(this);
|
currentProgram.removeListener(this);
|
||||||
}
|
}
|
||||||
swingMgr.dispose();
|
swingMgr.dispose();
|
||||||
|
if (provider != null) {
|
||||||
provider.dispose();
|
provider.dispose();
|
||||||
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
|
|
||||||
functionComparisonManager.domainObjectRestored(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!provider.isVisible()) {
|
if (!provider.isVisible()) {
|
||||||
return;
|
return;
|
||||||
|
@ -181,82 +199,64 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createActions() {
|
private void createActions() {
|
||||||
addSelectAction();
|
|
||||||
addCompareAction();
|
|
||||||
|
|
||||||
DockingAction action = new SelectionNavigationAction(this, provider.getTable());
|
DockingAction action = new SelectionNavigationAction(this, provider.getTable());
|
||||||
tool.addLocalAction(provider, action);
|
tool.addLocalAction(provider, action);
|
||||||
}
|
|
||||||
|
|
||||||
private void addSelectAction() {
|
|
||||||
|
|
||||||
selectAction = new MakeProgramSelectionAction(this, provider.getTable());
|
selectAction = new MakeProgramSelectionAction(this, provider.getTable());
|
||||||
tool.addLocalAction(provider, selectAction);
|
tool.addLocalAction(provider, selectAction);
|
||||||
|
|
||||||
|
compareFunctionsAction = new CompareFunctionsFromFunctionTableAction(tool);
|
||||||
|
tool.addLocalAction(provider, compareFunctionsAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCompareAction() {
|
// private void installDummyAction(DockingAction action) {
|
||||||
compareAction = new DockingAction("Compare Selected Functions", getName()) {
|
// 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));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||||
compareSelectedFunctions();
|
Object newValue) {
|
||||||
|
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
tool.addLocalAction(provider, compareAction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setActionsEnabled(boolean enabled) {
|
void setActionsEnabled(boolean enabled) {
|
||||||
selectAction.setEnabled(enabled);
|
selectAction.setEnabled(enabled);
|
||||||
compareAction.setEnabled(enabled);
|
compareFunctionsAction.setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showFunctions() {
|
void showFunctions() {
|
||||||
provider.showFunctions();
|
provider.showFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectFunctions(ProgramSelection selection) {
|
@Override
|
||||||
ProgramSelectionPluginEvent pspe =
|
public void componentProviderActivated(ComponentProvider componentProvider) {
|
||||||
new ProgramSelectionPluginEvent("Selection", selection, currentProgram);
|
if (componentProvider instanceof FunctionComparisonProvider) {
|
||||||
firePluginEvent(pspe);
|
tool.contextChanged(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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
|
@Override
|
||||||
protected void programClosed(Program program) {
|
public void componentProviderDeactivated(ComponentProvider componentProvider) {
|
||||||
functionComparisonManager.closeProviders(program);
|
if (componentProvider instanceof FunctionComparisonProvider) {
|
||||||
|
tool.contextChanged(provider);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ import ghidra.util.table.*;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider for the equates table.
|
* Provider that displays all functions in the selected program
|
||||||
*/
|
*/
|
||||||
public class FunctionWindowProvider extends ComponentProviderAdapter {
|
public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
|
@ -45,9 +45,13 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||||
private JComponent mainPanel;
|
private JComponent mainPanel;
|
||||||
|
|
||||||
private GhidraTableFilterPanel<FunctionRowObject> tableFilterPanel;
|
private GhidraTableFilterPanel<FunctionRowObject> tableFilterPanel;
|
||||||
|
|
||||||
private GhidraThreadedTablePanel<FunctionRowObject> threadedTablePanel;
|
private GhidraThreadedTablePanel<FunctionRowObject> threadedTablePanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param plugin the function window plugin
|
||||||
|
*/
|
||||||
FunctionWindowProvider(FunctionWindowPlugin plugin) {
|
FunctionWindowProvider(FunctionWindowPlugin plugin) {
|
||||||
super(plugin.getTool(), "Functions Window", plugin.getName());
|
super(plugin.getTool(), "Functions Window", plugin.getName());
|
||||||
setTitle("Functions");
|
setTitle("Functions");
|
||||||
|
@ -124,7 +128,8 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||||
functionTable.setPreferredScrollableViewportSize(new Dimension(350, 150));
|
functionTable.setPreferredScrollableViewportSize(new Dimension(350, 150));
|
||||||
functionTable.setRowSelectionAllowed(true);
|
functionTable.setRowSelectionAllowed(true);
|
||||||
functionTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
functionTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
functionTable.getSelectionModel().addListSelectionListener(
|
functionTable.getSelectionModel()
|
||||||
|
.addListSelectionListener(
|
||||||
e -> plugin.setActionsEnabled(functionTable.getSelectedRowCount() > 0));
|
e -> plugin.setActionsEnabled(functionTable.getSelectedRowCount() > 0));
|
||||||
|
|
||||||
functionModel.addTableModelListener(e -> {
|
functionModel.addTableModelListener(e -> {
|
||||||
|
@ -158,7 +163,9 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFunctionTableRenderer() {
|
private void setFunctionTableRenderer() {
|
||||||
functionTable.getColumnModel().getColumn(FunctionTableModel.LOCATION_COL).setPreferredWidth(
|
functionTable.getColumnModel()
|
||||||
|
.getColumn(FunctionTableModel.LOCATION_COL)
|
||||||
|
.setPreferredWidth(
|
||||||
FunctionTableModel.LOCATION_COL_WIDTH);
|
FunctionTableModel.LOCATION_COL_WIDTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,5 +210,4 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||||
public boolean isTransient() {
|
public boolean isTransient() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ package ghidra.app.util.viewer.listingpanel;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
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;
|
import ghidra.app.util.viewer.util.CodeComparisonPanel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -361,15 +361,23 @@ public class ListingCodeComparisonPanel
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLeftListingTitle() {
|
private void updateLeftListingTitle() {
|
||||||
|
titlePanels[LEFT].setTitleName(getLeftProgramName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLeftProgramName() {
|
||||||
String leftProgramName =
|
String leftProgramName =
|
||||||
(programs[LEFT] != null) ? programs[LEFT].getDomainFile().toString() : "none";
|
(programs[LEFT] != null) ? programs[LEFT].getDomainFile().toString() : "none";
|
||||||
titlePanels[LEFT].setTitleName(leftProgramName);
|
return leftProgramName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRightListingTitle() {
|
private void updateRightListingTitle() {
|
||||||
|
titlePanels[RIGHT].setTitleName(getRightProgramName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRightProgramName() {
|
||||||
String rightProgramName =
|
String rightProgramName =
|
||||||
(programs[RIGHT] != null) ? programs[RIGHT].getDomainFile().toString() : "none";
|
(programs[RIGHT] != null) ? programs[RIGHT].getDomainFile().toString() : "none";
|
||||||
titlePanels[RIGHT].setTitleName(rightProgramName);
|
return rightProgramName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeListingFieldNavigation() {
|
private void initializeListingFieldNavigation() {
|
||||||
|
@ -466,18 +474,10 @@ public class ListingCodeComparisonPanel
|
||||||
public void updateActionEnablement() {
|
public void updateActionEnablement() {
|
||||||
boolean isShowing = isShowing();
|
boolean isShowing = isShowing();
|
||||||
boolean listingDiffActionEnablement = isShowing && listingDiff.hasCorrelation();
|
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);
|
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 {
|
class ToggleHeaderAction extends ToggleDockingAction {
|
||||||
|
@ -537,6 +537,11 @@ public class ListingCodeComparisonPanel
|
||||||
setHover(true);
|
setHover(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
return isShowing();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
setHover(isSelected());
|
setHover(isSelected());
|
||||||
|
@ -684,6 +689,11 @@ public class ListingCodeComparisonPanel
|
||||||
previousDiffAction.setMenuString();
|
previousDiffAction.setMenuString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
return isShowing() && listingDiff.hasCorrelation();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionStateChanged(ActionState<String> newActionState, EventTrigger trigger) {
|
public void actionStateChanged(ActionState<String> newActionState, EventTrigger trigger) {
|
||||||
adjustNextPreviousAreaType();
|
adjustNextPreviousAreaType();
|
||||||
|
@ -718,6 +728,11 @@ public class ListingCodeComparisonPanel
|
||||||
return isValidPanelContext(context);
|
return isValidPanelContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
return isShowing() && listingDiff.hasCorrelation();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
if (isValidContext(context)) {
|
if (isValidContext(context)) {
|
||||||
|
@ -757,6 +772,11 @@ public class ListingCodeComparisonPanel
|
||||||
return isValidPanelContext(context);
|
return isValidPanelContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
return isShowing() && listingDiff.hasCorrelation();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
if (isValidContext(context)) {
|
if (isValidContext(context)) {
|
||||||
|
@ -784,6 +804,11 @@ public class ListingCodeComparisonPanel
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
|
return isShowing() && listingDiff.hasCorrelation();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValidContext(ActionContext context) {
|
public boolean isValidContext(ActionContext context) {
|
||||||
return isValidPanelContext(context);
|
return isValidPanelContext(context);
|
||||||
|
@ -1518,7 +1543,8 @@ public class ListingCodeComparisonPanel
|
||||||
comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
|
comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
|
||||||
if (programs[LEFT] != null) {
|
if (programs[LEFT] != null) {
|
||||||
AddressIndexMap indexMap = listingPanels[LEFT].getAddressIndexMap();
|
AddressIndexMap indexMap = listingPanels[LEFT].getAddressIndexMap();
|
||||||
listingPanels[LEFT].getFieldPanel().setBackgroundColorModel(
|
listingPanels[LEFT].getFieldPanel()
|
||||||
|
.setBackgroundColorModel(
|
||||||
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMap));
|
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMap));
|
||||||
markerManagers[LEFT].setProgram(programs[LEFT]);
|
markerManagers[LEFT].setProgram(programs[LEFT]);
|
||||||
unmatchedCodeMarkers[LEFT] =
|
unmatchedCodeMarkers[LEFT] =
|
||||||
|
@ -1532,8 +1558,10 @@ public class ListingCodeComparisonPanel
|
||||||
}
|
}
|
||||||
if (programs[RIGHT] != null) {
|
if (programs[RIGHT] != null) {
|
||||||
AddressIndexMap rightIndexMap = listingPanels[RIGHT].getAddressIndexMap();
|
AddressIndexMap rightIndexMap = listingPanels[RIGHT].getAddressIndexMap();
|
||||||
listingPanels[RIGHT].getFieldPanel().setBackgroundColorModel(
|
listingPanels[RIGHT].getFieldPanel()
|
||||||
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], rightIndexMap));
|
.setBackgroundColorModel(
|
||||||
|
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT],
|
||||||
|
rightIndexMap));
|
||||||
markerManagers[RIGHT].setProgram(programs[RIGHT]);
|
markerManagers[RIGHT].setProgram(programs[RIGHT]);
|
||||||
unmatchedCodeMarkers[RIGHT] =
|
unmatchedCodeMarkers[RIGHT] =
|
||||||
markerManagers[RIGHT].createAreaMarker("Listing2 Unmatched Code",
|
markerManagers[RIGHT].createAreaMarker("Listing2 Unmatched Code",
|
||||||
|
@ -1633,7 +1661,8 @@ public class ListingCodeComparisonPanel
|
||||||
|
|
||||||
indexMaps[LEFT] = new AddressIndexMap(addressSets[LEFT]);
|
indexMaps[LEFT] = new AddressIndexMap(addressSets[LEFT]);
|
||||||
markerManagers[LEFT].getOverviewProvider().setAddressIndexMap(indexMaps[LEFT]);
|
markerManagers[LEFT].getOverviewProvider().setAddressIndexMap(indexMaps[LEFT]);
|
||||||
listingPanels[LEFT].getFieldPanel().setBackgroundColorModel(
|
listingPanels[LEFT].getFieldPanel()
|
||||||
|
.setBackgroundColorModel(
|
||||||
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMaps[LEFT]));
|
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMaps[LEFT]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1649,7 +1678,8 @@ public class ListingCodeComparisonPanel
|
||||||
|
|
||||||
indexMaps[RIGHT] = new AddressIndexMap(addressSets[RIGHT]);
|
indexMaps[RIGHT] = new AddressIndexMap(addressSets[RIGHT]);
|
||||||
markerManagers[RIGHT].getOverviewProvider().setAddressIndexMap(indexMaps[RIGHT]);
|
markerManagers[RIGHT].getOverviewProvider().setAddressIndexMap(indexMaps[RIGHT]);
|
||||||
listingPanels[RIGHT].getFieldPanel().setBackgroundColorModel(
|
listingPanels[RIGHT].getFieldPanel()
|
||||||
|
.setBackgroundColorModel(
|
||||||
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], indexMaps[RIGHT]));
|
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], indexMaps[RIGHT]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1992,6 +2022,11 @@ public class ListingCodeComparisonPanel
|
||||||
setDualPanelFocus(i);
|
setDualPanelFocus(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kick the tool so action buttons will be updated
|
||||||
|
if (tool.getActiveComponentProvider() != null) {
|
||||||
|
tool.getActiveComponentProvider().contextChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDualPanelFocus(int leftOrRight) {
|
private void setDualPanelFocus(int leftOrRight) {
|
||||||
|
|
|
@ -127,10 +127,12 @@ public class ListingComparisonFieldPanelCoordinator extends LayoutLockedFieldPan
|
||||||
ListingPanel rightListingPanel = dualListingPanel.getRightPanel();
|
ListingPanel rightListingPanel = dualListingPanel.getRightPanel();
|
||||||
AddressIndexMap leftAddressIndexMap = leftListingPanel.getAddressIndexMap();
|
AddressIndexMap leftAddressIndexMap = leftListingPanel.getAddressIndexMap();
|
||||||
AddressIndexMap rightAddressIndexMap = rightListingPanel.getAddressIndexMap();
|
AddressIndexMap rightAddressIndexMap = rightListingPanel.getAddressIndexMap();
|
||||||
|
|
||||||
BigInteger leftIndex =
|
BigInteger leftIndex =
|
||||||
(leftAddress != null) ? leftAddressIndexMap.getIndex(leftAddress) : null;
|
(leftAddress != null) ? leftAddressIndexMap.getIndex(leftAddress) : null;
|
||||||
BigInteger rightIndex =
|
BigInteger rightIndex =
|
||||||
(rightAddress != null) ? rightAddressIndexMap.getIndex(rightAddress) : null;
|
(rightAddress != null) ? rightAddressIndexMap.getIndex(rightAddress) : null;
|
||||||
|
|
||||||
BigInteger[] lineNumbers =
|
BigInteger[] lineNumbers =
|
||||||
new BigInteger[] { (leftIndex != null) ? leftIndex : BigInteger.ZERO,
|
new BigInteger[] { (leftIndex != null) ? leftIndex : BigInteger.ZERO,
|
||||||
(rightIndex != null) ? rightIndex : BigInteger.ZERO };
|
(rightIndex != null) ? rightIndex : BigInteger.ZERO };
|
||||||
|
|
|
@ -31,15 +31,17 @@ import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.AddressSet;
|
import ghidra.program.model.address.AddressSet;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
import ghidra.util.classfinder.ExtensionPoint;
|
import ghidra.util.classfinder.ExtensionPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CodeComparisonPanel class should be extended by any class that is to be discovered by
|
* The CodeComparisonPanel class should be extended by any class that is to be
|
||||||
* the {@link FunctionComparisonPanel} class and included as a form of comparing two sections
|
* discovered by the {@link FunctionComparisonPanel} class and included as a
|
||||||
* of code within the same or different programs.
|
* form of comparing two sections of code within the same or different programs
|
||||||
* <br><br>
|
* <p>
|
||||||
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN "CodeComparisonPanel".
|
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN <
|
||||||
* If not, the ClassSearcher will not find them.
|
* code>CodeComparisonPanel</code> so they are discoverable by the
|
||||||
|
* {@link ClassSearcher}
|
||||||
*/
|
*/
|
||||||
public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> extends JPanel
|
public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> extends JPanel
|
||||||
implements ExtensionPoint, FocusListener {
|
implements ExtensionPoint, FocusListener {
|
||||||
|
@ -68,15 +70,18 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
|
||||||
protected Function[] functions = new Function[2];
|
protected Function[] functions = new Function[2];
|
||||||
protected Data[] data = new Data[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 boolean syncScrolling = false;
|
||||||
|
|
||||||
private T fieldPanelCoordinator;
|
private T fieldPanelCoordinator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base constructor
|
* Constructor
|
||||||
* @param owner the name of the owner of this component (typically the name of the plugin that
|
*
|
||||||
* owns this panel.)
|
* @param owner the name of the owner of this component
|
||||||
* @param tool the tool that contains the component.
|
* @param tool the tool that contains the component
|
||||||
*/
|
*/
|
||||||
protected CodeComparisonPanel(String owner, PluginTool tool) {
|
protected CodeComparisonPanel(String owner, PluginTool tool) {
|
||||||
this.owner = owner;
|
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
|
* The GUI component for this CodeComparisonPanel
|
||||||
* dual display with a left and right side for comparing some part of the code for two programs.
|
*
|
||||||
* @return the component.
|
* @return the component
|
||||||
*/
|
*/
|
||||||
public abstract JComponent getComponent();
|
public abstract JComponent getComponent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The title for this code comparison component.
|
* The title for this code comparison panel
|
||||||
* @return the title.
|
*
|
||||||
|
* @return the title
|
||||||
*/
|
*/
|
||||||
public abstract String getTitle();
|
public abstract String getTitle();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the two programs to be compared by this panel.
|
* 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.
|
* @param leftProgram the program for the left side
|
||||||
|
* @param rightProgram the program for the right side
|
||||||
*/
|
*/
|
||||||
protected abstract void setPrograms(Program leftProgram, Program rightProgram);
|
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 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
|
* @param rightFunction the function to show in the right side of the code comparison view
|
||||||
*/
|
*/
|
||||||
public abstract void loadFunctions(Function leftFunction, Function rightFunction);
|
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 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
|
* @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
|
* 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 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 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
|
* @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);
|
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();
|
public abstract void dispose();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable/disable navigation in this panel using the mouse.
|
* Enable/disable navigation in this panel using the mouse
|
||||||
* @param enabled false disables mouse navigation.
|
*
|
||||||
|
* @param enabled false disables mouse navigation
|
||||||
*/
|
*/
|
||||||
public abstract void setMouseNavigationEnabled(boolean enabled);
|
public abstract void setMouseNavigationEnabled(boolean enabled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the actions for this CodeComparisonPanel.
|
* Returns the actions for this panel
|
||||||
* @return an array containing the actions
|
*
|
||||||
|
* @return an array of docking actions
|
||||||
*/
|
*/
|
||||||
public DockingAction[] getActions() {
|
public DockingAction[] getActions() {
|
||||||
// No actions currently that appear for each CodeComparisonPanel.
|
// No actions currently that appear for each CodeComparisonPanel.
|
||||||
|
@ -151,6 +163,14 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
|
||||||
return actions;
|
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
|
* 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.
|
* the class of the panel to be superseded.
|
||||||
|
|
BIN
Ghidra/Features/Base/src/main/resources/images/bullet_star.png
Normal file
After Width: | Height: | Size: 331 B |
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ package ghidra.app.decompiler.component;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
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;
|
import ghidra.app.util.viewer.util.CodeComparisonPanel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -41,7 +41,7 @@ import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.HTMLUtilities;
|
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>
|
public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFieldPanelCoordinator>
|
||||||
extends CodeComparisonPanel<DualDecompilerFieldPanelCoordinator> {
|
extends CodeComparisonPanel<DualDecompilerFieldPanelCoordinator> {
|
||||||
|
@ -68,7 +68,8 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
|
||||||
private ProgramLocationListener rightDecompilerLocationListener;
|
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 owner the owner of this panel
|
||||||
* @param tool the tool displaying this panel
|
* @param tool the tool displaying this panel
|
||||||
*/
|
*/
|
||||||
|
@ -189,7 +190,13 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
|
||||||
|
|
||||||
loadLeftFunction(leftFunction);
|
loadLeftFunction(leftFunction);
|
||||||
loadRightFunction(rightFunction);
|
loadRightFunction(rightFunction);
|
||||||
|
|
||||||
|
if (getShowTitles()) {
|
||||||
setTitles(leftFunction, rightFunction);
|
setTitles(leftFunction, rightFunction);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setTitles("", "");
|
||||||
|
}
|
||||||
if (dualDecompilerCoordinator != null) {
|
if (dualDecompilerCoordinator != null) {
|
||||||
dualDecompilerCoordinator.leftLocationChanged((ProgramLocation) null);
|
dualDecompilerCoordinator.leftLocationChanged((ProgramLocation) null);
|
||||||
}
|
}
|
||||||
|
@ -429,6 +436,9 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
|
||||||
setDualPanelFocus(i);
|
setDualPanelFocus(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kick the tool so action buttons will be updated
|
||||||
|
tool.getActiveComponentProvider().contextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDualPanelFocus(int leftOrRight) {
|
private void setDualPanelFocus(int leftOrRight) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package docking.widgets;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -30,6 +31,7 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
|
||||||
|
|
||||||
private GTable gTable;
|
private GTable gTable;
|
||||||
private T selectedValue;
|
private T selectedValue;
|
||||||
|
private List<T> selectedValues = new ArrayList<>();
|
||||||
private GTableFilterPanel<T> filterPanel;
|
private GTableFilterPanel<T> filterPanel;
|
||||||
private RowObjectTableModel<T> model;
|
private RowObjectTableModel<T> model;
|
||||||
|
|
||||||
|
@ -55,10 +57,15 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void okCallback() {
|
protected void okCallback() {
|
||||||
int selectedRow = gTable.getSelectedRow();
|
int[] selectedRows = gTable.getSelectedRows();
|
||||||
if (selectedRow >= 0) {
|
if (selectedRows.length > 0) {
|
||||||
|
selectedValues.clear();
|
||||||
|
for (int selectedRow : selectedRows) {
|
||||||
int modelRow = filterPanel.getModelRow(selectedRow);
|
int modelRow = filterPanel.getModelRow(selectedRow);
|
||||||
selectedValue = model.getRowObject(modelRow);
|
T rowObject = model.getRowObject(modelRow);
|
||||||
|
selectedValues.add(rowObject);
|
||||||
|
}
|
||||||
|
selectedValue = selectedValues.isEmpty() ? null : selectedValues.get(0);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,15 +108,26 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
|
||||||
return selectedValue;
|
return selectedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<T> getSelectedItems() {
|
||||||
|
return selectedValues;
|
||||||
|
}
|
||||||
|
|
||||||
public T show(Component parent) {
|
public T show(Component parent) {
|
||||||
|
setMultiSelectionMode(false);
|
||||||
DockingWindowManager.showDialog(parent, this);
|
DockingWindowManager.showDialog(parent, this);
|
||||||
return getSelectedItem();
|
return getSelectedItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<T> showSelectMultiple(Component parent) {
|
||||||
|
setMultiSelectionMode(true);
|
||||||
|
DockingWindowManager.showDialog(parent, this);
|
||||||
|
return getSelectedItems();
|
||||||
|
}
|
||||||
|
|
||||||
public void setMultiSelectionMode(boolean enable) {
|
public void setMultiSelectionMode(boolean enable) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
gTable.getSelectionModel().setSelectionMode(
|
gTable.getSelectionModel()
|
||||||
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
gTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,13 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.fieldpanel.internal;
|
package docking.widgets.fieldpanel.internal;
|
||||||
|
|
||||||
import ghidra.util.exception.AssertException;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.FieldPanel;
|
import docking.widgets.fieldpanel.FieldPanel;
|
||||||
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A LineLockedFieldPanelCoordinator coordinates the scrolling of a set of field panels by sharing
|
* 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) {
|
public void setLockedLines(BigInteger[] lockedLineNumbers) {
|
||||||
if (lockedLineNumbers.length != this.lockedLineNumbers.length) {
|
if (lockedLineNumbers.length != this.lockedLineNumbers.length) {
|
||||||
throw new AssertException("The number of lines(" + 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++) {
|
for (int i = 0; i < lockedLineNumbers.length; i++) {
|
||||||
if (lockedLineNumbers[i] == null) {
|
if (lockedLineNumbers[i] == null) {
|
||||||
throw new AssertException("lockedLineNumber for field panel [" + i +
|
lockedLineNumbers[i] = BigInteger.ZERO;
|
||||||
"] was unexpectedly null.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < lockedLineNumbers.length; i++) {
|
for (int i = 0; i < lockedLineNumbers.length; i++) {
|
||||||
|
@ -99,7 +98,7 @@ public class LineLockedFieldPanelCoordinator extends FieldPanelCoordinator {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void remove(FieldPanel fp) {
|
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.
|
// Adjust our locked line number array.
|
||||||
int length = panels.length;
|
int length = panels.length;
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
|
|
|
@ -46,7 +46,8 @@ public abstract class ComponentProviderAdapter extends ComponentProvider {
|
||||||
* @param tool the plugin tool.
|
* @param tool the plugin tool.
|
||||||
* @param name The providers name. This is used to group similar providers into a tab within
|
* @param name The providers name. This is used to group similar providers into a tab within
|
||||||
* the same window.
|
* 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,
|
public ComponentProviderAdapter(PluginTool tool, String name, String owner,
|
||||||
Class<?> contextType) {
|
Class<?> contextType) {
|
||||||
|
|
|
@ -15,12 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package help.screenshot;
|
package help.screenshot;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.awt.Rectangle;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import docking.ComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||||
import ghidra.app.plugin.core.functioncompare.*;
|
import ghidra.app.plugin.core.functioncompare.*;
|
||||||
import ghidra.app.util.viewer.listingpanel.ListingCodeComparisonPanel;
|
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.listing.*;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.program.model.symbol.Namespace;
|
|
||||||
import ghidra.program.model.symbol.SourceType;
|
import ghidra.program.model.symbol.SourceType;
|
||||||
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.exception.InvalidInputException;
|
import ghidra.util.exception.InvalidInputException;
|
||||||
|
|
||||||
|
@ -52,19 +55,27 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
plugin = getPlugin(tool, FunctionComparisonPlugin.class);
|
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
|
@Test
|
||||||
public void testFunctionComparisonWindow() {
|
public void testFunctionComparisonWindow() {
|
||||||
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
|
|
||||||
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
|
|
||||||
|
|
||||||
positionListingTop(0x004118f0);
|
positionListingTop(0x004118f0);
|
||||||
int txId1 = sourceProgram.startTransaction("Modify Program1");
|
int txId1 = sourceProgram.startTransaction("Modify Program1");
|
||||||
int txId2 = destinationProgram.startTransaction("Modify Program2");
|
int txId2 = destinationProgram.startTransaction("Modify Program2");
|
||||||
try {
|
try {
|
||||||
sourceProgram.setName("TestProgram");
|
sourceProgram.getDomainFile().setName("FirstProgram");
|
||||||
destinationProgram.setName("OtherProgram");
|
destinationProgram.getDomainFile().setName("SecondProgram");
|
||||||
|
sourceProgram.setName("FirstProgram");
|
||||||
|
destinationProgram.setName("SecondProgram");
|
||||||
|
|
||||||
Listing sourceListing = sourceProgram.getListing();
|
Listing sourceListing = sourceProgram.getListing();
|
||||||
Listing destListing = destinationProgram.getListing();
|
Listing destListing = destinationProgram.getListing();
|
||||||
Memory sourceMemory = sourceProgram.getMemory();
|
Memory sourceMemory = sourceProgram.getMemory();
|
||||||
|
@ -81,100 +92,10 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
|
||||||
f2.setName("FunctionB", SourceType.USER_DEFINED);
|
f2.setName("FunctionB", SourceType.USER_DEFINED);
|
||||||
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
|
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
|
||||||
|
|
||||||
Function[] functions = new Function[] { f1, f2 };
|
|
||||||
FunctionComparisonProviderManager providerMgr =
|
FunctionComparisonProviderManager providerMgr =
|
||||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||||
FunctionComparisonProvider functionComparisonProvider =
|
FunctionComparisonProvider functionComparisonProvider =
|
||||||
providerMgr.showFunctionComparisonProvider(functions);
|
providerMgr.compareFunctions(f1, f2);
|
||||||
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);
|
|
||||||
FunctionComparisonPanel functionComparisonPanel =
|
FunctionComparisonPanel functionComparisonPanel =
|
||||||
functionComparisonProvider.getComponent();
|
functionComparisonProvider.getComponent();
|
||||||
runSwing(() -> {
|
runSwing(() -> {
|
||||||
|
@ -183,12 +104,12 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
|
||||||
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
|
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
|
||||||
ListingPanel leftPanel = dualListing.getLeftPanel();
|
ListingPanel leftPanel = dualListing.getLeftPanel();
|
||||||
leftPanel.goTo(addr(0x004119aa));
|
leftPanel.goTo(addr(0x004119aa));
|
||||||
|
|
||||||
});
|
});
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 550);
|
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 550);
|
||||||
}
|
}
|
||||||
catch (DuplicateNameException | InvalidInputException | MemoryAccessException e) {
|
catch (DuplicateNameException | InvalidInputException | MemoryAccessException
|
||||||
|
| InvalidNameException | IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -198,136 +119,102 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListingCodeComparisonOptions() {
|
public void testAddToComparisonIcon() {
|
||||||
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));
|
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);
|
|
||||||
|
|
||||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
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 =
|
FunctionComparisonProviderManager providerMgr =
|
||||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||||
FunctionComparisonProvider functionComparisonProvider =
|
FunctionComparisonProvider functionComparisonProvider =
|
||||||
providerMgr.showFunctionComparisonProvider(functions);
|
providerMgr.compareFunctions(f1, f2);
|
||||||
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();
|
waitForSwing();
|
||||||
}
|
|
||||||
catch (DuplicateNameException e) {
|
captureProvider(functionComparisonProvider);
|
||||||
e.printStackTrace();
|
crop(new Rectangle(261, 2, 24, 24));
|
||||||
}
|
|
||||||
catch (InvalidInputException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
catch (MemoryAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
destinationProgram.endTransaction(txId2, false);
|
|
||||||
sourceProgram.endTransaction(txId1, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiFunctionComparisonWindow() {
|
public void testRemoveFromComparisonIcon() {
|
||||||
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));
|
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);
|
|
||||||
|
|
||||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
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 =
|
FunctionComparisonProviderManager providerMgr =
|
||||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||||
FunctionComparisonProvider functionComparisonProvider =
|
FunctionComparisonProvider functionComparisonProvider =
|
||||||
providerMgr.showFunctionComparisonProvider(functions);
|
providerMgr.compareFunctions(f1, f2);
|
||||||
FunctionComparisonPanel functionComparisonPanel =
|
|
||||||
functionComparisonProvider.getComponent();
|
|
||||||
runSwing(() -> {
|
|
||||||
functionComparisonPanel.setCurrentTabbedComponent("Listing View");
|
|
||||||
ListingCodeComparisonPanel dualListing =
|
|
||||||
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
|
|
||||||
ListingPanel leftPanel = dualListing.getLeftPanel();
|
|
||||||
leftPanel.goTo(addr(0x004119a5));
|
|
||||||
|
|
||||||
});
|
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 598);
|
|
||||||
}
|
captureProvider(functionComparisonProvider);
|
||||||
catch (DuplicateNameException | InvalidInputException | MemoryAccessException e) {
|
crop(new Rectangle(351, 2, 24, 24));
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
destinationProgram.endTransaction(txId2, false);
|
|
||||||
sourceProgram.endTransaction(txId1, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
FunctionManager functionManager = program1.getFunctionManager();
|
||||||
return functionManager.getFunctionAt(entryPoint);
|
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);
|
Address address = addr(addressAsLong);
|
||||||
DisassembleCommand cmd = new DisassembleCommand(address,
|
DisassembleCommand cmd = new DisassembleCommand(address,
|
||||||
new AddressSet(address, address.add(length - 1)), followFlows);
|
new AddressSet(address, address.add(length - 1)), followFlows);
|
||||||
|
|