mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-3696 - cleaning up function compare windows.
This commit is contained in:
parent
770f5447e1
commit
5ea8e97805
77 changed files with 4065 additions and 5654 deletions
|
@ -1,262 +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.codecompare;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import docking.action.ToolBarData;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.decompiler.component.DecompileData;
|
||||
import ghidra.app.decompiler.component.DecompilerCodeComparisonPanel;
|
||||
import ghidra.app.decompiler.component.DualDecompileResultsListener;
|
||||
import ghidra.app.decompiler.component.DualDecompilerActionContext;
|
||||
import ghidra.codecompare.graphanalysis.TokenBin;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskBuilder;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
import ghidra.util.task.TaskListener;
|
||||
import resources.Icons;
|
||||
import resources.MultiIcon;
|
||||
|
||||
/**
|
||||
* This is a CodeComparisonPanel that gets discovered by other providers that display dual
|
||||
* comparison views.<br>
|
||||
* Note: Therefore there may not be any other classes that refer directly to it.
|
||||
*/
|
||||
public class DecompilerDiffCodeComparisonPanel
|
||||
extends DecompilerCodeComparisonPanel<CodeDiffFieldPanelCoordinator>
|
||||
implements DualDecompileResultsListener, OptionsChangeListener {
|
||||
|
||||
public static final String CODE_DIFF_VIEW = "Decompiler Diff View";
|
||||
private static final String HELP_TOPIC = "FunctionComparison";
|
||||
private DecompileDataDiff decompileDataDiff;
|
||||
private DiffClangHighlightController leftHighlightController;
|
||||
private DiffClangHighlightController rightHighlightController;
|
||||
private CodeDiffFieldPanelCoordinator decompilerFieldPanelCoordinator;
|
||||
private MyToggleExactConstantMatching toggleExactConstantMatchingAction;
|
||||
private boolean isMatchingConstantsExactly = true;
|
||||
private boolean toggleFlagWhenLastVisible = isMatchingConstantsExactly;
|
||||
private CompareFuncsFromMatchedTokensAction compareFuncsAction;
|
||||
private DecompilerCodeComparisonOptions comparisonOptions;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param owner owner
|
||||
* @param tool tool
|
||||
*/
|
||||
public DecompilerDiffCodeComparisonPanel(String owner, PluginTool tool) {
|
||||
super(owner, tool);
|
||||
|
||||
comparisonOptions = new DecompilerCodeComparisonOptions();
|
||||
initializeOptions();
|
||||
leftHighlightController = new DiffClangHighlightController(comparisonOptions);
|
||||
rightHighlightController = new DiffClangHighlightController(comparisonOptions);
|
||||
setHighlightControllers(leftHighlightController, rightHighlightController);
|
||||
|
||||
// Make the left highlight listen to the right.
|
||||
leftHighlightController.addListener(rightHighlightController);
|
||||
// Make the right highlight listen to the left.
|
||||
rightHighlightController.addListener(leftHighlightController);
|
||||
|
||||
addDualDecompileResultsListener(this);
|
||||
decompilerFieldPanelCoordinator = new CodeDiffFieldPanelCoordinator(this);
|
||||
setFieldPanelCoordinator(decompilerFieldPanelCoordinator);
|
||||
|
||||
}
|
||||
|
||||
private void initializeOptions() {
|
||||
ToolOptions options =
|
||||
tool.getOptions(DecompilerCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
|
||||
options.addOptionsChangeListener(this);
|
||||
comparisonOptions.registerOptions(options);
|
||||
comparisonOptions.loadOptions(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
comparisonOptions.loadOptions(options);
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean aFlag) {
|
||||
if (aFlag == isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (aFlag) {
|
||||
// Becoming visible.
|
||||
if (toggleFlagWhenLastVisible != isMatchingConstantsExactly) {
|
||||
if (decompileDataDiff != null) {
|
||||
determineDecompilerDifferences();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No longer visible.
|
||||
toggleFlagWhenLastVisible = isMatchingConstantsExactly;
|
||||
}
|
||||
super.setVisible(aFlag);
|
||||
updateActionEnablement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return CODE_DIFF_VIEW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompileResultsSet(DecompileData leftDecompileResults,
|
||||
DecompileData rightDecompileResults) {
|
||||
|
||||
if ((leftDecompileResults == null) || (rightDecompileResults == null) ||
|
||||
(leftDecompileResults.getFunction() == null) ||
|
||||
(rightDecompileResults.getFunction() == null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
decompileDataDiff = new DecompileDataDiff(leftDecompileResults, rightDecompileResults);
|
||||
determineDecompilerDifferences();
|
||||
}
|
||||
|
||||
List<TokenBin> getHighBins() {
|
||||
return decompilerFieldPanelCoordinator.getHighBins();
|
||||
}
|
||||
|
||||
private void determineDecompilerDifferences() {
|
||||
if (decompileDataDiff == null) {
|
||||
return;
|
||||
}
|
||||
DetermineDecompilerDifferencesTask task =
|
||||
new DetermineDecompilerDifferencesTask(decompileDataDiff, isMatchingConstantsExactly,
|
||||
leftHighlightController, rightHighlightController, decompilerFieldPanelCoordinator);
|
||||
|
||||
task.addTaskListener(new TaskListener() {
|
||||
|
||||
@Override
|
||||
public void taskCompleted(Task completedTask) {
|
||||
// Does this need anything here?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskCancelled(Task cancelledTask) {
|
||||
// Does this need anything here?
|
||||
}
|
||||
});
|
||||
|
||||
new TaskLauncher(task, getComponent());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createActions() {
|
||||
super.createActions();
|
||||
toggleExactConstantMatchingAction = new MyToggleExactConstantMatching(getClass().getName());
|
||||
compareFuncsAction = new CompareFuncsFromMatchedTokensAction(this, tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockingAction[] getActions() {
|
||||
DockingAction[] parentActions = super.getActions();
|
||||
DockingAction[] myActions =
|
||||
new DockingAction[] { toggleExactConstantMatchingAction, compareFuncsAction };
|
||||
DockingAction[] allActions = new DockingAction[parentActions.length + myActions.length];
|
||||
System.arraycopy(parentActions, 0, allActions, 0, parentActions.length);
|
||||
System.arraycopy(myActions, 0, allActions, parentActions.length, myActions.length);
|
||||
return allActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActionEnablement() {
|
||||
// Need to enable/disable toolbar button.
|
||||
toggleExactConstantMatchingAction.setEnabled(isVisible());
|
||||
}
|
||||
|
||||
public class MyToggleExactConstantMatching extends ToggleDockingAction {
|
||||
|
||||
private final Icon EXACT_CONSTANT_MATCHING_ICON = new GIcon("icon.base.source.c");
|
||||
private final Icon NO_EXACT_CONSTANT_MATCHING_ICON =
|
||||
new MultiIcon(EXACT_CONSTANT_MATCHING_ICON, Icons.NOT_ALLOWED_ICON);
|
||||
|
||||
/**
|
||||
* Creates an action for toggling exact constant matching in the code diff's
|
||||
* dual decompiler.
|
||||
* @param owner the owner of this action (typically the provider).
|
||||
*/
|
||||
public MyToggleExactConstantMatching(String owner) {
|
||||
super("Toggle Exact Constant Matching", owner);
|
||||
setHelpLocation(new HelpLocation(HELP_TOPIC, "Toggle Exact Constant Matching"));
|
||||
|
||||
this.setToolBarData(new ToolBarData(NO_EXACT_CONSTANT_MATCHING_ICON, "toggles"));
|
||||
|
||||
setDescription(HTMLUtilities.toHTML("Toggle whether or not constants must\n" +
|
||||
"be exactly the same value to be a match\n" + "in the " + CODE_DIFF_VIEW + "."));
|
||||
setSelected(false);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (context instanceof DualDecompilerActionContext) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
isMatchingConstantsExactly = !isSelected();
|
||||
if (DecompilerDiffCodeComparisonPanel.this.isVisible()) {
|
||||
DecompilerDiffCodeComparisonPanel.this.determineDecompilerDifferences();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(boolean selected) {
|
||||
getToolBarData().setIcon(
|
||||
selected ? NO_EXACT_CONSTANT_MATCHING_ICON : EXACT_CONSTANT_MATCHING_ICON);
|
||||
super.setSelected(selected);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CodeDiffFieldPanelCoordinator createFieldPanelCoordinator() {
|
||||
CodeDiffFieldPanelCoordinator coordinator = new CodeDiffFieldPanelCoordinator(this);
|
||||
if (decompileDataDiff != null) {
|
||||
TaskBuilder.withRunnable(monitor -> {
|
||||
try {
|
||||
coordinator.replaceDecompileDataDiff(decompileDataDiff,
|
||||
isMatchingConstantsExactly, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
coordinator.clearLineNumberPairing();
|
||||
}
|
||||
}).setTitle("Initializing Code Compare").launchNonModal();
|
||||
}
|
||||
return coordinator;
|
||||
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ import generic.theme.GColor;
|
|||
import ghidra.app.decompiler.ClangSyntaxToken;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
import ghidra.codecompare.decompile.DecompilerCodeComparisonOptions;
|
||||
import ghidra.codecompare.graphanalysis.TokenBin;
|
||||
import ghidra.util.ColorUtils;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.codecompare;
|
||||
package ghidra.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -22,16 +24,17 @@ import docking.ActionContext;
|
|||
import docking.action.DockingAction;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.DecompilerLocation;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.codecompare.graphanalysis.TokenBin;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
/**
|
||||
* This is a base class for actions in a {@link DecompilerDiffCodeComparisonPanel}
|
||||
*/
|
||||
public abstract class AbstractMatchedTokensAction extends DockingAction {
|
||||
|
||||
protected DecompilerDiffCodeComparisonPanel diffPanel;
|
||||
protected DecompilerCodeComparisonPanel diffPanel;
|
||||
protected boolean disableOnReadOnly;
|
||||
|
||||
/**
|
||||
|
@ -43,7 +46,7 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
|
|||
* @param disableOnReadOnly if true, action will be disabled for read-only programs
|
||||
*/
|
||||
public AbstractMatchedTokensAction(String actionName, String owner,
|
||||
DecompilerDiffCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
|
||||
DecompilerCodeComparisonPanel diffPanel, boolean disableOnReadOnly) {
|
||||
super(actionName, owner);
|
||||
this.diffPanel = diffPanel;
|
||||
this.disableOnReadOnly = disableOnReadOnly;
|
||||
|
@ -63,26 +66,20 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
|
|||
if (!(context instanceof DualDecompilerActionContext compareContext)) {
|
||||
return false;
|
||||
}
|
||||
if (!(compareContext
|
||||
.getCodeComparisonPanel() instanceof DecompilerCodeComparisonPanel decompPanel)) {
|
||||
return false;
|
||||
}
|
||||
DecompilerCodeComparisonPanel decompPanel = compareContext.getCodeComparisonPanel();
|
||||
|
||||
if (disableOnReadOnly) {
|
||||
//get the program corresponding to the panel with focus
|
||||
Program program = decompPanel.getLeftProgram();
|
||||
Side focusedSide = decompPanel.getActiveSide();
|
||||
Program program = decompPanel.getProgram(focusedSide);
|
||||
if (program == null) {
|
||||
return false; //panel initializing; don't enable action
|
||||
}
|
||||
if (!decompPanel.leftPanelHasFocus()) {
|
||||
program = decompPanel.getRightProgram();
|
||||
}
|
||||
if (!program.canSave()) {
|
||||
return false; //program is read-only, don't enable action
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
TokenPair currentPair = getCurrentTokenPair(decompPanel);
|
||||
return enabledForTokens(currentPair);
|
||||
|
||||
|
@ -96,9 +93,9 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
|
|||
* @return matching tokens (or null if no match)
|
||||
*/
|
||||
protected TokenPair getCurrentTokenPair(
|
||||
DecompilerCodeComparisonPanel<? extends DualDecompilerFieldPanelCoordinator> decompPanel) {
|
||||
DecompilerCodeComparisonPanel decompPanel) {
|
||||
|
||||
DecompilerPanel focusedPanel = decompPanel.getFocusedDecompilerPanel().getDecompilerPanel();
|
||||
DecompilerPanel focusedPanel = decompPanel.getActiveDisplay().getDecompilerPanel();
|
||||
|
||||
if (!(focusedPanel.getCurrentLocation() instanceof DecompilerLocation focusedLocation)) {
|
||||
return null;
|
||||
|
@ -126,7 +123,8 @@ public abstract class AbstractMatchedTokensAction extends DockingAction {
|
|||
while (tokenIter.hasNext()) {
|
||||
ClangToken currentMatch = tokenIter.next();
|
||||
if (currentMatch.getClass().equals(focusedToken.getClass())) {
|
||||
return decompPanel.leftPanelHasFocus() ? new TokenPair(focusedToken, currentMatch)
|
||||
return decompPanel.getActiveSide() == LEFT
|
||||
? new TokenPair(focusedToken, currentMatch)
|
||||
: new TokenPair(currentMatch, focusedToken);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/* ###
|
||||
* 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.codecompare.decompile;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.decompiler.DecompileOptions;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
import ghidra.codecompare.DiffClangHighlightController;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* Represents one side of a dual decompiler compare window. It holds the decompiler controller and
|
||||
* related state information for one side.
|
||||
*/
|
||||
public class CDisplay {
|
||||
private final static String OPTIONS_TITLE = "Decompiler";
|
||||
|
||||
private DecompilerController controller;
|
||||
private DecompileOptions decompileOptions;
|
||||
private FieldLocation lastCursorPosition;
|
||||
private DiffClangHighlightController highlightController;
|
||||
private Program program;
|
||||
|
||||
private DecompilerProgramListener programListener;
|
||||
|
||||
public CDisplay(DecompilerCodeComparisonOptions comparisonOptions,
|
||||
DecompileResultsListener decompileListener,
|
||||
Consumer<ProgramLocation> locationConsumer) {
|
||||
|
||||
highlightController = new DiffClangHighlightController(comparisonOptions);
|
||||
|
||||
decompileOptions = new DecompileOptions();
|
||||
|
||||
DecompilerCallbackHandler handler = new DecompilerCallbackHandlerAdapter() {
|
||||
@Override
|
||||
public void locationChanged(ProgramLocation programLocation) {
|
||||
locationConsumer.accept(programLocation);
|
||||
}
|
||||
};
|
||||
|
||||
controller = new DecompilerController(handler, decompileOptions, null) {
|
||||
@Override
|
||||
public void setDecompileData(DecompileData decompileData) {
|
||||
super.setDecompileData(decompileData);
|
||||
decompileListener.setDecompileData(decompileData);
|
||||
controller.getDecompilerPanel().validate();
|
||||
}
|
||||
};
|
||||
controller.getDecompilerPanel().setHighlightController(highlightController);
|
||||
|
||||
programListener = new DecompilerProgramListener(controller, () -> refresh());
|
||||
|
||||
}
|
||||
|
||||
public DecompilerPanel getDecompilerPanel() {
|
||||
return controller.getDecompilerPanel();
|
||||
}
|
||||
|
||||
private void updateProgram(PluginTool tool, Function function) {
|
||||
Program newProgram = function == null ? null : function.getProgram();
|
||||
if (program == newProgram) {
|
||||
return;
|
||||
}
|
||||
if (program != null) {
|
||||
program.removeListener(programListener);
|
||||
}
|
||||
|
||||
program = newProgram;
|
||||
|
||||
if (program != null) {
|
||||
program.addListener(programListener);
|
||||
initializeOptions(tool, function);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void showFunction(PluginTool tool, Function function) {
|
||||
updateProgram(tool, function);
|
||||
|
||||
lastCursorPosition = null;
|
||||
if (function == null) {
|
||||
clearAndShowMessage("No Function");
|
||||
return;
|
||||
}
|
||||
if (function.isExternal()) {
|
||||
clearAndShowMessage("\"" + function.getName(true) + "\" is an external function.");
|
||||
return;
|
||||
}
|
||||
|
||||
Address entry = function.getEntryPoint();
|
||||
ProgramLocation location = new ProgramLocation(program, entry);
|
||||
controller.display(program, location, new ViewerPosition(0, 0, 0));
|
||||
}
|
||||
|
||||
public void clearAndShowMessage(String message) {
|
||||
controller.setDecompileData(new EmptyDecompileData(message));
|
||||
DecompilerPanel decompilerPanel = getDecompilerPanel();
|
||||
decompilerPanel.paintImmediately(decompilerPanel.getBounds());
|
||||
}
|
||||
|
||||
public void setMouseNavigationEnabled(boolean enabled) {
|
||||
controller.setMouseNavigationEnabled(enabled);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (program != null) {
|
||||
program.removeListener(programListener);
|
||||
program = null;
|
||||
}
|
||||
programListener.dispose();
|
||||
controller.dispose();
|
||||
}
|
||||
|
||||
public DecompilerController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
saveCursorPosition();
|
||||
DecompileData data = getDecompileData();
|
||||
if (data != null) {
|
||||
controller.refreshDisplay(data.getProgram(), data.getLocation(), null);
|
||||
}
|
||||
}
|
||||
|
||||
public void programClosed(Program closedProgram) {
|
||||
if (closedProgram == this.program) {
|
||||
controller.clear();
|
||||
controller.programClosed(closedProgram);
|
||||
updateProgram(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return controller.isDecompiling();
|
||||
}
|
||||
|
||||
public DecompileData getDecompileData() {
|
||||
DecompileData decompileData = controller.getDecompileData();
|
||||
if (decompileData instanceof EmptyDecompileData) {
|
||||
return null;
|
||||
}
|
||||
return decompileData;
|
||||
}
|
||||
|
||||
public void initializeOptions(PluginTool tool, Function function) {
|
||||
if (tool == null) {
|
||||
return;
|
||||
}
|
||||
ToolOptions fieldOptions = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS);
|
||||
ToolOptions options = tool.getOptions(OPTIONS_TITLE);
|
||||
Program program = function == null ? null : function.getProgram();
|
||||
decompileOptions.grabFromToolAndProgram(fieldOptions, options, program);
|
||||
}
|
||||
|
||||
DiffClangHighlightController getHighlightController() {
|
||||
return highlightController;
|
||||
}
|
||||
|
||||
private void saveCursorPosition() {
|
||||
lastCursorPosition = getDecompilerPanel().getFieldPanel().getCursorLocation();
|
||||
}
|
||||
|
||||
void restoreCursorPosition() {
|
||||
if (lastCursorPosition != null) {
|
||||
BigInteger index = lastCursorPosition.getIndex();
|
||||
int fieldNum = lastCursorPosition.getFieldNum();
|
||||
int row = lastCursorPosition.getRow();
|
||||
int col = lastCursorPosition.getCol();
|
||||
|
||||
FieldPanel fieldPanel = getDecompilerPanel().getFieldPanel();
|
||||
fieldPanel.setCursorPosition(index, fieldNum, row, col);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -13,7 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.codecompare;
|
||||
package ghidra.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
|
@ -25,7 +27,6 @@ import org.apache.commons.collections4.bidimap.DualHashBidiMap;
|
|||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.decompiler.component.DualDecompilerFieldPanelCoordinator;
|
||||
import ghidra.codecompare.graphanalysis.TokenBin;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
@ -52,10 +53,10 @@ public class CodeDiffFieldPanelCoordinator extends DualDecompilerFieldPanelCoord
|
|||
* Constructor
|
||||
* @param dualDecompilerPanel decomp comparison panel
|
||||
*/
|
||||
public CodeDiffFieldPanelCoordinator(DecompilerDiffCodeComparisonPanel dualDecompilerPanel) {
|
||||
public CodeDiffFieldPanelCoordinator(DecompilerCodeComparisonPanel dualDecompilerPanel) {
|
||||
super(dualDecompilerPanel);
|
||||
this.leftDecompilerPanel = dualDecompilerPanel.getLeftDecompilerPanel();
|
||||
this.rightDecompilerPanel = dualDecompilerPanel.getRightDecompilerPanel();
|
||||
this.leftDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(LEFT);
|
||||
this.rightDecompilerPanel = dualDecompilerPanel.getDecompilerPanel(RIGHT);
|
||||
leftToRightLineNumberPairing = new DualHashBidiMap<>();
|
||||
}
|
||||
|
|
@ -13,13 +13,13 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.codecompare;
|
||||
package ghidra.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.decompiler.ClangFuncNameToken;
|
||||
import ghidra.app.decompiler.component.DecompilerCodeComparisonPanel;
|
||||
import ghidra.app.decompiler.component.DualDecompilerActionContext;
|
||||
import ghidra.app.plugin.core.functioncompare.FunctionComparisonProvider;
|
||||
import ghidra.app.services.FunctionComparisonService;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -45,7 +45,7 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
|
|||
* @param diffPanel diff Panel
|
||||
* @param tool tool
|
||||
*/
|
||||
public CompareFuncsFromMatchedTokensAction(DecompilerDiffCodeComparisonPanel diffPanel,
|
||||
public CompareFuncsFromMatchedTokensAction(DecompilerCodeComparisonPanel diffPanel,
|
||||
PluginTool tool) {
|
||||
super(ACTION_NAME, tool.getName(), diffPanel, false);
|
||||
this.tool = tool;
|
||||
|
@ -84,12 +84,8 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
|
|||
return;
|
||||
}
|
||||
|
||||
if (!(compareContext
|
||||
.getCodeComparisonPanel() instanceof DecompilerCodeComparisonPanel decompPanel)) {
|
||||
return;
|
||||
}
|
||||
DecompilerCodeComparisonPanel decompPanel = compareContext.getCodeComparisonPanel();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
TokenPair currentPair = getCurrentTokenPair(decompPanel);
|
||||
if (currentPair == null || currentPair.leftToken() == null ||
|
||||
currentPair.rightToken() == null) {
|
||||
|
@ -99,8 +95,8 @@ public class CompareFuncsFromMatchedTokensAction extends AbstractMatchedTokensAc
|
|||
ClangFuncNameToken leftFuncToken = (ClangFuncNameToken) currentPair.leftToken();
|
||||
ClangFuncNameToken rightFuncToken = (ClangFuncNameToken) currentPair.rightToken();
|
||||
|
||||
Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getLeftProgram());
|
||||
Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getRightProgram());
|
||||
Function leftFunction = getFuncFromToken(leftFuncToken, decompPanel.getProgram(LEFT));
|
||||
Function rightFunction = getFuncFromToken(rightFuncToken, decompPanel.getProgram(RIGHT));
|
||||
if (leftFunction == null || rightFunction == null) {
|
||||
return;
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.codecompare;
|
||||
package ghidra.codecompare.decompile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
|
@ -13,18 +13,23 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.codecompare;
|
||||
package ghidra.codecompare.decompile;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import generic.theme.GColor;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.bean.opteditor.OptionsVetoException;
|
||||
import utility.function.Callback;
|
||||
import utility.function.Dummy;
|
||||
|
||||
/**
|
||||
* This class holds the options for the decompiler diff view.
|
||||
*/
|
||||
public class DecompilerCodeComparisonOptions {
|
||||
public class DecompilerCodeComparisonOptions implements OptionsChangeListener {
|
||||
|
||||
private static final String MATCHING_TOKEN_HIGHLIGHT_KEY = "Focused Token Match Highlight";
|
||||
private static final String UNMATCHED_TOKEN_HIGHLIGHT_KEY = "Focused Token Unmatched Highlight";
|
||||
|
@ -53,15 +58,18 @@ public class DecompilerCodeComparisonOptions {
|
|||
private Color unmatchedTokenHighlight;
|
||||
private Color ineligibleTokenHighlight;
|
||||
private Color diffHighlight;
|
||||
private Callback optionsChangedCallback;
|
||||
|
||||
public static final String OPTIONS_CATEGORY_NAME = "Decompiler Code Comparison";
|
||||
public static final String HELP_TOPIC = "FunctionComparison";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DecompilerCodeComparisonOptions() {
|
||||
|
||||
public DecompilerCodeComparisonOptions(PluginTool tool, Callback optionsChangedCallback) {
|
||||
this.optionsChangedCallback = Dummy.ifNull(optionsChangedCallback);
|
||||
ToolOptions options =
|
||||
tool.getOptions(DecompilerCodeComparisonOptions.OPTIONS_CATEGORY_NAME);
|
||||
options.addOptionsChangeListener(this);
|
||||
registerOptions(options);
|
||||
loadOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,4 +144,11 @@ public class DecompilerCodeComparisonOptions {
|
|||
return diffHighlight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
|
||||
Object newValue) throws OptionsVetoException {
|
||||
loadOptions(options);
|
||||
optionsChangedCallback.call();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,395 @@
|
|||
/* ###
|
||||
* 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.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.*;
|
||||
import docking.options.OptionsService;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.decompiler.component.DecompileData;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.util.viewer.util.CodeComparisonPanel;
|
||||
import ghidra.codecompare.DiffClangHighlightController;
|
||||
import ghidra.codecompare.graphanalysis.TokenBin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskBuilder;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
import resources.Icons;
|
||||
import resources.MultiIcon;
|
||||
|
||||
/**
|
||||
* Panel that displays two decompilers for comparison
|
||||
*/
|
||||
public class DecompilerCodeComparisonPanel
|
||||
extends CodeComparisonPanel {
|
||||
|
||||
public static final String NAME = "Decompile Diff View";
|
||||
|
||||
private boolean isStale = true;
|
||||
|
||||
private Duo<CDisplay> cDisplays = new Duo<>();
|
||||
|
||||
private DecompilerCodeComparisonOptions comparisonOptions;
|
||||
private CodeDiffFieldPanelCoordinator coordinator;
|
||||
private DecompileDataDiff decompileDataDiff;
|
||||
|
||||
private ToggleExactConstantMatching toggleExactConstantMatchingAction;
|
||||
private List<DockingAction> actions = new ArrayList<>();
|
||||
private Duo<Function> functions = new Duo<>();
|
||||
|
||||
/**
|
||||
* Creates a comparison panel with two decompilers
|
||||
*
|
||||
* @param owner the owner of this panel
|
||||
* @param tool the tool displaying this panel
|
||||
*/
|
||||
public DecompilerCodeComparisonPanel(String owner, PluginTool tool) {
|
||||
super(owner, tool);
|
||||
comparisonOptions = new DecompilerCodeComparisonOptions(tool, () -> repaint());
|
||||
|
||||
cDisplays = new Duo<>(buildCDisplay(LEFT), buildCDisplay(RIGHT));
|
||||
cDisplays.get(LEFT).initializeOptions(tool, getFunction(LEFT));
|
||||
cDisplays.get(RIGHT).initializeOptions(tool, getFunction(RIGHT));
|
||||
|
||||
buildPanel();
|
||||
setSynchronizedScrolling(true);
|
||||
linkHighlightControllers();
|
||||
createActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void comparisonDataChanged() {
|
||||
|
||||
maybeLoadFunction(LEFT, comparisonData.get(LEFT).getFunction());
|
||||
maybeLoadFunction(RIGHT, comparisonData.get(RIGHT).getFunction());
|
||||
if (coordinator != null) {
|
||||
coordinator.leftLocationChanged((ProgramLocation) null);
|
||||
}
|
||||
}
|
||||
|
||||
public void maybeLoadFunction(Side side, Function function) {
|
||||
// we keep a local copy of the function so we know if it is already decompiled
|
||||
if (functions.get(side) == function) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the scroll info and highlight info to prevent unnecessary highlighting, etc.
|
||||
loadFunction(side, null);
|
||||
loadFunction(side, function);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
setSynchronizedScrolling(false); // disposes any exiting coordinator
|
||||
cDisplays.each(CDisplay::dispose);
|
||||
comparisonOptions = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display from the active side.
|
||||
* @return the active display
|
||||
*/
|
||||
public CDisplay getActiveDisplay() {
|
||||
return cDisplays.get(activeSide);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the left side's C display panel.
|
||||
* @return the left C display panel
|
||||
*/
|
||||
public CDisplay getLeftPanel() {
|
||||
return cDisplays.get(LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the right side's C display panel.
|
||||
* @return the right C display panel
|
||||
*/
|
||||
public CDisplay getRightPanel() {
|
||||
return cDisplays.get(RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockingAction> getActions() {
|
||||
List<DockingAction> allActions = super.getActions();
|
||||
allActions.addAll(actions);
|
||||
return allActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(ComponentProvider provider, MouseEvent event) {
|
||||
|
||||
Component component = event != null ? event.getComponent()
|
||||
: getActiveDisplay().getDecompilerPanel().getFieldPanel();
|
||||
|
||||
DualDecompilerActionContext dualDecompContext =
|
||||
new DualDecompilerActionContext(provider, this, component);
|
||||
|
||||
return dualDecompContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programClosed(Program program) {
|
||||
cDisplays.each(c -> c.programClosed(program));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComparisonComponent(Side side) {
|
||||
return cDisplays.get(side).getDecompilerPanel();
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
toggleExactConstantMatchingAction = new ToggleExactConstantMatching(getClass().getName());
|
||||
|
||||
actions.add(new DecompilerDiffViewFindAction(owner, tool));
|
||||
actions.add(new DecompilerCodeComparisonOptionsAction());
|
||||
actions.add(toggleExactConstantMatchingAction);
|
||||
actions.add(new CompareFuncsFromMatchedTokensAction(this, tool));
|
||||
}
|
||||
|
||||
private void decompileDataSet(Side side, DecompileData dcompileData) {
|
||||
cDisplays.get(side).restoreCursorPosition();
|
||||
updateDiffs();
|
||||
}
|
||||
|
||||
private boolean isMatchingConstantsExactly() {
|
||||
return !toggleExactConstantMatchingAction.isSelected();
|
||||
}
|
||||
|
||||
private void loadFunction(Side side, Function function) {
|
||||
if (functions.get(side) != function) {
|
||||
functions = functions.with(side, function);
|
||||
cDisplays.get(side).showFunction(tool, function);
|
||||
}
|
||||
}
|
||||
|
||||
private void locationChanged(Side side, ProgramLocation location) {
|
||||
if (coordinator != null) {
|
||||
if (side == LEFT) {
|
||||
coordinator.leftLocationChanged(location);
|
||||
}
|
||||
else {
|
||||
coordinator.rightLocationChanged(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDiffs() {
|
||||
DecompileData leftDecompileData = cDisplays.get(LEFT).getDecompileData();
|
||||
DecompileData rightDecompileData = cDisplays.get(RIGHT).getDecompileData();
|
||||
|
||||
if (isValid(leftDecompileData) && isValid(rightDecompileData)) {
|
||||
decompileDataDiff = new DecompileDataDiff(leftDecompileData, rightDecompileData);
|
||||
determineDecompilerDifferences();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValid(DecompileData decompileData) {
|
||||
return decompileData != null && decompileData.isValid();
|
||||
}
|
||||
|
||||
private CDisplay buildCDisplay(Side side) {
|
||||
return new CDisplay(comparisonOptions,
|
||||
decompileData -> decompileDataSet(side, decompileData),
|
||||
l -> locationChanged(side, l));
|
||||
}
|
||||
|
||||
private void linkHighlightControllers() {
|
||||
DiffClangHighlightController left = cDisplays.get(LEFT).getHighlightController();
|
||||
DiffClangHighlightController right = cDisplays.get(RIGHT).getHighlightController();
|
||||
left.addListener(right);
|
||||
right.addListener(left);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSynchronizedScrolling(boolean synchronize) {
|
||||
|
||||
if (coordinator != null) {
|
||||
coordinator.dispose();
|
||||
coordinator = null;
|
||||
}
|
||||
|
||||
if (synchronize) {
|
||||
coordinator = createCoordinator();
|
||||
doSynchronize();
|
||||
}
|
||||
}
|
||||
|
||||
private void doSynchronize() {
|
||||
CDisplay activeDisplay = getActiveDisplay();
|
||||
ProgramLocation programLocation =
|
||||
activeDisplay.getDecompilerPanel().getCurrentLocation();
|
||||
if (activeDisplay == cDisplays.get(LEFT)) {
|
||||
coordinator.leftLocationChanged(programLocation);
|
||||
}
|
||||
else {
|
||||
coordinator.rightLocationChanged(programLocation);
|
||||
}
|
||||
}
|
||||
|
||||
private CodeDiffFieldPanelCoordinator createCoordinator() {
|
||||
CodeDiffFieldPanelCoordinator panelCoordinator = new CodeDiffFieldPanelCoordinator(this);
|
||||
if (decompileDataDiff != null) {
|
||||
TaskBuilder.withRunnable(monitor -> {
|
||||
try {
|
||||
panelCoordinator.replaceDecompileDataDiff(decompileDataDiff,
|
||||
isMatchingConstantsExactly(), monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
panelCoordinator.clearLineNumberPairing();
|
||||
}
|
||||
}).setTitle("Initializing Code Compare").launchNonModal();
|
||||
}
|
||||
return panelCoordinator;
|
||||
|
||||
}
|
||||
|
||||
private class DecompilerCodeComparisonOptionsAction extends DockingAction {
|
||||
|
||||
DecompilerCodeComparisonOptionsAction() {
|
||||
super("Decompiler Code Comparison Options", owner);
|
||||
setDescription("Show the tool options for the Decompiler Code Comparison.");
|
||||
setPopupMenuData(new MenuData(new String[] { "Properties" }, null, "Z_Properties"));
|
||||
setHelpLocation(
|
||||
new HelpLocation("FunctionComparison", "Decompiler_Code_Comparison_Options"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return (context instanceof DualDecompilerActionContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
OptionsService service = tool.getService(OptionsService.class);
|
||||
service.showOptionsDialog("FunctionComparison", "Decompiler Code Comparison");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
super.setVisible(b);
|
||||
if (b && isStale) {
|
||||
determineDecompilerDifferences();
|
||||
isStale = false;
|
||||
}
|
||||
updateActionEnablement();
|
||||
}
|
||||
|
||||
List<TokenBin> getHighBins() {
|
||||
return coordinator == null ? null : coordinator.getHighBins();
|
||||
}
|
||||
|
||||
private void determineDecompilerDifferences() {
|
||||
if (decompileDataDiff == null) {
|
||||
return;
|
||||
}
|
||||
DiffClangHighlightController leftHighlights = cDisplays.get(LEFT).getHighlightController();
|
||||
DiffClangHighlightController rightHighlights =
|
||||
cDisplays.get(RIGHT).getHighlightController();
|
||||
DetermineDecompilerDifferencesTask task =
|
||||
new DetermineDecompilerDifferencesTask(decompileDataDiff, isMatchingConstantsExactly(),
|
||||
leftHighlights, rightHighlights, coordinator);
|
||||
|
||||
new TaskLauncher(task, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateActionEnablement() {
|
||||
// Need to enable/disable toolbar button.
|
||||
toggleExactConstantMatchingAction.setEnabled(isVisible());
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return cDisplays.get(LEFT).isBusy() || cDisplays.get(RIGHT).isBusy();
|
||||
}
|
||||
|
||||
public DecompilerPanel getDecompilerPanel(Side side) {
|
||||
return cDisplays.get(side).getDecompilerPanel();
|
||||
}
|
||||
|
||||
public class ToggleExactConstantMatching extends ToggleDockingAction {
|
||||
|
||||
private final Icon EXACT_CONSTANT_MATCHING_ICON = new GIcon("icon.base.source.c");
|
||||
private final Icon NO_EXACT_CONSTANT_MATCHING_ICON =
|
||||
new MultiIcon(EXACT_CONSTANT_MATCHING_ICON, Icons.NOT_ALLOWED_ICON);
|
||||
|
||||
/**
|
||||
* Creates an action for toggling exact constant matching in the code diff's
|
||||
* dual decompiler.
|
||||
* @param owner the owner of this action (typically the provider).
|
||||
*/
|
||||
public ToggleExactConstantMatching(String owner) {
|
||||
super("Toggle Exact Constant Matching", owner);
|
||||
setHelpLocation(new HelpLocation(HELP_TOPIC, "Toggle Exact Constant Matching"));
|
||||
|
||||
this.setToolBarData(new ToolBarData(NO_EXACT_CONSTANT_MATCHING_ICON, "toggles"));
|
||||
|
||||
setDescription(HTMLUtilities.toHTML("Toggle whether or not constants must\n" +
|
||||
"be exactly the same value to be a match\n" + "in the Decomiler Diff View."));
|
||||
setSelected(false);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (context instanceof DualDecompilerActionContext) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (isVisible()) {
|
||||
determineDecompilerDifferences();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(boolean selected) {
|
||||
getToolBarData().setIcon(
|
||||
selected ? NO_EXACT_CONSTANT_MATCHING_ICON : EXACT_CONSTANT_MATCHING_ICON);
|
||||
super.setSelected(selected);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingUtils;
|
||||
import docking.action.*;
|
||||
import docking.widgets.FindDialog;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.plugin.core.decompile.actions.DecompilerSearcher;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Duo;
|
||||
import ghidra.util.datastruct.Duo.Side;
|
||||
|
||||
public class DecompilerDiffViewFindAction extends DockingAction {
|
||||
|
||||
private Duo<FindDialog> findDialogs;
|
||||
private PluginTool tool;
|
||||
|
||||
public DecompilerDiffViewFindAction(String owner, PluginTool tool) {
|
||||
super("Find", owner, true);
|
||||
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionFind"));
|
||||
setPopupMenuData(new MenuData(new String[] { "Find..." }, "Decompile"));
|
||||
setKeyBindingData(
|
||||
new KeyBindingData(KeyEvent.VK_F, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
||||
setEnabled(true);
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
return (context instanceof DualDecompilerActionContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return (context instanceof DualDecompilerActionContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
DualDecompilerActionContext dualContext = (DualDecompilerActionContext) context;
|
||||
DecompilerCodeComparisonPanel decompilerCompPanel =
|
||||
dualContext.getCodeComparisonPanel();
|
||||
|
||||
Side focusedSide = decompilerCompPanel.getActiveSide();
|
||||
DecompilerPanel focusedPanel = decompilerCompPanel.getDecompilerPanel(focusedSide);
|
||||
FindDialog dialog = findDialogs.get(focusedSide);
|
||||
if (dialog == null) {
|
||||
dialog = createFindDialog(focusedPanel, focusedSide);
|
||||
findDialogs = findDialogs.with(focusedSide, dialog);
|
||||
}
|
||||
|
||||
String text = focusedPanel.getSelectedText();
|
||||
if (!StringUtils.isBlank(text)) {
|
||||
dialog.setSearchText(text);
|
||||
}
|
||||
tool.showDialog(dialog);
|
||||
}
|
||||
|
||||
private FindDialog createFindDialog(DecompilerPanel decompilerPanel, Side side) {
|
||||
String title = (side == LEFT ? "Left" : "Right");
|
||||
title += " Decompiler Find Text";
|
||||
|
||||
FindDialog dialog = new FindDialog(title, new DecompilerSearcher(decompilerPanel)) {
|
||||
@Override
|
||||
protected void dialogClosed() {
|
||||
// clear the search results when the dialog is closed
|
||||
decompilerPanel.setSearchResults(null);
|
||||
}
|
||||
};
|
||||
dialog.setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionFind"));
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
}
|
|
@ -13,12 +13,13 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.codecompare;
|
||||
package ghidra.codecompare.decompile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.codecompare.DiffClangHighlightController;
|
||||
import ghidra.codecompare.graphanalysis.TokenBin;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
|
@ -0,0 +1,52 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.codecompare.decompile;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import ghidra.app.context.RestrictedAddressSetContext;
|
||||
import ghidra.app.util.viewer.util.CodeComparisonActionContext;
|
||||
|
||||
/**
|
||||
* Action context for a dual decompiler panel.
|
||||
*/
|
||||
public class DualDecompilerActionContext extends CodeComparisonActionContext
|
||||
implements RestrictedAddressSetContext {
|
||||
|
||||
private DecompilerCodeComparisonPanel decompilerComparisonPanel = null;
|
||||
|
||||
/**
|
||||
* Creates an action context for a dual decompiler panel.
|
||||
* @param provider the provider for this context
|
||||
* @param panel the DecompilerComparisonPanel
|
||||
* @param source the source of the action
|
||||
*/
|
||||
public DualDecompilerActionContext(ComponentProvider provider,
|
||||
DecompilerCodeComparisonPanel panel, Component source) {
|
||||
super(provider, panel, source);
|
||||
this.decompilerComparisonPanel = panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DecompilerCodeComparisonPanel} that generated this context
|
||||
* @return the decompiler comparison panel that generated this context
|
||||
*/
|
||||
@Override
|
||||
public DecompilerCodeComparisonPanel getCodeComparisonPanel() {
|
||||
return decompilerComparisonPanel;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/* ###
|
||||
* 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.codecompare.decompile;
|
||||
|
||||
import static ghidra.util.datastruct.Duo.Side.*;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.internal.LineLockedFieldPanelCoordinator;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
abstract public class DualDecompilerFieldPanelCoordinator extends LineLockedFieldPanelCoordinator {
|
||||
|
||||
public DualDecompilerFieldPanelCoordinator(
|
||||
DecompilerCodeComparisonPanel dualDecompilerPanel) {
|
||||
super(new FieldPanel[] { dualDecompilerPanel.getDecompilerPanel(LEFT).getFieldPanel(),
|
||||
dualDecompilerPanel.getDecompilerPanel(RIGHT).getFieldPanel() });
|
||||
}
|
||||
|
||||
abstract public void leftLocationChanged(ProgramLocation leftLocation);
|
||||
|
||||
abstract public void rightLocationChanged(ProgramLocation rightLocation);
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.codecompare;
|
||||
package ghidra.codecompare.decompile;
|
||||
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue