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.combobox.GhidraComboBox;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import docking.widgets.dialogs.InputDialogListener;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import docking.widgets.pathmanager.PathnameTablePanel;
|
||||
|
@ -446,8 +445,7 @@ class ParseDialog extends DialogComponentProvider {
|
|||
|
||||
private void saveAs(ComboBoxItem item) {
|
||||
|
||||
InputDialog d =
|
||||
new InputDialog("Enter Profile Name", "Profile Name", (InputDialogListener) null);
|
||||
InputDialog d = new InputDialog("Enter Profile Name", "Profile Name");
|
||||
plugin.getTool().showDialog(d, getComponent());
|
||||
|
||||
String name = d.getValue();
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -78,7 +77,6 @@ public abstract class AbstractProgramBasedTest extends AbstractGhidraHeadedInteg
|
|||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
env.release(program);
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ Module.manifest||GHIDRA||||END|
|
|||
build.gradle||GHIDRA||||END|
|
||||
src/decompile/.cproject||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/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|
|
||||
|
|
|
@ -572,9 +572,9 @@
|
|||
<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>
|
||||
|
||||
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0">There is a
|
||||
prototype plug-in that automatically pulls in the decompiler derived 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">The
|
||||
Decompiler Parameter ID analyzer automatically pulls in the decompiler derived
|
||||
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
|
||||
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>
|
||||
</P>
|
||||
|
||||
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0"> There is a
|
||||
prototype plug-in that automatically pulls in the decompiler derived information and applies it
|
||||
to each function as the function is created. The plugin by default will not commit local
|
||||
variable definitions, either stack or register locals. Committing locals automatically
|
||||
can be turned on by changing the analysis options for the Decompiler Parameter ID plugin.
|
||||
<P style="margin-left: 80px;"><IMG alt="" src="../../shared/note.png" border="0">he
|
||||
Decompiler Parameter ID analyzer automatically pulls in the decompiler derived
|
||||
information and applies it to each function as the function is created. The analyzer by default
|
||||
will not commit local variable definitions, either stack or register locals.
|
||||
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
|
||||
about, or after the data type definitions (structures, etc...) have settled down for the
|
||||
program you are Reverse Engineering.<BR>
|
||||
|
@ -720,6 +721,79 @@
|
|||
contribute to the creation of the value in the variable under the cursor.</P>
|
||||
</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>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
|
@ -67,6 +67,10 @@ public class ClangLine {
|
|||
return tokens.get(i);
|
||||
}
|
||||
|
||||
public int indexOfToken(ClangToken token) {
|
||||
return tokens.indexOf(token);
|
||||
}
|
||||
|
||||
public String toDebugString(List<ClangToken> calloutTokens) {
|
||||
|
||||
return toDebugString(calloutTokens, "[", "]");
|
||||
|
|
|
@ -245,6 +245,9 @@ public class ClangToken implements ClangNode {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (lineparent != null) {
|
||||
return lineparent.getLineNumber() + ": " + text;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
import utility.function.Callback;
|
||||
|
||||
public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
||||
|
||||
|
@ -157,6 +158,11 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
|||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWheNotBusy(Callback c) {
|
||||
// stub
|
||||
}
|
||||
|
||||
public void clearAndShowMessage(String message) {
|
||||
controller.setDecompileData(new EmptyDecompileData(message));
|
||||
paintImmediately(getBounds());
|
||||
|
|
|
@ -17,70 +17,62 @@ package ghidra.app.decompiler.component;
|
|||
|
||||
import java.awt.Color;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
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;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.ColorUtils;
|
||||
import util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
// and the DecompilerPanel class.
|
||||
|
||||
protected Color defaultNonFunctionBackgroundColor = new Color(220, 220, 220);
|
||||
protected Color defaultHighlightColor = new Color(255, 255, 0, 128); // Default color for highlighting tokens
|
||||
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 Color defaultHighlightColor = DEFAULT_HIGHLIGHT_COLOR;
|
||||
protected Color defaultParenColor = DEFAULT_HIGHLIGHT_COLOR;
|
||||
|
||||
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<>();
|
||||
|
||||
public ClangHighlightController() {
|
||||
}
|
||||
private List<ClangHighlightListener> listeners = new ArrayList<>();
|
||||
|
||||
public abstract void fieldLocationChanged(FieldLocation location, Field field,
|
||||
EventTrigger trigger);
|
||||
|
||||
void loadOptions(DecompileOptions options) {
|
||||
Color currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
|
||||
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;
|
||||
void setHighlightColor(Color c) {
|
||||
defaultHighlightColor = c;
|
||||
}
|
||||
|
||||
public String getHighlightedText() {
|
||||
|
@ -91,144 +83,287 @@ public abstract class ClangHighlightController {
|
|||
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 token or null
|
||||
*/
|
||||
public ClangToken getHighlightedToken() {
|
||||
if (highlightTokenSet.size() == 1) {
|
||||
ClangToken[] tokenArray =
|
||||
highlightTokenSet.toArray(new ClangToken[highlightTokenSet.size()]);
|
||||
return tokenArray[0];
|
||||
private ClangToken getHighlightedToken() {
|
||||
if (primaryHighlightTokens.size() == 1) {
|
||||
HighlightToken hlToken = CollectionUtils.any(primaryHighlightTokens);
|
||||
return hlToken.getToken();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addVarnodesToHighlight(ClangNode parentNode, Set<Varnode> varnodes,
|
||||
Color highlightColor, Varnode specificvn, PcodeOp specificop, Color specialColor) {
|
||||
int nchild = parentNode.numChildren();
|
||||
for (int i = 0; i < nchild; ++i) {
|
||||
private void gatherAllTokens(ClangNode parentNode, Set<ClangToken> results) {
|
||||
|
||||
int n = parentNode.numChildren();
|
||||
for (int i = 0; i < n; i++) {
|
||||
ClangNode node = parentNode.Child(i);
|
||||
if (node.numChildren() > 0) {
|
||||
addVarnodesToHighlight(node, varnodes, highlightColor, specificvn, specificop,
|
||||
specialColor);
|
||||
gatherAllTokens(node, results);
|
||||
}
|
||||
else if (node instanceof ClangToken) {
|
||||
ClangToken tok = (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);
|
||||
}
|
||||
}
|
||||
results.add((ClangToken) node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public void addPcodeOpsToHighlight(ClangNode parentNode, Set<PcodeOp> ops,
|
||||
Color highlightColor) {
|
||||
int nchild = parentNode.numChildren();
|
||||
for (int i = 0; i < nchild; ++i) {
|
||||
ClangNode node = parentNode.Child(i);
|
||||
if (node.numChildren() > 0) {
|
||||
addPcodeOpsToHighlight(node, ops, highlightColor);
|
||||
}
|
||||
else if (node instanceof ClangToken) {
|
||||
ClangToken tok = (ClangToken) node;
|
||||
PcodeOp op = tok.getPcodeOp();
|
||||
if (ops.contains(op)) {
|
||||
addHighlight(tok, highlightColor);
|
||||
}
|
||||
}
|
||||
public void removeSecondaryHighlights(ClangToken token) {
|
||||
secondaryHighlightTokens.remove(token);
|
||||
}
|
||||
|
||||
public void removeSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens) {
|
||||
for (ClangToken clangToken : tokens.get()) {
|
||||
secondaryHighlightTokens.remove(clangToken);
|
||||
updateHighlightColor(clangToken);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public void addTokensToHighlights(List<ClangToken> tokenList, Color highlightColor) {
|
||||
for (ClangToken clangToken : tokenList) {
|
||||
doAddHighlight(clangToken, highlightColor);
|
||||
public void addSecondaryHighlights(String tokenText,
|
||||
Supplier<? extends Collection<ClangToken>> tokens) {
|
||||
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();
|
||||
}
|
||||
|
||||
public void clearHighlights() {
|
||||
for (ClangToken clangToken : highlightTokenSet) {
|
||||
clangToken.setHighlight(null);
|
||||
if (clangToken.isMatchingToken()) {
|
||||
clangToken.setMatchingToken(false);
|
||||
protected void addPrimaryHighlight(ClangToken token, Color highlightColor) {
|
||||
addPrimaryHighlights(Set.of(token), highlightColor);
|
||||
}
|
||||
|
||||
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) {
|
||||
doAddHighlight(clangToken, highlightColor);
|
||||
notifyListeners();
|
||||
}
|
||||
if (secondary == null) {
|
||||
return primary;
|
||||
}
|
||||
|
||||
public void doAddHighlight(ClangToken clangToken, Color highlightColor) {
|
||||
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);
|
||||
return ColorUtils.blend(primary, secondary, .8f);
|
||||
}
|
||||
|
||||
/**
|
||||
* If input token is a parenthesis, highlight all
|
||||
* 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.
|
||||
*/
|
||||
public List<ClangToken> addHighlightParen(ClangSyntaxToken tok, Color highlightColor) {
|
||||
ArrayList<ClangToken> tokenList = new ArrayList<>();
|
||||
protected List<ClangToken> addPrimaryHighlightToTokensForParenthesis(ClangSyntaxToken tok,
|
||||
Color highlightColor) {
|
||||
|
||||
int paren = tok.getOpen();
|
||||
if (paren == -1) {
|
||||
paren = tok.getClose();
|
||||
}
|
||||
|
||||
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();
|
||||
while (par != null) {
|
||||
boolean outside = true;
|
||||
if (par instanceof ClangTokenGroup) {
|
||||
ArrayList<ClangNode> list = new ArrayList<>();
|
||||
((ClangTokenGroup) par).flatten(list);
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
ClangToken tk = (ClangToken) list.get(i);
|
||||
if (tk instanceof ClangSyntaxToken) {
|
||||
ClangSyntaxToken syn = (ClangSyntaxToken) tk;
|
||||
if (syn.getOpen() == paren) {
|
||||
outside = false;
|
||||
}
|
||||
else if (syn.getClose() == paren) {
|
||||
outside = true;
|
||||
addHighlight(syn, highlightColor);
|
||||
tokenList.add(syn);
|
||||
}
|
||||
if (!(par instanceof ClangTokenGroup)) {
|
||||
par = par.Parent();
|
||||
continue;
|
||||
}
|
||||
|
||||
List<ClangNode> list = new ArrayList<>();
|
||||
((ClangTokenGroup) par).flatten(list);
|
||||
|
||||
for (ClangNode node : list) {
|
||||
ClangToken tk = (ClangToken) node;
|
||||
if (tk instanceof ClangSyntaxToken) {
|
||||
ClangSyntaxToken syn = (ClangSyntaxToken) tk;
|
||||
if (syn.getOpen() == parenId) {
|
||||
parenCount++;
|
||||
outside = false;
|
||||
}
|
||||
if (!outside) {
|
||||
addHighlight(tk, highlightColor);
|
||||
tokenList.add(tk);
|
||||
else if (syn.getClose() == parenId) {
|
||||
parenCount++;
|
||||
outside = true;
|
||||
results.add(syn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!outside) {
|
||||
results.add(tk);
|
||||
}
|
||||
|
||||
if (parenCount == 2) {
|
||||
return results; // found both parens; break out early
|
||||
}
|
||||
}
|
||||
par = par.Parent();
|
||||
}
|
||||
return tokenList;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public void addHighlightBrace(ClangSyntaxToken token, Color highlightColor) {
|
||||
|
@ -244,76 +379,20 @@ public abstract class ClangHighlightController {
|
|||
ClangSyntaxToken matchingBrace = DecompilerUtils.getMatchingBrace(startToken);
|
||||
if (matchingBrace != null) {
|
||||
matchingBrace.setMatchingToken(true); // this is a signal to the painter
|
||||
addHighlight(matchingBrace, highlightColor);
|
||||
addPrimaryHighlights(Set.of(matchingBrace), highlightColor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add highlighting to tokens that are surrounded by
|
||||
* 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 void addListener(ClangHighlightListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public boolean addListener(ClangHighlightListener listener) {
|
||||
return highlightListenerList.add(listener);
|
||||
}
|
||||
|
||||
public boolean removeListener(ClangHighlightListener listener) {
|
||||
return highlightListenerList.remove(listener);
|
||||
public void removeListener(ClangHighlightListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
private void notifyListeners() {
|
||||
for (ClangHighlightListener listener : highlightListenerList) {
|
||||
for (ClangHighlightListener listener : listeners) {
|
||||
listener.tokenHighlightsChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import ghidra.program.model.listing.Function;
|
|||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
import utility.function.Callback;
|
||||
|
||||
public interface DecompilerCallbackHandler {
|
||||
|
||||
|
@ -44,4 +45,6 @@ public interface DecompilerCallbackHandler {
|
|||
void exportLocation();
|
||||
|
||||
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.ProgramSelection;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
import utility.function.Callback;
|
||||
|
||||
/**
|
||||
* 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 location the location containing the function to be displayed and the location in
|
||||
* that function to position the cursor.
|
||||
* @param viewerPosition the viewer position
|
||||
*/
|
||||
public void display(Program program, ProgramLocation location, ViewerPosition viewerPosition) {
|
||||
if (!decompilerMgr.isBusy() && decompilerPanel.containsLocation(location)) {
|
||||
|
@ -121,7 +123,7 @@ public class DecompilerController {
|
|||
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();
|
||||
setDecompileData(
|
||||
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.
|
||||
* @param decompilerOptions
|
||||
* @param decompilerOptions the options
|
||||
*/
|
||||
public void setOptions(DecompileOptions decompilerOptions) {
|
||||
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) {
|
||||
updateCache(decompileData);
|
||||
|
@ -193,12 +196,18 @@ public class DecompilerController {
|
|||
//==================================================================================================
|
||||
// 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
|
||||
* decompilerPanel's cursor to the closest equivalent position.
|
||||
* @param program the program for the given location
|
||||
* @param location the location containing the function to be displayed and the location in
|
||||
* that function to position the cursor.
|
||||
* @param debugFile the debug file
|
||||
*/
|
||||
public void refreshDisplay(Program program, ProgramLocation location, File debugFile) {
|
||||
clearCache();
|
||||
|
@ -304,5 +313,4 @@ public class DecompilerController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@ import java.awt.event.MouseEvent;
|
|||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
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.JPanel;
|
||||
|
@ -40,6 +43,7 @@ import ghidra.app.decompiler.*;
|
|||
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
|
||||
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.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
@ -48,6 +52,8 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* 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 ClangLayoutController layoutMgr;
|
||||
|
||||
private HighlightFactory hlFactory;
|
||||
private ClangHighlightController highlightController;
|
||||
private PendingHighlightUpdate pendingHighlightUpdate;
|
||||
private SwingUpdateManager highlighCursorUpdater = new SwingUpdateManager(() -> {
|
||||
if (pendingHighlightUpdate != null) {
|
||||
pendingHighlightUpdate.doUpdate();
|
||||
pendingHighlightUpdate = null;
|
||||
}
|
||||
});
|
||||
|
||||
private int currentMiddleMouseHighlightButton;
|
||||
private Color currentSearchHighlightColor;
|
||||
private Color currentHighlightColor;
|
||||
private int middleMouseHighlightButton;
|
||||
private Color middleMouseHighlightColor;
|
||||
private Color currentVariableHighlightColor;
|
||||
private Color searchHighlightColor;
|
||||
private SearchLocation currentSearchLocation;
|
||||
|
||||
private DecompileData decompileData = new EmptyDecompileData("No Function");
|
||||
|
@ -104,9 +119,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
|
||||
decompilerHoverProvider = new DecompilerHoverProvider();
|
||||
|
||||
currentSearchHighlightColor = options.getSearchHighlightColor();
|
||||
currentHighlightColor = options.getMiddleMouseHighlightColor();
|
||||
currentMiddleMouseHighlightButton = options.getMiddleMouseHighlightButton();
|
||||
searchHighlightColor = options.getSearchHighlightColor();
|
||||
currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
|
||||
middleMouseHighlightColor = options.getMiddleMouseHighlightColor();
|
||||
middleMouseHighlightButton = options.getMiddleMouseHighlightButton();
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
add(scroller);
|
||||
|
@ -128,6 +144,67 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
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
|
||||
public void setBackground(Color bg) {
|
||||
originalBackgroundColor = bg;
|
||||
|
@ -168,17 +245,48 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
|
||||
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;
|
||||
setBackground(originalBackgroundColor);
|
||||
|
||||
if (clipboard != null) {
|
||||
clipboard.selectionChanged(null);
|
||||
}
|
||||
|
||||
// don't highlight search results across functions
|
||||
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) {
|
||||
|
@ -424,9 +532,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
public void dispose() {
|
||||
setDecompileData(new EmptyDecompileData("Disposed"));
|
||||
layoutMgr = null;
|
||||
|
||||
decompilerHoverProvider.dispose();
|
||||
|
||||
highlighCursorUpdater.dispose();
|
||||
highlightController.clearAllHighlights();
|
||||
}
|
||||
|
||||
private FontMetrics getFontMetrics(DecompileOptions decompileOptions) {
|
||||
|
@ -464,8 +572,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
}
|
||||
}
|
||||
|
||||
if (buttonState == currentMiddleMouseHighlightButton && clickCount == 1) {
|
||||
highlightVariable(location, field, currentHighlightColor);
|
||||
if (buttonState == middleMouseHighlightButton && clickCount == 1) {
|
||||
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() {
|
||||
return decompileData.getProgram();
|
||||
}
|
||||
|
@ -654,9 +751,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
return;
|
||||
}
|
||||
|
||||
if (highlightController != null) {
|
||||
highlightController.fieldLocationChanged(location, field, trigger);
|
||||
}
|
||||
pendingHighlightUpdate = new PendingHighlightUpdate(location, field, trigger);
|
||||
highlighCursorUpdater.update();
|
||||
|
||||
if (!(field instanceof ClangTextField)) {
|
||||
return;
|
||||
|
@ -743,19 +839,25 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
// End Search Methods
|
||||
//==================================================================================================
|
||||
|
||||
public Color getDefaultHighlightColor() {
|
||||
return currentHighlightColor;
|
||||
public Color getCurrentVariableHighlightColor() {
|
||||
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;
|
||||
}
|
||||
|
||||
public String getHighlightedText() {
|
||||
if (highlightController != null) {
|
||||
return highlightController.getHighlightedText();
|
||||
}
|
||||
return null;
|
||||
return highlightController.getHighlightedText();
|
||||
}
|
||||
|
||||
public FieldLocation getCursorPosition() {
|
||||
|
@ -797,6 +899,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
return ((ClangTextField) field).getToken(cursorPosition);
|
||||
}
|
||||
|
||||
public DecompileOptions getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void addHoverService(DecompilerHoverService hoverService) {
|
||||
decompilerHoverProvider.addHoverService(hoverService);
|
||||
}
|
||||
|
@ -819,38 +925,40 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
return decompilerHoverProvider.isShowing();
|
||||
}
|
||||
|
||||
public void clearHighlights() {
|
||||
if (highlightController != null) {
|
||||
highlightController.clearHighlights();
|
||||
}
|
||||
public void clearPrimaryHighlights() {
|
||||
highlightController.clearPrimaryHighlights();
|
||||
}
|
||||
|
||||
public void addVarnodeHighlights(Set<Varnode> varnodes, Color highlightColor,
|
||||
Varnode specificvn, PcodeOp specificop, Color specialColor) {
|
||||
if (highlightController != null) {
|
||||
ClangTokenGroup root = layoutMgr.getRoot();
|
||||
highlightController.addVarnodesToHighlight(root, varnodes, highlightColor, specificvn,
|
||||
specificop, specialColor);
|
||||
}
|
||||
public void addVarnodeHighlights(Set<Varnode> varnodes,
|
||||
TokenHighlightColorProvider colorProvider) {
|
||||
|
||||
ClangTokenGroup root = layoutMgr.getRoot();
|
||||
highlightController.addPrimaryHighlights(root, colorProvider);
|
||||
}
|
||||
|
||||
public void addPcodeOpHighlights(Set<PcodeOp> ops, Color highlightColor) {
|
||||
if (highlightController != null) {
|
||||
ClangTokenGroup root = layoutMgr.getRoot();
|
||||
highlightController.addPcodeOpsToHighlight(root, ops, highlightColor);
|
||||
}
|
||||
public void addPcodeOpHighlights(Set<PcodeOp> ops, Color hlColor) {
|
||||
ClangTokenGroup root = layoutMgr.getRoot();
|
||||
highlightController.addPrimaryHighlights(root, ops, hlColor);
|
||||
}
|
||||
|
||||
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) {
|
||||
ClangNode child = group.Child(i);
|
||||
if (child instanceof ClangTokenGroup) {
|
||||
findTokensByName(tokenList, (ClangTokenGroup) child, name);
|
||||
doFindTokensByName(tokens, (ClangTokenGroup) child, name);
|
||||
}
|
||||
else if (child instanceof ClangToken) {
|
||||
ClangToken token = (ClangToken) child;
|
||||
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();
|
||||
selection.addRange(BigInteger.ZERO, numIndexes);
|
||||
fieldPanel.setSelection(selection);
|
||||
|
||||
// fake it out that the selection was caused by the field panel GUI.
|
||||
selectionChanged(selection, EventTrigger.GUI_ACTION);
|
||||
}
|
||||
|
||||
public void optionsChanged(DecompileOptions decompilerOptions) {
|
||||
setBackground(decompilerOptions.getCodeViewerBackgroundColor());
|
||||
currentHighlightColor = decompilerOptions.getMiddleMouseHighlightColor();
|
||||
currentMiddleMouseHighlightButton = decompilerOptions.getMiddleMouseHighlightButton();
|
||||
currentSearchHighlightColor = decompilerOptions.getSearchHighlightColor();
|
||||
if (highlightController != null) {
|
||||
highlightController.loadOptions(decompilerOptions);
|
||||
}
|
||||
currentVariableHighlightColor = options.getCurrentVariableHighlightColor();
|
||||
middleMouseHighlightColor = decompilerOptions.getMiddleMouseHighlightColor();
|
||||
middleMouseHighlightButton = decompilerOptions.getMiddleMouseHighlightButton();
|
||||
searchHighlightColor = decompilerOptions.getSearchHighlightColor();
|
||||
|
||||
highlightController.setHighlightColor(currentVariableHighlightColor);
|
||||
}
|
||||
|
||||
public void setHighlightController(ClangHighlightController highlightController) {
|
||||
this.highlightController = highlightController;
|
||||
highlightController.loadOptions(options);
|
||||
if (this.highlightController != null) {
|
||||
this.highlightController.removeListener(this);
|
||||
}
|
||||
|
||||
this.highlightController = ClangHighlightController.dummyIfNull(highlightController);
|
||||
highlightController.setHighlightColor(currentVariableHighlightColor);
|
||||
highlightController.addListener(this);
|
||||
}
|
||||
|
||||
|
@ -900,6 +1013,36 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
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
|
||||
//==================================================================================================
|
||||
|
@ -924,7 +1067,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import ghidra.app.decompiler.ClangSyntaxToken;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
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.
|
||||
*/
|
||||
|
||||
public class LocationClangHighlightController extends ClangHighlightController {
|
||||
|
||||
public LocationClangHighlightController() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) {
|
||||
|
||||
clearHighlights();
|
||||
clearPrimaryHighlights();
|
||||
|
||||
if (!(field instanceof ClangTextField)) {
|
||||
return;
|
||||
|
@ -44,12 +40,9 @@ public class LocationClangHighlightController extends ClangHighlightController {
|
|||
return;
|
||||
}
|
||||
|
||||
// // clear any highlighted searchResults
|
||||
// decompilerPanel.setSearchResults(null);
|
||||
|
||||
addHighlight(tok, defaultHighlightColor);
|
||||
addPrimaryHighlight(tok, defaultHighlightColor);
|
||||
if (tok instanceof ClangSyntaxToken) {
|
||||
addHighlightParen((ClangSyntaxToken) tok, defaultParenColor);
|
||||
addPrimaryHighlightToTokensForParenthesis((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 ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.decompiler.component.DecompilerHighlightService;
|
||||
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
|
||||
import ghidra.app.events.*;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
|
@ -49,7 +48,6 @@ import ghidra.util.task.SwingUpdateManager;
|
|||
GoToService.class, NavigationHistoryService.class, ClipboardService.class,
|
||||
DataTypeManagerService.class /*, ProgramManager.class */
|
||||
},
|
||||
servicesProvided = { DecompilerHighlightService.class },
|
||||
eventsConsumed = {
|
||||
ProgramActivatedPluginEvent.class, ProgramOpenedPluginEvent.class,
|
||||
ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class,
|
||||
|
@ -83,12 +81,6 @@ public class DecompilePlugin extends Plugin {
|
|||
|
||||
disconnectedProviders = new ArrayList<>();
|
||||
connectedProvider = new PrimaryDecompilerProvider(this);
|
||||
|
||||
registerServices();
|
||||
}
|
||||
|
||||
private void registerServices() {
|
||||
registerServiceProvided(DecompilerHighlightService.class, connectedProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -102,10 +94,6 @@ public class DecompilePlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the Plugin to write any data-dependent state to the
|
||||
* output stream.
|
||||
*/
|
||||
@Override
|
||||
public void writeDataState(SaveState saveState) {
|
||||
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
|
||||
public void readDataState(SaveState saveState) {
|
||||
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||
|
|
|
@ -23,7 +23,9 @@ import ghidra.app.context.NavigatableActionContext;
|
|||
import ghidra.app.context.RestrictedAddressSetContext;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.UndefinedFunction;
|
||||
import utility.function.Callback;
|
||||
|
||||
public class DecompilerActionContext extends NavigatableActionContext
|
||||
|
@ -55,6 +57,15 @@ public class DecompilerActionContext extends NavigatableActionContext
|
|||
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
|
||||
* must call this method from their {@link DockingActionIf#actionPerformed(ActionContext)}
|
||||
|
|
|
@ -15,17 +15,17 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.decompile;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.widgets.fieldpanel.LayoutModel;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import ghidra.GhidraOptions;
|
||||
|
@ -51,10 +51,10 @@ import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
|||
import ghidra.util.task.SwingUpdateManager;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
import utility.function.Callback;
|
||||
|
||||
public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler,
|
||||
DecompilerHighlightService {
|
||||
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler {
|
||||
final static String OPTIONS_TITLE = "Decompiler";
|
||||
|
||||
private static Icon REFRESH_ICON = Icons.REFRESH_ICON;
|
||||
|
@ -84,6 +84,15 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
private DockingAction renameFunctionAction;
|
||||
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 ClipboardService clipboardService;
|
||||
private DecompilerClipboardProvider clipboardProvider;
|
||||
|
@ -97,13 +106,15 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
private DecoratorPanel decorationPanel;
|
||||
private ClangHighlightController highlightController;
|
||||
|
||||
private CloneDecompilerAction cloneDecompilerAction;
|
||||
|
||||
private SelectAllAction selectAllAction;
|
||||
|
||||
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() {
|
||||
|
||||
@Override
|
||||
|
@ -131,9 +142,11 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
|
||||
decompilerOptions = new DecompileOptions();
|
||||
initializeDecompilerOptions();
|
||||
highlightController = new LocationClangHighlightController();
|
||||
controller = new DecompilerController(this, decompilerOptions, clipboardProvider);
|
||||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
||||
|
||||
// TODO move the hl controller into the panel
|
||||
highlightController = new LocationClangHighlightController();
|
||||
decompilerPanel.setHighlightController(highlightController);
|
||||
decorationPanel = new DecoratorPanel(decompilerPanel, isConnected);
|
||||
|
||||
|
@ -155,7 +168,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
setHelpLocation(new HelpLocation(plugin.getName(), "Decompiler"));
|
||||
addToTool();
|
||||
|
||||
redecompilerUpdater = new SwingUpdateManager(500, 5000, () -> doRefresh());
|
||||
redecompileUpdater = new SwingUpdateManager(500, 5000, () -> doRefresh());
|
||||
followUpWorkUpdater = new SwingUpdateManager(() -> doFollowUpWork());
|
||||
|
||||
plugin.getTool().addServiceListener(serviceListener);
|
||||
}
|
||||
|
@ -287,7 +301,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
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
|
||||
//==================================================================================================
|
||||
|
@ -332,7 +360,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
public void dispose() {
|
||||
super.dispose();
|
||||
|
||||
redecompilerUpdater.dispose();
|
||||
redecompileUpdater.dispose();
|
||||
followUpWorkUpdater.dispose();
|
||||
|
||||
if (clipboardService != null) {
|
||||
clipboardService.deRegisterClipboardContentProvider(clipboardProvider);
|
||||
|
@ -420,8 +449,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
return null;
|
||||
}
|
||||
|
||||
boolean isDecompiling() {
|
||||
return controller.isDecompiling();
|
||||
boolean isBusy() {
|
||||
return redecompileUpdater.isBusy() || controller.isDecompiling();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -457,6 +486,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
decompilerPanel.setCursorPosition(location);
|
||||
}
|
||||
|
||||
DecompilerController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// methods called from the controller
|
||||
//==================================================================================================
|
||||
|
@ -466,10 +499,6 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
tool.setStatusInfo(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the DecompilerController to indicate that the decompilerData has changed.
|
||||
* @param decompileData the new DecompilerData
|
||||
*/
|
||||
@Override
|
||||
public void decompileDataChanged(DecompileData decompileData) {
|
||||
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
|
||||
//==================================================================================================
|
||||
|
@ -610,14 +645,28 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
}
|
||||
|
||||
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
|
||||
// (its done in an invoke later)
|
||||
Swing.runLater(() -> {
|
||||
|
||||
ViewerPosition myViewPosition = controller.getDecompilerPanel().getViewerPosition();
|
||||
newProvider.doSetProgram(program);
|
||||
newProvider.controller.setDecompileData(controller.getDecompileData());
|
||||
newProvider.setLocation(currentLocation,
|
||||
controller.getDecompilerPanel().getViewerPosition());
|
||||
|
||||
// Any change in the HighlightTokens should be delivered to the new panel
|
||||
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);
|
||||
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
|
||||
//
|
||||
|
@ -768,18 +802,51 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
lockLocalAction = new CommitLocalsAction(tool, controller);
|
||||
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
|
||||
//
|
||||
// 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
|
||||
// arrangement by setting the values, we can
|
||||
// in relation to our local actions.
|
||||
//
|
||||
|
||||
//
|
||||
// Search
|
||||
//
|
||||
|
||||
String searchGroup = "comment2 - Search Group";
|
||||
subGroupPosition = 0; // reset for the next group
|
||||
|
||||
|
@ -835,6 +902,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
addLocalAction(lockProtoAction);
|
||||
addLocalAction(lockLocalAction);
|
||||
addLocalAction(renameVarAction);
|
||||
addLocalAction(setSecondaryHighlightAction);
|
||||
addLocalAction(setSecondaryHighlightColorChooserAction);
|
||||
addLocalAction(removeSecondaryHighlightAction);
|
||||
addLocalAction(removeAllSecondadryHighlightsAction);
|
||||
addLocalAction(retypeVarAction);
|
||||
addLocalAction(decompilerCreateStructureAction);
|
||||
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) {
|
||||
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 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.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
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");
|
||||
this.controller = controller;
|
||||
setPopupMenuData(new MenuData(new String[] { "Highlight Backward Slice" }, "Decompile"));
|
||||
setPopupMenuData(new MenuData(new String[] { "Highlight", "Backward Slice" }, "Decompile"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
||||
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
|
||||
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
||||
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
||||
return varnode != null;
|
||||
|
@ -43,18 +44,21 @@ public class BackwardsSliceAction extends AbstractDecompilerAction {
|
|||
|
||||
@Override
|
||||
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
||||
|
||||
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
|
||||
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
||||
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
||||
if (varnode != null) {
|
||||
PcodeOp op = tokenAtCursor.getPcodeOp();
|
||||
Set<Varnode> backwardSlice = DecompilerUtils.getBackwardSlice(varnode);
|
||||
decompilerPanel.clearHighlights();
|
||||
decompilerPanel.addVarnodeHighlights(backwardSlice,
|
||||
decompilerPanel.getDefaultHighlightColor(), varnode, op,
|
||||
decompilerPanel.getDefaultSpecialColor());
|
||||
decompilerPanel.repaint();
|
||||
if (varnode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
this.controller = controller;
|
||||
setPopupMenuData(
|
||||
new MenuData(new String[] { "Highlight Backward Inst Slice" }, "Decompile"));
|
||||
new MenuData(new String[] { "Highlight", "Backward Inst Slice" }, "Decompile"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,10 +51,9 @@ public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction {
|
|||
PcodeOp op = tokenAtCursor.getPcodeOp();
|
||||
Set<PcodeOp> backwardSlice = DecompilerUtils.getBackwardSliceToPCodeOps(varnode);
|
||||
backwardSlice.add(op);
|
||||
decompilerPanel.clearHighlights();
|
||||
decompilerPanel.clearPrimaryHighlights();
|
||||
decompilerPanel.addPcodeOpHighlights(backwardSlice,
|
||||
decompilerPanel.getDefaultHighlightColor());
|
||||
decompilerPanel.repaint();
|
||||
decompilerPanel.getCurrentVariableHighlightColor());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,12 +22,10 @@ import ghidra.app.decompiler.component.DecompilerController;
|
|||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.util.UndefinedFunction;
|
||||
|
||||
public class CommitLocalsAction extends AbstractDecompilerAction {
|
||||
private final DecompilerController controller;
|
||||
|
@ -55,8 +53,7 @@ public class CommitLocalsAction extends AbstractDecompilerAction {
|
|||
|
||||
@Override
|
||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||
Function function = controller.getFunction();
|
||||
if (function == null || function instanceof UndefinedFunction) {
|
||||
if (!context.hasRealFunction()) {
|
||||
return false;
|
||||
}
|
||||
return getHighFunction() != null;
|
||||
|
|
|
@ -25,13 +25,11 @@ import ghidra.app.decompiler.component.DecompilerController;
|
|||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.UndefinedFunction;
|
||||
import ghidra.util.exception.*;
|
||||
|
||||
public class CommitParamsAction extends AbstractDecompilerAction {
|
||||
|
@ -65,9 +63,7 @@ public class CommitParamsAction extends AbstractDecompilerAction {
|
|||
|
||||
@Override
|
||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||
|
||||
Function function = controller.getFunction();
|
||||
if (function == null || function instanceof UndefinedFunction) {
|
||||
if (!context.hasRealFunction()) {
|
||||
return false;
|
||||
}
|
||||
return getHighFunction() != null;
|
||||
|
|
|
@ -30,7 +30,7 @@ public class ForwardSliceAction extends AbstractDecompilerAction {
|
|||
public ForwardSliceAction(DecompilerController controller) {
|
||||
super("Highlight Forward Slice");
|
||||
this.controller = controller;
|
||||
setPopupMenuData(new MenuData(new String[] { "Highlight Forward Slice" }, "Decompile"));
|
||||
setPopupMenuData(new MenuData(new String[] { "Highlight", "Forward Slice" }, "Decompile"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,15 +46,17 @@ public class ForwardSliceAction extends AbstractDecompilerAction {
|
|||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
||||
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
||||
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
||||
if (varnode != null) {
|
||||
PcodeOp op = tokenAtCursor.getPcodeOp();
|
||||
Set<Varnode> forwardSlice = DecompilerUtils.getForwardSlice(varnode);
|
||||
decompilerPanel.clearHighlights();
|
||||
decompilerPanel.addVarnodeHighlights(forwardSlice,
|
||||
decompilerPanel.getDefaultHighlightColor(), varnode, op,
|
||||
decompilerPanel.getDefaultSpecialColor());
|
||||
decompilerPanel.repaint();
|
||||
if (varnode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
this.controller = controller;
|
||||
setPopupMenuData(
|
||||
new MenuData(new String[] { "Highlight Forward Inst Slice" }, "Decompile"));
|
||||
new MenuData(new String[] { "Highlight", "Forward Inst Slice" }, "Decompile"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,10 +51,9 @@ public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction {
|
|||
PcodeOp op = tokenAtCursor.getPcodeOp();
|
||||
Set<PcodeOp> forwardSlice = DecompilerUtils.getForwardSliceToPCodeOps(varnode);
|
||||
forwardSlice.add(op);
|
||||
decompilerPanel.clearHighlights();
|
||||
decompilerPanel.clearPrimaryHighlights();
|
||||
decompilerPanel.addPcodeOpHighlights(forwardSlice,
|
||||
decompilerPanel.getDefaultHighlightColor());
|
||||
decompilerPanel.repaint();
|
||||
decompilerPanel.getCurrentVariableHighlightColor());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.decompile.actions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
||||
public class HighlightDefinedUseAction extends AbstractDecompilerAction {
|
||||
|
@ -29,7 +30,7 @@ public class HighlightDefinedUseAction extends AbstractDecompilerAction {
|
|||
public HighlightDefinedUseAction(DecompilerController controller) {
|
||||
super("Highlight Defined Use");
|
||||
this.controller = controller;
|
||||
setPopupMenuData(new MenuData(new String[] { "Highlight Def-use" }, "Decompile"));
|
||||
setPopupMenuData(new MenuData(new String[] { "Highlight", "Def-use" }, "Decompile"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,15 +46,17 @@ public class HighlightDefinedUseAction extends AbstractDecompilerAction {
|
|||
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
|
||||
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
|
||||
Varnode varnode = DecompilerUtils.getVarnodeRef(tokenAtCursor);
|
||||
if (varnode != null) {
|
||||
HashSet<Varnode> varnodes = new HashSet<Varnode>();
|
||||
varnodes.add(varnode);
|
||||
decompilerPanel.clearHighlights();
|
||||
decompilerPanel.addVarnodeHighlights(varnodes,
|
||||
decompilerPanel.getDefaultHighlightColor(), varnode, varnode.getDef(),
|
||||
decompilerPanel.getDefaultSpecialColor());
|
||||
decompilerPanel.repaint();
|
||||
if (varnode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
* 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();
|
||||
|
||||
public void clearHighlights();
|
||||
/**
|
||||
* Provides highlight color for the given token
|
||||
*/
|
||||
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() {
|
||||
waitForCondition(() -> !provider.isDecompiling());
|
||||
waitForSwing();
|
||||
waitForCondition(() -> !provider.isBusy());
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
|
@ -79,6 +80,7 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
|
|||
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||
FieldPanel fp = panel.getFieldPanel();
|
||||
click(fp, 1, true);
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
protected void doubleClick() {
|
||||
|
@ -89,7 +91,7 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -97,20 +99,56 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
|
|||
|
||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||
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;
|
||||
}
|
||||
|
||||
protected String getTokenText(FieldLocation loc) {
|
||||
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
|
||||
// note: the index is 0-based; use getFieldForLine() when using 1-based line numbers
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
protected void assertToken(String tokenText, int line, int... cols) {
|
||||
for (int col : cols) {
|
||||
FieldLocation loc = loc(line, col);
|
||||
String text = getTokenText(loc);
|
||||
ClangToken token = getToken(line, col);
|
||||
String text = token.getText();
|
||||
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);
|
||||
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);
|
||||
10| _call_structure_B(&a->b);
|
||||
11| return;
|
||||
12| }
|
||||
13|
|
||||
10| _call_structure_B(&a->b);
|
||||
11| return;
|
||||
12| }
|
||||
13|
|
||||
|
||||
*/
|
||||
decompile(CALL_STRUCTURE_A_ADDRESS);
|
||||
|
|
|
@ -15,14 +15,13 @@
|
|||
*/
|
||||
package docking.options.editor;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JColorChooser;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.*;
|
||||
import javax.swing.colorchooser.AbstractColorChooserPanel;
|
||||
|
||||
public class GhidraColorChooser extends JColorChooser {
|
||||
|
@ -31,6 +30,7 @@ public class GhidraColorChooser extends JColorChooser {
|
|||
|
||||
private String title = DEFAULT_TITLE;
|
||||
private RecentColorCache recentColorCache = new RecentColorCache();
|
||||
private String activeTabName;
|
||||
|
||||
public GhidraColorChooser() {
|
||||
super();
|
||||
|
@ -54,12 +54,24 @@ public class GhidraColorChooser extends JColorChooser {
|
|||
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")
|
||||
public Color showDialog(Component centerOverComponent) {
|
||||
maybeInstallSettableColorSwatchChooserPanel();
|
||||
|
||||
OKListener okListener = new OKListener();
|
||||
JDialog dialog = createDialog(centerOverComponent, title, true, this, okListener, null);
|
||||
doSetActiveTab(dialog);
|
||||
|
||||
dialog.show(); // blocks until user brings dialog down...
|
||||
Color color = okListener.getColor();
|
||||
if (color != null) {
|
||||
|
@ -68,6 +80,48 @@ public class GhidraColorChooser extends JColorChooser {
|
|||
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() {
|
||||
if (recentColorCache.size() == 0) {
|
||||
return;
|
||||
|
@ -87,7 +141,13 @@ public class GhidraColorChooser extends JColorChooser {
|
|||
SettableColorSwatchChooserPanel newSwatchPanel =
|
||||
new SettableColorSwatchChooserPanel(mruColorList);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -56,21 +56,6 @@ public class InputDialog extends DialogComponentProvider {
|
|||
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,
|
||||
* 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 label value to use for the label of 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,
|
||||
InputDialogListener 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,
|
||||
* 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,
|
||||
InputDialogListener listener) {
|
||||
super(dialogTitle, isModal, (listener != null), // status area needed?
|
||||
true, false); // do need button panel
|
||||
super(dialogTitle, isModal, (listener != null) /* status */, true /* buttons */,
|
||||
false /* no tasks */);
|
||||
this.listener = listener;
|
||||
|
||||
// create the key listener all the text fields will use
|
||||
|
@ -253,40 +224,36 @@ public class InputDialog extends DialogComponentProvider {
|
|||
close();
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
//* API methods
|
||||
//**************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns if this dialog is cancelled.
|
||||
* Returns if this dialog is cancelled
|
||||
* @return true if cancelled
|
||||
*/
|
||||
public boolean 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() {
|
||||
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() {
|
||||
return inputValues;
|
||||
public void setValue(String text) {
|
||||
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() {
|
||||
for (int v = 0; v < inputValues.length; v++) {
|
||||
String value = initialValues[v];
|
||||
inputValues[v] = value;
|
||||
textFields[v].setText(value);
|
||||
}
|
||||
public String[] getValues() {
|
||||
return inputValues;
|
||||
}
|
||||
|
||||
private class MyTextField extends JTextField {
|
||||
|
|
|
@ -1633,8 +1633,8 @@ public class FieldPanel extends JPanel
|
|||
if (e.getButton() != MouseEvent.BUTTON1) {
|
||||
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) {
|
||||
selectionHandler.clearSelection();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue