mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch
'origin/GT-3292_dragonmacher_PR-784_00rsiere_decompiler-multi-highlight' (closes #784) Conflicts: Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileOptions.java
This commit is contained in:
commit
59e88c6ca9
40 changed files with 2821 additions and 490 deletions
|
@ -31,7 +31,6 @@ import docking.action.*;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.combobox.GhidraComboBox;
|
import docking.widgets.combobox.GhidraComboBox;
|
||||||
import docking.widgets.dialogs.InputDialog;
|
import docking.widgets.dialogs.InputDialog;
|
||||||
import docking.widgets.dialogs.InputDialogListener;
|
|
||||||
import docking.widgets.filechooser.GhidraFileChooser;
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||||
import docking.widgets.pathmanager.PathnameTablePanel;
|
import docking.widgets.pathmanager.PathnameTablePanel;
|
||||||
|
@ -446,8 +445,7 @@ class ParseDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
private void saveAs(ComboBoxItem item) {
|
private void saveAs(ComboBoxItem item) {
|
||||||
|
|
||||||
InputDialog d =
|
InputDialog d = new InputDialog("Enter Profile Name", "Profile Name");
|
||||||
new InputDialog("Enter Profile Name", "Profile Name", (InputDialogListener) null);
|
|
||||||
plugin.getTool().showDialog(d, getComponent());
|
plugin.getTool().showDialog(d, getComponent());
|
||||||
|
|
||||||
String name = d.getValue();
|
String name = d.getValue();
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.test;
|
package ghidra.test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -78,7 +77,6 @@ public abstract class AbstractProgramBasedTest extends AbstractGhidraHeadedInteg
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
env.release(program);
|
|
||||||
env.dispose();
|
env.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ Module.manifest||GHIDRA||||END|
|
||||||
build.gradle||GHIDRA||||END|
|
build.gradle||GHIDRA||||END|
|
||||||
src/decompile/.cproject||GHIDRA||||END|
|
src/decompile/.cproject||GHIDRA||||END|
|
||||||
src/decompile/.project||GHIDRA||||END|
|
src/decompile/.project||GHIDRA||||END|
|
||||||
src/decompile/.settings/org.eclipse.jdt.core.prefs||GHIDRA||||END|
|
|
||||||
src/decompile/build.gradle||GHIDRA||||END|
|
src/decompile/build.gradle||GHIDRA||||END|
|
||||||
src/decompile/cpp/.gitignore||GHIDRA||||END|
|
src/decompile/cpp/.gitignore||GHIDRA||||END|
|
||||||
src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END|
|
src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END|
|
||||||
|
|
|
@ -572,9 +572,9 @@
|
||||||
<P style="margin-left: 80px;">Ghidra will do stack analysis that will recover parameters and
|
<P style="margin-left: 80px;">Ghidra will do stack analysis that will recover parameters and
|
||||||
return types, but for many programs, the analysis the decompiler does is better.</P>
|
return types, but for many programs, the analysis the decompiler does is better.</P>
|
||||||
|
|
||||||
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0">There is a
|
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0">The
|
||||||
prototype plug-in that automatically pulls in the decompiler derived information and applies it
|
Decompiler Parameter ID analyzer automatically pulls in the decompiler derived
|
||||||
to each function as the function is created.</P>
|
information and applies it to each function as the function is created.</P>
|
||||||
|
|
||||||
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0"> If a variable
|
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0"> If a variable
|
||||||
displayed in the assembly window has an undefined type, the decompiler will still respect the
|
displayed in the assembly window has an undefined type, the decompiler will still respect the
|
||||||
|
@ -597,11 +597,12 @@
|
||||||
on the stack, but for many programs, the analysis the decompiler does is better.<BR>
|
on the stack, but for many programs, the analysis the decompiler does is better.<BR>
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0"> There is a
|
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0">he
|
||||||
prototype plug-in that automatically pulls in the decompiler derived information and applies it
|
Decompiler Parameter ID analyzer automatically pulls in the decompiler derived
|
||||||
to each function as the function is created. The plugin by default will not commit local
|
information and applies it to each function as the function is created. The analyzer by default
|
||||||
variable definitions, either stack or register locals. Committing locals automatically
|
will not commit local variable definitions, either stack or register locals.
|
||||||
can be turned on by changing the analysis options for the Decompiler Parameter ID plugin.
|
Committing locals automatically
|
||||||
|
can be turned on by changing the Decompiler Parameter ID analysis options.
|
||||||
In most cases it is better to commit locals only for certain functions that you really care
|
In most cases it is better to commit locals only for certain functions that you really care
|
||||||
about, or after the data type definitions (structures, etc...) have settled down for the
|
about, or after the data type definitions (structures, etc...) have settled down for the
|
||||||
program you are Reverse Engineering.<BR>
|
program you are Reverse Engineering.<BR>
|
||||||
|
@ -720,6 +721,79 @@
|
||||||
contribute to the creation of the value in the variable under the cursor.</P>
|
contribute to the creation of the value in the variable under the cursor.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<!-- Secondary Highlights -->
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2>Secondary Highlights</H2>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
Secondary highlights are highlights that you can apply to text that will
|
||||||
|
stay until you remove them (this is in contrast to normal highlights, which go
|
||||||
|
away whenever you click in the Decompiler). Further, each secondary highlight will get a
|
||||||
|
different color (chosen randomly or by the user). The Decompiler will remember
|
||||||
|
the color previously used for a particular highlighted piece of text <b>for a
|
||||||
|
given Ghidra session</b>. Use the actions listed below to add and remove
|
||||||
|
secondary highlights.
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<H3><A name="Set_Secondary_Highlight"></A>Set Secondary Highlight</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
Use this action to apply a secondary highlight to all text matching the text of the
|
||||||
|
token under the cursor. This action will reuse the previously applied color for
|
||||||
|
that text or choose a new color randomly. This action is accessible from the
|
||||||
|
context menu via
|
||||||
|
<b>Secondary Highlight<img border="0" src="../../shared/arrow.gif">Set Highlight</b>
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H3><A name="Set_Secondary_Highlight_With_Color"></A>Set Secondary Highlight With Color</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
Use this action to apply a secondary highlight to all text matching the text of the
|
||||||
|
token under the cursor. This action will show a Color Chooser dialog initialized with
|
||||||
|
the previously applied color for
|
||||||
|
that text or choose a new color randomly. This action is accessible from the
|
||||||
|
context menu via
|
||||||
|
<b>Secondary Highlight<img border="0" src="../../shared/arrow.gif">Set Highlight...</b>
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H3><A name="Remove_Secondary_Highlight"></A>Remove Secondary Highlight</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
Use this action to remove an existing secondary highlight under the cursor.
|
||||||
|
This action is accessible from the context menu via
|
||||||
|
<b>Secondary Highlight<img border="0" src="../../shared/arrow.gif">Remove Highlight</b>
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
|
<H3><A name="Remove_All_Secondary_Highlights"></A>Remove All Secondary Highlights</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
Use this action to remove all existing secondary highlights.
|
||||||
|
This action is accessible from the context menu via
|
||||||
|
<b>Secondary Highlight<img border="0" src="../../shared/arrow.gif">Remove All Highlights</b>
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<!-- End Secondary Highlights -->
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
<BR>
|
||||||
|
|
||||||
<H3><A name="Rename_Variable"></A>Rename Variable</H3>
|
<H3><A name="Rename_Variable"></A>Rename Variable</H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
|
|
|
@ -67,6 +67,10 @@ public class ClangLine {
|
||||||
return tokens.get(i);
|
return tokens.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int indexOfToken(ClangToken token) {
|
||||||
|
return tokens.indexOf(token);
|
||||||
|
}
|
||||||
|
|
||||||
public String toDebugString(List<ClangToken> calloutTokens) {
|
public String toDebugString(List<ClangToken> calloutTokens) {
|
||||||
|
|
||||||
return toDebugString(calloutTokens, "[", "]");
|
return toDebugString(calloutTokens, "[", "]");
|
||||||
|
|
|
@ -245,6 +245,9 @@ public class ClangToken implements ClangNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
if (lineparent != null) {
|
||||||
|
return lineparent.getLineNumber() + ": " + text;
|
||||||
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||||
|
import utility.function.Callback;
|
||||||
|
|
||||||
public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
||||||
|
|
||||||
|
@ -157,6 +158,11 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doWheNotBusy(Callback c) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
public void clearAndShowMessage(String message) {
|
public void clearAndShowMessage(String message) {
|
||||||
controller.setDecompileData(new EmptyDecompileData(message));
|
controller.setDecompileData(new EmptyDecompileData(message));
|
||||||
paintImmediately(getBounds());
|
paintImmediately(getBounds());
|
||||||
|
|
|
@ -17,70 +17,62 @@ package ghidra.app.decompiler.component;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import docking.widgets.fieldpanel.field.Field;
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
import ghidra.app.decompiler.*;
|
import ghidra.app.decompiler.*;
|
||||||
|
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.util.ColorUtils;
|
||||||
|
import util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to handle highlights for a decompiled function.
|
* Class to handle highlights for a decompiled function.
|
||||||
|
*
|
||||||
|
* <p>This class does not painting directly. Rather, this class tracks the currently highlighted
|
||||||
|
* tokens and then sets the highlight color on the token when it is highlighted and clears the
|
||||||
|
* highlight color when the highlight is removed.
|
||||||
|
*
|
||||||
|
* <p>This class maintains the notion of 'primary' highlights and 'secondary' highlights.
|
||||||
|
* Primary highlights are considered transient and get cleared whenever the location changes.
|
||||||
|
* Secondary highlights will stay until they are manually cleared by a user action. Primary
|
||||||
|
* highlights happen when the user clicks around the Decompiler. They show state such as the
|
||||||
|
* current field, impact of a variable (via a slicing action), or related syntax (such as
|
||||||
|
* matching braces). Secondary highlights are triggered by the user to show all occurrences of
|
||||||
|
* a particular variable. Further, the user can apply multiple secondary highlights at the
|
||||||
|
* same time, with different colors for each highlight.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public abstract class ClangHighlightController {
|
public abstract class ClangHighlightController {
|
||||||
|
|
||||||
|
public static Color DEFAULT_HIGHLIGHT_COLOR = new Color(255, 255, 0, 128);
|
||||||
|
|
||||||
|
public static ClangHighlightController dummyIfNull(ClangHighlightController c) {
|
||||||
|
if (c == null) {
|
||||||
|
return new NullClangHighlightController();
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
// Note: Most of the methods in this class were extracted from the ClangLayoutController class
|
// Note: Most of the methods in this class were extracted from the ClangLayoutController class
|
||||||
// and the DecompilerPanel class.
|
// and the DecompilerPanel class.
|
||||||
|
|
||||||
protected Color defaultNonFunctionBackgroundColor = new Color(220, 220, 220);
|
protected Color defaultHighlightColor = DEFAULT_HIGHLIGHT_COLOR;
|
||||||
protected Color defaultHighlightColor = new Color(255, 255, 0, 128); // Default color for highlighting tokens
|
protected Color defaultParenColor = DEFAULT_HIGHLIGHT_COLOR;
|
||||||
protected Color defaultSpecialColor = new Color(255, 100, 0, 128); // Default color for specially highlighted tokens
|
|
||||||
protected Color defaultParenColor = new Color(255, 255, 0, 128); // Default color for highlighting parentheses
|
|
||||||
|
|
||||||
protected HashSet<ClangToken> highlightTokenSet = new HashSet<>();
|
private TokenHighlights primaryHighlightTokens = new TokenHighlights();
|
||||||
|
private TokenHighlights secondaryHighlightTokens = new TokenHighlights();
|
||||||
|
private TokenHighlightColors secondaryHighlightColors = new TokenHighlightColors();
|
||||||
|
|
||||||
protected ArrayList<ClangHighlightListener> highlightListenerList = new ArrayList<>();
|
private List<ClangHighlightListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
public ClangHighlightController() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void fieldLocationChanged(FieldLocation location, Field field,
|
public abstract void fieldLocationChanged(FieldLocation location, Field field,
|
||||||
EventTrigger trigger);
|
EventTrigger trigger);
|
||||||
|
|
||||||
void loadOptions(DecompileOptions options) {
|
void setHighlightColor(Color c) {
|
||||||
Color currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
|
defaultHighlightColor = c;
|
||||||
if (currentVariableHighlightColor != null) {
|
|
||||||
setDefaultHighlightColor(currentVariableHighlightColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultHighlightColor(Color highlightColor) {
|
|
||||||
defaultHighlightColor = highlightColor;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultSpecialColor(Color specialColor) {
|
|
||||||
defaultSpecialColor = specialColor;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultParenColor(Color parenColor) {
|
|
||||||
defaultParenColor = parenColor;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color getDefaultHighlightColor() {
|
|
||||||
return defaultHighlightColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color getDefaultSpecialColor() {
|
|
||||||
return defaultSpecialColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color getDefaultParenColor() {
|
|
||||||
return defaultParenColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHighlightedText() {
|
public String getHighlightedText() {
|
||||||
|
@ -91,144 +83,287 @@ public abstract class ClangHighlightController {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TokenHighlightColors getSecondaryHighlightColors() {
|
||||||
|
return secondaryHighlightColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenHighlights getPrimaryHighlightedTokens() {
|
||||||
|
return primaryHighlightTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenHighlights getSecondaryHighlightedTokens() {
|
||||||
|
return secondaryHighlightTokens;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current highlighted token (if exists and unique)
|
* Return the current highlighted token (if exists and unique)
|
||||||
* @return token or null
|
* @return token or null
|
||||||
*/
|
*/
|
||||||
public ClangToken getHighlightedToken() {
|
private ClangToken getHighlightedToken() {
|
||||||
if (highlightTokenSet.size() == 1) {
|
if (primaryHighlightTokens.size() == 1) {
|
||||||
ClangToken[] tokenArray =
|
HighlightToken hlToken = CollectionUtils.any(primaryHighlightTokens);
|
||||||
highlightTokenSet.toArray(new ClangToken[highlightTokenSet.size()]);
|
return hlToken.getToken();
|
||||||
return tokenArray[0];
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVarnodesToHighlight(ClangNode parentNode, Set<Varnode> varnodes,
|
private void gatherAllTokens(ClangNode parentNode, Set<ClangToken> results) {
|
||||||
Color highlightColor, Varnode specificvn, PcodeOp specificop, Color specialColor) {
|
|
||||||
int nchild = parentNode.numChildren();
|
int n = parentNode.numChildren();
|
||||||
for (int i = 0; i < nchild; ++i) {
|
for (int i = 0; i < n; i++) {
|
||||||
ClangNode node = parentNode.Child(i);
|
ClangNode node = parentNode.Child(i);
|
||||||
if (node.numChildren() > 0) {
|
if (node.numChildren() > 0) {
|
||||||
addVarnodesToHighlight(node, varnodes, highlightColor, specificvn, specificop,
|
gatherAllTokens(node, results);
|
||||||
specialColor);
|
|
||||||
}
|
}
|
||||||
else if (node instanceof ClangToken) {
|
else if (node instanceof ClangToken) {
|
||||||
ClangToken tok = (ClangToken) node;
|
results.add((ClangToken) node);
|
||||||
Varnode vn = DecompilerUtils.getVarnodeRef(tok);
|
|
||||||
if (varnodes.contains(vn)) {
|
|
||||||
addHighlight(tok, highlightColor);
|
|
||||||
}
|
|
||||||
if (vn == specificvn) { // Look for specific varnode to label with specialColor
|
|
||||||
if ((specificop != null) && (tok.getPcodeOp() == specificop)) {
|
|
||||||
addHighlight(tok, specialColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearPrimaryHighlights() {
|
||||||
|
doClearHighlights(primaryHighlightTokens);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAllHighlights() {
|
||||||
|
doClearHighlights(primaryHighlightTokens);
|
||||||
|
doClearHighlights(secondaryHighlightTokens);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doClearHighlights(TokenHighlights tokenHighlights) {
|
||||||
|
|
||||||
|
Iterator<HighlightToken> it = tokenHighlights.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
HighlightToken highlight = it.next();
|
||||||
|
it.remove();
|
||||||
|
ClangToken token = highlight.getToken();
|
||||||
|
token.setMatchingToken(false);
|
||||||
|
updateHighlightColor(token);
|
||||||
|
}
|
||||||
|
tokenHighlights.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void togglePrimaryHighlights(Color hlColor, Supplier<List<ClangToken>> tokens) {
|
||||||
|
|
||||||
|
boolean isAllHighlighted = true;
|
||||||
|
for (ClangToken otherToken : tokens.get()) {
|
||||||
|
if (!hasPrimaryHighlight(otherToken)) {
|
||||||
|
isAllHighlighted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a bit odd, but whenever we change the primary highlights, we always reset any
|
||||||
|
// previous primary highlight (see javadoc header)
|
||||||
|
clearPrimaryHighlights();
|
||||||
|
|
||||||
|
if (isAllHighlighted) {
|
||||||
|
// nothing to do; we toggled from 'all on' to 'all off'
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPrimaryHighlights(tokens, hlColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPrimaryHighlight(ClangToken token) {
|
||||||
|
return primaryHighlightTokens.contains(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSecondaryHighlight(ClangToken token) {
|
||||||
|
return secondaryHighlightTokens.contains(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<HighlightToken> getSecondaryHighlightsByFunction(
|
||||||
|
ghidra.program.model.listing.Function f) {
|
||||||
|
Set<HighlightToken> highlights = secondaryHighlightTokens.getHighlightsByFunction(f);
|
||||||
|
return highlights;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSecondaryHighlights(ghidra.program.model.listing.Function f) {
|
||||||
|
Set<HighlightToken> oldHighlights = secondaryHighlightTokens.removeHighlightsByFunction(f);
|
||||||
|
for (HighlightToken hl : oldHighlights) {
|
||||||
|
ClangToken token = hl.getToken();
|
||||||
|
updateHighlightColor(token);
|
||||||
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPcodeOpsToHighlight(ClangNode parentNode, Set<PcodeOp> ops,
|
public void removeSecondaryHighlights(ClangToken token) {
|
||||||
Color highlightColor) {
|
secondaryHighlightTokens.remove(token);
|
||||||
int nchild = parentNode.numChildren();
|
}
|
||||||
for (int i = 0; i < nchild; ++i) {
|
|
||||||
ClangNode node = parentNode.Child(i);
|
public void removeSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens) {
|
||||||
if (node.numChildren() > 0) {
|
for (ClangToken clangToken : tokens.get()) {
|
||||||
addPcodeOpsToHighlight(node, ops, highlightColor);
|
secondaryHighlightTokens.remove(clangToken);
|
||||||
}
|
updateHighlightColor(clangToken);
|
||||||
else if (node instanceof ClangToken) {
|
|
||||||
ClangToken tok = (ClangToken) node;
|
|
||||||
PcodeOp op = tok.getPcodeOp();
|
|
||||||
if (ops.contains(op)) {
|
|
||||||
addHighlight(tok, highlightColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTokensToHighlights(List<ClangToken> tokenList, Color highlightColor) {
|
public void addSecondaryHighlights(String tokenText,
|
||||||
for (ClangToken clangToken : tokenList) {
|
Supplier<? extends Collection<ClangToken>> tokens) {
|
||||||
doAddHighlight(clangToken, highlightColor);
|
Color highlightColor = secondaryHighlightColors.getColor(tokenText);
|
||||||
|
addSecondaryHighlights(tokens, highlightColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
|
||||||
|
Color hlColor) {
|
||||||
|
Function<ClangToken, Color> colorProvider = token -> hlColor;
|
||||||
|
addTokensToHighlights(tokens.get(), colorProvider, secondaryHighlightTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated // for use by subclasses
|
||||||
|
protected void addHighlight(ClangToken tok, Color hlColor) {
|
||||||
|
addPrimaryHighlight(tok, hlColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
|
||||||
|
Color hlColor) {
|
||||||
|
Function<ClangToken, Color> colorProvider = token -> hlColor;
|
||||||
|
addTokensToHighlights(tokens.get(), colorProvider, primaryHighlightTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPrimaryHighlights(ClangNode parentNode,
|
||||||
|
TokenHighlightColorProvider colorProvider) {
|
||||||
|
|
||||||
|
Set<ClangToken> tokens = new HashSet<>();
|
||||||
|
gatherAllTokens(parentNode, tokens);
|
||||||
|
addTokensToHighlights(tokens, colorProvider::getColor, primaryHighlightTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPrimaryHighlights(ClangNode parentNode, Set<PcodeOp> ops, Color hlColor) {
|
||||||
|
|
||||||
|
addPrimaryHighlights(parentNode, token -> {
|
||||||
|
PcodeOp op = token.getPcodeOp();
|
||||||
|
return ops.contains(op) ? hlColor : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPrimaryHighlights(Collection<ClangToken> tokens, Color hlColor) {
|
||||||
|
Function<ClangToken, Color> colorProvider = token -> hlColor;
|
||||||
|
addTokensToHighlights(tokens, colorProvider, primaryHighlightTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTokensToHighlights(Collection<ClangToken> tokens,
|
||||||
|
Function<ClangToken, Color> colorProvider, TokenHighlights currentHighlights) {
|
||||||
|
for (ClangToken clangToken : tokens) {
|
||||||
|
Color color = colorProvider.apply(clangToken);
|
||||||
|
doAddHighlight(clangToken, color, currentHighlights);
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearHighlights() {
|
protected void addPrimaryHighlight(ClangToken token, Color highlightColor) {
|
||||||
for (ClangToken clangToken : highlightTokenSet) {
|
addPrimaryHighlights(Set.of(token), highlightColor);
|
||||||
clangToken.setHighlight(null);
|
}
|
||||||
if (clangToken.isMatchingToken()) {
|
|
||||||
clangToken.setMatchingToken(false);
|
private void doAddHighlight(ClangToken clangToken, Color highlightColor,
|
||||||
|
TokenHighlights currentHighlights) {
|
||||||
|
|
||||||
|
if (highlightColor == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the actual requested color
|
||||||
|
currentHighlights.add(new HighlightToken(clangToken, highlightColor));
|
||||||
|
updateHighlightColor(clangToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateHighlightColor(ClangToken t) {
|
||||||
|
// set the color to the current combined value of both highlight types
|
||||||
|
Color combinedColor = getCombinedColor(t);
|
||||||
|
t.setHighlight(combinedColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getCombinedColor(ClangToken t) {
|
||||||
|
|
||||||
|
HighlightToken primaryHl = primaryHighlightTokens.get(t);
|
||||||
|
HighlightToken secondaryHl = secondaryHighlightTokens.get(t);
|
||||||
|
Color primary = primaryHl == null ? null : primaryHl.getColor();
|
||||||
|
Color secondary = secondaryHl == null ? null : secondaryHl.getColor();
|
||||||
|
|
||||||
|
if (primary == null) {
|
||||||
|
if (secondary == null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
return secondary;
|
||||||
}
|
}
|
||||||
highlightTokenSet.clear();
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addHighlight(ClangToken clangToken, Color highlightColor) {
|
if (secondary == null) {
|
||||||
doAddHighlight(clangToken, highlightColor);
|
return primary;
|
||||||
notifyListeners();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void doAddHighlight(ClangToken clangToken, Color highlightColor) {
|
return ColorUtils.blend(primary, secondary, .8f);
|
||||||
clangToken.setHighlight(highlightColor);
|
|
||||||
highlightTokenSet.add(clangToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearHighlight(ClangToken clangToken) {
|
|
||||||
clangToken.setHighlight(null);
|
|
||||||
highlightTokenSet.remove(clangToken);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isHighlighted(ClangToken clangToken) {
|
|
||||||
return highlightTokenSet.contains(clangToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If input token is a parenthesis, highlight all
|
* If input token is a parenthesis, highlight all
|
||||||
* tokens between it and its match
|
* tokens between it and its match
|
||||||
* @param tok = potential parenthesis token
|
* @param tok potential parenthesis token
|
||||||
|
* @param highlightColor the highlight color
|
||||||
* @return a list of all tokens that were highlighted.
|
* @return a list of all tokens that were highlighted.
|
||||||
*/
|
*/
|
||||||
public List<ClangToken> addHighlightParen(ClangSyntaxToken tok, Color highlightColor) {
|
protected List<ClangToken> addPrimaryHighlightToTokensForParenthesis(ClangSyntaxToken tok,
|
||||||
ArrayList<ClangToken> tokenList = new ArrayList<>();
|
Color highlightColor) {
|
||||||
|
|
||||||
int paren = tok.getOpen();
|
int paren = tok.getOpen();
|
||||||
if (paren == -1) {
|
if (paren == -1) {
|
||||||
paren = tok.getClose();
|
paren = tok.getClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paren == -1) {
|
if (paren == -1) {
|
||||||
return tokenList; // Not a parenthesis
|
return new ArrayList<>(); // Not a parenthesis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<ClangToken> results = gatherContentsOfParenthesis(tok, paren);
|
||||||
|
addPrimaryHighlights(results, highlightColor);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ClangToken> gatherContentsOfParenthesis(ClangSyntaxToken tok, int parenId) {
|
||||||
|
|
||||||
|
List<ClangToken> results = new ArrayList<>();
|
||||||
|
int parenCount = 0;
|
||||||
ClangNode par = tok.Parent();
|
ClangNode par = tok.Parent();
|
||||||
while (par != null) {
|
while (par != null) {
|
||||||
boolean outside = true;
|
boolean outside = true;
|
||||||
if (par instanceof ClangTokenGroup) {
|
if (!(par instanceof ClangTokenGroup)) {
|
||||||
ArrayList<ClangNode> list = new ArrayList<>();
|
par = par.Parent();
|
||||||
((ClangTokenGroup) par).flatten(list);
|
continue;
|
||||||
for (int i = 0; i < list.size(); ++i) {
|
}
|
||||||
ClangToken tk = (ClangToken) list.get(i);
|
|
||||||
if (tk instanceof ClangSyntaxToken) {
|
List<ClangNode> list = new ArrayList<>();
|
||||||
ClangSyntaxToken syn = (ClangSyntaxToken) tk;
|
((ClangTokenGroup) par).flatten(list);
|
||||||
if (syn.getOpen() == paren) {
|
|
||||||
outside = false;
|
for (ClangNode node : list) {
|
||||||
}
|
ClangToken tk = (ClangToken) node;
|
||||||
else if (syn.getClose() == paren) {
|
if (tk instanceof ClangSyntaxToken) {
|
||||||
outside = true;
|
ClangSyntaxToken syn = (ClangSyntaxToken) tk;
|
||||||
addHighlight(syn, highlightColor);
|
if (syn.getOpen() == parenId) {
|
||||||
tokenList.add(syn);
|
parenCount++;
|
||||||
}
|
outside = false;
|
||||||
}
|
}
|
||||||
if (!outside) {
|
else if (syn.getClose() == parenId) {
|
||||||
addHighlight(tk, highlightColor);
|
parenCount++;
|
||||||
tokenList.add(tk);
|
outside = true;
|
||||||
|
results.add(syn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!outside) {
|
||||||
|
results.add(tk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parenCount == 2) {
|
||||||
|
return results; // found both parens; break out early
|
||||||
|
}
|
||||||
}
|
}
|
||||||
par = par.Parent();
|
par = par.Parent();
|
||||||
}
|
}
|
||||||
return tokenList;
|
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHighlightBrace(ClangSyntaxToken token, Color highlightColor) {
|
public void addHighlightBrace(ClangSyntaxToken token, Color highlightColor) {
|
||||||
|
@ -244,76 +379,20 @@ public abstract class ClangHighlightController {
|
||||||
ClangSyntaxToken matchingBrace = DecompilerUtils.getMatchingBrace(startToken);
|
ClangSyntaxToken matchingBrace = DecompilerUtils.getMatchingBrace(startToken);
|
||||||
if (matchingBrace != null) {
|
if (matchingBrace != null) {
|
||||||
matchingBrace.setMatchingToken(true); // this is a signal to the painter
|
matchingBrace.setMatchingToken(true); // this is a signal to the painter
|
||||||
addHighlight(matchingBrace, highlightColor);
|
addPrimaryHighlights(Set.of(matchingBrace), highlightColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void addListener(ClangHighlightListener listener) {
|
||||||
* Add highlighting to tokens that are surrounded by
|
listeners.add(listener);
|
||||||
* highlighted tokens, but which have no address
|
|
||||||
*/
|
|
||||||
public void addHighlightFill() {
|
|
||||||
ClangTokenGroup lastgroup = null;
|
|
||||||
ArrayList<ClangNode> newhi = new ArrayList<>();
|
|
||||||
ArrayList<Color> newcolor = new ArrayList<>();
|
|
||||||
for (ClangToken tok : highlightTokenSet) {
|
|
||||||
if (tok.Parent() instanceof ClangTokenGroup) {
|
|
||||||
ClangTokenGroup par = (ClangTokenGroup) tok.Parent();
|
|
||||||
if (par == lastgroup) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
lastgroup = par;
|
|
||||||
int beg = -1;
|
|
||||||
int end = par.numChildren();
|
|
||||||
for (int j = 0; j < par.numChildren(); ++j) {
|
|
||||||
if (par.Child(j) instanceof ClangToken) {
|
|
||||||
ClangToken token = (ClangToken) par.Child(j);
|
|
||||||
Color curcolor = token.getHighlight();
|
|
||||||
if (curcolor != null) {
|
|
||||||
if (beg == -1) {
|
|
||||||
beg = j;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
end = j;
|
|
||||||
for (int k = beg + 1; k < end; ++k) {
|
|
||||||
if (par.Child(k) instanceof ClangToken) {
|
|
||||||
newhi.add(par.Child(k));
|
|
||||||
newcolor.add(curcolor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
beg = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (token.getMinAddress() != null) {
|
|
||||||
beg = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
beg = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < newhi.size(); ++i) {
|
|
||||||
ClangToken tok = (ClangToken) newhi.get(i);
|
|
||||||
if (tok.getHighlight() != null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
addHighlight(tok, newcolor.get(i));
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addListener(ClangHighlightListener listener) {
|
public void removeListener(ClangHighlightListener listener) {
|
||||||
return highlightListenerList.add(listener);
|
listeners.remove(listener);
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeListener(ClangHighlightListener listener) {
|
|
||||||
return highlightListenerList.remove(listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyListeners() {
|
private void notifyListeners() {
|
||||||
for (ClangHighlightListener listener : highlightListenerList) {
|
for (ClangHighlightListener listener : listeners) {
|
||||||
listener.tokenHighlightsChanged();
|
listener.tokenHighlightsChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||||
|
import utility.function.Callback;
|
||||||
|
|
||||||
public interface DecompilerCallbackHandler {
|
public interface DecompilerCallbackHandler {
|
||||||
|
|
||||||
|
@ -44,4 +45,6 @@ public interface DecompilerCallbackHandler {
|
||||||
void exportLocation();
|
void exportLocation();
|
||||||
|
|
||||||
void goToFunction(Function function, boolean newWindow);
|
void goToFunction(Function function, boolean newWindow);
|
||||||
|
|
||||||
|
void doWheNotBusy(Callback c);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import ghidra.program.model.pcode.HighFunction;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||||
|
import utility.function.Callback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Coordinates the interactions between the DecompilerProvider, DecompilerPanel, and the DecompilerManager
|
* Coordinates the interactions between the DecompilerProvider, DecompilerPanel, and the DecompilerManager
|
||||||
|
@ -93,6 +94,7 @@ public class DecompilerController {
|
||||||
* @param program the program for the given location
|
* @param program the program for the given location
|
||||||
* @param location the location containing the function to be displayed and the location in
|
* @param location the location containing the function to be displayed and the location in
|
||||||
* that function to position the cursor.
|
* that function to position the cursor.
|
||||||
|
* @param viewerPosition the viewer position
|
||||||
*/
|
*/
|
||||||
public void display(Program program, ProgramLocation location, ViewerPosition viewerPosition) {
|
public void display(Program program, ProgramLocation location, ViewerPosition viewerPosition) {
|
||||||
if (!decompilerMgr.isBusy() && decompilerPanel.containsLocation(location)) {
|
if (!decompilerMgr.isBusy() && decompilerPanel.containsLocation(location)) {
|
||||||
|
@ -121,7 +123,7 @@ public class DecompilerController {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel any pending decompile tasks, so that previous requests don't overwrite the latest request
|
// cancel pending decompile tasks; previous requests shouldn't overwrite the latest request
|
||||||
decompilerMgr.cancelAll();
|
decompilerMgr.cancelAll();
|
||||||
setDecompileData(
|
setDecompileData(
|
||||||
new DecompileData(program, function, location, results, null, null, viewerPosition));
|
new DecompileData(program, function, location, results, null, null, viewerPosition));
|
||||||
|
@ -135,7 +137,7 @@ public class DecompilerController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets new decompiler options and triggers a new decompile.
|
* Sets new decompiler options and triggers a new decompile.
|
||||||
* @param decompilerOptions
|
* @param decompilerOptions the options
|
||||||
*/
|
*/
|
||||||
public void setOptions(DecompileOptions decompilerOptions) {
|
public void setOptions(DecompileOptions decompilerOptions) {
|
||||||
clearCache();
|
clearCache();
|
||||||
|
@ -168,7 +170,8 @@ public class DecompilerController {
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the DecompilerManager to update the currently displayed DecompileData.
|
* Called by the DecompilerManager to update the currently displayed DecompileData
|
||||||
|
* @param decompileData the new data
|
||||||
*/
|
*/
|
||||||
public void setDecompileData(DecompileData decompileData) {
|
public void setDecompileData(DecompileData decompileData) {
|
||||||
updateCache(decompileData);
|
updateCache(decompileData);
|
||||||
|
@ -193,12 +196,18 @@ public class DecompilerController {
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Methods called by actions and other miscellaneous classes
|
// Methods called by actions and other miscellaneous classes
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
|
public void doWhenNotBusy(Callback c) {
|
||||||
|
callbackHandler.doWheNotBusy(c);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Always decompiles the function containing the given location before positioning the
|
* Always decompiles the function containing the given location before positioning the
|
||||||
* decompilerPanel's cursor to the closest equivalent position.
|
* decompilerPanel's cursor to the closest equivalent position.
|
||||||
* @param program the program for the given location
|
* @param program the program for the given location
|
||||||
* @param location the location containing the function to be displayed and the location in
|
* @param location the location containing the function to be displayed and the location in
|
||||||
* that function to position the cursor.
|
* that function to position the cursor.
|
||||||
|
* @param debugFile the debug file
|
||||||
*/
|
*/
|
||||||
public void refreshDisplay(Program program, ProgramLocation location, File debugFile) {
|
public void refreshDisplay(Program program, ProgramLocation location, File debugFile) {
|
||||||
clearCache();
|
clearCache();
|
||||||
|
@ -304,5 +313,4 @@ public class DecompilerController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,9 @@ import java.awt.event.MouseEvent;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
@ -40,6 +43,7 @@ import ghidra.app.decompiler.*;
|
||||||
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
||||||
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
|
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
|
||||||
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
|
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
|
||||||
|
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -48,6 +52,8 @@ import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||||
|
import ghidra.util.task.SwingUpdateManager;
|
||||||
|
import util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to handle the display of a decompiled function
|
* Class to handle the display of a decompiled function
|
||||||
|
@ -65,12 +71,21 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
|
|
||||||
private DecompilerFieldPanel fieldPanel;
|
private DecompilerFieldPanel fieldPanel;
|
||||||
private ClangLayoutController layoutMgr;
|
private ClangLayoutController layoutMgr;
|
||||||
|
|
||||||
private HighlightFactory hlFactory;
|
private HighlightFactory hlFactory;
|
||||||
private ClangHighlightController highlightController;
|
private ClangHighlightController highlightController;
|
||||||
|
private PendingHighlightUpdate pendingHighlightUpdate;
|
||||||
|
private SwingUpdateManager highlighCursorUpdater = new SwingUpdateManager(() -> {
|
||||||
|
if (pendingHighlightUpdate != null) {
|
||||||
|
pendingHighlightUpdate.doUpdate();
|
||||||
|
pendingHighlightUpdate = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private int currentMiddleMouseHighlightButton;
|
private int middleMouseHighlightButton;
|
||||||
private Color currentSearchHighlightColor;
|
private Color middleMouseHighlightColor;
|
||||||
private Color currentHighlightColor;
|
private Color currentVariableHighlightColor;
|
||||||
|
private Color searchHighlightColor;
|
||||||
private SearchLocation currentSearchLocation;
|
private SearchLocation currentSearchLocation;
|
||||||
|
|
||||||
private DecompileData decompileData = new EmptyDecompileData("No Function");
|
private DecompileData decompileData = new EmptyDecompileData("No Function");
|
||||||
|
@ -104,9 +119,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
|
|
||||||
decompilerHoverProvider = new DecompilerHoverProvider();
|
decompilerHoverProvider = new DecompilerHoverProvider();
|
||||||
|
|
||||||
currentSearchHighlightColor = options.getSearchHighlightColor();
|
searchHighlightColor = options.getSearchHighlightColor();
|
||||||
currentHighlightColor = options.getMiddleMouseHighlightColor();
|
currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
|
||||||
currentMiddleMouseHighlightButton = options.getMiddleMouseHighlightButton();
|
middleMouseHighlightColor = options.getMiddleMouseHighlightColor();
|
||||||
|
middleMouseHighlightButton = options.getMiddleMouseHighlightButton();
|
||||||
|
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
add(scroller);
|
add(scroller);
|
||||||
|
@ -128,6 +144,67 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
return fieldPanel;
|
return fieldPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void applySecondaryHighlights(Map<String, Color> highlightsByName) {
|
||||||
|
|
||||||
|
Set<Entry<String, Color>> entries = highlightsByName.entrySet();
|
||||||
|
for (Entry<String, Color> entry : entries) {
|
||||||
|
String tokenName = entry.getKey();
|
||||||
|
Color color = entry.getValue();
|
||||||
|
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenName);
|
||||||
|
highlightController.addSecondaryHighlights(lazyTokens, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenHighlightColors getSecondaryHighlightColors() {
|
||||||
|
return highlightController.getSecondaryHighlightColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenHighlights getSecondaryHighlightedTokens() {
|
||||||
|
return highlightController.getSecondaryHighlightedTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSecondaryHighlights() {
|
||||||
|
Function function = controller.getFunction();
|
||||||
|
highlightController.removeSecondaryHighlights(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSecondaryHighlight(ClangToken token) {
|
||||||
|
removeSecondaryHighlight(token.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeSecondaryHighlight(String tokenText) {
|
||||||
|
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenText);
|
||||||
|
highlightController.removeSecondaryHighlights(lazyTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSecondaryHighlight(ClangToken token) {
|
||||||
|
String tokenText = token.getText();
|
||||||
|
addSecondaryHighlight(tokenText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSecondaryHighlight(String tokenText) {
|
||||||
|
Supplier<List<ClangToken>> lazyTokens = () -> {
|
||||||
|
return findTokensByName(tokenText);
|
||||||
|
};
|
||||||
|
highlightController.addSecondaryHighlights(tokenText, lazyTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSecondaryHighlight(ClangToken token, Color color) {
|
||||||
|
addSecondaryHighlight(token.getText(), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSecondaryHighlight(String tokenText, Color color) {
|
||||||
|
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenText);
|
||||||
|
highlightController.addSecondaryHighlights(lazyTokens, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void togglePrimaryHighlight(FieldLocation location, Field field, Color highlightColor) {
|
||||||
|
|
||||||
|
ClangToken token = ((ClangTextField) field).getToken(location);
|
||||||
|
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(token.getText());
|
||||||
|
highlightController.togglePrimaryHighlights(middleMouseHighlightColor, lazyTokens);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBackground(Color bg) {
|
public void setBackground(Color bg) {
|
||||||
originalBackgroundColor = bg;
|
originalBackgroundColor = bg;
|
||||||
|
@ -168,17 +245,48 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
|
|
||||||
decompilerHoverProvider.setProgram(decompileData.getProgram());
|
decompilerHoverProvider.setProgram(decompileData.getProgram());
|
||||||
|
|
||||||
/*
|
// give user notice when seeing the decompile of a non-function
|
||||||
* Give user notice when seeing the decompile of a non-function.
|
|
||||||
*/
|
|
||||||
useNonFunctionColor = function instanceof UndefinedFunction;
|
useNonFunctionColor = function instanceof UndefinedFunction;
|
||||||
setBackground(originalBackgroundColor);
|
setBackground(originalBackgroundColor);
|
||||||
|
|
||||||
if (clipboard != null) {
|
if (clipboard != null) {
|
||||||
clipboard.selectionChanged(null);
|
clipboard.selectionChanged(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't highlight search results across functions
|
// don't highlight search results across functions
|
||||||
currentSearchLocation = null;
|
currentSearchLocation = null;
|
||||||
|
|
||||||
|
reapplySecondaryHighlights();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reapplySecondaryHighlights() {
|
||||||
|
|
||||||
|
Function function = decompileData.getFunction();
|
||||||
|
if (function == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The existing highlights are based on the previously generated tokens, which no longer
|
||||||
|
// exist. Use those tokens to highlight the current tokens, which are conceptually the
|
||||||
|
// same tokens.
|
||||||
|
Set<HighlightToken> oldHighlights =
|
||||||
|
highlightController.getSecondaryHighlightsByFunction(function);
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
Map<String, List<ClangToken>> tokensByName =
|
||||||
|
CollectionUtils.asStream(oldHighlights)
|
||||||
|
.map(ht -> ht.getToken())
|
||||||
|
.collect(Collectors.groupingBy(t -> t.getText()))
|
||||||
|
;
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
Set<Entry<String, List<ClangToken>>> entries = tokensByName.entrySet();
|
||||||
|
for (Entry<String, List<ClangToken>> entry : entries) {
|
||||||
|
String name = entry.getKey();
|
||||||
|
List<ClangToken> oldTokens = entry.getValue();
|
||||||
|
highlightController.removeSecondaryHighlights(() -> oldTokens);
|
||||||
|
addSecondaryHighlight(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLocation(DecompileData oldData, DecompileData newData) {
|
private void setLocation(DecompileData oldData, DecompileData newData) {
|
||||||
|
@ -424,9 +532,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
setDecompileData(new EmptyDecompileData("Disposed"));
|
setDecompileData(new EmptyDecompileData("Disposed"));
|
||||||
layoutMgr = null;
|
layoutMgr = null;
|
||||||
|
|
||||||
decompilerHoverProvider.dispose();
|
decompilerHoverProvider.dispose();
|
||||||
|
highlighCursorUpdater.dispose();
|
||||||
|
highlightController.clearAllHighlights();
|
||||||
}
|
}
|
||||||
|
|
||||||
private FontMetrics getFontMetrics(DecompileOptions decompileOptions) {
|
private FontMetrics getFontMetrics(DecompileOptions decompileOptions) {
|
||||||
|
@ -464,8 +572,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonState == currentMiddleMouseHighlightButton && clickCount == 1) {
|
if (buttonState == middleMouseHighlightButton && clickCount == 1) {
|
||||||
highlightVariable(location, field, currentHighlightColor);
|
togglePrimaryHighlight(location, field, middleMouseHighlightColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,17 +732,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void highlightVariable(FieldLocation location, Field field, Color highlightColor) {
|
|
||||||
if (highlightController != null) {
|
|
||||||
ClangToken token = ((ClangTextField) field).getToken(location);
|
|
||||||
List<ClangToken> tokenList = new ArrayList<>();
|
|
||||||
findTokensByName(tokenList, layoutMgr.getRoot(), token.getText());
|
|
||||||
highlightController.clearHighlights();
|
|
||||||
highlightController.addTokensToHighlights(tokenList, highlightColor);
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Program getProgram() {
|
Program getProgram() {
|
||||||
return decompileData.getProgram();
|
return decompileData.getProgram();
|
||||||
}
|
}
|
||||||
|
@ -654,9 +751,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (highlightController != null) {
|
pendingHighlightUpdate = new PendingHighlightUpdate(location, field, trigger);
|
||||||
highlightController.fieldLocationChanged(location, field, trigger);
|
highlighCursorUpdater.update();
|
||||||
}
|
|
||||||
|
|
||||||
if (!(field instanceof ClangTextField)) {
|
if (!(field instanceof ClangTextField)) {
|
||||||
return;
|
return;
|
||||||
|
@ -743,19 +839,25 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
// End Search Methods
|
// End Search Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
public Color getDefaultHighlightColor() {
|
public Color getCurrentVariableHighlightColor() {
|
||||||
return currentHighlightColor;
|
return currentVariableHighlightColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getDefaultSpecialColor() {
|
public Color getMiddleMouseHighlightColor() {
|
||||||
|
return middleMouseHighlightColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used in a primary highlight to mark the token that was clicked. This is used
|
||||||
|
* in 'slice' actions to mark the source of the slice.
|
||||||
|
* @return the color
|
||||||
|
*/
|
||||||
|
public Color getSpecialHighlightColor() {
|
||||||
return SPECIAL_COLOR_DEF;
|
return SPECIAL_COLOR_DEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHighlightedText() {
|
public String getHighlightedText() {
|
||||||
if (highlightController != null) {
|
return highlightController.getHighlightedText();
|
||||||
return highlightController.getHighlightedText();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FieldLocation getCursorPosition() {
|
public FieldLocation getCursorPosition() {
|
||||||
|
@ -797,6 +899,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
return ((ClangTextField) field).getToken(cursorPosition);
|
return ((ClangTextField) field).getToken(cursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DecompileOptions getOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
public void addHoverService(DecompilerHoverService hoverService) {
|
public void addHoverService(DecompilerHoverService hoverService) {
|
||||||
decompilerHoverProvider.addHoverService(hoverService);
|
decompilerHoverProvider.addHoverService(hoverService);
|
||||||
}
|
}
|
||||||
|
@ -819,38 +925,40 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
return decompilerHoverProvider.isShowing();
|
return decompilerHoverProvider.isShowing();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearHighlights() {
|
public void clearPrimaryHighlights() {
|
||||||
if (highlightController != null) {
|
highlightController.clearPrimaryHighlights();
|
||||||
highlightController.clearHighlights();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addVarnodeHighlights(Set<Varnode> varnodes, Color highlightColor,
|
public void addVarnodeHighlights(Set<Varnode> varnodes,
|
||||||
Varnode specificvn, PcodeOp specificop, Color specialColor) {
|
TokenHighlightColorProvider colorProvider) {
|
||||||
if (highlightController != null) {
|
|
||||||
ClangTokenGroup root = layoutMgr.getRoot();
|
ClangTokenGroup root = layoutMgr.getRoot();
|
||||||
highlightController.addVarnodesToHighlight(root, varnodes, highlightColor, specificvn,
|
highlightController.addPrimaryHighlights(root, colorProvider);
|
||||||
specificop, specialColor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPcodeOpHighlights(Set<PcodeOp> ops, Color highlightColor) {
|
public void addPcodeOpHighlights(Set<PcodeOp> ops, Color hlColor) {
|
||||||
if (highlightController != null) {
|
ClangTokenGroup root = layoutMgr.getRoot();
|
||||||
ClangTokenGroup root = layoutMgr.getRoot();
|
highlightController.addPrimaryHighlights(root, ops, hlColor);
|
||||||
highlightController.addPcodeOpsToHighlight(root, ops, highlightColor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findTokensByName(List<ClangToken> tokenList, ClangTokenGroup group, String name) {
|
public List<ClangToken> findTokensByName(String name) {
|
||||||
|
List<ClangToken> tokens = new ArrayList<>();
|
||||||
|
doFindTokensByName(tokens, layoutMgr.getRoot(), name);
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doFindTokensByName(List<ClangToken> tokens, ClangTokenGroup group, String name) {
|
||||||
|
|
||||||
|
// TODO is it possible that two or more different variable tokens share the same name?
|
||||||
for (int i = 0; i < group.numChildren(); ++i) {
|
for (int i = 0; i < group.numChildren(); ++i) {
|
||||||
ClangNode child = group.Child(i);
|
ClangNode child = group.Child(i);
|
||||||
if (child instanceof ClangTokenGroup) {
|
if (child instanceof ClangTokenGroup) {
|
||||||
findTokensByName(tokenList, (ClangTokenGroup) child, name);
|
doFindTokensByName(tokens, (ClangTokenGroup) child, name);
|
||||||
}
|
}
|
||||||
else if (child instanceof ClangToken) {
|
else if (child instanceof ClangToken) {
|
||||||
ClangToken token = (ClangToken) child;
|
ClangToken token = (ClangToken) child;
|
||||||
if (name.equals(token.getText())) {
|
if (name.equals(token.getText())) {
|
||||||
tokenList.add(token);
|
tokens.add(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -875,23 +983,28 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
FieldSelection selection = new FieldSelection();
|
FieldSelection selection = new FieldSelection();
|
||||||
selection.addRange(BigInteger.ZERO, numIndexes);
|
selection.addRange(BigInteger.ZERO, numIndexes);
|
||||||
fieldPanel.setSelection(selection);
|
fieldPanel.setSelection(selection);
|
||||||
|
|
||||||
// fake it out that the selection was caused by the field panel GUI.
|
// fake it out that the selection was caused by the field panel GUI.
|
||||||
selectionChanged(selection, EventTrigger.GUI_ACTION);
|
selectionChanged(selection, EventTrigger.GUI_ACTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void optionsChanged(DecompileOptions decompilerOptions) {
|
public void optionsChanged(DecompileOptions decompilerOptions) {
|
||||||
setBackground(decompilerOptions.getCodeViewerBackgroundColor());
|
setBackground(decompilerOptions.getCodeViewerBackgroundColor());
|
||||||
currentHighlightColor = decompilerOptions.getMiddleMouseHighlightColor();
|
currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
|
||||||
currentMiddleMouseHighlightButton = decompilerOptions.getMiddleMouseHighlightButton();
|
middleMouseHighlightColor = decompilerOptions.getMiddleMouseHighlightColor();
|
||||||
currentSearchHighlightColor = decompilerOptions.getSearchHighlightColor();
|
middleMouseHighlightButton = decompilerOptions.getMiddleMouseHighlightButton();
|
||||||
if (highlightController != null) {
|
searchHighlightColor = decompilerOptions.getSearchHighlightColor();
|
||||||
highlightController.loadOptions(decompilerOptions);
|
|
||||||
}
|
highlightController.setHighlightColor(currentVariableHighlightColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHighlightController(ClangHighlightController highlightController) {
|
public void setHighlightController(ClangHighlightController highlightController) {
|
||||||
this.highlightController = highlightController;
|
if (this.highlightController != null) {
|
||||||
highlightController.loadOptions(options);
|
this.highlightController.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.highlightController = ClangHighlightController.dummyIfNull(highlightController);
|
||||||
|
highlightController.setHighlightColor(currentVariableHighlightColor);
|
||||||
highlightController.addListener(this);
|
highlightController.addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,6 +1013,36 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is function is used to alert the panel that a token was renamed.
|
||||||
|
* If the token that is being renamed had a secondary highlight, we must re-apply the highlight
|
||||||
|
* to the new token.
|
||||||
|
*
|
||||||
|
* @param token the token being renamed
|
||||||
|
* @param newName the new name of the token
|
||||||
|
*/
|
||||||
|
public void tokenRenamed(ClangToken token, String newName) {
|
||||||
|
|
||||||
|
if (!highlightController.hasSecondaryHighlight(token)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenHighlightColors colors = highlightController.getSecondaryHighlightColors();
|
||||||
|
String oldName = token.getText();
|
||||||
|
Color hlColor = colors.getColor(oldName);
|
||||||
|
highlightController.removeSecondaryHighlights(token);
|
||||||
|
|
||||||
|
controller.doWhenNotBusy(() -> {
|
||||||
|
|
||||||
|
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(newName);
|
||||||
|
highlightController.addSecondaryHighlights(lazyTokens, hlColor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClangHighlightController getHighlightController() {
|
||||||
|
return highlightController;
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Inner Classes
|
// Inner Classes
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -924,7 +1067,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Highlight[] { new Highlight(currentSearchLocation.getStartIndexInclusive(),
|
return new Highlight[] { new Highlight(currentSearchLocation.getStartIndexInclusive(),
|
||||||
currentSearchLocation.getEndIndexInclusive(), currentSearchHighlightColor) };
|
currentSearchLocation.getEndIndexInclusive(), searchHighlightColor) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -998,4 +1141,25 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||||
EventTrigger.GUI_ACTION);
|
EventTrigger.GUI_ACTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to track pending location updates. This allows us to buffer updates, only sending
|
||||||
|
* the last one received.
|
||||||
|
*/
|
||||||
|
private class PendingHighlightUpdate {
|
||||||
|
|
||||||
|
private FieldLocation location;
|
||||||
|
private Field field;
|
||||||
|
private EventTrigger trigger;
|
||||||
|
|
||||||
|
PendingHighlightUpdate(FieldLocation location, Field field, EventTrigger trigger) {
|
||||||
|
this.location = location;
|
||||||
|
this.field = field;
|
||||||
|
this.trigger = trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doUpdate() {
|
||||||
|
highlightController.fieldLocationChanged(location, field, trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompiler.component;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to used to track a {@link Decompiler} token along with its highlight color
|
||||||
|
*/
|
||||||
|
public class HighlightToken {
|
||||||
|
|
||||||
|
private ClangToken token;
|
||||||
|
private Color color;
|
||||||
|
|
||||||
|
public HighlightToken(ClangToken token, Color color) {
|
||||||
|
this.token = token;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClangToken getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return token.toString() + "; color=" + color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,25 +15,21 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.decompiler.component;
|
package ghidra.app.decompiler.component;
|
||||||
|
|
||||||
import ghidra.app.decompiler.ClangSyntaxToken;
|
|
||||||
import ghidra.app.decompiler.ClangToken;
|
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import docking.widgets.fieldpanel.field.Field;
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
|
import ghidra.app.decompiler.ClangSyntaxToken;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to handle location based highlights for a decompiled function.
|
* Class to handle location based highlights for a decompiled function.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class LocationClangHighlightController extends ClangHighlightController {
|
public class LocationClangHighlightController extends ClangHighlightController {
|
||||||
|
|
||||||
public LocationClangHighlightController() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) {
|
public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) {
|
||||||
|
|
||||||
clearHighlights();
|
clearPrimaryHighlights();
|
||||||
|
|
||||||
if (!(field instanceof ClangTextField)) {
|
if (!(field instanceof ClangTextField)) {
|
||||||
return;
|
return;
|
||||||
|
@ -44,12 +40,9 @@ public class LocationClangHighlightController extends ClangHighlightController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // clear any highlighted searchResults
|
addPrimaryHighlight(tok, defaultHighlightColor);
|
||||||
// decompilerPanel.setSearchResults(null);
|
|
||||||
|
|
||||||
addHighlight(tok, defaultHighlightColor);
|
|
||||||
if (tok instanceof ClangSyntaxToken) {
|
if (tok instanceof ClangSyntaxToken) {
|
||||||
addHighlightParen((ClangSyntaxToken) tok, defaultParenColor);
|
addPrimaryHighlightToTokensForParenthesis((ClangSyntaxToken) tok, defaultParenColor);
|
||||||
addHighlightBrace((ClangSyntaxToken) tok, defaultParenColor);
|
addHighlightBrace((ClangSyntaxToken) tok, defaultParenColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompiler.component;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import docking.widgets.EventTrigger;
|
||||||
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
|
import ghidra.app.decompiler.*;
|
||||||
|
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stub implementation of the highlight controller that allows clients to avoid null checks
|
||||||
|
*/
|
||||||
|
public class NullClangHighlightController extends ClangHighlightController {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHighlightedText() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPrimaryHighlights(ClangNode parentNode,
|
||||||
|
TokenHighlightColorProvider colorProvider) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPrimaryHighlights(ClangNode parentNode, Set<PcodeOp> ops, Color highlightColor) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
|
||||||
|
Color highlightColor) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearAllHighlights() {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addHighlightBrace(ClangSyntaxToken token, Color highlightColor) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(ClangHighlightListener listener) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeListener(ClangHighlightListener listener) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompiler.component;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to create and store colors related to token names
|
||||||
|
*/
|
||||||
|
public class TokenHighlightColors {
|
||||||
|
|
||||||
|
private int minColorSaturation = 100;
|
||||||
|
private int defaultColorAlpha = 100;
|
||||||
|
private Map<String, Color> colorsByName = new HashMap<>();
|
||||||
|
private List<Color> recentColors = new ArrayList<>();
|
||||||
|
|
||||||
|
private Color generateColor() {
|
||||||
|
return new Color((int) (minColorSaturation + Math.random() * (256 - minColorSaturation)),
|
||||||
|
(int) (minColorSaturation + Math.random() * (256 - minColorSaturation)),
|
||||||
|
(int) (minColorSaturation + Math.random() * (256 - minColorSaturation)),
|
||||||
|
defaultColorAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getColor(String text) {
|
||||||
|
return colorsByName.computeIfAbsent(text, t -> generateColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(String text, Color color) {
|
||||||
|
colorsByName.put(text, color);
|
||||||
|
recentColors.add(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Color> getRecentColors() {
|
||||||
|
return recentColors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.decompiler.component;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import ghidra.app.decompiler.*;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.pcode.HighFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple class to manage {@link HighlightToken}s used to create highlights in the Decompiler.
|
||||||
|
* This class allows clients to access highlights either by a {@link ClangToken} or a
|
||||||
|
* {@link HighlightToken}.
|
||||||
|
*/
|
||||||
|
public class TokenHighlights implements Iterable<HighlightToken> {
|
||||||
|
|
||||||
|
private Map<TokenKey, HighlightToken> highlightsByToken = new HashMap<>();
|
||||||
|
|
||||||
|
public Map<String, Color> copyHighlightsByName() {
|
||||||
|
Map<String, Color> results = new HashMap<>();
|
||||||
|
|
||||||
|
Collection<HighlightToken> values = highlightsByToken.values();
|
||||||
|
for (HighlightToken hl : values) {
|
||||||
|
String name = hl.getToken().getText();
|
||||||
|
results.put(name, hl.getColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TokenKey getKey(HighlightToken ht) {
|
||||||
|
return new TokenKey(ht);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TokenKey getKey(ClangToken t) {
|
||||||
|
return new TokenKey(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function getFunction(ClangToken t) {
|
||||||
|
ClangFunction cFunction = t.getClangFunction();
|
||||||
|
if (cFunction == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
HighFunction highFunction = cFunction.getHighFunction();
|
||||||
|
if (highFunction == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return highFunction.getFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are not highlights
|
||||||
|
* @return true if there are not highlights
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of highlights
|
||||||
|
* @return the number of highlights
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return highlightsByToken.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given highlight to this container
|
||||||
|
* @param t the highlight
|
||||||
|
*/
|
||||||
|
public void add(HighlightToken t) {
|
||||||
|
highlightsByToken.put(getKey(t), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current highlight for the given token
|
||||||
|
* @param t the token
|
||||||
|
* @return the highlight
|
||||||
|
*/
|
||||||
|
public HighlightToken get(ClangToken t) {
|
||||||
|
return highlightsByToken.get(getKey(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all highlights for the given function
|
||||||
|
*
|
||||||
|
* @param f the function
|
||||||
|
* @return the highlights
|
||||||
|
*/
|
||||||
|
public Set<HighlightToken> getHighlightsByFunction(Function f) {
|
||||||
|
Set<HighlightToken> results = new HashSet<>();
|
||||||
|
Set<TokenKey> keys = getHighlightKeys(f);
|
||||||
|
for (TokenKey key : keys) {
|
||||||
|
HighlightToken hl = highlightsByToken.get(key);
|
||||||
|
results.add(hl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this class has a highlight for the given token
|
||||||
|
* @param t the token
|
||||||
|
* @return true if this class has a highlight for the given token
|
||||||
|
*/
|
||||||
|
public boolean contains(ClangToken t) {
|
||||||
|
return highlightsByToken.containsKey(getKey(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all highlights from this container
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
highlightsByToken.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the highlight for the given token
|
||||||
|
* @param t the token
|
||||||
|
*/
|
||||||
|
public void remove(ClangToken t) {
|
||||||
|
highlightsByToken.remove(getKey(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all highlights associated with the given function
|
||||||
|
*
|
||||||
|
* @param function the function
|
||||||
|
* @return the removed highlights; empty if no highlights existed
|
||||||
|
*/
|
||||||
|
public Set<HighlightToken> removeHighlightsByFunction(Function function) {
|
||||||
|
Set<HighlightToken> oldHighlights = new HashSet<>();
|
||||||
|
Set<TokenKey> keys = getHighlightKeys(function);
|
||||||
|
for (TokenKey key : keys) {
|
||||||
|
HighlightToken hl = highlightsByToken.remove(key);
|
||||||
|
oldHighlights.add(hl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldHighlights;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<TokenKey> getHighlightKeys(Function function) {
|
||||||
|
Set<TokenKey> results = new HashSet<>();
|
||||||
|
|
||||||
|
Set<Entry<TokenKey, HighlightToken>> entries = highlightsByToken.entrySet();
|
||||||
|
for (Entry<TokenKey, HighlightToken> entry : entries) {
|
||||||
|
HighlightToken highlight = entry.getValue();
|
||||||
|
ClangToken token = highlight.getToken();
|
||||||
|
Function tokenFunction = getFunction(token);
|
||||||
|
if (function.equals(tokenFunction)) {
|
||||||
|
results.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<HighlightToken> iterator() {
|
||||||
|
return highlightsByToken.values().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return highlightsByToken.values().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Inner Classes
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
// a key that allows us to equate tokens that are not the same instance
|
||||||
|
private class TokenKey {
|
||||||
|
private ClangToken token;
|
||||||
|
|
||||||
|
TokenKey(ClangToken token) {
|
||||||
|
this.token = Objects.requireNonNull(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenKey(HighlightToken t) {
|
||||||
|
this(t.getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
String text = token.getText();
|
||||||
|
return text == null ? 0 : text.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClangToken otherToken = ((TokenKey) obj).token;
|
||||||
|
if (token.getClass() != otherToken.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(token.getText(), otherToken.getText())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClangLine lineParent = token.getLineParent();
|
||||||
|
ClangLine otherLineParent = otherToken.getLineParent();
|
||||||
|
if (!sameLines(lineParent, otherLineParent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (lineParent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int positionInLine = lineParent.indexOfToken(token);
|
||||||
|
int otherPositionInLine = otherLineParent.indexOfToken(otherToken);
|
||||||
|
return positionInLine == otherPositionInLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sameLines(ClangLine l1, ClangLine l2) {
|
||||||
|
|
||||||
|
if (l1 == null) {
|
||||||
|
if (l2 != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (l2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return l1.getLineNumber() == l2.getLineNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return token.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ import java.util.*;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.decompiler.component.DecompilerHighlightService;
|
|
||||||
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
||||||
import ghidra.app.events.*;
|
import ghidra.app.events.*;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
|
@ -49,7 +48,6 @@ import ghidra.util.task.SwingUpdateManager;
|
||||||
GoToService.class, NavigationHistoryService.class, ClipboardService.class,
|
GoToService.class, NavigationHistoryService.class, ClipboardService.class,
|
||||||
DataTypeManagerService.class /*, ProgramManager.class */
|
DataTypeManagerService.class /*, ProgramManager.class */
|
||||||
},
|
},
|
||||||
servicesProvided = { DecompilerHighlightService.class },
|
|
||||||
eventsConsumed = {
|
eventsConsumed = {
|
||||||
ProgramActivatedPluginEvent.class, ProgramOpenedPluginEvent.class,
|
ProgramActivatedPluginEvent.class, ProgramOpenedPluginEvent.class,
|
||||||
ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class,
|
ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class,
|
||||||
|
@ -83,12 +81,6 @@ public class DecompilePlugin extends Plugin {
|
||||||
|
|
||||||
disconnectedProviders = new ArrayList<>();
|
disconnectedProviders = new ArrayList<>();
|
||||||
connectedProvider = new PrimaryDecompilerProvider(this);
|
connectedProvider = new PrimaryDecompilerProvider(this);
|
||||||
|
|
||||||
registerServices();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerServices() {
|
|
||||||
registerServiceProvided(DecompilerHighlightService.class, connectedProvider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -102,10 +94,6 @@ public class DecompilePlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the Plugin to write any data-dependent state to the
|
|
||||||
* output stream.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void writeDataState(SaveState saveState) {
|
public void writeDataState(SaveState saveState) {
|
||||||
if (connectedProvider != null) {
|
if (connectedProvider != null) {
|
||||||
|
@ -128,11 +116,6 @@ public class DecompilePlugin extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Read data state; called after readConfigState(). Events generated
|
|
||||||
* by plugins we depend on should have been already been thrown by the
|
|
||||||
* time this method is called.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void readDataState(SaveState saveState) {
|
public void readDataState(SaveState saveState) {
|
||||||
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||||
|
|
|
@ -23,7 +23,9 @@ import ghidra.app.context.NavigatableActionContext;
|
||||||
import ghidra.app.context.RestrictedAddressSetContext;
|
import ghidra.app.context.RestrictedAddressSetContext;
|
||||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.UndefinedFunction;
|
||||||
import utility.function.Callback;
|
import utility.function.Callback;
|
||||||
|
|
||||||
public class DecompilerActionContext extends NavigatableActionContext
|
public class DecompilerActionContext extends NavigatableActionContext
|
||||||
|
@ -55,6 +57,15 @@ public class DecompilerActionContext extends NavigatableActionContext
|
||||||
return getComponentProvider().getDecompilerPanel();
|
return getComponentProvider().getDecompilerPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Function getFunction() {
|
||||||
|
return getComponentProvider().getController().getFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasRealFunction() {
|
||||||
|
Function f = getFunction();
|
||||||
|
return f != null && !(f instanceof UndefinedFunction);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The companion method of {@link #checkActionEnablement(Supplier)}. Decompiler actions
|
* The companion method of {@link #checkActionEnablement(Supplier)}. Decompiler actions
|
||||||
* must call this method from their {@link DockingActionIf#actionPerformed(ActionContext)}
|
* must call this method from their {@link DockingActionIf#actionPerformed(ActionContext)}
|
||||||
|
|
|
@ -15,17 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.decompile;
|
package ghidra.app.plugin.core.decompile;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.widgets.fieldpanel.LayoutModel;
|
|
||||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||||
import ghidra.GhidraOptions;
|
import ghidra.GhidraOptions;
|
||||||
|
@ -51,10 +51,10 @@ import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||||
import ghidra.util.task.SwingUpdateManager;
|
import ghidra.util.task.SwingUpdateManager;
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
import utility.function.Callback;
|
||||||
|
|
||||||
public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler,
|
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler {
|
||||||
DecompilerHighlightService {
|
|
||||||
final static String OPTIONS_TITLE = "Decompiler";
|
final static String OPTIONS_TITLE = "Decompiler";
|
||||||
|
|
||||||
private static Icon REFRESH_ICON = Icons.REFRESH_ICON;
|
private static Icon REFRESH_ICON = Icons.REFRESH_ICON;
|
||||||
|
@ -84,6 +84,15 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
private DockingAction renameFunctionAction;
|
private DockingAction renameFunctionAction;
|
||||||
private DockingAction findReferencesAction;
|
private DockingAction findReferencesAction;
|
||||||
|
|
||||||
|
private CloneDecompilerAction cloneDecompilerAction;
|
||||||
|
|
||||||
|
private SelectAllAction selectAllAction;
|
||||||
|
|
||||||
|
private SetSecondaryHighlightAction setSecondaryHighlightAction;
|
||||||
|
private SetSecondaryHighlightColorChooserAction setSecondaryHighlightColorChooserAction;
|
||||||
|
private RemoveAllSecondaryHighlightsAction removeAllSecondadryHighlightsAction;
|
||||||
|
private RemoveSecondaryHighlightAction removeSecondaryHighlightAction;
|
||||||
|
|
||||||
private final DecompilePlugin plugin;
|
private final DecompilePlugin plugin;
|
||||||
private ClipboardService clipboardService;
|
private ClipboardService clipboardService;
|
||||||
private DecompilerClipboardProvider clipboardProvider;
|
private DecompilerClipboardProvider clipboardProvider;
|
||||||
|
@ -97,13 +106,15 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
private DecoratorPanel decorationPanel;
|
private DecoratorPanel decorationPanel;
|
||||||
private ClangHighlightController highlightController;
|
private ClangHighlightController highlightController;
|
||||||
|
|
||||||
private CloneDecompilerAction cloneDecompilerAction;
|
|
||||||
|
|
||||||
private SelectAllAction selectAllAction;
|
|
||||||
|
|
||||||
private ViewerPosition pendingViewerPosition;
|
private ViewerPosition pendingViewerPosition;
|
||||||
|
|
||||||
private SwingUpdateManager redecompilerUpdater;
|
private SwingUpdateManager redecompileUpdater;
|
||||||
|
|
||||||
|
// Follow-up work can be items that need to happen after a pending decompile is finished, such
|
||||||
|
// as updating highlights after a variable rename
|
||||||
|
private SwingUpdateManager followUpWorkUpdater;
|
||||||
|
private Queue<Callback> followUpWork = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
private ServiceListener serviceListener = new ServiceListener() {
|
private ServiceListener serviceListener = new ServiceListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -131,9 +142,11 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
|
|
||||||
decompilerOptions = new DecompileOptions();
|
decompilerOptions = new DecompileOptions();
|
||||||
initializeDecompilerOptions();
|
initializeDecompilerOptions();
|
||||||
highlightController = new LocationClangHighlightController();
|
|
||||||
controller = new DecompilerController(this, decompilerOptions, clipboardProvider);
|
controller = new DecompilerController(this, decompilerOptions, clipboardProvider);
|
||||||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
||||||
|
|
||||||
|
// TODO move the hl controller into the panel
|
||||||
|
highlightController = new LocationClangHighlightController();
|
||||||
decompilerPanel.setHighlightController(highlightController);
|
decompilerPanel.setHighlightController(highlightController);
|
||||||
decorationPanel = new DecoratorPanel(decompilerPanel, isConnected);
|
decorationPanel = new DecoratorPanel(decompilerPanel, isConnected);
|
||||||
|
|
||||||
|
@ -155,7 +168,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
setHelpLocation(new HelpLocation(plugin.getName(), "Decompiler"));
|
setHelpLocation(new HelpLocation(plugin.getName(), "Decompiler"));
|
||||||
addToTool();
|
addToTool();
|
||||||
|
|
||||||
redecompilerUpdater = new SwingUpdateManager(500, 5000, () -> doRefresh());
|
redecompileUpdater = new SwingUpdateManager(500, 5000, () -> doRefresh());
|
||||||
|
followUpWorkUpdater = new SwingUpdateManager(() -> doFollowUpWork());
|
||||||
|
|
||||||
plugin.getTool().addServiceListener(serviceListener);
|
plugin.getTool().addServiceListener(serviceListener);
|
||||||
}
|
}
|
||||||
|
@ -287,7 +301,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
controller.resetDecompiler();
|
controller.resetDecompiler();
|
||||||
}
|
}
|
||||||
|
|
||||||
redecompilerUpdater.update();
|
redecompileUpdater.update();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,6 +314,20 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void doFollowUpWork() {
|
||||||
|
if (isBusy()) {
|
||||||
|
// try again later
|
||||||
|
followUpWorkUpdater.updateLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Callback work = followUpWork.poll();
|
||||||
|
while (work != null) {
|
||||||
|
work.call();
|
||||||
|
work = followUpWork.poll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// OptionsListener methods
|
// OptionsListener methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -332,7 +360,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
||||||
redecompilerUpdater.dispose();
|
redecompileUpdater.dispose();
|
||||||
|
followUpWorkUpdater.dispose();
|
||||||
|
|
||||||
if (clipboardService != null) {
|
if (clipboardService != null) {
|
||||||
clipboardService.deRegisterClipboardContentProvider(clipboardProvider);
|
clipboardService.deRegisterClipboardContentProvider(clipboardProvider);
|
||||||
|
@ -420,8 +449,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isDecompiling() {
|
boolean isBusy() {
|
||||||
return controller.isDecompiling();
|
return redecompileUpdater.isBusy() || controller.isDecompiling();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -457,6 +486,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
decompilerPanel.setCursorPosition(location);
|
decompilerPanel.setCursorPosition(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DecompilerController getController() {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// methods called from the controller
|
// methods called from the controller
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -466,10 +499,6 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
tool.setStatusInfo(message);
|
tool.setStatusInfo(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called from the DecompilerController to indicate that the decompilerData has changed.
|
|
||||||
* @param decompileData the new DecompilerData
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void decompileDataChanged(DecompileData decompileData) {
|
public void decompileDataChanged(DecompileData decompileData) {
|
||||||
updateTitle();
|
updateTitle();
|
||||||
|
@ -601,6 +630,12 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doWheNotBusy(Callback c) {
|
||||||
|
followUpWork.offer(c);
|
||||||
|
followUpWorkUpdater.update();
|
||||||
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// methods called from other members
|
// methods called from other members
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -610,14 +645,28 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cloneWindow() {
|
public void cloneWindow() {
|
||||||
final DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
|
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
|
||||||
|
|
||||||
// invoke later to give the window manage a chance to create the new window
|
// invoke later to give the window manage a chance to create the new window
|
||||||
// (its done in an invoke later)
|
// (its done in an invoke later)
|
||||||
Swing.runLater(() -> {
|
Swing.runLater(() -> {
|
||||||
|
|
||||||
|
ViewerPosition myViewPosition = controller.getDecompilerPanel().getViewerPosition();
|
||||||
newProvider.doSetProgram(program);
|
newProvider.doSetProgram(program);
|
||||||
newProvider.controller.setDecompileData(controller.getDecompileData());
|
|
||||||
newProvider.setLocation(currentLocation,
|
// Any change in the HighlightTokens should be delivered to the new panel
|
||||||
controller.getDecompilerPanel().getViewerPosition());
|
DecompilerPanel myPanel = getDecompilerPanel();
|
||||||
|
TokenHighlights myHighlights = myPanel.getSecondaryHighlightedTokens();
|
||||||
|
newProvider.setLocation(currentLocation, myPanel.getViewerPosition());
|
||||||
|
|
||||||
|
// transfer any state after the new decompiler is initialized
|
||||||
|
DecompilerPanel newPanel = newProvider.getDecompilerPanel();
|
||||||
|
Map<String, Color> highlightsByName = myHighlights.copyHighlightsByName();
|
||||||
|
newProvider.doWheNotBusy(() -> {
|
||||||
|
|
||||||
|
newPanel.setViewerPosition(myViewPosition);
|
||||||
|
newPanel.applySecondaryHighlights(highlightsByName);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,21 +785,6 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
editDataTypeAction = new EditDataTypeAction(tool, controller);
|
editDataTypeAction = new EditDataTypeAction(tool, controller);
|
||||||
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
|
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
|
||||||
|
|
||||||
defUseHighlightAction = new HighlightDefinedUseAction(controller);
|
|
||||||
setGroupInfo(defUseHighlightAction, variableGroup, subGroupPosition++);
|
|
||||||
|
|
||||||
forwardSliceAction = new ForwardSliceAction(controller);
|
|
||||||
setGroupInfo(forwardSliceAction, variableGroup, subGroupPosition++);
|
|
||||||
|
|
||||||
backwardSliceAction = new BackwardsSliceAction(controller);
|
|
||||||
setGroupInfo(backwardSliceAction, variableGroup, subGroupPosition++);
|
|
||||||
|
|
||||||
forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction(controller);
|
|
||||||
setGroupInfo(forwardSliceToOpsAction, variableGroup, subGroupPosition++);
|
|
||||||
|
|
||||||
backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction(controller);
|
|
||||||
setGroupInfo(backwardSliceToOpsAction, variableGroup, subGroupPosition++);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listing action for Creating Structure on a Variable
|
// Listing action for Creating Structure on a Variable
|
||||||
//
|
//
|
||||||
|
@ -768,18 +802,51 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
lockLocalAction = new CommitLocalsAction(tool, controller);
|
lockLocalAction = new CommitLocalsAction(tool, controller);
|
||||||
setGroupInfo(lockLocalAction, commitGroup, subGroupPosition++);
|
setGroupInfo(lockLocalAction, commitGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
subGroupPosition = 0; // reset for the next group
|
||||||
|
|
||||||
|
//
|
||||||
|
// Highlight
|
||||||
|
//
|
||||||
|
String highlightGroup = "4a - Highlight Group";
|
||||||
|
tool.setMenuGroup(new String[] { "Highlight" }, highlightGroup);
|
||||||
|
defUseHighlightAction = new HighlightDefinedUseAction(controller);
|
||||||
|
setGroupInfo(defUseHighlightAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
forwardSliceAction = new ForwardSliceAction(controller);
|
||||||
|
setGroupInfo(forwardSliceAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
backwardSliceAction = new BackwardsSliceAction();
|
||||||
|
setGroupInfo(backwardSliceAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
forwardSliceToOpsAction = new ForwardSliceToPCodeOpsAction(controller);
|
||||||
|
setGroupInfo(forwardSliceToOpsAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
backwardSliceToOpsAction = new BackwardsSliceToPCodeOpsAction(controller);
|
||||||
|
setGroupInfo(backwardSliceToOpsAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
tool.setMenuGroup(new String[] { "Secondary Highlight" }, highlightGroup);
|
||||||
|
setSecondaryHighlightAction = new SetSecondaryHighlightAction();
|
||||||
|
setGroupInfo(setSecondaryHighlightAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
setSecondaryHighlightColorChooserAction = new SetSecondaryHighlightColorChooserAction();
|
||||||
|
setGroupInfo(setSecondaryHighlightColorChooserAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
removeSecondaryHighlightAction = new RemoveSecondaryHighlightAction();
|
||||||
|
setGroupInfo(removeSecondaryHighlightAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
removeAllSecondadryHighlightsAction = new RemoveAllSecondaryHighlightsAction();
|
||||||
|
setGroupInfo(removeAllSecondadryHighlightsAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Comments
|
// Comments
|
||||||
//
|
//
|
||||||
// NOTE: this is just a placeholder to represent where the comment actions should appear
|
// NOTE: this is just a placeholder to represent where the comment actions should appear
|
||||||
// in relation to our local actions. We cannot control the comment action
|
// in relation to our local actions.
|
||||||
// arrangement by setting the values, we can
|
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// Search
|
// Search
|
||||||
//
|
//
|
||||||
|
|
||||||
String searchGroup = "comment2 - Search Group";
|
String searchGroup = "comment2 - Search Group";
|
||||||
subGroupPosition = 0; // reset for the next group
|
subGroupPosition = 0; // reset for the next group
|
||||||
|
|
||||||
|
@ -835,6 +902,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
addLocalAction(lockProtoAction);
|
addLocalAction(lockProtoAction);
|
||||||
addLocalAction(lockLocalAction);
|
addLocalAction(lockLocalAction);
|
||||||
addLocalAction(renameVarAction);
|
addLocalAction(renameVarAction);
|
||||||
|
addLocalAction(setSecondaryHighlightAction);
|
||||||
|
addLocalAction(setSecondaryHighlightColorChooserAction);
|
||||||
|
addLocalAction(removeSecondaryHighlightAction);
|
||||||
|
addLocalAction(removeAllSecondadryHighlightsAction);
|
||||||
addLocalAction(retypeVarAction);
|
addLocalAction(retypeVarAction);
|
||||||
addLocalAction(decompilerCreateStructureAction);
|
addLocalAction(decompilerCreateStructureAction);
|
||||||
tool.addAction(listingCreateStructureAction);
|
tool.addAction(listingCreateStructureAction);
|
||||||
|
@ -921,16 +992,6 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LayoutModel getLayoutModel() {
|
|
||||||
return controller.getDecompilerPanel().getLayoutModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearHighlights() {
|
|
||||||
controller.getDecompilerPanel().clearHighlights();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void programClosed(Program closedProgram) {
|
public void programClosed(Program closedProgram) {
|
||||||
controller.programClosed(closedProgram);
|
controller.programClosed(closedProgram);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
|
import ghidra.app.decompiler.component.TokenHighlights;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
|
||||||
|
public abstract class AbstractSetSecondaryHighlightAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
|
AbstractSetSecondaryHighlightAction(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
|
if (!context.hasRealFunction()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
ClangToken tokenAtCursor = panel.getTokenAtCursor();
|
||||||
|
if (tokenAtCursor == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
|
||||||
|
if (highlightedTokens.contains(tokenAtCursor)) {
|
||||||
|
return false; // already highlighted
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,23 +19,24 @@ import java.util.Set;
|
||||||
|
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
import ghidra.app.decompiler.ClangToken;
|
import ghidra.app.decompiler.ClangToken;
|
||||||
import ghidra.app.decompiler.component.*;
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
public class BackwardsSliceAction extends AbstractDecompilerAction {
|
public class BackwardsSliceAction extends AbstractDecompilerAction {
|
||||||
private final DecompilerController controller;
|
|
||||||
|
|
||||||
public BackwardsSliceAction(DecompilerController controller) {
|
public static final String NAME = "Highlight Backward Slice";
|
||||||
|
|
||||||
|
public BackwardsSliceAction() {
|
||||||
super("Highlight Backward Slice");
|
super("Highlight Backward Slice");
|
||||||
this.controller = controller;
|
setPopupMenuData(new MenuData(new String[] { "Highlight", "Backward Slice" }, "Decompile"));
|
||||||
setPopupMenuData(new MenuData(new String[] { "Highlight Backward Slice" }, "Decompile"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
|
||||||
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
||||||
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
||||||
return varnode != null;
|
return varnode != null;
|
||||||
|
@ -43,18 +44,21 @@ public class BackwardsSliceAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
|
||||||
|
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
|
||||||
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
||||||
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
||||||
if (varnode != null) {
|
if (varnode == null) {
|
||||||
PcodeOp op = tokenAtCursor.getPcodeOp();
|
return;
|
||||||
Set<Varnode> backwardSlice = DecompilerUtils.getBackwardSlice(varnode);
|
|
||||||
decompilerPanel.clearHighlights();
|
|
||||||
decompilerPanel.addVarnodeHighlights(backwardSlice,
|
|
||||||
decompilerPanel.getDefaultHighlightColor(), varnode, op,
|
|
||||||
decompilerPanel.getDefaultSpecialColor());
|
|
||||||
decompilerPanel.repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decompilerPanel.clearPrimaryHighlights();
|
||||||
|
|
||||||
|
PcodeOp op = tokenAtCursor.getPcodeOp();
|
||||||
|
Set<Varnode> backwardSlice = DecompilerUtils.getBackwardSlice(varnode);
|
||||||
|
SliceHighlightColorProvider colorProvider =
|
||||||
|
new SliceHighlightColorProvider(decompilerPanel, backwardSlice, varnode, op);
|
||||||
|
decompilerPanel.addVarnodeHighlights(backwardSlice, colorProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction {
|
||||||
super("Highlight Backward Inst Slice");
|
super("Highlight Backward Inst Slice");
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
setPopupMenuData(
|
setPopupMenuData(
|
||||||
new MenuData(new String[] { "Highlight Backward Inst Slice" }, "Decompile"));
|
new MenuData(new String[] { "Highlight", "Backward Inst Slice" }, "Decompile"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,10 +51,9 @@ public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction {
|
||||||
PcodeOp op = tokenAtCursor.getPcodeOp();
|
PcodeOp op = tokenAtCursor.getPcodeOp();
|
||||||
Set<PcodeOp> backwardSlice = DecompilerUtils.getBackwardSliceToPCodeOps(varnode);
|
Set<PcodeOp> backwardSlice = DecompilerUtils.getBackwardSliceToPCodeOps(varnode);
|
||||||
backwardSlice.add(op);
|
backwardSlice.add(op);
|
||||||
decompilerPanel.clearHighlights();
|
decompilerPanel.clearPrimaryHighlights();
|
||||||
decompilerPanel.addPcodeOpHighlights(backwardSlice,
|
decompilerPanel.addPcodeOpHighlights(backwardSlice,
|
||||||
decompilerPanel.getDefaultHighlightColor());
|
decompilerPanel.getCurrentVariableHighlightColor());
|
||||||
decompilerPanel.repaint();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,10 @@ import ghidra.app.decompiler.component.DecompilerController;
|
||||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.listing.Function;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.pcode.HighFunction;
|
import ghidra.program.model.pcode.HighFunction;
|
||||||
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||||
import ghidra.program.model.symbol.SourceType;
|
import ghidra.program.model.symbol.SourceType;
|
||||||
import ghidra.util.UndefinedFunction;
|
|
||||||
|
|
||||||
public class CommitLocalsAction extends AbstractDecompilerAction {
|
public class CommitLocalsAction extends AbstractDecompilerAction {
|
||||||
private final DecompilerController controller;
|
private final DecompilerController controller;
|
||||||
|
@ -55,8 +53,7 @@ public class CommitLocalsAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
Function function = controller.getFunction();
|
if (!context.hasRealFunction()) {
|
||||||
if (function == null || function instanceof UndefinedFunction) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return getHighFunction() != null;
|
return getHighFunction() != null;
|
||||||
|
|
|
@ -25,13 +25,11 @@ import ghidra.app.decompiler.component.DecompilerController;
|
||||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.listing.Function;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.pcode.HighFunction;
|
import ghidra.program.model.pcode.HighFunction;
|
||||||
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||||
import ghidra.program.model.symbol.SourceType;
|
import ghidra.program.model.symbol.SourceType;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.UndefinedFunction;
|
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
|
|
||||||
public class CommitParamsAction extends AbstractDecompilerAction {
|
public class CommitParamsAction extends AbstractDecompilerAction {
|
||||||
|
@ -65,9 +63,7 @@ public class CommitParamsAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
|
if (!context.hasRealFunction()) {
|
||||||
Function function = controller.getFunction();
|
|
||||||
if (function == null || function instanceof UndefinedFunction) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return getHighFunction() != null;
|
return getHighFunction() != null;
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class ForwardSliceAction extends AbstractDecompilerAction {
|
||||||
public ForwardSliceAction(DecompilerController controller) {
|
public ForwardSliceAction(DecompilerController controller) {
|
||||||
super("Highlight Forward Slice");
|
super("Highlight Forward Slice");
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
setPopupMenuData(new MenuData(new String[] { "Highlight Forward Slice" }, "Decompile"));
|
setPopupMenuData(new MenuData(new String[] { "Highlight", "Forward Slice" }, "Decompile"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,15 +46,17 @@ public class ForwardSliceAction extends AbstractDecompilerAction {
|
||||||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
||||||
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
||||||
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
||||||
if (varnode != null) {
|
if (varnode == null) {
|
||||||
PcodeOp op = tokenAtCursor.getPcodeOp();
|
return;
|
||||||
Set<Varnode> forwardSlice = DecompilerUtils.getForwardSlice(varnode);
|
|
||||||
decompilerPanel.clearHighlights();
|
|
||||||
decompilerPanel.addVarnodeHighlights(forwardSlice,
|
|
||||||
decompilerPanel.getDefaultHighlightColor(), varnode, op,
|
|
||||||
decompilerPanel.getDefaultSpecialColor());
|
|
||||||
decompilerPanel.repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PcodeOp op = tokenAtCursor.getPcodeOp();
|
||||||
|
Set<Varnode> forwardSlice = DecompilerUtils.getForwardSlice(varnode);
|
||||||
|
decompilerPanel.clearPrimaryHighlights();
|
||||||
|
|
||||||
|
SliceHighlightColorProvider colorProvider =
|
||||||
|
new SliceHighlightColorProvider(decompilerPanel, forwardSlice, varnode, op);
|
||||||
|
decompilerPanel.addVarnodeHighlights(forwardSlice, colorProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction {
|
||||||
super("Highlight Forward Inst Slice");
|
super("Highlight Forward Inst Slice");
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
setPopupMenuData(
|
setPopupMenuData(
|
||||||
new MenuData(new String[] { "Highlight Forward Inst Slice" }, "Decompile"));
|
new MenuData(new String[] { "Highlight", "Forward Inst Slice" }, "Decompile"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,10 +51,9 @@ public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction {
|
||||||
PcodeOp op = tokenAtCursor.getPcodeOp();
|
PcodeOp op = tokenAtCursor.getPcodeOp();
|
||||||
Set<PcodeOp> forwardSlice = DecompilerUtils.getForwardSliceToPCodeOps(varnode);
|
Set<PcodeOp> forwardSlice = DecompilerUtils.getForwardSliceToPCodeOps(varnode);
|
||||||
forwardSlice.add(op);
|
forwardSlice.add(op);
|
||||||
decompilerPanel.clearHighlights();
|
decompilerPanel.clearPrimaryHighlights();
|
||||||
decompilerPanel.addPcodeOpHighlights(forwardSlice,
|
decompilerPanel.addPcodeOpHighlights(forwardSlice,
|
||||||
decompilerPanel.getDefaultHighlightColor());
|
decompilerPanel.getCurrentVariableHighlightColor());
|
||||||
decompilerPanel.repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.decompile.actions;
|
package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.Set;
|
||||||
|
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
import ghidra.app.decompiler.ClangToken;
|
import ghidra.app.decompiler.ClangToken;
|
||||||
import ghidra.app.decompiler.component.*;
|
import ghidra.app.decompiler.component.*;
|
||||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
public class HighlightDefinedUseAction extends AbstractDecompilerAction {
|
public class HighlightDefinedUseAction extends AbstractDecompilerAction {
|
||||||
|
@ -29,7 +30,7 @@ public class HighlightDefinedUseAction extends AbstractDecompilerAction {
|
||||||
public HighlightDefinedUseAction(DecompilerController controller) {
|
public HighlightDefinedUseAction(DecompilerController controller) {
|
||||||
super("Highlight Defined Use");
|
super("Highlight Defined Use");
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
setPopupMenuData(new MenuData(new String[] { "Highlight Def-use" }, "Decompile"));
|
setPopupMenuData(new MenuData(new String[] { "Highlight", "Def-use" }, "Decompile"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,15 +46,17 @@ public class HighlightDefinedUseAction extends AbstractDecompilerAction {
|
||||||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
||||||
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
||||||
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
||||||
if (varnode != null) {
|
if (varnode == null) {
|
||||||
HashSet<Varnode> varnodes = new HashSet<Varnode>();
|
return;
|
||||||
varnodes.add(varnode);
|
|
||||||
decompilerPanel.clearHighlights();
|
|
||||||
decompilerPanel.addVarnodeHighlights(varnodes,
|
|
||||||
decompilerPanel.getDefaultHighlightColor(), varnode, varnode.getDef(),
|
|
||||||
decompilerPanel.getDefaultSpecialColor());
|
|
||||||
decompilerPanel.repaint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decompilerPanel.clearPrimaryHighlights();
|
||||||
|
|
||||||
|
Set<Varnode> varnodes = Set.of(varnode);
|
||||||
|
PcodeOp op = varnode.getDef();
|
||||||
|
SliceHighlightColorProvider colorProvider =
|
||||||
|
new SliceHighlightColorProvider(decompilerPanel, varnodes, varnode, op);
|
||||||
|
decompilerPanel.addVarnodeHighlights(varnodes, colorProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompile.actions;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.decompiler.component.*;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.app.util.HelpTopics;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all secondary highlights for the current function
|
||||||
|
*
|
||||||
|
* @see ClangHighlightController
|
||||||
|
*/
|
||||||
|
public class RemoveAllSecondaryHighlightsAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
|
public static final String NAME = "Remove All Secondary Highlights";
|
||||||
|
|
||||||
|
public RemoveAllSecondaryHighlightsAction() {
|
||||||
|
super(NAME);
|
||||||
|
|
||||||
|
setPopupMenuData(new MenuData(
|
||||||
|
new String[] { "Secondary Highlight", "Remove All Highlights" }, "Decompile"));
|
||||||
|
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
|
if (!context.hasRealFunction()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
|
||||||
|
return !highlightedTokens.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
panel.removeSecondaryHighlights();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompile.actions;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.component.*;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.app.util.HelpTopics;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the selected token's secondary highlight
|
||||||
|
*
|
||||||
|
* @see ClangHighlightController
|
||||||
|
*/
|
||||||
|
public class RemoveSecondaryHighlightAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
|
public static final String NAME = "Remove Secondary Highlight";
|
||||||
|
|
||||||
|
public RemoveSecondaryHighlightAction() {
|
||||||
|
super(NAME);
|
||||||
|
|
||||||
|
setPopupMenuData(
|
||||||
|
new MenuData(new String[] { "Secondary Highlight", "Remove Highlight" }, "Decompile"));
|
||||||
|
setHelpLocation(new HelpLocation(HelpTopics.SELECTION, getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
|
if (!context.hasRealFunction()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
ClangToken token = panel.getTokenAtCursor();
|
||||||
|
if (token == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
|
||||||
|
return highlightedTokens.contains(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
ClangToken token = panel.getTokenAtCursor();
|
||||||
|
panel.removeSecondaryHighlight(token);
|
||||||
|
}
|
||||||
|
}
|
|
@ -259,6 +259,7 @@ public class RenameVariableAction extends AbstractDecompilerAction {
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(transaction, commit);
|
program.endTransaction(transaction, commit);
|
||||||
|
controller.getDecompilerPanel().tokenRenamed(tokenAtCursor, nameTask.getNewName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompile.actions;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.component.ClangHighlightController;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the secondary highlight on the selected token
|
||||||
|
*
|
||||||
|
* @see ClangHighlightController
|
||||||
|
*/
|
||||||
|
public class SetSecondaryHighlightAction extends AbstractSetSecondaryHighlightAction {
|
||||||
|
|
||||||
|
public static String NAME = "Set Secondary Highlight";
|
||||||
|
|
||||||
|
public SetSecondaryHighlightAction() {
|
||||||
|
super(NAME);
|
||||||
|
|
||||||
|
setPopupMenuData(
|
||||||
|
new MenuData(new String[] { "Secondary Highlight", "Set Highlight" }, "Decompile"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||||
|
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
ClangToken token = panel.getTokenAtCursor();
|
||||||
|
panel.addSecondaryHighlight(token);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompile.actions;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import docking.options.editor.GhidraColorChooser;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
|
import ghidra.app.decompiler.component.TokenHighlightColors;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
|
||||||
|
public class SetSecondaryHighlightColorChooserAction extends AbstractSetSecondaryHighlightAction {
|
||||||
|
|
||||||
|
public static String NAME = "Set Secondary Highlight With Color";
|
||||||
|
|
||||||
|
public SetSecondaryHighlightColorChooserAction() {
|
||||||
|
super(NAME);
|
||||||
|
|
||||||
|
setPopupMenuData(
|
||||||
|
new MenuData(new String[] { "Secondary Highlight", "Set Highlight..." }, "Decompile"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||||
|
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
ClangToken token = panel.getTokenAtCursor();
|
||||||
|
TokenHighlightColors colors = panel.getSecondaryHighlightColors();
|
||||||
|
List<Color> recentColors = colors.getRecentColors();
|
||||||
|
|
||||||
|
String name = token.getText();
|
||||||
|
Color currentColor = colors.getColor(name);
|
||||||
|
GhidraColorChooser chooser = new GhidraColorChooser(currentColor);
|
||||||
|
chooser.setColorHistory(recentColors);
|
||||||
|
chooser.setActiveTab("RGB");
|
||||||
|
|
||||||
|
Color colorChoice = chooser.showDialog(null);
|
||||||
|
if (colorChoice == null) {
|
||||||
|
return; // cancelled
|
||||||
|
}
|
||||||
|
|
||||||
|
colors.setColor(name, colorChoice);
|
||||||
|
panel.addSecondaryHighlight(token, colorChoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/* ###
|
||||||
|
* 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.decompile.actions;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||||
|
import ghidra.app.decompiler.component.DecompilerUtils;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to provider a color for highlight a variable using one of the 'slice' actions
|
||||||
|
*
|
||||||
|
* @see ForwardSliceAction
|
||||||
|
* @see BackwardsSliceAction
|
||||||
|
*/
|
||||||
|
public class SliceHighlightColorProvider implements TokenHighlightColorProvider {
|
||||||
|
|
||||||
|
private Set<Varnode> varnodes;
|
||||||
|
private Varnode specialVn;
|
||||||
|
private PcodeOp specialOp;
|
||||||
|
private Color hlColor;
|
||||||
|
private Color specialHlColor;
|
||||||
|
|
||||||
|
SliceHighlightColorProvider(DecompilerPanel panel, Set<Varnode> varnodes, Varnode specialVn,
|
||||||
|
PcodeOp specialOp) {
|
||||||
|
this.varnodes = varnodes;
|
||||||
|
this.specialVn = specialVn;
|
||||||
|
this.specialOp = specialOp;
|
||||||
|
|
||||||
|
hlColor = panel.getCurrentVariableHighlightColor();
|
||||||
|
specialHlColor = panel.getSpecialHighlightColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getColor(ClangToken token) {
|
||||||
|
|
||||||
|
Varnode vn = DecompilerUtils.getVarnodeRef(token);
|
||||||
|
if (vn == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color c = null;
|
||||||
|
if (varnodes.contains(vn)) {
|
||||||
|
c = hlColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (specialOp == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for specific varnode to label with special color
|
||||||
|
if (vn == specialVn && token.getPcodeOp() == specialOp) {
|
||||||
|
c = specialHlColor;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,14 +13,22 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.decompiler.component;
|
package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.LayoutModel;
|
import java.awt.Color;
|
||||||
|
|
||||||
public interface DecompilerHighlightService {
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
|
||||||
public LayoutModel getLayoutModel();
|
/**
|
||||||
|
* Provides highlight color for the given token
|
||||||
public void clearHighlights();
|
*/
|
||||||
|
public interface TokenHighlightColorProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a color for the given token
|
||||||
|
*
|
||||||
|
* @param token the token
|
||||||
|
* @return the color
|
||||||
|
*/
|
||||||
|
public Color getColor(ClangToken token);
|
||||||
}
|
}
|
|
@ -69,7 +69,8 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void waitForDecompiler() {
|
protected void waitForDecompiler() {
|
||||||
waitForCondition(() -> !provider.isDecompiling());
|
waitForSwing();
|
||||||
|
waitForCondition(() -> !provider.isBusy());
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
|
||||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||||
FieldPanel fp = panel.getFieldPanel();
|
FieldPanel fp = panel.getFieldPanel();
|
||||||
click(fp, 1, true);
|
click(fp, 1, true);
|
||||||
|
waitForSwing();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doubleClick() {
|
protected void doubleClick() {
|
||||||
|
@ -89,7 +91,7 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FieldLocation loc(int lineNumber, int col) {
|
protected FieldLocation loc(int lineNumber, int col) {
|
||||||
FieldLocation loc = new FieldLocation(lineNumber, 0, 0, col);
|
FieldLocation loc = new FieldLocation(lineNumber - 1, 0, 0, col);
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,20 +99,56 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
|
||||||
|
|
||||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||||
List<Field> fields = panel.getFields();
|
List<Field> fields = panel.getFields();
|
||||||
Field line = fields.get(lineNumber - 1); // 0-based
|
Field line = fields.get(lineNumber - 1); // -1 for 1-based line number
|
||||||
return (ClangTextField) line;
|
return (ClangTextField) line;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getTokenText(FieldLocation loc) {
|
// note: the index is 0-based; use getFieldForLine() when using 1-based line numbers
|
||||||
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
|
protected ClangTextField getFieldForIndex(int lineIndex) {
|
||||||
|
|
||||||
|
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||||
|
List<Field> fields = panel.getFields();
|
||||||
|
Field line = fields.get(lineIndex);
|
||||||
|
return (ClangTextField) line;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ClangToken getToken(int line, int col) {
|
||||||
|
FieldLocation loc = loc(line, col);
|
||||||
|
ClangTextField field = getFieldForLine(line);
|
||||||
ClangToken token = field.getToken(loc);
|
ClangToken token = field.getToken(loc);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ClangToken getToken(FieldLocation loc) {
|
||||||
|
int lineNumber = loc.getIndex().intValue() + 1; // 0-based
|
||||||
|
ClangTextField field = getFieldForLine(lineNumber);
|
||||||
|
ClangToken token = field.getToken(loc);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DecompilerPanel getDecompilerPanel() {
|
||||||
|
return provider.getDecompilerPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the token under the cursor
|
||||||
|
* @return the token under the cursor
|
||||||
|
*/
|
||||||
|
protected ClangToken getToken() {
|
||||||
|
DecompilerPanel panel = getDecompilerPanel();
|
||||||
|
FieldLocation loc = panel.getCursorPosition();
|
||||||
|
return getToken(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getTokenText(FieldLocation loc) {
|
||||||
|
ClangToken token = getToken(loc);
|
||||||
return token.getText();
|
return token.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertToken(String tokenText, int line, int... cols) {
|
protected void assertToken(String tokenText, int line, int... cols) {
|
||||||
for (int col : cols) {
|
for (int col : cols) {
|
||||||
FieldLocation loc = loc(line, col);
|
ClangToken token = getToken(line, col);
|
||||||
String text = getTokenText(loc);
|
String text = token.getText();
|
||||||
assertEquals(tokenText, text);
|
assertEquals(tokenText, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -48,10 +48,10 @@ public class DecompilerFindReferencesToNestedStructureActionTest
|
||||||
7| _printf("call_structure_A: %s\n",(a->b).c.name);
|
7| _printf("call_structure_A: %s\n",(a->b).c.name);
|
||||||
8| _printf("call_structure_A: %s\n",(a->b).c.d.name);
|
8| _printf("call_structure_A: %s\n",(a->b).c.d.name);
|
||||||
9| _printf("call_structure_A: %s\n",(a->b).c.d.e.name);
|
9| _printf("call_structure_A: %s\n",(a->b).c.d.e.name);
|
||||||
10| _call_structure_B(&a->b);
|
10| _call_structure_B(&a->b);
|
||||||
11| return;
|
11| return;
|
||||||
12| }
|
12| }
|
||||||
13|
|
13|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
decompile(CALL_STRUCTURE_A_ADDRESS);
|
decompile(CALL_STRUCTURE_A_ADDRESS);
|
||||||
|
|
|
@ -15,14 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package docking.options.editor;
|
package docking.options.editor;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.*;
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.JColorChooser;
|
import javax.swing.*;
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.colorchooser.AbstractColorChooserPanel;
|
import javax.swing.colorchooser.AbstractColorChooserPanel;
|
||||||
|
|
||||||
public class GhidraColorChooser extends JColorChooser {
|
public class GhidraColorChooser extends JColorChooser {
|
||||||
|
@ -31,6 +30,7 @@ public class GhidraColorChooser extends JColorChooser {
|
||||||
|
|
||||||
private String title = DEFAULT_TITLE;
|
private String title = DEFAULT_TITLE;
|
||||||
private RecentColorCache recentColorCache = new RecentColorCache();
|
private RecentColorCache recentColorCache = new RecentColorCache();
|
||||||
|
private String activeTabName;
|
||||||
|
|
||||||
public GhidraColorChooser() {
|
public GhidraColorChooser() {
|
||||||
super();
|
super();
|
||||||
|
@ -54,12 +54,24 @@ public class GhidraColorChooser extends JColorChooser {
|
||||||
return recentColorCache.getMRUColorList();
|
return recentColorCache.getMRUColorList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the active tab of this chooser to be the given tab name, if it exists (the color chooser
|
||||||
|
* UI may be different, depending upon the current Look and Feel)
|
||||||
|
*
|
||||||
|
* @param tabName the tab name
|
||||||
|
*/
|
||||||
|
public void setActiveTab(String tabName) {
|
||||||
|
activeTabName = tabName;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public Color showDialog(Component centerOverComponent) {
|
public Color showDialog(Component centerOverComponent) {
|
||||||
maybeInstallSettableColorSwatchChooserPanel();
|
maybeInstallSettableColorSwatchChooserPanel();
|
||||||
|
|
||||||
OKListener okListener = new OKListener();
|
OKListener okListener = new OKListener();
|
||||||
JDialog dialog = createDialog(centerOverComponent, title, true, this, okListener, null);
|
JDialog dialog = createDialog(centerOverComponent, title, true, this, okListener, null);
|
||||||
|
doSetActiveTab(dialog);
|
||||||
|
|
||||||
dialog.show(); // blocks until user brings dialog down...
|
dialog.show(); // blocks until user brings dialog down...
|
||||||
Color color = okListener.getColor();
|
Color color = okListener.getColor();
|
||||||
if (color != null) {
|
if (color != null) {
|
||||||
|
@ -68,6 +80,48 @@ public class GhidraColorChooser extends JColorChooser {
|
||||||
return color; // null if the user cancels
|
return color; // null if the user cancels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void doSetActiveTab(JDialog dialog) {
|
||||||
|
if (activeTabName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JTabbedPane pane = findTabbedPane(dialog);
|
||||||
|
if (pane == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = pane.getTabCount();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
String tabTitle = pane.getTitleAt(i);
|
||||||
|
if (activeTabName.equals(tabTitle)) {
|
||||||
|
pane.setSelectedIndex(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JTabbedPane findTabbedPane(Component component) {
|
||||||
|
if (!(component instanceof Container)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Container parent = (Container) component;
|
||||||
|
if (parent instanceof JTabbedPane) {
|
||||||
|
return (JTabbedPane) parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = parent.getComponentCount();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
Component child = parent.getComponent(i);
|
||||||
|
JTabbedPane pane = findTabbedPane(child);
|
||||||
|
if (pane != null) {
|
||||||
|
return pane;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void maybeInstallSettableColorSwatchChooserPanel() {
|
private void maybeInstallSettableColorSwatchChooserPanel() {
|
||||||
if (recentColorCache.size() == 0) {
|
if (recentColorCache.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -87,7 +141,13 @@ public class GhidraColorChooser extends JColorChooser {
|
||||||
SettableColorSwatchChooserPanel newSwatchPanel =
|
SettableColorSwatchChooserPanel newSwatchPanel =
|
||||||
new SettableColorSwatchChooserPanel(mruColorList);
|
new SettableColorSwatchChooserPanel(mruColorList);
|
||||||
AbstractColorChooserPanel[] newChooserPanels =
|
AbstractColorChooserPanel[] newChooserPanels =
|
||||||
new AbstractColorChooserPanel[] { newSwatchPanel, chooserPanels[1], chooserPanels[2] };
|
new AbstractColorChooserPanel[chooserPanels.length];
|
||||||
|
newChooserPanels[0] = newSwatchPanel;
|
||||||
|
for (int i = 1; i < chooserPanels.length; i++) {
|
||||||
|
AbstractColorChooserPanel panel = chooserPanels[i];
|
||||||
|
newChooserPanels[i] = panel;
|
||||||
|
}
|
||||||
|
|
||||||
setChooserPanels(newChooserPanels);
|
setChooserPanels(newChooserPanels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,21 +56,6 @@ public class InputDialog extends DialogComponentProvider {
|
||||||
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, true, null);
|
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a generic input dialog with the specified title, a text field,
|
|
||||||
* labeled by the specified label. The user should check the value of
|
|
||||||
* "isCanceled()" to know whether or not the user canceled the operation.
|
|
||||||
* Otherwise, use the "getValue()" or "getValues()" to get the value(s)
|
|
||||||
* entered by the user. Use the tool's "showDialog()" to display the dialog.
|
|
||||||
* <P>
|
|
||||||
* @param dialogTitle used as the name of the dialog's title bar
|
|
||||||
* @param label value to use for the label of the text field
|
|
||||||
* @param listener listener that is called when the OK button is hit
|
|
||||||
*/
|
|
||||||
public InputDialog(String dialogTitle, String label, InputDialogListener listener) {
|
|
||||||
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, true, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a generic input dialog with the specified title, a text field,
|
* Creates a generic input dialog with the specified title, a text field,
|
||||||
* labeled by the specified label. The user should check the value of
|
* labeled by the specified label. The user should check the value of
|
||||||
|
@ -96,27 +81,13 @@ public class InputDialog extends DialogComponentProvider {
|
||||||
* @param dialogTitle used as the name of the dialog's title bar
|
* @param dialogTitle used as the name of the dialog's title bar
|
||||||
* @param label value to use for the label of the text field
|
* @param label value to use for the label of the text field
|
||||||
* @param initialValue initial value to use for the text field
|
* @param initialValue initial value to use for the text field
|
||||||
|
* @param listener the dialog listener (may be null)
|
||||||
*/
|
*/
|
||||||
public InputDialog(String dialogTitle, String label, String initialValue,
|
public InputDialog(String dialogTitle, String label, String initialValue,
|
||||||
InputDialogListener listener) {
|
InputDialogListener listener) {
|
||||||
this(dialogTitle, new String[] { label }, new String[] { initialValue }, true, listener);
|
this(dialogTitle, new String[] { label }, new String[] { initialValue }, true, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a generic input dialog with the specified title, a text field,
|
|
||||||
* labeled by the specified label. The user should check the value of
|
|
||||||
* "isCanceled()" to know whether or not the user canceled the operation.
|
|
||||||
* Otherwise, use the "getValue()" or "getValues()" to get the value(s)
|
|
||||||
* entered by the user. Use the tool's "showDialog()" to display the dialog.
|
|
||||||
* <P>
|
|
||||||
* @param dialogTitle used as the name of the dialog's title bar
|
|
||||||
* @param label values to use for the label of the text field
|
|
||||||
* @param isModal whether or not the dialog is to be modal
|
|
||||||
*/
|
|
||||||
public InputDialog(String dialogTitle, String label, boolean isModal) {
|
|
||||||
this(dialogTitle, new String[] { label }, new String[] { DEFAULT_VALUE }, isModal, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a generic input dialog with the specified title, a text field,
|
* Creates a generic input dialog with the specified title, a text field,
|
||||||
* labeled by the specified label. The user should check the value of
|
* labeled by the specified label. The user should check the value of
|
||||||
|
@ -163,8 +134,8 @@ public class InputDialog extends DialogComponentProvider {
|
||||||
*/
|
*/
|
||||||
public InputDialog(String dialogTitle, String[] labels, String[] initialValues, boolean isModal,
|
public InputDialog(String dialogTitle, String[] labels, String[] initialValues, boolean isModal,
|
||||||
InputDialogListener listener) {
|
InputDialogListener listener) {
|
||||||
super(dialogTitle, isModal, (listener != null), // status area needed?
|
super(dialogTitle, isModal, (listener != null) /* status */, true /* buttons */,
|
||||||
true, false); // do need button panel
|
false /* no tasks */);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
|
||||||
// create the key listener all the text fields will use
|
// create the key listener all the text fields will use
|
||||||
|
@ -253,40 +224,36 @@ public class InputDialog extends DialogComponentProvider {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
//***************************************************************************
|
|
||||||
//* API methods
|
|
||||||
//**************************************************************************/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if this dialog is cancelled.
|
* Returns if this dialog is cancelled
|
||||||
|
* @return true if cancelled
|
||||||
*/
|
*/
|
||||||
public boolean isCanceled() {
|
public boolean isCanceled() {
|
||||||
return isCanceled;
|
return isCanceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return the value of the first (and maybe only) text field
|
* Return the value of the first (and maybe only) text field
|
||||||
|
* @return the text field value
|
||||||
*/
|
*/
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return inputValues[0];
|
return inputValues[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return the values for all the text field(s)
|
* Sets the text of the primary text field
|
||||||
|
* @param text the text
|
||||||
*/
|
*/
|
||||||
public String[] getValues() {
|
public void setValue(String text) {
|
||||||
return inputValues;
|
textFields[0].setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reset all the text fields to their initial values
|
* Return the values for all the text field(s)
|
||||||
|
* @return the text field values
|
||||||
*/
|
*/
|
||||||
public void resetValues() {
|
public String[] getValues() {
|
||||||
for (int v = 0; v < inputValues.length; v++) {
|
return inputValues;
|
||||||
String value = initialValues[v];
|
|
||||||
inputValues[v] = value;
|
|
||||||
textFields[v].setText(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyTextField extends JTextField {
|
private class MyTextField extends JTextField {
|
||||||
|
|
|
@ -1633,8 +1633,8 @@ public class FieldPanel extends JPanel
|
||||||
if (e.getButton() != MouseEvent.BUTTON1) {
|
if (e.getButton() != MouseEvent.BUTTON1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cursorHandler.setCursorPos(e.getX(), e.getY(), null);
|
|
||||||
cursorHandler.notifyCursorChanged(EventTrigger.GUI_ACTION);
|
cursorHandler.setCursorPos(e.getX(), e.getY(), EventTrigger.GUI_ACTION);
|
||||||
if (!selectionHandler.isInProgress() && !didDrag) {
|
if (!selectionHandler.isInProgress() && !didDrag) {
|
||||||
selectionHandler.clearSelection();
|
selectionHandler.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue