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/user.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/help/help/topics/FunctionComparison/FunctionComparison.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/AddFunctionsPanel.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/AddToComparisonIcon.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/FunctionComparisonWindow.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/FunctionScope.gif||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/ListingCodeComparisonOptions.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/MultiFunctionComparisonWindow.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/NavNextIcon.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/NavPreviousIcon.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/RemoveFromComparisonIcon.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/binaryData.gif||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/class.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/cursor_arrow_flipped.gif||GHIDRA||||END|
|
||||
@ -946,6 +951,7 @@ src/main/resources/images/arrow_up.png||FAMFAMFAM Icons - CC 2.5|||famfamfam sil
|
||||
src/main/resources/images/binaryData.gif||GHIDRA||||END|
|
||||
src/main/resources/images/browser.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
||||
src/main/resources/images/bullet_green.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/resources/images/bullet_star.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/resources/images/cUnion.png||GHIDRA||||END|
|
||||
src/main/resources/images/camera-photo.png||Tango Icons - Public Domain|||tango|END|
|
||||
src/main/resources/images/carry.png||GHIDRA||||END|
|
||||
|
@ -11,38 +11,21 @@
|
||||
<H1><A name="FunctionComparisonPlugin"></A> <A name="Function_Comparison"></A> <A name=
|
||||
"FunctionComparison"></A> Function Comparison Window</H1>
|
||||
|
||||
<P>The Function Comparison window can provide different types of panels for visually comparing
|
||||
two or more functions. The Listing View of the Function Comparison window and its capabilities
|
||||
is explained below.</P>
|
||||
<P>The Function Comparison window provides a way to compare two or more
|
||||
functions in a simple side-by-side panel. </P>
|
||||
|
||||
<H2><A name="Compare Selected Functions"></A>Compare Selected Functions</H2>
|
||||
<P>To begin, select a function (or multiple functions) from the listing or
|
||||
the function table. Then right-click and select the <b>Compare Selected
|
||||
Functions</b> option.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>A Function Comparison window can be displayed from a selection containing two or more
|
||||
functions in the CodeBrowser listing.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<OL>
|
||||
<LI>In the CodeBrowser Listing make a selection containing two or more functions that you
|
||||
want to compare.</LI>
|
||||
|
||||
<LI>Right mouse click and select the <IMG alt="" src="images/page_white_c.png"> <B>Compare
|
||||
Selected Functions...</B> option on the right mouse popup menu.</LI>
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P><A name="Dual_Listing"></A>A Function Comparison window will appear.</P>
|
||||
<P><A name="Dual_Listing"></A>A new function comparison window will appear (subsequent
|
||||
invocations of this option will create a new tab in the existing window).</P>
|
||||
<BR><BR>
|
||||
|
||||
<CENTER>
|
||||
<IMG alt="" border="0" src="images/FunctionComparisonWindow.png">
|
||||
</CENTER><BR>
|
||||
<BR>
|
||||
|
||||
|
||||
<P>This window can have multiple tabs, one for each type of function comparison view that is
|
||||
available.</P>
|
||||
|
||||
<H2><A name="Listing_View"></A>Listing View</H2>
|
||||
|
||||
@ -426,87 +409,40 @@
|
||||
<H2><A name="Compare Multiple Functions"></A>Comparing Multiple Functions</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>When comparing more than two functions, a Function Comparison window will have additional
|
||||
components at the top of it. This window displays two functions side by side again, but has
|
||||
two choice components at the top of the panel, which indicates the current function being
|
||||
displayed in each side (left and right) of the panel. Each choice component can display a
|
||||
list indicating the functions that can be displayed in the listing below it. Only one
|
||||
function at a a time from the list can be displayed below it based on the type of view
|
||||
currently set. You can simply change the current function for either side of the function
|
||||
comparison using the choice component in order to get a different function comparison.</P>
|
||||
<P>There is no limit to the number of functions that can be compared. Users may
|
||||
add and remove functions from each comparison panel as desired. Simply use the pulldowns
|
||||
above the listing panels to change what is being compared.</P>
|
||||
|
||||
<P>There are two toolbar actions with quick keys that allow you to change to the
|
||||
next/previous function for the side that has focus as indicated by the pink border.</P>
|
||||
<P>The following toolbar options are available:</P>
|
||||
|
||||
<BR><BR>
|
||||
<CENTER>
|
||||
<IMG alt="" border="0" src="images/MultiFunctionComparisonWindow.png">
|
||||
</CENTER><BR>
|
||||
<BR>
|
||||
<blockquote>
|
||||
<h3><IMG src="images/AddToComparisonIcon.png" border="0"> <a name="Add_To_Comparison"></a>Add To Existing Comparison</h3>
|
||||
<p>Allows the user to add functions to the current comparison window. When
|
||||
selected, the following table containing all functions in the current program is
|
||||
displayed:</p>
|
||||
<p>
|
||||
<IMG src="images/AddFunctionsPanel.png" border="0">
|
||||
</p>
|
||||
<p>Select the functions to be added and hit the <b>OK</b>
|
||||
button; the selected functions will be available in both the left and right
|
||||
sides of the comparison window.
|
||||
</p>
|
||||
|
||||
<h3><IMG src="images/RemoveFromComparisonIcon.png" border="0"> <a name="Remove_From_Comparison"></a>Remove Function From Comparison</h3>
|
||||
<p>Removes the function in the focused panel from the comparison. This
|
||||
will remove the function from both the source and target selection pulldowns.</p>
|
||||
|
||||
<h3><IMG src="images/NavNextIcon.png" border="0"> <a name="Navigate_Next"></a>Go To Next Function</h3>
|
||||
<p>Navigates to the next available function in the selection pulldown</p>
|
||||
|
||||
<h3><IMG src="images/NavPreviousIcon.png" border="0"> <a name="Navigate_Previous"></a>Go To Previous Function</h3>
|
||||
<p>Navigates to the previous available function in the selection pulldown</p>
|
||||
|
||||
<p><IMG src="../../shared/note.png"> The Remove and Go To actions described
|
||||
above will operate on the comparison panel that has focus, identified by the
|
||||
pink border.</p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
<H3>Multi-Function Comparison Actions</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>There are two additional actions that are available when you are comparing more than
|
||||
just two functions. The following describes them.</P>
|
||||
|
||||
<H4><A name="Compare_The_Next_Function"></A>Compare The Next Function</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The Function Comparison window has an icon (<IMG alt="" src=
|
||||
"images/FunctionScope.gif"><IMG alt="" src="images/arrow_down.png">) on the tool bar to
|
||||
change the function being compared to the next one in the list for the side that
|
||||
currently has focus.</P>
|
||||
|
||||
<OL>
|
||||
<LI>Make sure the side (left or right) has focus for the function you want to change to
|
||||
the next one. The side with focus will have the pink border around it. It if doesn't,
|
||||
you can mouse click in the other side to get the focused side to change or you can use
|
||||
the Tab key to move the focus until it moves the pink border around the correct
|
||||
side.</LI>
|
||||
|
||||
<LI>Right mouse click and select the <IMG alt="" src="images/FunctionScope.gif"><IMG
|
||||
alt="" src="images/arrow_down.png"> <B>Compare The Next Function</B> option, OR select
|
||||
the <IMG alt="" src="images/FunctionScope.gif"><IMG alt="" src="images/arrow_down.png">
|
||||
button on the tool bar.</LI>
|
||||
</OL>
|
||||
|
||||
<P>The function should change to the next one available in the list from the choice
|
||||
component. The differences will be recomputed for the new function comparison that is now
|
||||
displayed. If the currently displayed function is at the end of the list, a message will
|
||||
display in the tool's status area indicating there isn't a next function.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4><A name="Compare_The_Previous_Function"></A>Compare The Previous Function</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The Function Comparison window has an icon (<IMG alt="" src=
|
||||
"images/FunctionScope.gif"><IMG alt="" src="images/arrow_up.png">) on the tool bar to
|
||||
change the function being compared to the previous one in the list for the side that
|
||||
currently has focus.</P>
|
||||
|
||||
<OL>
|
||||
<LI>Make sure the side (left or right) has focus for the function you want to change
|
||||
to the previous one. The side with focus will have the pink border around it. It if
|
||||
doesn't, you can mouse click in the other side to get the focused side to change or
|
||||
you can use the Tab key to move the focus until it moves the pink border around the
|
||||
correct side.</LI>
|
||||
|
||||
<LI>Right mouse click and select the <IMG alt="" src="images/FunctionScope.gif"><IMG
|
||||
alt="" src="images/arrow_up.png"> <B>Compare The Previous Function</B> option, OR
|
||||
select the <IMG alt="" src="images/FunctionScope.gif"><IMG alt="" src=
|
||||
"images/arrow_up.png"> button on the tool bar.</LI>
|
||||
</OL>
|
||||
|
||||
<P>The function should change to the previous one available in the list from the choice
|
||||
component. The differences will be recomputed for the new function comparison that is
|
||||
now displayed. If the currently displayed function is at the beginning of the list, a
|
||||
message will display in the tool's status area indicating there isn't a previous
|
||||
function.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Other Function Comparison Actions</H3>
|
||||
|
||||
<P>The following are additional actions that are available in the Function Comparison
|
||||
@ -582,9 +518,7 @@
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE><BR>
|
||||
<BR>
|
||||
|
||||
|
||||
<P class="providedbyplugin">Provided By: <I>FunctionComparisonPlugin</I></P>
|
||||
|
||||
<P class="relatedtopic">Related Topics:</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>
|
||||
<blockquote>
|
||||
<p>The Functions window has an icon (<img src="images/page_white_c.png">)
|
||||
on the tool bar to compare the functions whose rows are currently selected in the table.
|
||||
</p>
|
||||
<p>The Functions window has an icon (<img src="images/page_white_c.png"> which contains a star
|
||||
<img src="images/bullet_star.png">) on the tool bar that allows users to compare
|
||||
the functions currently selected in the table.</p>
|
||||
<p><img border="0" src="../../shared/note.png"> Note that selecting this
|
||||
will always create a new comparison. If you have an existing comparison
|
||||
and wish to add functions to it, you must initiate that directly from the
|
||||
existing comparison window itself.</p>
|
||||
<p>To create a new comparison:</p>
|
||||
<ol>
|
||||
<li> Select the functions you want to compare from the table in the Functions window.
|
||||
You must select two or more rows in the table.</li>
|
||||
<li>Right mouse click and select the <img src="images/page_white_c.png">
|
||||
<b>Compare Selected Functions</b> option, OR select the <img src="images/page_white_c.png">
|
||||
button on the tool bar.</li>
|
||||
<li>Select the functions you want to compare from the table in the Functions window.
|
||||
You must select one or more rows in the table.</li>
|
||||
<li>Right mouse click and select the <b>Compare Selected Functions</b> option,
|
||||
OR click the create-comparison icon on the tool bar.</li>
|
||||
</ol>
|
||||
<p>This will display a
|
||||
<a href="help/topics/FunctionComparison/FunctionComparison.htm">Function Comparison</a>
|
||||
window which allows the functions to be viewed
|
||||
side by side.</p>
|
||||
|
||||
<p>The resulting <a href="help/topics/FunctionComparison/FunctionComparison.htm">Function Comparison</a>
|
||||
window will allow the functions to be viewed side by side.</p>
|
||||
|
||||
</blockquote>
|
||||
|
||||
<p> </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;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import docking.ComponentProviderActivationListener;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.events.ProgramClosedPluginEvent;
|
||||
import ghidra.app.events.*;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.app.plugin.core.functioncompare.actions.CompareFunctionsAction;
|
||||
import ghidra.app.plugin.core.functioncompare.actions.CompareFunctionsFromListingAction;
|
||||
import ghidra.app.services.FunctionComparisonService;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ChangeManager;
|
||||
import ghidra.program.util.ProgramChangeRecord;
|
||||
|
||||
/**
|
||||
* Plugin that provides the actions that allow the user to compare functions using a
|
||||
* FunctionComparisonPanel.
|
||||
* Allows users to create function comparisons that are displayed
|
||||
* side-by-side in a provider. Comparisons can be initiated via the listing
|
||||
* or function table and are displayed in a {@link FunctionComparisonProvider}.
|
||||
* <p>
|
||||
* The underlying data backing the comparison provider is managed by the
|
||||
* {@link FunctionComparisonService}.
|
||||
*/
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
@ -36,36 +48,36 @@ import ghidra.program.model.listing.Program;
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.DIFF,
|
||||
shortDescription = "Compare Functions",
|
||||
description = "This plugin provides actions that allow you to compare two or more functions with each other.",
|
||||
eventsConsumed = { ProgramClosedPluginEvent.class }
|
||||
description = "Allows users to compare two or more functions",
|
||||
servicesProvided = { FunctionComparisonService.class },
|
||||
eventsConsumed = { ProgramSelectionPluginEvent.class, ProgramActivatedPluginEvent.class,
|
||||
ProgramClosedPluginEvent.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class FunctionComparisonPlugin extends ProgramPlugin implements DomainObjectListener {
|
||||
public class FunctionComparisonPlugin extends ProgramPlugin
|
||||
implements DomainObjectListener, FunctionComparisonService {
|
||||
|
||||
public final static String FUNCTION_MENU_SUBGROUP = "Function";
|
||||
static final String MENU_PULLRIGHT = "CompareFunctions";
|
||||
static final String POPUP_MENU_GROUP = "CompareFunction";
|
||||
|
||||
private FunctionComparisonProviderManager functionComparisonManager;
|
||||
|
||||
/**
|
||||
* Creates a plugin that provides actions for comparing functions.
|
||||
* @param tool the tool that owns this plugin.
|
||||
* Constructor
|
||||
*
|
||||
* @param tool the tool that owns this plugin
|
||||
*/
|
||||
public FunctionComparisonPlugin(PluginTool tool) {
|
||||
super(tool, true, true);
|
||||
|
||||
functionComparisonManager = new FunctionComparisonProviderManager(this);
|
||||
|
||||
tool.setMenuGroup(new String[] { MENU_PULLRIGHT }, POPUP_MENU_GROUP);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
createActions();
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
tool.addAction(new CompareFunctionsAction(this));
|
||||
CompareFunctionsAction compareFunctionsAction = new CompareFunctionsFromListingAction(tool);
|
||||
tool.addAction(compareFunctionsAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -85,18 +97,69 @@ public class FunctionComparisonPlugin extends ProgramPlugin implements DomainObj
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a panel for comparing the specified functions.
|
||||
* @param functions the functions that are used to populate both the left and right side
|
||||
* of the function comparison panel.
|
||||
* Overridden to listen for two event types:
|
||||
* <li>Object Restored: In the event of a redo/undo that affects a function
|
||||
* being shown in the comparison provider, this will allow tell the provider
|
||||
* to reload</li>
|
||||
* <li>Object Removed: If a function is deleted, this will tell the provider
|
||||
* to purge it from the view</li>
|
||||
*/
|
||||
void showFunctionComparisonProvider(Function[] functions) {
|
||||
functionComparisonManager.showFunctionComparisonProvider(functions);
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
for (int i = 0; i < ev.numRecords(); ++i) {
|
||||
DomainObjectChangeRecord doRecord = ev.getChangeRecord(i);
|
||||
|
||||
int eventType = doRecord.getEventType();
|
||||
|
||||
switch (eventType) {
|
||||
case DomainObject.DO_OBJECT_RESTORED:
|
||||
functionComparisonManager.domainObjectRestored(ev);
|
||||
break;
|
||||
case ChangeManager.DOCR_FUNCTION_REMOVED:
|
||||
ProgramChangeRecord rec = (ProgramChangeRecord) ev.getChangeRecord(i);
|
||||
Function function = (Function) rec.getObject();
|
||||
if (function != null) {
|
||||
removeFunction(function);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
|
||||
functionComparisonManager.domainObjectRestored(ev);
|
||||
}
|
||||
public void addFunctionComparisonProviderListener(
|
||||
ComponentProviderActivationListener listener) {
|
||||
functionComparisonManager.addProviderListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFunction(Function function) {
|
||||
functionComparisonManager.removeFunction(function);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFunction(Function function, FunctionComparisonProvider provider) {
|
||||
functionComparisonManager.removeFunction(function, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionComparisonProvider compareFunctions(Function source, Function target) {
|
||||
return functionComparisonManager.compareFunctions(source, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compareFunctions(Set<Function> functions, FunctionComparisonProvider provider) {
|
||||
functionComparisonManager.compareFunctions(functions, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionComparisonProvider compareFunctions(Set<Function> functions) {
|
||||
return functionComparisonManager.compareFunctions(functions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compareFunctions(Function source, Function target,
|
||||
FunctionComparisonProvider provider) {
|
||||
functionComparisonManager.compareFunctions(source, target, provider);
|
||||
}
|
||||
}
|
||||
|
@ -18,140 +18,75 @@ package ghidra.app.plugin.core.functioncompare;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingTool;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.actions.PopupActionProvider;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
import ghidra.app.services.FunctionComparisonModel;
|
||||
import ghidra.app.services.FunctionComparisonService;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingCodeComparisonPanel;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.app.util.viewer.util.CodeComparisonPanel;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* This is the dockable provider that displays a FunctionComparisonPanel.
|
||||
* Dockable provider that displays function comparisons Clients create/modify
|
||||
* these comparisons using the {@link FunctionComparisonService}, which in turn
|
||||
* creates instances of this provider as-needed.
|
||||
*/
|
||||
public class FunctionComparisonProvider extends ComponentProviderAdapter implements PopupActionProvider {
|
||||
public class FunctionComparisonProvider extends ComponentProviderAdapter
|
||||
implements PopupActionProvider, FunctionComparisonModelListener {
|
||||
|
||||
private static final String HELP_TOPIC = "FunctionComparison";
|
||||
private static final Icon ICON = ResourceManager.loadImage("images/page_white_c.png");
|
||||
private FunctionComparisonPanel functionComparisonPanel;
|
||||
private FunctionComparisonProviderListener listener;
|
||||
protected static final String HELP_TOPIC = "FunctionComparison";
|
||||
protected FunctionComparisonPanel functionComparisonPanel;
|
||||
protected Plugin plugin;
|
||||
|
||||
/** Contains all the comparison data to be displayed by this provider */
|
||||
protected FunctionComparisonModel model;
|
||||
|
||||
/**
|
||||
* Creates a provider for displaying a FunctionComparisonPanel that allows two or more
|
||||
* functions to be compared. This constructor will load the functions so they are available
|
||||
* for display in both the left side and right side of the panel. By default the first function
|
||||
* will be loaded into the left side and the second function will be loaded in the right side.
|
||||
* @param plugin the plugin that owns this provider.
|
||||
* @param functions the functions that are used to populate both the left and right side
|
||||
* of the function comparison panel.
|
||||
* @param listener the listener to notify when the provider is closing.
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin the active plugin
|
||||
* @param name the providers name; used to group similar providers into a tab within
|
||||
* the same window
|
||||
* @param owner the provider owner, usually a plugin name
|
||||
*/
|
||||
public FunctionComparisonProvider(Plugin plugin, Function[] functions,
|
||||
FunctionComparisonProviderListener listener) {
|
||||
super(plugin.getTool(), "Function Comparison", plugin.getName());
|
||||
this.listener = listener;
|
||||
if (ICON != null) {
|
||||
setIcon(ICON);
|
||||
}
|
||||
functionComparisonPanel = new MultiFunctionComparisonPanel(this, tool, functions);
|
||||
public FunctionComparisonProvider(Plugin plugin, String name, String owner) {
|
||||
this(plugin, name, owner, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin the active plugin
|
||||
* @param name the providers name; used to group similar providers into a tab within
|
||||
* the same window
|
||||
* @param owner the provider owner, usually a plugin name
|
||||
* @param contextType the type of context supported by this provider; may be null
|
||||
*/
|
||||
public FunctionComparisonProvider(Plugin plugin, String name, String owner,
|
||||
Class<?> contextType) {
|
||||
super(plugin.getTool(), name, owner, contextType);
|
||||
this.plugin = plugin;
|
||||
model = new FunctionComparisonModel();
|
||||
model.addFunctionComparisonModelListener(this);
|
||||
functionComparisonPanel = getComponent();
|
||||
initFunctionComparisonPanel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a provider for displaying two or more functions to be compared. This will load the
|
||||
* functions so the leftFunctions are available for display in the left side and the
|
||||
* rightFunctions are available for display in the right side of the function comparison panel.
|
||||
* By default the first function from each array will be the one initially displayed in its
|
||||
* associated side.
|
||||
* @param plugin the plugin that owns this provider.
|
||||
* @param leftFunctions the functions that are used to populate the left side
|
||||
* @param rightFunctions the functions that are used to populate the right side
|
||||
* @param listener the listener to notify when the provider is closing.
|
||||
*/
|
||||
public FunctionComparisonProvider(Plugin plugin, Function[] leftFunctions,
|
||||
Function[] rightFunctions, FunctionComparisonProviderListener listener) {
|
||||
super(plugin.getTool(), "FunctionComparison", plugin.getName());
|
||||
this.listener = listener;
|
||||
if (ICON != null) {
|
||||
setIcon(ICON);
|
||||
}
|
||||
functionComparisonPanel =
|
||||
new MultiFunctionComparisonPanel(this, tool, leftFunctions, rightFunctions);
|
||||
initFunctionComparisonPanel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a provider for displaying two or more functions to be compared. This will load
|
||||
* the functions so the leftFunctions are available for display in the left side. For
|
||||
* each left side function there is a list of right side functions for comparison.
|
||||
* rightFunctions are available for display in the right side of the function comparison panel.
|
||||
* By default the first function from each array will be the one initially displayed in its
|
||||
* associated side.
|
||||
* @param plugin the plugin that owns this provider.
|
||||
* @param functionMap maps each left function to its own set of right functions for comparison.
|
||||
* @param listener the listener to notify when the provider is closing.
|
||||
*/
|
||||
public FunctionComparisonProvider(Plugin plugin,
|
||||
HashMap<Function, HashSet<Function>> functionMap,
|
||||
FunctionComparisonProviderListener listener) {
|
||||
|
||||
super(plugin.getTool(), "FunctionComparison", plugin.getName());
|
||||
this.listener = listener;
|
||||
if (ICON != null) {
|
||||
setIcon(ICON);
|
||||
}
|
||||
functionComparisonPanel = new MappedFunctionComparisonPanel(this, tool, functionMap);
|
||||
initFunctionComparisonPanel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform initialization for this provider and its panel. This includes setting the
|
||||
* tab text, getting actions, establishing the popup listener, and specifying help.
|
||||
*/
|
||||
private void initFunctionComparisonPanel() {
|
||||
setTransient();
|
||||
setTabText(functionComparisonPanel);
|
||||
addSpecificCodeComparisonActions();
|
||||
tool.addPopupActionProvider(this);
|
||||
setHelpLocation(new HelpLocation(HELP_TOPIC, "Function Comparison"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the text that is displayed on the tab for this provider.
|
||||
* @param functionCompPanel the function comparison panel for this provider.
|
||||
*/
|
||||
private void setTabText(FunctionComparisonPanel functionCompPanel) {
|
||||
Function leftFunction = functionCompPanel.getLeftFunction();
|
||||
Function rightFunction = functionCompPanel.getRightFunction();
|
||||
String tabText = (leftFunction == null && rightFunction == null) ? "No Functions Yet"
|
||||
: getTabText(leftFunction, rightFunction);
|
||||
setTabText(tabText);
|
||||
}
|
||||
|
||||
private String getTabText(Function function1, Function function2) {
|
||||
return ((function1 != null) ? function1.getName() : "none") + " & " +
|
||||
((function2 != null) ? function2.getName() : "none");
|
||||
}
|
||||
|
||||
private void addSpecificCodeComparisonActions() {
|
||||
DockingAction[] actions = functionComparisonPanel.getCodeComparisonActions();
|
||||
for (DockingAction dockingAction : actions) {
|
||||
addLocalAction(dockingAction);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionComparisonPanel getComponent() {
|
||||
if (functionComparisonPanel == null) {
|
||||
functionComparisonPanel = new FunctionComparisonPanel(this, tool, null, null);
|
||||
}
|
||||
return functionComparisonPanel;
|
||||
}
|
||||
|
||||
@ -161,6 +96,8 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
|
||||
buff.append("FunctionComparisonProvider\n");
|
||||
buff.append("Name: ");
|
||||
buff.append(getName() + "\n");
|
||||
buff.append("Tab Text: ");
|
||||
buff.append(getTabText() + "\n");
|
||||
Function leftFunction = functionComparisonPanel.getLeftFunction();
|
||||
String leftName = (leftFunction != null) ? leftFunction.getName() : "No Function";
|
||||
buff.append("Function 1: " + leftName + "\n");
|
||||
@ -181,44 +118,15 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
|
||||
@Override
|
||||
public void removeFromTool() {
|
||||
tool.removePopupActionProvider(this);
|
||||
|
||||
super.removeFromTool();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeComponent() {
|
||||
super.closeComponent();
|
||||
|
||||
if (listener != null) {
|
||||
listener.providerClosed(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the specified program has been closed, so this provider can close its
|
||||
* component, if any of its functions were from that program.
|
||||
* @param program the program that was closed.
|
||||
*/
|
||||
public void programClosed(Program program) {
|
||||
// For now close the panel if it has any functions with this program.
|
||||
Function[] functions = functionComparisonPanel.getFunctions();
|
||||
for (Function function : functions) {
|
||||
if ((function != null) && function.getProgram() == program) {
|
||||
closeComponent();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the specified program has been restored, so this can refresh the code
|
||||
* comparison panel.
|
||||
* @param program the program that was restored (undo/redo).
|
||||
*/
|
||||
public void programRestored(Program program) {
|
||||
CodeComparisonPanel<? extends FieldPanelCoordinator> comparePanel =
|
||||
functionComparisonPanel.getCurrentComponent();
|
||||
comparePanel.programRestored(program);
|
||||
public void modelChanged(List<FunctionComparison> model) {
|
||||
this.model.setComparisons(model);
|
||||
functionComparisonPanel.reload();
|
||||
setTabText(functionComparisonPanel);
|
||||
closeIfEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -235,7 +143,72 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the function comparison provider's components to the indicated saved configuration state.
|
||||
* Returns the comparison model
|
||||
*
|
||||
* @return the comparison model
|
||||
*/
|
||||
public FunctionComparisonModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the comparison model with the one provided
|
||||
*
|
||||
* @param model the comparison model
|
||||
*/
|
||||
public void setModel(FunctionComparisonModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any functions being displayed by this provider that are from
|
||||
* the given program. If there are no functions left to display, the
|
||||
* provider is closed.
|
||||
*
|
||||
* @param program the program being closed
|
||||
*/
|
||||
public void programClosed(Program program) {
|
||||
model.removeFunctions(program);
|
||||
closeIfEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all functions for the specified program from the comparison
|
||||
* model
|
||||
*
|
||||
* @param program the program whose functions require removal
|
||||
*/
|
||||
public void removeFunctions(Program program) {
|
||||
model.removeFunctions(program);
|
||||
closeIfEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the set of functions from the comparison model
|
||||
*
|
||||
* @param functions the functions to remove
|
||||
*/
|
||||
public void removeFunctions(Set<Function> functions) {
|
||||
functions.stream().forEach(f -> model.removeFunction(f));
|
||||
closeIfEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the specified program has been restored, so the
|
||||
* comparison panel should be refreshed
|
||||
*
|
||||
* @param program the program that was restored (undo/redo)
|
||||
*/
|
||||
public void programRestored(Program program) {
|
||||
CodeComparisonPanel<? extends FieldPanelCoordinator> comparePanel =
|
||||
functionComparisonPanel.getCurrentComponent();
|
||||
comparePanel.programRestored(program);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the function comparison providers components to the indicated
|
||||
* saved configuration state
|
||||
*
|
||||
* @param saveState the configuration state to restore
|
||||
*/
|
||||
public void readConfigState(SaveState saveState) {
|
||||
@ -243,10 +216,78 @@ public class FunctionComparisonProvider extends ComponentProviderAdapter impleme
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current configuration state of the components that compose the function comparison provider.
|
||||
* Saves the current configuration state of the components that compose
|
||||
* the function comparison provider
|
||||
*
|
||||
* @param saveState the new configuration state
|
||||
*/
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
functionComparisonPanel.writeConfigState(getName(), saveState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the text that is displayed on the tab for this provider
|
||||
*
|
||||
* @param comparisonPanel the function comparison panel for this provider
|
||||
*/
|
||||
public void setTabText(FunctionComparisonPanel comparisonPanel) {
|
||||
Function leftFunction = comparisonPanel.getLeftFunction();
|
||||
Function rightFunction = comparisonPanel.getRightFunction();
|
||||
String tabText = (leftFunction == null && rightFunction == null) ? "No Functions Yet"
|
||||
: getTabText(leftFunction, rightFunction);
|
||||
setTabText(tabText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform initialization for this provider and its panel
|
||||
*/
|
||||
protected void initFunctionComparisonPanel() {
|
||||
setTransient();
|
||||
setTabText(functionComparisonPanel);
|
||||
addSpecificCodeComparisonActions();
|
||||
tool.addPopupActionProvider(this);
|
||||
setHelpLocation(new HelpLocation(HELP_TOPIC, "Function Comparison"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the comparison panel is empty
|
||||
*
|
||||
* @return true if the panel is empty
|
||||
*/
|
||||
boolean isEmpty() {
|
||||
return functionComparisonPanel.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this provider if there are no comparisons to view
|
||||
*/
|
||||
void closeIfEmpty() {
|
||||
if (isEmpty()) {
|
||||
closeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text that should be displayed in the tab, given the two
|
||||
* left/right functions
|
||||
*
|
||||
* @param leftFunction
|
||||
* @param rightFunction
|
||||
* @return the tab text
|
||||
*/
|
||||
private String getTabText(Function leftFunction, Function rightFunction) {
|
||||
return ((leftFunction != null) ? leftFunction.getName() : "none") + " & " +
|
||||
((rightFunction != null) ? rightFunction.getName() : "none");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets actions specific to the code comparison panel and adds them to this
|
||||
* provider
|
||||
*/
|
||||
private void addSpecificCodeComparisonActions() {
|
||||
DockingAction[] actions = functionComparisonPanel.getCodeComparisonActions();
|
||||
for (DockingAction dockingAction : actions) {
|
||||
addLocalAction(dockingAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,13 +16,22 @@
|
||||
package ghidra.app.plugin.core.functioncompare;
|
||||
|
||||
/**
|
||||
* Listener for a FunctionComparisonProvider.
|
||||
* Allows subscribers to register for function comparison provider changes
|
||||
* (eg: when the provider is opened/closed)
|
||||
*/
|
||||
public interface FunctionComparisonProviderListener {
|
||||
|
||||
/**
|
||||
* Notification method that will get called when the provider is being closed.
|
||||
* @param provider the provider that is being closed.
|
||||
* Invoked when the provider is being closed
|
||||
*
|
||||
* @param provider the closed provider
|
||||
*/
|
||||
public void providerClosed(FunctionComparisonProvider provider);
|
||||
|
||||
/**
|
||||
* Invoked when the provider is being opened
|
||||
*
|
||||
* @param provider the opened provider
|
||||
*/
|
||||
public void providerOpened(FunctionComparisonProvider provider);
|
||||
}
|
||||
|
@ -15,219 +15,199 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.functioncompare;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import docking.ComponentProviderActivationListener;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* FunctionComparisonProviderManager allows a plugin to display function comparison panels.
|
||||
* It responds to program close events and closes any panels that have a function from the
|
||||
* closed program. It also updates the displays if needed due to an Undo.
|
||||
* Provides access to all open {@link FunctionComparisonProvider comparison providers}
|
||||
* and allows users to do the following:
|
||||
* <li>create new providers</li>
|
||||
* <li>add comparisons to existing providers</li>
|
||||
* <li>remove comparisons</li>
|
||||
* <li>notify subscribers when providers are opened/closed</li>
|
||||
*/
|
||||
public class FunctionComparisonProviderManager implements FunctionComparisonProviderListener {
|
||||
|
||||
private HashSet<FunctionComparisonProvider> functionComparisonProviders = new HashSet<>();
|
||||
private ProgramPlugin plugin;
|
||||
private PluginTool tool;
|
||||
private Set<FunctionComparisonProvider> providers = new CopyOnWriteArraySet<>();
|
||||
private Set<ComponentProviderActivationListener> listeners = new HashSet<>();
|
||||
private Plugin plugin;
|
||||
|
||||
/**
|
||||
* Constructs a FunctionComparisonProviderManager.
|
||||
* @param plugin the plugin that owns this manager.
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin the parent plugin
|
||||
*/
|
||||
public FunctionComparisonProviderManager(ProgramPlugin plugin) {
|
||||
public FunctionComparisonProviderManager(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
tool = plugin.getTool();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will create a new function comparison panel with the specified functions available for
|
||||
* display in both the left and right side of the panel. Initially the function comparison panel
|
||||
* will display the first function in the left side and the second function in the right side of
|
||||
* the panel. If the manager already has a provider to display the specified functions it will
|
||||
* be brought to the front instead of creating a new panel.
|
||||
* @param functions the functions that are used to populate both the left and right side
|
||||
* of the function comparison panel.
|
||||
* @return the FunctionComparisonProvider that is displaying these functions.
|
||||
*/
|
||||
public FunctionComparisonProvider showFunctionComparisonProvider(Function[] functions) {
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
findFunctionComparisonProvider(functions);
|
||||
if (functionComparisonProvider != null) {
|
||||
// If it is already displayed then bring it to the front.
|
||||
tool.toFront(functionComparisonProvider);
|
||||
return functionComparisonProvider;
|
||||
}
|
||||
FunctionComparisonProvider provider =
|
||||
new FunctionComparisonProvider(plugin, functions, this);
|
||||
functionComparisonProviders.add(provider);
|
||||
provider.setVisible(true);
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a new function comparison panel with the specified leftFunctions available for
|
||||
* display in the left side of the panel and the rightFunctions available for display in the right side.
|
||||
* Initially the function comparison panel will display the first leftFunction and the first
|
||||
* rightFunction. If the manager already has a provider to display the specified leftFunctions
|
||||
* and rightFunctions it will be brought to the front instead of creating a new panel.
|
||||
* @param leftFunctions the functions that are used to populate the left side
|
||||
* @param rightFunctions the functions that are used to populate the right side
|
||||
* @return the FunctionComparisonProvider that is displaying these functions.
|
||||
*/
|
||||
public FunctionComparisonProvider showFunctionComparisonProvider(Function[] leftFunctions,
|
||||
Function[] rightFunctions) {
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
findFunctionComparisonProvider(leftFunctions, rightFunctions);
|
||||
if (functionComparisonProvider != null) {
|
||||
// If it is already displayed then bring it to the front.
|
||||
tool.toFront(functionComparisonProvider);
|
||||
return functionComparisonProvider;
|
||||
}
|
||||
FunctionComparisonProvider provider =
|
||||
new FunctionComparisonProvider(plugin, leftFunctions, rightFunctions, this);
|
||||
functionComparisonProviders.add(provider);
|
||||
provider.setVisible(true);
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a new function comparison panel with the specified left functions
|
||||
* available for display in the left side of the panel and a list of functions for the
|
||||
* right side for each function in the left.
|
||||
* If the manager already has a provider to display the specified function map it will
|
||||
* be brought to the front instead of creating a new panel.
|
||||
* @param functionMap map of the functions that are used to populate both the left and
|
||||
* right side of the function comparison panel.
|
||||
* @return the FunctionComparisonProvider that is displaying these functions.
|
||||
*/
|
||||
public FunctionComparisonProvider showFunctionComparisonProvider(
|
||||
HashMap<Function, HashSet<Function>> functionMap) {
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
findFunctionComparisonProvider(functionMap);
|
||||
if (functionComparisonProvider != null) {
|
||||
// If it is already displayed then bring it to the front.
|
||||
tool.toFront(functionComparisonProvider);
|
||||
return functionComparisonProvider;
|
||||
}
|
||||
FunctionComparisonProvider provider =
|
||||
new FunctionComparisonProvider(plugin, functionMap, this);
|
||||
functionComparisonProviders.add(provider);
|
||||
provider.setVisible(true);
|
||||
return provider;
|
||||
}
|
||||
|
||||
private FunctionComparisonProvider findFunctionComparisonProvider(Function[] functions) {
|
||||
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
|
||||
FunctionComparisonPanel functionComparisonPanel =
|
||||
functionComparisonProvider.getComponent();
|
||||
if (functionComparisonPanel instanceof MultiFunctionComparisonPanel) {
|
||||
MultiFunctionComparisonPanel multiPanel =
|
||||
(MultiFunctionComparisonPanel) functionComparisonPanel;
|
||||
if (multiPanel.matchesTheseFunctions(functions, functions)) {
|
||||
return functionComparisonProvider;
|
||||
}
|
||||
}
|
||||
else { // basic FunctionComparisonPanel
|
||||
Function[] panelFunctions = functionComparisonPanel.getFunctions();
|
||||
if (Arrays.equals(panelFunctions, functions)) {
|
||||
return functionComparisonProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private FunctionComparisonProvider findFunctionComparisonProvider(Function[] functionsL,
|
||||
Function[] functionsR) {
|
||||
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
|
||||
FunctionComparisonPanel functionComparisonPanel =
|
||||
functionComparisonProvider.getComponent();
|
||||
if (!(functionComparisonPanel instanceof MultiFunctionComparisonPanel)) {
|
||||
continue;
|
||||
}
|
||||
MultiFunctionComparisonPanel multiPanel =
|
||||
(MultiFunctionComparisonPanel) functionComparisonPanel;
|
||||
if (multiPanel.matchesTheseFunctions(functionsL, functionsR)) {
|
||||
return functionComparisonProvider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private FunctionComparisonProvider findFunctionComparisonProvider(
|
||||
HashMap<Function, HashSet<Function>> functionMap) {
|
||||
|
||||
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
|
||||
FunctionComparisonPanel functionComparisonPanel =
|
||||
functionComparisonProvider.getComponent();
|
||||
if (functionComparisonPanel instanceof MappedFunctionComparisonPanel) {
|
||||
MappedFunctionComparisonPanel mappedPanel =
|
||||
(MappedFunctionComparisonPanel) functionComparisonPanel;
|
||||
if (mappedPanel.matchesTheseFunctions(functionMap)) {
|
||||
return functionComparisonProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void providerClosed(FunctionComparisonProvider provider) {
|
||||
functionComparisonProviders.remove(provider);
|
||||
providers.remove(provider);
|
||||
listeners.stream().forEach(l -> l.componentProviderDeactivated(provider));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void providerOpened(FunctionComparisonProvider provider) {
|
||||
listeners.stream().forEach(l -> l.componentProviderActivated(provider));
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all the function comparison providers that have a function from the indicated program.
|
||||
* This method should be called when a program is closing.
|
||||
* @param program the program whose function providers need to close.
|
||||
* Creates a new comparison between the given set of functions
|
||||
*
|
||||
* @param functions the functions to compare
|
||||
* @return the new comparison provider
|
||||
*/
|
||||
public FunctionComparisonProvider compareFunctions(Set<Function> functions) {
|
||||
if (functions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
FunctionComparisonProvider provider = new MultiFunctionComparisonProvider(plugin);
|
||||
provider.addToTool();
|
||||
provider.getModel().compareFunctions(functions);
|
||||
providers.add(provider);
|
||||
provider.setVisible(true);
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new comparison comparison between two functions
|
||||
*
|
||||
* @param source the source function
|
||||
* @param target the target function
|
||||
* @return the new comparison provider
|
||||
*/
|
||||
public FunctionComparisonProvider compareFunctions(Function source,
|
||||
Function target) {
|
||||
FunctionComparisonProvider provider = new MultiFunctionComparisonProvider(plugin);
|
||||
provider.addToTool();
|
||||
provider.getModel().compareFunctions(source, target);
|
||||
providers.add(provider);
|
||||
provider.setVisible(true);
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of functions to an existing comparison provider
|
||||
*
|
||||
* @param functions the functions to compare
|
||||
* @param provider the provider to add the functions to
|
||||
*/
|
||||
public void compareFunctions(Set<Function> functions, FunctionComparisonProvider provider) {
|
||||
if (functions.isEmpty() || provider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
providers.add(provider);
|
||||
provider.setVisible(true);
|
||||
provider.getModel().compareFunctions(functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given functions to an existing comparison provider
|
||||
*
|
||||
* @param source the source function
|
||||
* @param target the target function
|
||||
* @param provider the provider to add the functions to
|
||||
*/
|
||||
public void compareFunctions(Function source, Function target,
|
||||
FunctionComparisonProvider provider) {
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
providers.add(provider);
|
||||
provider.setVisible(true);
|
||||
provider.getModel().compareFunctions(source, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given function from all comparisons across all providers
|
||||
*
|
||||
* @param function the function to remove
|
||||
*/
|
||||
public void removeFunction(Function function) {
|
||||
providers.stream().forEach(p -> p.getModel().removeFunction(function));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given function from a specified provider
|
||||
*
|
||||
* @param function the function to remove
|
||||
* @param provider the provider to remove the function from
|
||||
*/
|
||||
public void removeFunction(Function function, FunctionComparisonProvider provider) {
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
provider.getModel().removeFunction(function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers subscribers who wish to know of provider activation status
|
||||
*
|
||||
* @param listener the subscriber to register
|
||||
*/
|
||||
public void addProviderListener(ComponentProviderActivationListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all the comparison providers that contain a function from
|
||||
* the given program
|
||||
*
|
||||
* @param program the program whose function providers need to close
|
||||
*/
|
||||
public void closeProviders(Program program) {
|
||||
// Get an array of the providers and loop over it to notify them. This is to prevent
|
||||
// causing a ConcurrentModificationException. If a provider closes due to the indicated
|
||||
// program closing, this manager will get notified via the providerClosed method and
|
||||
// remove that provider from the functionComparisonProviders hashset.
|
||||
FunctionComparisonProvider[] providers = functionComparisonProviders
|
||||
.toArray(new FunctionComparisonProvider[functionComparisonProviders.size()]);
|
||||
for (FunctionComparisonProvider functionComparisonProvider : providers) {
|
||||
functionComparisonProvider.programClosed(program); // Allow the provider to close itself.
|
||||
}
|
||||
providers.stream().forEach(p -> p.programClosed(program));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up since this manager is being disposed. All function comparison providers will
|
||||
* close and be cleaned up.
|
||||
* Removes any comparisons that contain a function from the given program
|
||||
*
|
||||
* @param program the program whose functions require removal
|
||||
*/
|
||||
public void removeFunctions(Program program) {
|
||||
providers.stream().forEach(p -> p.removeFunctions(program));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up all providers, setting them invisible and removing any
|
||||
* associated ui components (eg: tabs)
|
||||
*/
|
||||
public void dispose() {
|
||||
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
|
||||
FunctionComparisonPanel functionComparisonPanel =
|
||||
functionComparisonProvider.getComponent();
|
||||
functionComparisonPanel.setVisible(false);
|
||||
functionComparisonPanel.dispose();
|
||||
for (FunctionComparisonProvider provider : providers) {
|
||||
FunctionComparisonPanel panel = provider.getComponent();
|
||||
panel.setVisible(false);
|
||||
panel.dispose();
|
||||
}
|
||||
functionComparisonProviders.clear();
|
||||
providers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is an Undo/Redo. If a program is being restored, this will notify all the
|
||||
* function comparison providers. This allows them to refresh if they are showing a function
|
||||
* from the program.
|
||||
* @param ev the event indicating if this is an Undo/Redo on a program.
|
||||
* Called when there is an Undo/Redo. If a program is being restored, this
|
||||
* will notify all the function comparison providers. This allows them to
|
||||
* refresh if they are showing a function from the program
|
||||
*
|
||||
* @param ev the object changed event
|
||||
*/
|
||||
public void domainObjectRestored(DomainObjectChangedEvent ev) {
|
||||
for (DomainObjectChangeRecord domainObjectChangeRecord : ev) {
|
||||
int eventType = domainObjectChangeRecord.getEventType();
|
||||
if (eventType == DomainObject.DO_OBJECT_RESTORED) {
|
||||
Object source = ev.getSource();
|
||||
if (source instanceof Program) {
|
||||
Program program = (Program) source;
|
||||
for (FunctionComparisonProvider functionComparisonProvider : functionComparisonProviders) {
|
||||
functionComparisonProvider.programRestored(program);
|
||||
}
|
||||
}
|
||||
if (eventType != DomainObject.DO_OBJECT_RESTORED) {
|
||||
return;
|
||||
}
|
||||
Object source = ev.getSource();
|
||||
if (source instanceof Program) {
|
||||
Program program = (Program) source;
|
||||
providers.stream().forEach(p -> p.programRestored(program));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.help.Help;
|
||||
import docking.help.HelpService;
|
||||
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
|
||||
import ghidra.app.services.FunctionComparisonModel;
|
||||
import ghidra.app.util.viewer.util.CodeComparisonPanel;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* Creates a panel for comparing two or more functions.
|
||||
* If there are multiple functions to display within either the left or right side of this panel,
|
||||
* then a combo box will appear above the left and right side of the CodeComparisonPanels.
|
||||
* Each combo box will allow the user to choose which function to display on that side of the panel.
|
||||
* Extends the basic {@link FunctionComparisonPanel one-to-one comparison panel}
|
||||
* to allow a many-to-many relationship. The panel provides a pair of combo
|
||||
* boxes above the function display area that allows users to select which
|
||||
* functions are to be compared.
|
||||
* <p>
|
||||
* Throughout this class the terms <code>source</code> and <code>target</code>
|
||||
* are used when referencing functions. This is because the model that backs
|
||||
* this panel maintains a relationship between the functions being compared
|
||||
* such that each source function can only be compared to a specific set
|
||||
* of target functions. For all practical purposes, the source functions
|
||||
* appear in the left-side panel and targets appear on the right.
|
||||
*
|
||||
*/
|
||||
public class MultiFunctionComparisonPanel extends FunctionChoiceComparisonPanel {
|
||||
public class MultiFunctionComparisonPanel extends FunctionComparisonPanel {
|
||||
|
||||
/** Functions that will show up on the left side of the panel */
|
||||
private JComboBox<Function> sourceFunctionsCB;
|
||||
|
||||
/** Functions that will show up on the right side of the panel */
|
||||
private JComboBox<Function> targetFunctionsCB;
|
||||
|
||||
/** Data models backing the source and target combo boxes */
|
||||
private DefaultComboBoxModel<Function> sourceFunctionsCBModel;
|
||||
private DefaultComboBoxModel<Function> targetFunctionsCBModel;
|
||||
|
||||
protected static final HelpService help = Help.getHelpService();
|
||||
public static final String HELP_TOPIC = "FunctionComparison";
|
||||
|
||||
/**
|
||||
* Creates a panel for displaying two or more functions to be compared. This makes the
|
||||
* functions available for display in both the left side and right side of the panel after
|
||||
* they are sorted ascending based on the program and function. The primary sort is on
|
||||
* program including pathname. The secondary sort is on function including namespace.
|
||||
* By default the first function will be loaded into the left side and the second function
|
||||
* will be loaded in the right side.
|
||||
* @param provider the provider displaying this panel.
|
||||
* @param tool the tool displaying this panel.
|
||||
* @param functions the functions that are used to populate both the left and right side
|
||||
* of the function comparison panel.
|
||||
*/
|
||||
public MultiFunctionComparisonPanel(ComponentProvider provider, PluginTool tool,
|
||||
Function[] functions) {
|
||||
super(provider, tool, null, null);
|
||||
// For now, sort the functions.
|
||||
Function[] sortedFunctions = getSortedFunctions(functions);
|
||||
leftWrappedFunctions = getWrappedFunctions(sortedFunctions);
|
||||
rightWrappedFunctions = leftWrappedFunctions;
|
||||
if (leftWrappedFunctions.length >= 2) {
|
||||
Function leftFunction = (leftIndex < leftWrappedFunctions.length)
|
||||
? leftWrappedFunctions[leftIndex].getFunction()
|
||||
: null;
|
||||
++rightIndex;
|
||||
Function rightFunction = (rightIndex < rightWrappedFunctions.length)
|
||||
? rightWrappedFunctions[rightIndex].getFunction()
|
||||
: null;
|
||||
loadFunctions(leftFunction, rightFunction);
|
||||
}
|
||||
// Don't include the choice panel with its combo boxes unless there are more than 2 functions.
|
||||
if (leftWrappedFunctions.length > 2) {
|
||||
addChoicePanel(); // This also populates the combo boxes.
|
||||
}
|
||||
createActions();
|
||||
help.registerHelp(this, new HelpLocation(HELP_TOPIC, "Function Comparison"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a panel for displaying two or more functions to be compared. This will load the
|
||||
* functions so the leftFunctions are available for display in the left side and the
|
||||
* rightFunctions are available for display in the right side of the function comparison panel.
|
||||
* The functions are sorted ascending based on the program and function. The primary sort
|
||||
* is on program including pathname. The secondary sort is on function including namespace.
|
||||
* By default the first function from each array will be the one initially displayed in its
|
||||
* associated side.
|
||||
* @param provider the provider displaying this panel.
|
||||
* @param tool the tool displaying this panel.
|
||||
* @param leftFunctions the functions that are used to populate the left side
|
||||
* @param rightFunctions the functions that are used to populate the right side
|
||||
*/
|
||||
public MultiFunctionComparisonPanel(ComponentProvider provider, PluginTool tool,
|
||||
Function[] leftFunctions, Function[] rightFunctions) {
|
||||
super(provider, tool, null, null);
|
||||
Function[] sortedLeftFunctions = getSortedFunctions(leftFunctions);
|
||||
Function[] sortedRightFunctions = getSortedFunctions(rightFunctions);
|
||||
leftWrappedFunctions = getWrappedFunctions(sortedLeftFunctions);
|
||||
rightWrappedFunctions =
|
||||
Arrays.equals(sortedLeftFunctions, sortedRightFunctions) ? leftWrappedFunctions
|
||||
: getWrappedFunctions(sortedRightFunctions);
|
||||
if ((leftWrappedFunctions.length >= 1) && (rightWrappedFunctions.length >= 1)) {
|
||||
Function leftFunction = (leftIndex < leftWrappedFunctions.length)
|
||||
? leftWrappedFunctions[leftIndex].getFunction()
|
||||
: null; // Initially leftIndex is 0.
|
||||
Function rightFunction = (rightIndex < rightWrappedFunctions.length)
|
||||
? rightWrappedFunctions[rightIndex].getFunction()
|
||||
: null; // Initially rightIndex is 0.
|
||||
if (leftFunction == rightFunction && rightWrappedFunctions.length > 1) {
|
||||
rightFunction = rightWrappedFunctions[++rightIndex].getFunction();
|
||||
}
|
||||
loadFunctions(leftFunction, rightFunction);
|
||||
}
|
||||
if (leftWrappedFunctions.length > 1 || rightWrappedFunctions.length > 1) {
|
||||
addChoicePanel(); // This also populates the combo boxes.
|
||||
}
|
||||
createActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if <code>functionsL</code> and <code>functionsR</code> match the functions
|
||||
* that can be displayed for comparison in the left and right side of this panel.
|
||||
* Constructor
|
||||
*
|
||||
* @param functionsL the functions to check against those used to populate the left side
|
||||
* @param functionsR the functions to check against those used to populate the right side
|
||||
* @return true if functionsL and functionsR match the functions that can be displayed by
|
||||
* this panel.
|
||||
* @param provider the comparison provider associated with this panel
|
||||
* @param tool the active plugin tool
|
||||
*/
|
||||
boolean matchesTheseFunctions(Function[] functionsL, Function[] functionsR) {
|
||||
return Arrays.equals(getLeftFunctions(), getSortedFunctions(functionsL)) &&
|
||||
Arrays.equals(getRightFunctions(), getSortedFunctions(functionsR));
|
||||
public MultiFunctionComparisonPanel(FunctionComparisonProvider provider,
|
||||
PluginTool tool) {
|
||||
super(provider, tool, null, null);
|
||||
|
||||
JPanel choicePanel = new JPanel(new GridLayout(1, 2));
|
||||
choicePanel.add(createSourcePanel());
|
||||
choicePanel.add(createTargetPanel());
|
||||
add(choicePanel, BorderLayout.NORTH);
|
||||
|
||||
// For the multi-panels we don't need to show the title of each
|
||||
// comparison panel because the name of the function/data being shown
|
||||
// is already visible in the combo box
|
||||
getComparisonPanels().forEach(p -> p.setShowTitles(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the given functions from the comparison panel (both the
|
||||
* source and target lists) (the default for this method is to only clear
|
||||
* out the functions visible in the left/right panels)
|
||||
*/
|
||||
@Override
|
||||
public void removeFunctions(Set<Function> functions) {
|
||||
((MultiFunctionComparisonProvider) provider).removeFunctions(functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the source and targets lists and reloads them to
|
||||
* ensure that they reflect the current state of the data model. Any
|
||||
* currently-selected list items will be restored after the lists
|
||||
* are reloaded.
|
||||
*/
|
||||
@Override
|
||||
public void reload() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
reloadSourceList();
|
||||
Function selectedSource = (Function) sourceFunctionsCBModel.getSelectedItem();
|
||||
reloadTargetList(selectedSource);
|
||||
loadFunctions(selectedSource, (Function) targetFunctionsCBModel.getSelectedItem());
|
||||
|
||||
((FunctionComparisonProvider) provider).setTabText(this);
|
||||
((FunctionComparisonProvider) provider)
|
||||
.setTitle(((FunctionComparisonProvider) provider).getTabText());
|
||||
|
||||
// Fire a notification to update the UI state; without this the
|
||||
// actions would not be properly enabled/disabled
|
||||
tool.contextChanged(provider);
|
||||
tool.setStatusInfo("function comparisons updated");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the combo box (source or target) which has focus
|
||||
*
|
||||
* @return the focused component
|
||||
*/
|
||||
public JComboBox<Function> getFocusedComponent() {
|
||||
CodeComparisonPanel<? extends FieldPanelCoordinator> currentComponent =
|
||||
getCurrentComponent();
|
||||
boolean sourceHasFocus = currentComponent.leftPanelHasFocus();
|
||||
return sourceHasFocus ? sourceFunctionsCB : targetFunctionsCB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out and reloads the source function list. Any selection currently
|
||||
* made on the list will be reestablished.
|
||||
*/
|
||||
private void reloadSourceList() {
|
||||
|
||||
// Save off any selected item so we can restore if it later
|
||||
Function selection = (Function) sourceFunctionsCBModel.getSelectedItem();
|
||||
|
||||
// Remove all functions
|
||||
sourceFunctionsCBModel.removeAllElements();
|
||||
|
||||
// Reload the functions
|
||||
FunctionComparisonModel model = ((FunctionComparisonProvider) provider).getModel();
|
||||
Iterator<FunctionComparison> compIter = model.getComparisons().iterator();
|
||||
while (compIter.hasNext()) {
|
||||
FunctionComparison fc = compIter.next();
|
||||
sourceFunctionsCBModel.addElement(fc.getSource());
|
||||
}
|
||||
|
||||
restoreSelection(sourceFunctionsCB, selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out and reloads the target function list with functions
|
||||
* associated with the given source function. Any selection currently made
|
||||
* on the list will be reestablished.
|
||||
*
|
||||
* @param source the selected source function
|
||||
*/
|
||||
private void reloadTargetList(Function source) {
|
||||
|
||||
// Save off any selected item so we can restore if it later
|
||||
Function selection = (Function) targetFunctionsCBModel.getSelectedItem();
|
||||
|
||||
// Remove all functions
|
||||
targetFunctionsCBModel.removeAllElements();
|
||||
|
||||
// Find all target functions associated with the given source function
|
||||
// and add them to the combo box model
|
||||
FunctionComparisonModel model = ((FunctionComparisonProvider) provider).getModel();
|
||||
Iterator<FunctionComparison> compIter = model.getComparisons().iterator();
|
||||
while (compIter.hasNext()) {
|
||||
FunctionComparison fc = compIter.next();
|
||||
if (fc.getSource().equals(source)) {
|
||||
Set<Function> targets = fc.getTargets();
|
||||
targetFunctionsCBModel.addAll(targets);
|
||||
}
|
||||
}
|
||||
|
||||
restoreSelection(targetFunctionsCB, selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a given function to be the selected item in a given combo
|
||||
* box. If the function isn't found, the first item in the box is
|
||||
* set.
|
||||
*
|
||||
* @param cb the combo box
|
||||
* @param selection the function to set
|
||||
*/
|
||||
private void restoreSelection(JComboBox<Function> cb, Function selection) {
|
||||
ComboBoxModel<Function> model = cb.getModel();
|
||||
|
||||
boolean found = false;
|
||||
for (int i = 0; i < model.getSize(); i++) {
|
||||
Function f = model.getElementAt(i);
|
||||
if (f.equals(selection)) {
|
||||
model.setSelectedItem(f);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && model.getSize() > 0) {
|
||||
cb.setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the panel displaying the source combo box
|
||||
* <p>
|
||||
* Note: The custom renderer is used so the name of the program associated
|
||||
* with each function can be displayed in the combo box; this is necessary
|
||||
* since a combo box may show functions from any number of programs, and
|
||||
* the default is to simply show the function name<br>
|
||||
* eg: "init (notepad)"<br>
|
||||
*
|
||||
* @return the source panel
|
||||
*/
|
||||
private JPanel createSourcePanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
sourceFunctionsCB = new JComboBox<>();
|
||||
sourceFunctionsCBModel = new DefaultComboBoxModel<>();
|
||||
sourceFunctionsCB.setModel(sourceFunctionsCBModel);
|
||||
sourceFunctionsCB.setRenderer(new FunctionListCellRenderer());
|
||||
sourceFunctionsCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() != ItemEvent.SELECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
Function selected = (Function) sourceFunctionsCBModel.getSelectedItem();
|
||||
loadFunctions(selected, null);
|
||||
|
||||
// Each time a source function is selected we need
|
||||
// to load the targets associated with it
|
||||
reloadTargetList((Function) sourceFunctionsCBModel.getSelectedItem());
|
||||
|
||||
// Fire a notification to update the UI state; without this the
|
||||
// actions would not be properly enabled/disabled
|
||||
tool.contextChanged(provider);
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(sourceFunctionsCB, BorderLayout.CENTER);
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the panel for the target functions selection components
|
||||
* <p>
|
||||
* Note: The custom renderer is used so the name of the program associated
|
||||
* with each function can be displayed in the combo box; this is necessary
|
||||
* since a combo box may show functions from any number of programs, and
|
||||
* the default is to simply show the function name<br>
|
||||
* eg: "init (notepad)"<br>
|
||||
*
|
||||
* @return the target panel
|
||||
*/
|
||||
private JPanel createTargetPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
targetFunctionsCB = new JComboBox<>();
|
||||
targetFunctionsCBModel = new DefaultComboBoxModel<>();
|
||||
targetFunctionsCB.setModel(targetFunctionsCBModel);
|
||||
targetFunctionsCB.setRenderer(new FunctionListCellRenderer());
|
||||
targetFunctionsCB.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() != ItemEvent.SELECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
Function selected = (Function) targetFunctionsCBModel.getSelectedItem();
|
||||
loadFunctions((Function) sourceFunctionsCBModel.getSelectedItem(), selected);
|
||||
|
||||
// Fire a notification to update the UI state; without this the
|
||||
// actions would not be properly enabled/disabled
|
||||
tool.contextChanged(provider);
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(targetFunctionsCB, BorderLayout.CENTER);
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cell renderer for combo boxes that changes the default display to show
|
||||
* both the function name and the program it comes from
|
||||
*/
|
||||
private class FunctionListCellRenderer extends DefaultListCellRenderer {
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
|
||||
boolean isSelected, boolean cellHasFocus) {
|
||||
|
||||
if (value == null) {
|
||||
// It's possible during a close program operation to have this
|
||||
// renderer called with a null value. If so, we can't get the
|
||||
// function so just use the default renderer.
|
||||
return super.getListCellRendererComponent(list, value, index, isSelected,
|
||||
cellHasFocus);
|
||||
}
|
||||
|
||||
Function f = (Function) value;
|
||||
String text = f.getName() + " (" + f.getProgram().getName() + ")";
|
||||
return super.getListCellRendererComponent(list, text, index, isSelected,
|
||||
cellHasFocus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.functioncompare;
|
||||
package ghidra.app.plugin.core.functioncompare.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
@ -30,10 +30,11 @@ import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
/**
|
||||
* Action that applies the signature of the function in the currently active side of a
|
||||
* code comparison panel to the function in the other side of the panel.
|
||||
* <br>Each CodeComparisonPanel can extend this class in order to provide this action
|
||||
* using its context.
|
||||
* Applies the signature of the function in the currently active side of a
|
||||
* code comparison panel to the function in the other side of the panel
|
||||
* <p>
|
||||
* Each CodeComparisonPanel can extend this class in order to provide this action
|
||||
* using its context
|
||||
*/
|
||||
public abstract class AbstractApplyFunctionSignatureAction extends DockingAction {
|
||||
|
||||
@ -42,9 +43,9 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
|
||||
private static final String ACTION_NAME = "Apply Function Signature To Other Side";
|
||||
|
||||
/**
|
||||
* Constructor for the action that applies a function signature from one side of a code
|
||||
* comparison panel to the other.
|
||||
* @param owner the owner of this action.
|
||||
* Constructor
|
||||
*
|
||||
* @param owner the owner of this action
|
||||
*/
|
||||
public AbstractApplyFunctionSignatureAction(String owner) {
|
||||
super(ACTION_NAME, owner);
|
||||
@ -97,13 +98,25 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the comparison panel opposite the one with focus,
|
||||
* is read-only
|
||||
* <p>
|
||||
* eg: if the right-side panel has focus, and the left-side panel is
|
||||
* read-only, this will return true
|
||||
*
|
||||
* @param codeComparisonPanel the comparison panel
|
||||
* @return true if the non-focused panel is read-only
|
||||
*/
|
||||
protected boolean hasReadOnlyNonFocusedSide(
|
||||
CodeComparisonPanel<? extends FieldPanelCoordinator> codeComparisonPanel) {
|
||||
Function leftFunction = codeComparisonPanel.getLeftFunction();
|
||||
Function rightFunction = codeComparisonPanel.getRightFunction();
|
||||
|
||||
if (leftFunction == null || rightFunction == null) {
|
||||
return false; // Doesn't have a function on both sides.
|
||||
}
|
||||
|
||||
boolean leftHasFocus = codeComparisonPanel.leftPanelHasFocus();
|
||||
Program leftProgram = leftFunction.getProgram();
|
||||
Program rightProgram = rightFunction.getProgram();
|
||||
@ -111,11 +124,22 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
|
||||
(leftHasFocus && rightProgram.getDomainFile().isReadOnly());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to change the signature of a function to that of another
|
||||
* function
|
||||
*
|
||||
* @param provider the parent component provider
|
||||
* @param destinationFunction the function to change
|
||||
* @param sourceFunction the function to copy
|
||||
* @return true if the operation was successful
|
||||
*/
|
||||
protected boolean updateFunction(ComponentProvider provider, Function destinationFunction,
|
||||
Function sourceFunction) {
|
||||
|
||||
Program program = destinationFunction.getProgram();
|
||||
int txID = program.startTransaction(ACTION_NAME);
|
||||
boolean commit = false;
|
||||
|
||||
try {
|
||||
FunctionUtility.updateFunction(destinationFunction, sourceFunction);
|
||||
commit = true;
|
||||
@ -129,6 +153,7 @@ public abstract class AbstractApplyFunctionSignatureAction extends DockingAction
|
||||
finally {
|
||||
program.endTransaction(txID, commit);
|
||||
}
|
||||
|
||||
return commit;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||
public class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||
|
||||
private final Function function;
|
||||
|
||||
FunctionRowObject(Function function) {
|
||||
public FunctionRowObject(Function function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
Function getFunction() {
|
||||
public Function getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||
return true;
|
||||
}
|
||||
|
||||
long getKey() {
|
||||
public long getKey() {
|
||||
return function.getID();
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ import ghidra.util.table.AddressBasedTableModel;
|
||||
import ghidra.util.table.field.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||
public class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||
|
||||
static final int LOCATION_COL_WIDTH = 50;
|
||||
|
||||
@ -39,7 +39,7 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||
|
||||
private FunctionManager functionMgr;
|
||||
|
||||
FunctionTableModel(PluginTool tool, Program program) {
|
||||
public FunctionTableModel(PluginTool tool, Program program) {
|
||||
super("Functions", tool, program, null);
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
void reload(Program newProgram) {
|
||||
public void reload(Program newProgram) {
|
||||
this.setProgram(newProgram);
|
||||
if (newProgram != null) {
|
||||
functionMgr = newProgram.getFunctionManager();
|
||||
|
@ -15,33 +15,34 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import java.util.List;
|
||||
import javax.swing.KeyStroke;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.ComponentProvider;
|
||||
import docking.ComponentProviderActivationListener;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.KeyBindingData;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.events.ProgramClosedPluginEvent;
|
||||
import ghidra.app.events.ProgramSelectionPluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProvider;
|
||||
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProviderManager;
|
||||
import ghidra.app.plugin.core.functioncompare.actions.CompareFunctionsFromFunctionTableAction;
|
||||
import ghidra.app.services.FunctionComparisonService;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import ghidra.program.util.ChangeManager;
|
||||
import ghidra.program.util.ProgramChangeRecord;
|
||||
import ghidra.util.table.SelectionNavigationAction;
|
||||
import ghidra.util.table.actions.MakeProgramSelectionAction;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import resources.ResourceManager;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
@ -50,24 +51,28 @@ import resources.ResourceManager;
|
||||
category = PluginCategoryNames.CODE_VIEWER,
|
||||
shortDescription = "Function Viewer",
|
||||
description = "Provides a window that displays the list of functions in the program.",
|
||||
servicesRequired = { FunctionComparisonService.class },
|
||||
eventsConsumed = { ProgramClosedPluginEvent.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectListener {
|
||||
public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectListener,
|
||||
OptionsChangeListener, ComponentProviderActivationListener {
|
||||
|
||||
private DockingAction selectAction;
|
||||
private DockingAction compareAction;
|
||||
private DockingAction compareFunctionsAction;
|
||||
private FunctionWindowProvider provider;
|
||||
private SwingUpdateManager swingMgr;
|
||||
private FunctionComparisonProviderManager functionComparisonManager;
|
||||
private FunctionComparisonService functionComparisonService;
|
||||
|
||||
public FunctionWindowPlugin(PluginTool tool) {
|
||||
super(tool, true, false);
|
||||
|
||||
functionComparisonManager = new FunctionComparisonProviderManager(this);
|
||||
|
||||
swingMgr = new SwingUpdateManager(1000, () -> provider.reload());
|
||||
|
||||
swingMgr = new SwingUpdateManager(1000, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
provider.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -75,7 +80,21 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
|
||||
super.init();
|
||||
|
||||
provider = new FunctionWindowProvider(this);
|
||||
functionComparisonService = tool.getService(FunctionComparisonService.class);
|
||||
createActions();
|
||||
|
||||
/**
|
||||
* Kicks the tool actions to set the proper enablement when selection changes
|
||||
* on the function table
|
||||
*/
|
||||
provider.getTable().getSelectionModel().addListSelectionListener(x -> {
|
||||
tool.contextChanged(provider);
|
||||
});
|
||||
|
||||
// Listen for providers being opened/closed to we can disable the
|
||||
// add-to-comparison action if there are no comparison windows
|
||||
// open.
|
||||
functionComparisonService.addFunctionComparisonProviderListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,15 +103,14 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
|
||||
currentProgram.removeListener(this);
|
||||
}
|
||||
swingMgr.dispose();
|
||||
provider.dispose();
|
||||
if (provider != null) {
|
||||
provider.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
|
||||
functionComparisonManager.domainObjectRestored(ev);
|
||||
}
|
||||
|
||||
if (!provider.isVisible()) {
|
||||
return;
|
||||
@ -181,82 +199,64 @@ public class FunctionWindowPlugin extends ProgramPlugin implements DomainObjectL
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
addSelectAction();
|
||||
addCompareAction();
|
||||
|
||||
DockingAction action = new SelectionNavigationAction(this, provider.getTable());
|
||||
tool.addLocalAction(provider, action);
|
||||
}
|
||||
|
||||
private void addSelectAction() {
|
||||
|
||||
selectAction = new MakeProgramSelectionAction(this, provider.getTable());
|
||||
tool.addLocalAction(provider, selectAction);
|
||||
|
||||
compareFunctionsAction = new CompareFunctionsFromFunctionTableAction(tool);
|
||||
tool.addLocalAction(provider, compareFunctionsAction);
|
||||
}
|
||||
|
||||
private void addCompareAction() {
|
||||
compareAction = new DockingAction("Compare Selected Functions", getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
compareSelectedFunctions();
|
||||
}
|
||||
};
|
||||
// private void installDummyAction(DockingAction action) {
|
||||
// DummyKeyBindingsOptionsAction dummyAction =
|
||||
// new DummyKeyBindingsOptionsAction(action.getName(), null);
|
||||
// tool.addAction(dummyAction);
|
||||
//
|
||||
// ToolOptions options = tool.getOptions(DockingToolConstants.KEY_BINDINGS);
|
||||
// options.addOptionsChangeListener(this);
|
||||
//
|
||||
// KeyStroke keyStroke = options.getKeyStroke(dummyAction.getFullName(), null);
|
||||
// if (keyStroke != null) {
|
||||
// action.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
|
||||
// }
|
||||
// }
|
||||
|
||||
ImageIcon icon = ResourceManager.loadImage("images/page_white_c.png");
|
||||
compareAction.setPopupMenuData(new MenuData(new String[] { "Compare Functions" }, icon));
|
||||
compareAction.setDescription("Compares the currently selected function(s) in the table.");
|
||||
compareAction.setToolBarData(new ToolBarData(icon));
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
|
||||
tool.addLocalAction(provider, compareAction);
|
||||
if (optionName.startsWith(selectAction.getName())) {
|
||||
KeyStroke keyStroke = (KeyStroke) newValue;
|
||||
selectAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
|
||||
}
|
||||
if (optionName.startsWith(compareFunctionsAction.getName())) {
|
||||
KeyStroke keyStroke = (KeyStroke) newValue;
|
||||
compareFunctionsAction.setUnvalidatedKeyBindingData(new KeyBindingData(keyStroke));
|
||||
}
|
||||
}
|
||||
|
||||
void setActionsEnabled(boolean enabled) {
|
||||
selectAction.setEnabled(enabled);
|
||||
compareAction.setEnabled(enabled);
|
||||
compareFunctionsAction.setEnabled(enabled);
|
||||
}
|
||||
|
||||
void showFunctions() {
|
||||
provider.showFunctions();
|
||||
}
|
||||
|
||||
private void selectFunctions(ProgramSelection selection) {
|
||||
ProgramSelectionPluginEvent pspe =
|
||||
new ProgramSelectionPluginEvent("Selection", selection, currentProgram);
|
||||
firePluginEvent(pspe);
|
||||
}
|
||||
|
||||
private FunctionComparisonProvider compareSelectedFunctions() {
|
||||
Function[] functions = getSelectedFunctions();
|
||||
if (functions.length < 2) {
|
||||
Msg.showError(this, provider.getComponent(), "Compare Selected Functions",
|
||||
"Select two or more rows in the table indicating functions to compare.");
|
||||
return null;
|
||||
@Override
|
||||
public void componentProviderActivated(ComponentProvider componentProvider) {
|
||||
if (componentProvider instanceof FunctionComparisonProvider) {
|
||||
tool.contextChanged(provider);
|
||||
}
|
||||
return functionComparisonManager.showFunctionComparisonProvider(functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the functions that are currently selected in the table.
|
||||
* @return the selected functions
|
||||
*/
|
||||
private Function[] getSelectedFunctions() {
|
||||
GhidraTable table = provider.getTable();
|
||||
int[] selectedRows = table.getSelectedRows();
|
||||
Function[] functions = new Function[selectedRows.length];
|
||||
FunctionTableModel model = provider.getModel();
|
||||
Program program = model.getProgram();
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
List<FunctionRowObject> functionRowObjects = model.getRowObjects(selectedRows);
|
||||
int index = 0;
|
||||
for (FunctionRowObject functionRowObject : functionRowObjects) {
|
||||
long key = functionRowObject.getKey();
|
||||
functions[index++] = functionManager.getFunction(key);
|
||||
}
|
||||
return functions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void programClosed(Program program) {
|
||||
functionComparisonManager.closeProviders(program);
|
||||
public void componentProviderDeactivated(ComponentProvider componentProvider) {
|
||||
if (componentProvider instanceof FunctionComparisonProvider) {
|
||||
tool.contextChanged(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ import ghidra.util.table.*;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Provider for the equates table.
|
||||
* Provider that displays all functions in the selected program
|
||||
*/
|
||||
public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||
|
||||
@ -45,9 +45,13 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||
private JComponent mainPanel;
|
||||
|
||||
private GhidraTableFilterPanel<FunctionRowObject> tableFilterPanel;
|
||||
|
||||
private GhidraThreadedTablePanel<FunctionRowObject> threadedTablePanel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param plugin the function window plugin
|
||||
*/
|
||||
FunctionWindowProvider(FunctionWindowPlugin plugin) {
|
||||
super(plugin.getTool(), "Functions Window", plugin.getName());
|
||||
setTitle("Functions");
|
||||
@ -124,8 +128,9 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||
functionTable.setPreferredScrollableViewportSize(new Dimension(350, 150));
|
||||
functionTable.setRowSelectionAllowed(true);
|
||||
functionTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
functionTable.getSelectionModel().addListSelectionListener(
|
||||
e -> plugin.setActionsEnabled(functionTable.getSelectedRowCount() > 0));
|
||||
functionTable.getSelectionModel()
|
||||
.addListSelectionListener(
|
||||
e -> plugin.setActionsEnabled(functionTable.getSelectedRowCount() > 0));
|
||||
|
||||
functionModel.addTableModelListener(e -> {
|
||||
int rowCount = functionModel.getRowCount();
|
||||
@ -158,8 +163,10 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
private void setFunctionTableRenderer() {
|
||||
functionTable.getColumnModel().getColumn(FunctionTableModel.LOCATION_COL).setPreferredWidth(
|
||||
FunctionTableModel.LOCATION_COL_WIDTH);
|
||||
functionTable.getColumnModel()
|
||||
.getColumn(FunctionTableModel.LOCATION_COL)
|
||||
.setPreferredWidth(
|
||||
FunctionTableModel.LOCATION_COL_WIDTH);
|
||||
}
|
||||
|
||||
void update(Function function) {
|
||||
@ -203,5 +210,4 @@ public class FunctionWindowProvider extends ComponentProviderAdapter {
|
||||
public boolean isTransient() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.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;
|
||||
|
||||
/**
|
||||
|
@ -361,15 +361,23 @@ public class ListingCodeComparisonPanel
|
||||
}
|
||||
|
||||
private void updateLeftListingTitle() {
|
||||
titlePanels[LEFT].setTitleName(getLeftProgramName());
|
||||
}
|
||||
|
||||
private String getLeftProgramName() {
|
||||
String leftProgramName =
|
||||
(programs[LEFT] != null) ? programs[LEFT].getDomainFile().toString() : "none";
|
||||
titlePanels[LEFT].setTitleName(leftProgramName);
|
||||
return leftProgramName;
|
||||
}
|
||||
|
||||
private void updateRightListingTitle() {
|
||||
titlePanels[RIGHT].setTitleName(getRightProgramName());
|
||||
}
|
||||
|
||||
private String getRightProgramName() {
|
||||
String rightProgramName =
|
||||
(programs[RIGHT] != null) ? programs[RIGHT].getDomainFile().toString() : "none";
|
||||
titlePanels[RIGHT].setTitleName(rightProgramName);
|
||||
return rightProgramName;
|
||||
}
|
||||
|
||||
private void initializeListingFieldNavigation() {
|
||||
@ -466,18 +474,10 @@ public class ListingCodeComparisonPanel
|
||||
public void updateActionEnablement() {
|
||||
boolean isShowing = isShowing();
|
||||
boolean listingDiffActionEnablement = isShowing && listingDiff.hasCorrelation();
|
||||
toggleHoverAction.setEnabled(isShowing);
|
||||
nextPreviousAreaMarkerAction.setEnabled(listingDiffActionEnablement);
|
||||
nextDiffAction.setEnabled(listingDiffActionEnablement);
|
||||
previousDiffAction.setEnabled(listingDiffActionEnablement);
|
||||
optionsAction.setEnabled(listingDiffActionEnablement);
|
||||
|
||||
// Diff actions
|
||||
tool.contextChanged(tool.getActiveComponentProvider());
|
||||
|
||||
diffActionManager.updateActionEnablement(listingDiffActionEnablement);
|
||||
|
||||
// applyFunctionSignature enablement is handled by context.
|
||||
|
||||
// For now don't do anything here with the header or orientation actions.
|
||||
}
|
||||
|
||||
class ToggleHeaderAction extends ToggleDockingAction {
|
||||
@ -537,6 +537,11 @@ public class ListingCodeComparisonPanel
|
||||
setHover(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return isShowing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
setHover(isSelected());
|
||||
@ -684,6 +689,11 @@ public class ListingCodeComparisonPanel
|
||||
previousDiffAction.setMenuString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return isShowing() && listingDiff.hasCorrelation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionStateChanged(ActionState<String> newActionState, EventTrigger trigger) {
|
||||
adjustNextPreviousAreaType();
|
||||
@ -718,6 +728,11 @@ public class ListingCodeComparisonPanel
|
||||
return isValidPanelContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return isShowing() && listingDiff.hasCorrelation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (isValidContext(context)) {
|
||||
@ -757,6 +772,11 @@ public class ListingCodeComparisonPanel
|
||||
return isValidPanelContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return isShowing() && listingDiff.hasCorrelation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (isValidContext(context)) {
|
||||
@ -784,6 +804,11 @@ public class ListingCodeComparisonPanel
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return isShowing() && listingDiff.hasCorrelation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidContext(ActionContext context) {
|
||||
return isValidPanelContext(context);
|
||||
@ -1518,8 +1543,9 @@ public class ListingCodeComparisonPanel
|
||||
comparisonOptions.getUnmatchedCodeUnitsBackgroundColor();
|
||||
if (programs[LEFT] != null) {
|
||||
AddressIndexMap indexMap = listingPanels[LEFT].getAddressIndexMap();
|
||||
listingPanels[LEFT].getFieldPanel().setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMap));
|
||||
listingPanels[LEFT].getFieldPanel()
|
||||
.setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMap));
|
||||
markerManagers[LEFT].setProgram(programs[LEFT]);
|
||||
unmatchedCodeMarkers[LEFT] =
|
||||
markerManagers[LEFT].createAreaMarker("Listing1 Unmatched Code",
|
||||
@ -1532,8 +1558,10 @@ public class ListingCodeComparisonPanel
|
||||
}
|
||||
if (programs[RIGHT] != null) {
|
||||
AddressIndexMap rightIndexMap = listingPanels[RIGHT].getAddressIndexMap();
|
||||
listingPanels[RIGHT].getFieldPanel().setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], rightIndexMap));
|
||||
listingPanels[RIGHT].getFieldPanel()
|
||||
.setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT],
|
||||
rightIndexMap));
|
||||
markerManagers[RIGHT].setProgram(programs[RIGHT]);
|
||||
unmatchedCodeMarkers[RIGHT] =
|
||||
markerManagers[RIGHT].createAreaMarker("Listing2 Unmatched Code",
|
||||
@ -1633,8 +1661,9 @@ public class ListingCodeComparisonPanel
|
||||
|
||||
indexMaps[LEFT] = new AddressIndexMap(addressSets[LEFT]);
|
||||
markerManagers[LEFT].getOverviewProvider().setAddressIndexMap(indexMaps[LEFT]);
|
||||
listingPanels[LEFT].getFieldPanel().setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMaps[LEFT]));
|
||||
listingPanels[LEFT].getFieldPanel()
|
||||
.setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManagers[LEFT], indexMaps[LEFT]));
|
||||
}
|
||||
|
||||
private void updateRightAddressSet(Function rightFunction) {
|
||||
@ -1649,8 +1678,9 @@ public class ListingCodeComparisonPanel
|
||||
|
||||
indexMaps[RIGHT] = new AddressIndexMap(addressSets[RIGHT]);
|
||||
markerManagers[RIGHT].getOverviewProvider().setAddressIndexMap(indexMaps[RIGHT]);
|
||||
listingPanels[RIGHT].getFieldPanel().setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], indexMaps[RIGHT]));
|
||||
listingPanels[RIGHT].getFieldPanel()
|
||||
.setBackgroundColorModel(
|
||||
new MarkerServiceBackgroundColorModel(markerManagers[RIGHT], indexMaps[RIGHT]));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1992,6 +2022,11 @@ public class ListingCodeComparisonPanel
|
||||
setDualPanelFocus(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Kick the tool so action buttons will be updated
|
||||
if (tool.getActiveComponentProvider() != null) {
|
||||
tool.getActiveComponentProvider().contextChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void setDualPanelFocus(int leftOrRight) {
|
||||
|
@ -127,10 +127,12 @@ public class ListingComparisonFieldPanelCoordinator extends LayoutLockedFieldPan
|
||||
ListingPanel rightListingPanel = dualListingPanel.getRightPanel();
|
||||
AddressIndexMap leftAddressIndexMap = leftListingPanel.getAddressIndexMap();
|
||||
AddressIndexMap rightAddressIndexMap = rightListingPanel.getAddressIndexMap();
|
||||
|
||||
BigInteger leftIndex =
|
||||
(leftAddress != null) ? leftAddressIndexMap.getIndex(leftAddress) : null;
|
||||
BigInteger rightIndex =
|
||||
(rightAddress != null) ? rightAddressIndexMap.getIndex(rightAddress) : null;
|
||||
|
||||
BigInteger[] lineNumbers =
|
||||
new BigInteger[] { (leftIndex != null) ? leftIndex : BigInteger.ZERO,
|
||||
(rightIndex != null) ? rightIndex : BigInteger.ZERO };
|
||||
|
@ -31,15 +31,17 @@ import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
/**
|
||||
* The CodeComparisonPanel class should be extended by any class that is to be discovered by
|
||||
* the {@link FunctionComparisonPanel} class and included as a form of comparing two sections
|
||||
* of code within the same or different programs.
|
||||
* <br><br>
|
||||
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN "CodeComparisonPanel".
|
||||
* If not, the ClassSearcher will not find them.
|
||||
* The CodeComparisonPanel class should be extended by any class that is to be
|
||||
* discovered by the {@link FunctionComparisonPanel} class and included as a
|
||||
* form of comparing two sections of code within the same or different programs
|
||||
* <p>
|
||||
* NOTE: ALL CodeComparisonPanel CLASSES MUST END IN <
|
||||
* code>CodeComparisonPanel</code> so they are discoverable by the
|
||||
* {@link ClassSearcher}
|
||||
*/
|
||||
public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> extends JPanel
|
||||
implements ExtensionPoint, FocusListener {
|
||||
@ -68,15 +70,18 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
|
||||
protected Function[] functions = new Function[2];
|
||||
protected Data[] data = new Data[2];
|
||||
|
||||
/** If true, the title of each comparison panel will be shown */
|
||||
private boolean showTitles = true;
|
||||
|
||||
private boolean syncScrolling = false;
|
||||
|
||||
private T fieldPanelCoordinator;
|
||||
|
||||
/**
|
||||
* Base constructor
|
||||
* @param owner the name of the owner of this component (typically the name of the plugin that
|
||||
* owns this panel.)
|
||||
* @param tool the tool that contains the component.
|
||||
* Constructor
|
||||
*
|
||||
* @param owner the name of the owner of this component
|
||||
* @param tool the tool that contains the component
|
||||
*/
|
||||
protected CodeComparisonPanel(String owner, PluginTool tool) {
|
||||
this.owner = owner;
|
||||
@ -84,34 +89,38 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
|
||||
}
|
||||
|
||||
/**
|
||||
* The GUI component for this CodeComparisonPanel. A CodeComparisonPanel provides a
|
||||
* dual display with a left and right side for comparing some part of the code for two programs.
|
||||
* @return the component.
|
||||
* The GUI component for this CodeComparisonPanel
|
||||
*
|
||||
* @return the component
|
||||
*/
|
||||
public abstract JComponent getComponent();
|
||||
|
||||
/**
|
||||
* The title for this code comparison component.
|
||||
* @return the title.
|
||||
* The title for this code comparison panel
|
||||
*
|
||||
* @return the title
|
||||
*/
|
||||
public abstract String getTitle();
|
||||
|
||||
/**
|
||||
* Specifies the two programs to be compared by this panel.
|
||||
* @param leftProgram the program for the left side.
|
||||
* @param rightProgram the program for the right side.
|
||||
* Specifies the two programs to be compared by this panel
|
||||
*
|
||||
* @param leftProgram the program for the left side
|
||||
* @param rightProgram the program for the right side
|
||||
*/
|
||||
protected abstract void setPrograms(Program leftProgram, Program rightProgram);
|
||||
|
||||
/**
|
||||
* Displays a comparison of two program's functions.
|
||||
* Displays a comparison of two program's functions
|
||||
*
|
||||
* @param leftFunction the function to show in the left side of the code comparison view
|
||||
* @param rightFunction the function to show in the right side of the code comparison view
|
||||
*/
|
||||
public abstract void loadFunctions(Function leftFunction, Function rightFunction);
|
||||
|
||||
/**
|
||||
* Displays a comparison of two program's data items.
|
||||
* Displays a comparison of two program's data items
|
||||
*
|
||||
* @param leftData the data item to show in the left side of the code comparison view
|
||||
* @param rightData the data item to show in the right side of the code comparison view
|
||||
*/
|
||||
@ -119,7 +128,8 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
|
||||
|
||||
/**
|
||||
* Displays program information for a particular set of addresses in the two programs
|
||||
* being compared.
|
||||
* being compared
|
||||
*
|
||||
* @param leftProgram the program in the left side of the code comparison view
|
||||
* @param rightProgram the program in the right side of the code comparison view
|
||||
* @param leftAddresses the addresses of the program info to show in the left side
|
||||
@ -129,19 +139,21 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
|
||||
AddressSetView leftAddresses, AddressSetView rightAddresses);
|
||||
|
||||
/**
|
||||
* Cleans up resources when this panel is no longer needed.
|
||||
* Cleans up resources when this panel is no longer needed
|
||||
*/
|
||||
public abstract void dispose();
|
||||
|
||||
/**
|
||||
* Enable/disable navigation in this panel using the mouse.
|
||||
* @param enabled false disables mouse navigation.
|
||||
* Enable/disable navigation in this panel using the mouse
|
||||
*
|
||||
* @param enabled false disables mouse navigation
|
||||
*/
|
||||
public abstract void setMouseNavigationEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Get the actions for this CodeComparisonPanel.
|
||||
* @return an array containing the actions
|
||||
* Returns the actions for this panel
|
||||
*
|
||||
* @return an array of docking actions
|
||||
*/
|
||||
public DockingAction[] getActions() {
|
||||
// No actions currently that appear for each CodeComparisonPanel.
|
||||
@ -151,6 +163,14 @@ public abstract class CodeComparisonPanel<T extends FieldPanelCoordinator> exten
|
||||
return actions;
|
||||
}
|
||||
|
||||
public boolean getShowTitles() {
|
||||
return showTitles;
|
||||
}
|
||||
|
||||
public void setShowTitles(boolean showTitles) {
|
||||
this.showTitles = showTitles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this panel is intended to take the place of another and if so it returns
|
||||
* the class of the panel to be superseded.
|
||||
|
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.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;
|
||||
|
||||
/**
|
||||
|
@ -41,7 +41,7 @@ import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
|
||||
/**
|
||||
* Panel that displays two decompilers for comparison.
|
||||
* Panel that displays two decompilers for comparison
|
||||
*/
|
||||
public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFieldPanelCoordinator>
|
||||
extends CodeComparisonPanel<DualDecompilerFieldPanelCoordinator> {
|
||||
@ -68,7 +68,8 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
|
||||
private ProgramLocationListener rightDecompilerLocationListener;
|
||||
|
||||
/**
|
||||
* Creates a comparison panel with two decompilers.
|
||||
* Creates a comparison panel with two decompilers
|
||||
*
|
||||
* @param owner the owner of this panel
|
||||
* @param tool the tool displaying this panel
|
||||
*/
|
||||
@ -189,7 +190,13 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
|
||||
|
||||
loadLeftFunction(leftFunction);
|
||||
loadRightFunction(rightFunction);
|
||||
setTitles(leftFunction, rightFunction);
|
||||
|
||||
if (getShowTitles()) {
|
||||
setTitles(leftFunction, rightFunction);
|
||||
}
|
||||
else {
|
||||
setTitles("", "");
|
||||
}
|
||||
if (dualDecompilerCoordinator != null) {
|
||||
dualDecompilerCoordinator.leftLocationChanged((ProgramLocation) null);
|
||||
}
|
||||
@ -429,6 +436,9 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
|
||||
setDualPanelFocus(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Kick the tool so action buttons will be updated
|
||||
tool.getActiveComponentProvider().contextChanged();
|
||||
}
|
||||
|
||||
private void setDualPanelFocus(int leftOrRight) {
|
||||
|
@ -18,6 +18,7 @@ package docking.widgets;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
@ -30,6 +31,7 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
|
||||
|
||||
private GTable gTable;
|
||||
private T selectedValue;
|
||||
private List<T> selectedValues = new ArrayList<>();
|
||||
private GTableFilterPanel<T> filterPanel;
|
||||
private RowObjectTableModel<T> model;
|
||||
|
||||
@ -55,10 +57,15 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
int selectedRow = gTable.getSelectedRow();
|
||||
if (selectedRow >= 0) {
|
||||
int modelRow = filterPanel.getModelRow(selectedRow);
|
||||
selectedValue = model.getRowObject(modelRow);
|
||||
int[] selectedRows = gTable.getSelectedRows();
|
||||
if (selectedRows.length > 0) {
|
||||
selectedValues.clear();
|
||||
for (int selectedRow : selectedRows) {
|
||||
int modelRow = filterPanel.getModelRow(selectedRow);
|
||||
T rowObject = model.getRowObject(modelRow);
|
||||
selectedValues.add(rowObject);
|
||||
}
|
||||
selectedValue = selectedValues.isEmpty() ? null : selectedValues.get(0);
|
||||
close();
|
||||
}
|
||||
}
|
||||
@ -101,15 +108,26 @@ public class ListSelectionTableDialog<T> extends DialogComponentProvider {
|
||||
return selectedValue;
|
||||
}
|
||||
|
||||
public List<T> getSelectedItems() {
|
||||
return selectedValues;
|
||||
}
|
||||
|
||||
public T show(Component parent) {
|
||||
setMultiSelectionMode(false);
|
||||
DockingWindowManager.showDialog(parent, this);
|
||||
return getSelectedItem();
|
||||
}
|
||||
|
||||
public List<T> showSelectMultiple(Component parent) {
|
||||
setMultiSelectionMode(true);
|
||||
DockingWindowManager.showDialog(parent, this);
|
||||
return getSelectedItems();
|
||||
}
|
||||
|
||||
public void setMultiSelectionMode(boolean enable) {
|
||||
if (enable) {
|
||||
gTable.getSelectionModel().setSelectionMode(
|
||||
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
gTable.getSelectionModel()
|
||||
.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
}
|
||||
else {
|
||||
gTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
@ -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;
|
||||
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* A LineLockedFieldPanelCoordinator coordinates the scrolling of a set of field panels by sharing
|
||||
@ -63,12 +62,12 @@ public class LineLockedFieldPanelCoordinator extends FieldPanelCoordinator {
|
||||
public void setLockedLines(BigInteger[] lockedLineNumbers) {
|
||||
if (lockedLineNumbers.length != this.lockedLineNumbers.length) {
|
||||
throw new AssertException("The number of lines(" + lockedLineNumbers.length +
|
||||
") must exactly match the number of panels(" + this.lockedLineNumbers.length + ").");
|
||||
") must exactly match the number of panels(" + this.lockedLineNumbers.length +
|
||||
").");
|
||||
}
|
||||
for (int i = 0; i < lockedLineNumbers.length; i++) {
|
||||
if (lockedLineNumbers[i] == null) {
|
||||
throw new AssertException("lockedLineNumber for field panel [" + i +
|
||||
"] was unexpectedly null.");
|
||||
lockedLineNumbers[i] = BigInteger.ZERO;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < lockedLineNumbers.length; i++) {
|
||||
@ -99,7 +98,7 @@ public class LineLockedFieldPanelCoordinator extends FieldPanelCoordinator {
|
||||
*/
|
||||
@Override
|
||||
public void remove(FieldPanel fp) {
|
||||
List<BigInteger> lineNumberList = new ArrayList<BigInteger>(panels.length);
|
||||
List<BigInteger> lineNumberList = new ArrayList<>(panels.length);
|
||||
// Adjust our locked line number array.
|
||||
int length = panels.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
@ -46,7 +46,8 @@ public abstract class ComponentProviderAdapter extends ComponentProvider {
|
||||
* @param tool the plugin tool.
|
||||
* @param name The providers name. This is used to group similar providers into a tab within
|
||||
* the same window.
|
||||
* @param owner The owner of this provider, usually a plugin name.
|
||||
* @param owner The owner of this provider, usually a plugin name
|
||||
* @param contextType the type of context supported by this provider; may be null
|
||||
*/
|
||||
public ComponentProviderAdapter(PluginTool tool, String name, String owner,
|
||||
Class<?> contextType) {
|
||||
|
@ -15,12 +15,15 @@
|
||||
*/
|
||||
package help.screenshot;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.awt.Rectangle;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import ghidra.app.plugin.core.functioncompare.*;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingCodeComparisonPanel;
|
||||
@ -30,8 +33,8 @@ import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
@ -52,19 +55,27 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
plugin = getPlugin(tool, FunctionComparisonPlugin.class);
|
||||
|
||||
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
|
||||
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
|
||||
}
|
||||
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionComparisonWindow() {
|
||||
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
|
||||
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
|
||||
|
||||
positionListingTop(0x004118f0);
|
||||
int txId1 = sourceProgram.startTransaction("Modify Program1");
|
||||
int txId2 = destinationProgram.startTransaction("Modify Program2");
|
||||
try {
|
||||
sourceProgram.setName("TestProgram");
|
||||
destinationProgram.setName("OtherProgram");
|
||||
sourceProgram.getDomainFile().setName("FirstProgram");
|
||||
destinationProgram.getDomainFile().setName("SecondProgram");
|
||||
sourceProgram.setName("FirstProgram");
|
||||
destinationProgram.setName("SecondProgram");
|
||||
|
||||
Listing sourceListing = sourceProgram.getListing();
|
||||
Listing destListing = destinationProgram.getListing();
|
||||
Memory sourceMemory = sourceProgram.getMemory();
|
||||
@ -81,100 +92,10 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
|
||||
f2.setName("FunctionB", SourceType.USER_DEFINED);
|
||||
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function[] functions = new Function[] { f1, f2 };
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
providerMgr.showFunctionComparisonProvider(functions);
|
||||
FunctionComparisonPanel functionComparisonPanel =
|
||||
functionComparisonProvider.getComponent();
|
||||
runSwing(() -> {
|
||||
functionComparisonPanel.setCurrentTabbedComponent("Listing View");
|
||||
ListingCodeComparisonPanel dualListing =
|
||||
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
|
||||
ListingPanel leftPanel = dualListing.getLeftPanel();
|
||||
dualListing.setLeftTitle("FunctionA() in /TestProgram");
|
||||
dualListing.setRightTitle("FunctionB() in /OtherProgram");
|
||||
leftPanel.goTo(addr(0x004119aa));
|
||||
|
||||
});
|
||||
waitForSwing();
|
||||
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 550);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
destinationProgram.endTransaction(txId2, false);
|
||||
sourceProgram.endTransaction(txId1, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionComparisonWindowFromMap() throws CircularDependencyException {
|
||||
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
|
||||
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
|
||||
|
||||
positionListingTop(0x004118f0);
|
||||
int txId1 = sourceProgram.startTransaction("Modify Program1");
|
||||
int txId2 = destinationProgram.startTransaction("Modify Program2");
|
||||
try {
|
||||
sourceProgram.setName("TestProgram");
|
||||
destinationProgram.setName("OtherProgram");
|
||||
Listing sourceListing = sourceProgram.getListing();
|
||||
Listing destListing = destinationProgram.getListing();
|
||||
Memory sourceMemory = sourceProgram.getMemory();
|
||||
|
||||
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
|
||||
f1.setName("Function1", SourceType.USER_DEFINED);
|
||||
Namespace parentNamespace = sourceProgram.getSymbolTable().createNameSpace(
|
||||
program.getGlobalNamespace(), "Namespace1", SourceType.USER_DEFINED);
|
||||
f1.setParentNamespace(parentNamespace);
|
||||
sourceListing.setComment(addr(0x004118f0), CodeUnit.PLATE_COMMENT, null);
|
||||
sourceListing.clearCodeUnits(addr(0x004119b1), addr(0x004119b4), false);
|
||||
sourceMemory.setByte(addr(0x004119b2), (byte) 0x55);
|
||||
sourceMemory.setByte(addr(0x004119b4), (byte) 0x52);
|
||||
disassemble(sourceProgram, 0x004119b1, 4, false);
|
||||
|
||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
||||
f2.setName("Function2", SourceType.USER_DEFINED);
|
||||
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function fA = getFunction(sourceProgram, addr(0x00411a30));
|
||||
fA.setName("FunctionA", SourceType.USER_DEFINED);
|
||||
sourceListing.setComment(addr(0x00411a30), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function fB = getFunction(destinationProgram, addr(0x00411a10));
|
||||
fB.setName("FunctionB", SourceType.USER_DEFINED);
|
||||
destListing.setComment(addr(0x00411a10), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function fC = getFunction(sourceProgram, addr(0x00411ab0));
|
||||
fC.setName("FunctionC", SourceType.USER_DEFINED);
|
||||
sourceListing.setComment(addr(0x00411ab0), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function fD = getFunction(destinationProgram, addr(0x00411a90));
|
||||
fD.setName("FunctionD", SourceType.USER_DEFINED);
|
||||
destListing.setComment(addr(0x00411a90), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
HashMap<Function, HashSet<Function>> functionMap = new HashMap<>();
|
||||
HashSet<Function> functionSet = new HashSet<>();
|
||||
functionSet.add(fA);
|
||||
functionSet.add(fB);
|
||||
functionMap.put(f1, functionSet);
|
||||
functionSet = new HashSet<>();
|
||||
functionSet.add(fC);
|
||||
functionSet.add(fD);
|
||||
functionMap.put(f2, functionSet);
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
providerMgr.showFunctionComparisonProvider(functionMap);
|
||||
providerMgr.compareFunctions(f1, f2);
|
||||
FunctionComparisonPanel functionComparisonPanel =
|
||||
functionComparisonProvider.getComponent();
|
||||
runSwing(() -> {
|
||||
@ -183,12 +104,12 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
|
||||
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
|
||||
ListingPanel leftPanel = dualListing.getLeftPanel();
|
||||
leftPanel.goTo(addr(0x004119aa));
|
||||
|
||||
});
|
||||
waitForSwing();
|
||||
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 550);
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException | MemoryAccessException e) {
|
||||
catch (DuplicateNameException | InvalidInputException | MemoryAccessException
|
||||
| InvalidNameException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
@ -198,136 +119,102 @@ public class FunctionComparisonScreenShots extends GhidraScreenShotGenerator {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListingCodeComparisonOptions() {
|
||||
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
|
||||
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
|
||||
public void testAddToComparisonIcon() {
|
||||
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
|
||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
||||
|
||||
positionListingTop(0x004118f0);
|
||||
int txId1 = sourceProgram.startTransaction("Modify Program1");
|
||||
int txId2 = destinationProgram.startTransaction("Modify Program2");
|
||||
try {
|
||||
sourceProgram.setName("TestProgram");
|
||||
destinationProgram.setName("OtherProgram");
|
||||
Listing sourceListing = sourceProgram.getListing();
|
||||
Listing destListing = destinationProgram.getListing();
|
||||
Memory sourceMemory = sourceProgram.getMemory();
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
providerMgr.compareFunctions(f1, f2);
|
||||
|
||||
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
|
||||
f1.setName("FunctionA", SourceType.USER_DEFINED);
|
||||
sourceListing.setComment(addr(0x004118f0), CodeUnit.PLATE_COMMENT, null);
|
||||
sourceListing.clearCodeUnits(addr(0x004119b1), addr(0x004119b4), false);
|
||||
sourceMemory.setByte(addr(0x004119b2), (byte) 0x55);
|
||||
sourceMemory.setByte(addr(0x004119b4), (byte) 0x52);
|
||||
disassemble(sourceProgram, 0x004119b1, 4, false);
|
||||
waitForSwing();
|
||||
|
||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
||||
f2.setName("FunctionB", SourceType.USER_DEFINED);
|
||||
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function[] functions = new Function[] { f1, f2 };
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
providerMgr.showFunctionComparisonProvider(functions);
|
||||
FunctionComparisonPanel functionComparisonPanel =
|
||||
functionComparisonProvider.getComponent();
|
||||
runSwing(() -> {
|
||||
functionComparisonPanel.setCurrentTabbedComponent("Listing View");
|
||||
});
|
||||
waitForSwing();
|
||||
|
||||
ComponentProvider provider = getProvider("Function Comparison");
|
||||
performAction("Listing Code Comparison Options", "Function Comparison", provider,
|
||||
false);
|
||||
|
||||
captureDialog(600, 300);
|
||||
pressButtonByText(getDialog(), "Cancel");
|
||||
|
||||
waitForSwing();
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
destinationProgram.endTransaction(txId2, false);
|
||||
sourceProgram.endTransaction(txId1, false);
|
||||
}
|
||||
captureProvider(functionComparisonProvider);
|
||||
crop(new Rectangle(261, 2, 24, 24));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiFunctionComparisonWindow() {
|
||||
destinationProgram = loadProgram(TEST_DESTINATION_PROGRAM_NAME);
|
||||
sourceProgram = loadProgram(TEST_SOURCE_PROGRAM_NAME);
|
||||
public void testRemoveFromComparisonIcon() {
|
||||
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
|
||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
||||
|
||||
positionListingTop(0x004118f0);
|
||||
int txId1 = sourceProgram.startTransaction("Modify Program1");
|
||||
int txId2 = destinationProgram.startTransaction("Modify Program2");
|
||||
try {
|
||||
sourceProgram.setName("TestProgram");
|
||||
destinationProgram.setName("OtherProgram");
|
||||
Listing sourceListing = sourceProgram.getListing();
|
||||
Listing destListing = destinationProgram.getListing();
|
||||
Memory sourceMemory = sourceProgram.getMemory();
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
providerMgr.compareFunctions(f1, f2);
|
||||
|
||||
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
|
||||
f1.setName("FunctionA", SourceType.USER_DEFINED);
|
||||
sourceListing.setComment(addr(0x004118f0), CodeUnit.PLATE_COMMENT, null);
|
||||
sourceListing.clearCodeUnits(addr(0x004119b1), addr(0x004119b4), false);
|
||||
sourceMemory.setByte(addr(0x004119b2), (byte) 0x55);
|
||||
sourceMemory.setByte(addr(0x004119b4), (byte) 0x52);
|
||||
disassemble(sourceProgram, 0x004119b1, 4, false);
|
||||
waitForSwing();
|
||||
|
||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
||||
f2.setName("FunctionB", SourceType.USER_DEFINED);
|
||||
destListing.setComment(addr(0x004118c0), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function f3 = getFunction(sourceProgram, addr(0x004117c0));
|
||||
f3.setName("FunctionC", SourceType.USER_DEFINED);
|
||||
sourceListing.setComment(addr(0x004117c0), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function f4 = getFunction(destinationProgram, addr(0x004117b0));
|
||||
f4.setName("FunctionD", SourceType.USER_DEFINED);
|
||||
destListing.setComment(addr(0x004117b0), CodeUnit.PLATE_COMMENT, null);
|
||||
|
||||
Function[] functions = new Function[] { f1, f2, f3, f4 };
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
providerMgr.showFunctionComparisonProvider(functions);
|
||||
FunctionComparisonPanel functionComparisonPanel =
|
||||
functionComparisonProvider.getComponent();
|
||||
runSwing(() -> {
|
||||
functionComparisonPanel.setCurrentTabbedComponent("Listing View");
|
||||
ListingCodeComparisonPanel dualListing =
|
||||
(ListingCodeComparisonPanel) functionComparisonPanel.getDisplayedPanel();
|
||||
ListingPanel leftPanel = dualListing.getLeftPanel();
|
||||
leftPanel.goTo(addr(0x004119a5));
|
||||
|
||||
});
|
||||
waitForSwing();
|
||||
captureIsolatedProvider(FunctionComparisonProvider.class, 1200, 598);
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException | MemoryAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
destinationProgram.endTransaction(txId2, false);
|
||||
sourceProgram.endTransaction(txId1, false);
|
||||
}
|
||||
captureProvider(functionComparisonProvider);
|
||||
crop(new Rectangle(351, 2, 24, 24));
|
||||
}
|
||||
|
||||
Function getFunction(Program program1, Address entryPoint) {
|
||||
@Test
|
||||
public void testNavNextIcon() {
|
||||
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
|
||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
||||
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
Set<Function> functions = new HashSet<>();
|
||||
functions.add(f1);
|
||||
functions.add(f2);
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
providerMgr.compareFunctions(functions);
|
||||
|
||||
waitForSwing();
|
||||
|
||||
captureProvider(functionComparisonProvider);
|
||||
crop(new Rectangle(293, 2, 24, 24));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNavPreviousIcon() {
|
||||
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
|
||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
||||
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
Set<Function> functions = new HashSet<>();
|
||||
functions.add(f1);
|
||||
functions.add(f2);
|
||||
FunctionComparisonProvider functionComparisonProvider =
|
||||
providerMgr.compareFunctions(functions);
|
||||
MultiFunctionComparisonPanel panel =
|
||||
(MultiFunctionComparisonPanel) functionComparisonProvider.getComponent();
|
||||
panel.getFocusedComponent().setSelectedIndex(1);
|
||||
|
||||
waitForSwing();
|
||||
|
||||
captureProvider(functionComparisonProvider);
|
||||
crop(new Rectangle(315, 2, 24, 24));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddFunctionsPanel() {
|
||||
Function f1 = getFunction(sourceProgram, addr(0x004118f0));
|
||||
Function f2 = getFunction(destinationProgram, addr(0x004118c0));
|
||||
|
||||
FunctionComparisonProviderManager providerMgr =
|
||||
getInstanceFieldByClassType(FunctionComparisonProviderManager.class, plugin);
|
||||
Set<Function> functions = new HashSet<>();
|
||||
functions.add(f1);
|
||||
functions.add(f2);
|
||||
providerMgr.compareFunctions(functions);
|
||||
|
||||
DockingActionIf openTableAction = getAction(plugin, "Add Functions To Comparison");
|
||||
performAction(openTableAction);
|
||||
DialogComponentProvider table = waitForDialogComponent("Select Functions");
|
||||
captureDialog(table);
|
||||
}
|
||||
|
||||
private Function getFunction(Program program1, Address entryPoint) {
|
||||
FunctionManager functionManager = program1.getFunctionManager();
|
||||
return functionManager.getFunctionAt(entryPoint);
|
||||
}
|
||||
|
||||
public void disassemble(Program pgm1, long addressAsLong, int length, boolean followFlows) {
|
||||
private void disassemble(Program pgm1, long addressAsLong, int length, boolean followFlows) {
|
||||
Address address = addr(addressAsLong);
|
||||
DisassembleCommand cmd = new DisassembleCommand(address,
|
||||
new AddressSet(address, address.add(length - 1)), followFlows);
|
||||
|