mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-17 08:00:15 +00:00
Merge remote-tracking branch
'origin/GP-3494-dragonmacher-decompiler-highlight-nav-v2--SQUASHED' (Closes #538)
This commit is contained in:
commit
0e33958c76
@ -22,7 +22,6 @@ import java.util.stream.Collectors;
|
|||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import docking.widgets.fieldpanel.field.Field;
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
import generic.theme.GColor;
|
|
||||||
import ghidra.app.decompiler.ClangSyntaxToken;
|
import ghidra.app.decompiler.ClangSyntaxToken;
|
||||||
import ghidra.app.decompiler.ClangToken;
|
import ghidra.app.decompiler.ClangToken;
|
||||||
import ghidra.app.decompiler.component.*;
|
import ghidra.app.decompiler.component.*;
|
||||||
@ -120,7 +119,7 @@ public class DiffClangHighlightController extends LocationClangHighlightControll
|
|||||||
List<ClangToken> tokens = addPrimaryHighlightToTokensForParenthesis(
|
List<ClangToken> tokens = addPrimaryHighlightToTokensForParenthesis(
|
||||||
(ClangSyntaxToken) tok, defaultParenColor);
|
(ClangSyntaxToken) tok, defaultParenColor);
|
||||||
reHighlightDiffs(tokens);
|
reHighlightDiffs(tokens);
|
||||||
addBraceHighlight((ClangSyntaxToken) tok, defaultParenColor);
|
addPrimaryHighlightToTokensForBrace((ClangSyntaxToken) tok, defaultParenColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenBin tokenBin = null;
|
TokenBin tokenBin = null;
|
||||||
|
@ -4370,7 +4370,8 @@
|
|||||||
<title>Middle-Click</title>
|
<title>Middle-Click</title>
|
||||||
<para>
|
<para>
|
||||||
Highlights every occurrence of a variable, constant, or operator represented by the selected
|
Highlights every occurrence of a variable, constant, or operator represented by the selected
|
||||||
token, within the Decompiler window.
|
token, within the Decompiler window. There are actions available from the popup menu and from
|
||||||
|
the keyboard to navigate to each highlighted token.
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
@ -4770,6 +4771,17 @@
|
|||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="GoToMiddleMouseHighlight">
|
||||||
|
<title>Go To Next/Previous Highlight</title>
|
||||||
|
<para>
|
||||||
|
These actions are available from the popup menu and keyboard. Only tokens highlighted from the
|
||||||
|
middle-mouse will be navigated. <emphasis role="bold">Shift-Comma</emphasis> will go to the
|
||||||
|
previous highlighted token. <emphasis role="bold">Shift-Period</emphasis> will go to the
|
||||||
|
next highlighted token. These key bindings can be changed via the
|
||||||
|
<link xlink:href="help/topics/Tool/ToolOptions_Dialog.htm#KeyBindings_Option">Tool Options Dialog</link>.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
<sect2 id="ActionHighlight">
|
<sect2 id="ActionHighlight">
|
||||||
<title>Highlight</title>
|
<title>Highlight</title>
|
||||||
<para>
|
<para>
|
||||||
|
@ -533,8 +533,10 @@
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
Highlights every occurrence of a variable, constant, or operator represented by the selected
|
Highlights every occurrence of a variable, constant, or operator represented by the selected
|
||||||
token, within the Decompiler window.
|
token, within the Decompiler window. There are actions available from the popup menu and from
|
||||||
|
the keyboard to navigate to each highlighted token.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -958,6 +960,19 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="sect2">
|
||||||
|
<div class="titlepage"><div><div><h3 class="title">
|
||||||
|
<a name="GoToMiddleMouseHighlight"></a>Go To Next/Previous Highlight</h3></div></div></div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
These actions are available from the popup menu and keyboard. Only tokens highlighted from the
|
||||||
|
middle-mouse will be navigated. <span class="bold"><strong>Shift-Comma</strong></span> will
|
||||||
|
go to the previous highlighted token. <span class="bold"><strong>Shift-Period</strong></span>
|
||||||
|
will go to the next highlighted token. These key bindings can be changed via the
|
||||||
|
<a class="ulink" href="help/topics/Tool/ToolOptions_Dialog.htm#KeyBindings_Option" target="_top">Tool Options Dialog</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
<div class="titlepage"><div><div><h3 class="title">
|
<div class="titlepage"><div><div><h3 class="title">
|
||||||
<a name="ActionHighlight"></a>Highlight</h3></div></div></div>
|
<a name="ActionHighlight"></a>Highlight</h3></div></div></div>
|
||||||
|
@ -25,7 +25,7 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public class ClangLine {
|
public class ClangLine {
|
||||||
private int indent_level;
|
private int indent_level;
|
||||||
private ArrayList<ClangToken> tokens;
|
private List<ClangToken> tokens;
|
||||||
private int lineNumber;
|
private int lineNumber;
|
||||||
|
|
||||||
public ClangLine(int lineNumber, int indent) {
|
public ClangLine(int lineNumber, int indent) {
|
||||||
@ -35,7 +35,7 @@ public class ClangLine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getIndentString() {
|
public String getIndentString() {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder buffer = new StringBuilder();
|
||||||
for (int i = 0; i < indent_level; i++) {
|
for (int i = 0; i < indent_level; i++) {
|
||||||
buffer.append(PrettyPrinter.INDENT_STRING);
|
buffer.append(PrettyPrinter.INDENT_STRING);
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ public class ClangLine {
|
|||||||
tok.setLineParent(this);
|
tok.setLineParent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<ClangToken> getAllTokens() {
|
public List<ClangToken> getAllTokens() {
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,6 @@ public class ClangLine {
|
|||||||
if (isCallout) {
|
if (isCallout) {
|
||||||
buffy.append(end);
|
buffy.append(end);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffy.toString();
|
return buffy.toString();
|
||||||
|
@ -37,7 +37,7 @@ public class PrettyPrinter {
|
|||||||
|
|
||||||
private Function function;
|
private Function function;
|
||||||
private ClangTokenGroup tokgroup;
|
private ClangTokenGroup tokgroup;
|
||||||
private ArrayList<ClangLine> lines = new ArrayList<>();
|
private List<ClangLine> lines = new ArrayList<>();
|
||||||
private NameTransformer transformer;
|
private NameTransformer transformer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,7 +58,7 @@ public class PrettyPrinter {
|
|||||||
|
|
||||||
private void padEmptyLines() {
|
private void padEmptyLines() {
|
||||||
for (ClangLine line : lines) {
|
for (ClangLine line : lines) {
|
||||||
ArrayList<ClangToken> tokenList = line.getAllTokens();
|
List<ClangToken> tokenList = line.getAllTokens();
|
||||||
if (tokenList.size() == 0) {
|
if (tokenList.size() == 0) {
|
||||||
ClangToken spacer = ClangToken.buildSpacer(null, line.getIndent(), INDENT_STRING);
|
ClangToken spacer = ClangToken.buildSpacer(null, line.getIndent(), INDENT_STRING);
|
||||||
spacer.setLineParent(line);
|
spacer.setLineParent(line);
|
||||||
@ -72,11 +72,11 @@ public class PrettyPrinter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array list of the C language lines contained in the
|
* Returns a list of the C language lines contained in the
|
||||||
* C language token group.
|
* C language token group.
|
||||||
* @return an array list of the C language lines
|
* @return a list of the C language lines
|
||||||
*/
|
*/
|
||||||
public ArrayList<ClangLine> getLines() {
|
public List<ClangLine> getLines() {
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,8 +86,7 @@ public class PrettyPrinter {
|
|||||||
* @return a string of readable C code
|
* @return a string of readable C code
|
||||||
*/
|
*/
|
||||||
public DecompiledFunction print() {
|
public DecompiledFunction print() {
|
||||||
StringBuffer buff = new StringBuffer();
|
StringBuilder buff = new StringBuilder();
|
||||||
|
|
||||||
for (ClangLine line : lines) {
|
for (ClangLine line : lines) {
|
||||||
buff.append(line.getIndentString());
|
buff.append(line.getIndentString());
|
||||||
List<ClangToken> tokens = line.getAllTokens();
|
List<ClangToken> tokens = line.getAllTokens();
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.decompiler;
|
package ghidra.app.decompiler;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An iterator over ClangToken objects. The iterator walks a tree of ClangNode objects based on
|
* An iterator over ClangToken objects. The iterator walks a tree of ClangNode objects based on
|
||||||
@ -126,7 +125,7 @@ public class TokenIterator implements Iterator<ClangToken> {
|
|||||||
* @param forward is true for a forward iterator, false for a backward iterator
|
* @param forward is true for a forward iterator, false for a backward iterator
|
||||||
*/
|
*/
|
||||||
public TokenIterator(ClangToken token, boolean forward) {
|
public TokenIterator(ClangToken token, boolean forward) {
|
||||||
ArrayList<ClangTokenGroup> groupList = new ArrayList<>();
|
List<ClangTokenGroup> groupList = new ArrayList<>();
|
||||||
ClangNode node = token.Parent();
|
ClangNode node = token.Parent();
|
||||||
while (node != null) {
|
while (node != null) {
|
||||||
groupList.add((ClangTokenGroup) node);
|
groupList.add((ClangTokenGroup) node);
|
||||||
@ -154,7 +153,7 @@ public class TokenIterator implements Iterator<ClangToken> {
|
|||||||
* @param forward is true for a forward iterator, false for a backward iterator
|
* @param forward is true for a forward iterator, false for a backward iterator
|
||||||
*/
|
*/
|
||||||
public TokenIterator(ClangTokenGroup group, boolean forward) {
|
public TokenIterator(ClangTokenGroup group, boolean forward) {
|
||||||
ArrayList<ClangTokenGroup> groupList = new ArrayList<>();
|
List<ClangTokenGroup> groupList = new ArrayList<>();
|
||||||
ClangNode node = group;
|
ClangNode node = group;
|
||||||
while (node instanceof ClangTokenGroup) {
|
while (node instanceof ClangTokenGroup) {
|
||||||
ClangTokenGroup curGroup = (ClangTokenGroup) node;
|
ClangTokenGroup curGroup = (ClangTokenGroup) node;
|
||||||
|
@ -19,6 +19,7 @@ import java.awt.Color;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import generic.json.Json;
|
||||||
import ghidra.app.decompiler.*;
|
import ghidra.app.decompiler.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,7 +96,7 @@ class ClangDecompilerHighlighter implements DecompilerHighlighter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Supplier<? extends Collection<ClangToken>> tokens = () -> highlights.keySet();
|
Supplier<? extends Collection<ClangToken>> tokens = () -> highlights.keySet();
|
||||||
ColorProvider colorProvider = t -> highlights.get(t);
|
ColorProvider colorProvider = new MappedTokenColorProvider(highlights);
|
||||||
decompilerPanel.addHighlighterHighlights(this, tokens, colorProvider);
|
decompilerPanel.addHighlighterHighlights(this, tokens, colorProvider);
|
||||||
|
|
||||||
clones.forEach(c -> c.applyHighlights());
|
clones.forEach(c -> c.applyHighlights());
|
||||||
@ -155,6 +156,25 @@ class ClangDecompilerHighlighter implements DecompilerHighlighter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + ' ' + id;
|
return Json.toString(this, "matcher", "id");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MappedTokenColorProvider implements ColorProvider {
|
||||||
|
|
||||||
|
private Map<ClangToken, Color> highlights;
|
||||||
|
|
||||||
|
MappedTokenColorProvider(Map<ClangToken, Color> highlights) {
|
||||||
|
this.highlights = highlights;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getColor(ClangToken token) {
|
||||||
|
return highlights.get(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Token Matcher Color " + matcher.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,6 @@ import java.util.*;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.apache.commons.collections4.map.LazyMap;
|
|
||||||
|
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import docking.widgets.fieldpanel.field.Field;
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
@ -42,7 +40,7 @@ import util.CollectionUtils;
|
|||||||
*
|
*
|
||||||
* <p>This class maintains the following types of highlights:
|
* <p>This class maintains the following types of highlights:
|
||||||
* <UL>
|
* <UL>
|
||||||
* <LI>Primary Highlights - triggered by user clicking and some user actions; considered transient
|
* <LI> Context Highlights - triggered by user clicking and some user actions; considered transient
|
||||||
* and get cleared whenever the location changes. These highlights show state such as the
|
* and get cleared whenever the location changes. These highlights show state such as the
|
||||||
* current field, impact of a variable (via a slicing action), or related syntax (such as
|
* current field, impact of a variable (via a slicing action), or related syntax (such as
|
||||||
* matching braces)
|
* matching braces)
|
||||||
@ -77,21 +75,8 @@ public abstract class ClangHighlightController {
|
|||||||
protected Color defaultHighlightColor = DEFAULT_HIGHLIGHT_COLOR;
|
protected Color defaultHighlightColor = DEFAULT_HIGHLIGHT_COLOR;
|
||||||
protected Color defaultParenColor = DEFAULT_HIGHLIGHT_COLOR;
|
protected Color defaultParenColor = DEFAULT_HIGHLIGHT_COLOR;
|
||||||
|
|
||||||
private TokenHighlights primaryHighlightTokens = new TokenHighlights();
|
private TokenHighlights contextHighlightTokens = new TokenHighlights();
|
||||||
|
private UserHighlights userHighlights = new UserHighlights();
|
||||||
private Map<Function, List<ClangDecompilerHighlighter>> secondaryHighlightersbyFunction =
|
|
||||||
LazyMap.lazyMap(new HashMap<>(), f -> new ArrayList<>());
|
|
||||||
|
|
||||||
// store the secondary highlighters here in addition to the map below so that we may discern
|
|
||||||
// between secondary highlights and highlight service highlights
|
|
||||||
private Set<ClangDecompilerHighlighter> secondaryHighlighters = new HashSet<>();
|
|
||||||
|
|
||||||
// all highlighters, including secondary and highlight service highlighters
|
|
||||||
private Map<ClangDecompilerHighlighter, TokenHighlights> highlighterHighlights =
|
|
||||||
new HashMap<>();
|
|
||||||
|
|
||||||
// color supplier for secondary highlights
|
|
||||||
private TokenHighlightColors secondaryHighlightColors = new TokenHighlightColors();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A counter to track updates so that clients know when a buffered highlight request is invalid
|
* A counter to track updates so that clients know when a buffered highlight request is invalid
|
||||||
@ -113,21 +98,8 @@ public abstract class ClangHighlightController {
|
|||||||
* color.
|
* color.
|
||||||
* @return the color provider
|
* @return the color provider
|
||||||
*/
|
*/
|
||||||
public ColorProvider getRandomColorProvider() {
|
public ColorProvider getGeneratedColorProvider() {
|
||||||
return token -> secondaryHighlightColors.getColor(token.getText());
|
return new GeneratedColorProvider();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the token that has the primary highlight applied, if any. If multiple tokens are
|
|
||||||
* highlighted, then the return value is arbitrary.
|
|
||||||
* @return the highlighted text
|
|
||||||
*/
|
|
||||||
public String getPrimaryHighlightedText() {
|
|
||||||
ClangToken highlightedToken = getHighlightedToken();
|
|
||||||
if (highlightedToken != null) {
|
|
||||||
return highlightedToken.getText();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,8 +111,8 @@ public abstract class ClangHighlightController {
|
|||||||
return updateId;
|
return updateId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPrimaryHighlight(ClangToken token) {
|
public boolean hasContextHighlight(ClangToken token) {
|
||||||
return primaryHighlightTokens.contains(token);
|
return contextHighlightTokens.contains(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSecondaryHighlight(ClangToken token) {
|
public boolean hasSecondaryHighlight(ClangToken token) {
|
||||||
@ -148,26 +120,19 @@ public abstract class ClangHighlightController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSecondaryHighlights(Function function) {
|
public boolean hasSecondaryHighlights(Function function) {
|
||||||
return !secondaryHighlightersbyFunction.get(function).isEmpty();
|
return userHighlights.hasSecondaryHighlights(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getSecondaryHighlight(ClangToken token) {
|
public Color getSecondaryHighlight(ClangToken token) {
|
||||||
DecompilerHighlighter highlighter = getSecondaryHighlighter(token);
|
return userHighlights.getSecondaryHighlight(token);
|
||||||
if (highlighter != null) {
|
|
||||||
TokenHighlights highlights = highlighterHighlights.get(highlighter);
|
|
||||||
HighlightToken hlToken = highlights.get(token);
|
|
||||||
return hlToken.getColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TokenHighlightColors getSecondaryHighlightColors() {
|
public TokenHighlightColors getSecondaryHighlightColors() {
|
||||||
return secondaryHighlightColors;
|
return userHighlights.getSecondaryHighlightColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TokenHighlights getPrimaryHighlights() {
|
public TokenHighlights getPrimaryHighlights() {
|
||||||
return primaryHighlightTokens;
|
return contextHighlightTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,8 +142,8 @@ public abstract class ClangHighlightController {
|
|||||||
* @param function the function
|
* @param function the function
|
||||||
* @return the highlighters
|
* @return the highlighters
|
||||||
*/
|
*/
|
||||||
public Set<ClangDecompilerHighlighter> getSecondaryHighlighters(Function function) {
|
public Set<DecompilerHighlighter> getSecondaryHighlighters(Function function) {
|
||||||
return new HashSet<>(secondaryHighlightersbyFunction.get(function));
|
return userHighlights.getSecondaryHighlighters(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,11 +152,8 @@ public abstract class ClangHighlightController {
|
|||||||
* function-specific.
|
* function-specific.
|
||||||
* @return the highlighters
|
* @return the highlighters
|
||||||
*/
|
*/
|
||||||
public Set<ClangDecompilerHighlighter> getGlobalHighlighters() {
|
public Set<DecompilerHighlighter> getGlobalHighlighters() {
|
||||||
Set<ClangDecompilerHighlighter> allHighlighters = highlighterHighlights.keySet();
|
return userHighlights.getGlobalHighlighters();
|
||||||
Set<ClangDecompilerHighlighter> results = new HashSet<>(allHighlighters);
|
|
||||||
results.removeAll(secondaryHighlighters);
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,7 +163,7 @@ public abstract class ClangHighlightController {
|
|||||||
* @see #getPrimaryHighlights()
|
* @see #getPrimaryHighlights()
|
||||||
*/
|
*/
|
||||||
public TokenHighlights getHighlighterHighlights(DecompilerHighlighter highlighter) {
|
public TokenHighlights getHighlighterHighlights(DecompilerHighlighter highlighter) {
|
||||||
return highlighterHighlights.get(highlighter);
|
return userHighlights.getHighlights(highlighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,8 +171,8 @@ public abstract class ClangHighlightController {
|
|||||||
* @return token or null
|
* @return token or null
|
||||||
*/
|
*/
|
||||||
public ClangToken getHighlightedToken() {
|
public ClangToken getHighlightedToken() {
|
||||||
if (primaryHighlightTokens.size() == 1) {
|
if (contextHighlightTokens.size() == 1) {
|
||||||
HighlightToken hlToken = CollectionUtils.any(primaryHighlightTokens);
|
HighlightToken hlToken = CollectionUtils.any(contextHighlightTokens);
|
||||||
return hlToken.getToken();
|
return hlToken.getToken();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -236,7 +198,7 @@ public abstract class ClangHighlightController {
|
|||||||
updateHighlightColor(token);
|
updateHighlightColor(token);
|
||||||
};
|
};
|
||||||
|
|
||||||
doClearHighlights(primaryHighlightTokens, clearAll);
|
doClearHighlights(contextHighlightTokens, clearAll);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,10 +224,9 @@ public abstract class ClangHighlightController {
|
|||||||
* @param tokens the tokens
|
* @param tokens the tokens
|
||||||
*/
|
*/
|
||||||
public void togglePrimaryHighlights(Color hlColor, Supplier<List<ClangToken>> tokens) {
|
public void togglePrimaryHighlights(Color hlColor, Supplier<List<ClangToken>> tokens) {
|
||||||
|
|
||||||
boolean isAllHighlighted = true;
|
boolean isAllHighlighted = true;
|
||||||
for (ClangToken otherToken : tokens.get()) {
|
for (ClangToken token : tokens.get()) {
|
||||||
if (!hasPrimaryHighlight(otherToken)) {
|
if (!hasContextHighlight(token)) {
|
||||||
isAllHighlighted = false;
|
isAllHighlighted = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -276,8 +237,7 @@ public abstract class ClangHighlightController {
|
|||||||
clearPrimaryHighlights();
|
clearPrimaryHighlights();
|
||||||
|
|
||||||
if (isAllHighlighted) {
|
if (isAllHighlighted) {
|
||||||
// nothing to do; we toggled from 'all on' to 'all off'
|
return; // nothing to do; we toggled from 'all on' to 'all off'
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addPrimaryHighlights(tokens, hlColor);
|
addPrimaryHighlights(tokens, hlColor);
|
||||||
@ -289,9 +249,11 @@ public abstract class ClangHighlightController {
|
|||||||
*/
|
*/
|
||||||
public void removeSecondaryHighlights(Function f) {
|
public void removeSecondaryHighlights(Function f) {
|
||||||
|
|
||||||
List<ClangDecompilerHighlighter> highlighters = secondaryHighlightersbyFunction.get(f);
|
List<DecompilerHighlighter> highlighters =
|
||||||
for (ClangDecompilerHighlighter highlighter : highlighters) {
|
userHighlights.getSecondaryHighlightersByFunction(f);
|
||||||
TokenHighlights highlights = highlighterHighlights.get(highlighter);
|
|
||||||
|
for (DecompilerHighlighter highlighter : highlighters) {
|
||||||
|
TokenHighlights highlights = userHighlights.getHighlights(highlighter);
|
||||||
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token);
|
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token);
|
||||||
doClearHighlights(highlights, clearHighlight);
|
doClearHighlights(highlights, clearHighlight);
|
||||||
}
|
}
|
||||||
@ -305,38 +267,16 @@ public abstract class ClangHighlightController {
|
|||||||
* @see #removeSecondaryHighlights(Function)
|
* @see #removeSecondaryHighlights(Function)
|
||||||
*/
|
*/
|
||||||
public void removeSecondaryHighlights(ClangToken token) {
|
public void removeSecondaryHighlights(ClangToken token) {
|
||||||
DecompilerHighlighter highlighter = getSecondaryHighlighter(token);
|
DecompilerHighlighter highlighter = userHighlights.getSecondaryHighlighter(token);
|
||||||
if (highlighter != null) {
|
if (highlighter != null) {
|
||||||
highlighter.dispose(); // this will call removeHighlighterHighlights()
|
highlighter.dispose(); // this will call removeHighlighterHighlights()
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DecompilerHighlighter getSecondaryHighlighter(ClangToken token) {
|
|
||||||
for (DecompilerHighlighter highlighter : secondaryHighlighters) {
|
|
||||||
TokenHighlights highlights = highlighterHighlights.get(highlighter);
|
|
||||||
HighlightToken hlToken = highlights.get(token);
|
|
||||||
if (hlToken != null) {
|
|
||||||
return highlighter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeHighlighter(DecompilerHighlighter highlighter) {
|
public void removeHighlighter(DecompilerHighlighter highlighter) {
|
||||||
|
|
||||||
removeHighlighterHighlights(highlighter);
|
removeHighlighterHighlights(highlighter);
|
||||||
highlighterHighlights.remove(highlighter);
|
userHighlights.remove(highlighter);
|
||||||
secondaryHighlighters.remove(highlighter);
|
|
||||||
|
|
||||||
Collection<List<ClangDecompilerHighlighter>> lists =
|
|
||||||
secondaryHighlightersbyFunction.values();
|
|
||||||
for (List<ClangDecompilerHighlighter> highlighters : lists) {
|
|
||||||
if (highlighters.remove(highlighter)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -345,7 +285,7 @@ public abstract class ClangHighlightController {
|
|||||||
*/
|
*/
|
||||||
public void removeHighlighterHighlights(DecompilerHighlighter highlighter) {
|
public void removeHighlighterHighlights(DecompilerHighlighter highlighter) {
|
||||||
|
|
||||||
TokenHighlights highlighterTokens = highlighterHighlights.get(highlighter);
|
TokenHighlights highlighterTokens = userHighlights.get(highlighter);
|
||||||
if (highlighterTokens == null) {
|
if (highlighterTokens == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -361,59 +301,56 @@ public abstract class ClangHighlightController {
|
|||||||
* @param function the function
|
* @param function the function
|
||||||
* @param highlighter the highlighter
|
* @param highlighter the highlighter
|
||||||
*/
|
*/
|
||||||
public void addSecondaryHighlighter(Function function, ClangDecompilerHighlighter highlighter) {
|
public void addSecondaryHighlighter(Function function, DecompilerHighlighter highlighter) {
|
||||||
|
userHighlights.addSecondaryHighlighter(function, highlighter);
|
||||||
// Note: this highlighter has likely already been added the the this class, but has not
|
|
||||||
// yet been bound to the given function.
|
|
||||||
secondaryHighlightersbyFunction.get(function).add(highlighter);
|
|
||||||
secondaryHighlighters.add(highlighter);
|
|
||||||
highlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this is used for all highlight types, secondary and highlighter service highlighters
|
// Note: this is used for all highlight types, secondary and highlighter service highlighters
|
||||||
public void addHighlighter(ClangDecompilerHighlighter highlighter) {
|
public void addHighlighter(ClangDecompilerHighlighter highlighter) {
|
||||||
highlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
|
userHighlights.add(highlighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this is used for all highlight types, secondary and highlighter service highlights
|
// Note: this is used for all highlight types, secondary and highlighter service highlights
|
||||||
public void addHighlighterHighlights(ClangDecompilerHighlighter highlighter,
|
public void addHighlighterHighlights(DecompilerHighlighter highlighter,
|
||||||
Supplier<? extends Collection<ClangToken>> tokens,
|
Supplier<? extends Collection<ClangToken>> tokens, ColorProvider colorProvider) {
|
||||||
ColorProvider colorProvider) {
|
|
||||||
|
|
||||||
Objects.requireNonNull(highlighter);
|
Objects.requireNonNull(highlighter);
|
||||||
TokenHighlights highlighterTokens =
|
TokenHighlights highlighterTokens = userHighlights.add(highlighter);
|
||||||
highlighterHighlights.computeIfAbsent(highlighter, k -> new TokenHighlights());
|
|
||||||
addTokensToHighlights(tokens.get(), colorProvider, highlighterTokens);
|
addTokensToHighlights(tokens.get(), colorProvider, highlighterTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
|
private void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
|
||||||
Color hlColor) {
|
Color hlColor) {
|
||||||
ColorProvider colorProvider = token -> hlColor;
|
addPrimaryHighlights(tokens.get(), hlColor);
|
||||||
addTokensToHighlights(tokens.get(), colorProvider, primaryHighlightTokens);
|
}
|
||||||
|
|
||||||
|
private void addPrimaryHighlights(Collection<ClangToken> tokens, Color hlColor) {
|
||||||
|
ColorProvider colorProvider = new DefaultColorProvider("Tokens Highlight Color", hlColor);
|
||||||
|
addTokensToHighlights(tokens, colorProvider, contextHighlightTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPrimaryHighlights(ClangNode parentNode, Set<PcodeOp> ops, Color hlColor) {
|
public void addPrimaryHighlights(ClangNode parentNode, Set<PcodeOp> ops, Color hlColor) {
|
||||||
|
|
||||||
addPrimaryHighlights(parentNode, token -> {
|
ColorProvider colorProvider = new DefaultColorProvider("PcodeOp Highlight Color", hlColor) {
|
||||||
PcodeOp op = token.getPcodeOp();
|
@Override
|
||||||
return ops.contains(op) ? hlColor : null;
|
public Color getColor(ClangToken token) {
|
||||||
});
|
PcodeOp op = token.getPcodeOp();
|
||||||
|
return ops.contains(op) ? hlColor : null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addPrimaryHighlights(parentNode, colorProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPrimaryHighlights(ClangNode parentNode, ColorProvider colorProvider) {
|
public void addPrimaryHighlights(ClangNode parentNode, ColorProvider colorProvider) {
|
||||||
|
|
||||||
Set<ClangToken> tokens = new HashSet<>();
|
Set<ClangToken> tokens = new HashSet<>();
|
||||||
gatherAllTokens(parentNode, tokens);
|
gatherAllTokens(parentNode, tokens);
|
||||||
addTokensToHighlights(tokens, colorProvider::getColor, primaryHighlightTokens);
|
addTokensToHighlights(tokens, colorProvider, contextHighlightTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPrimaryHighlights(Collection<ClangToken> tokens, Color hlColor) {
|
private void addTokensToHighlights(Collection<ClangToken> tokens, ColorProvider colorProvider,
|
||||||
ColorProvider colorProvider = token -> hlColor;
|
TokenHighlights currentHighlights) {
|
||||||
addTokensToHighlights(tokens, colorProvider, primaryHighlightTokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTokensToHighlights(Collection<ClangToken> tokens,
|
|
||||||
ColorProvider colorProvider, TokenHighlights currentHighlights) {
|
|
||||||
|
|
||||||
updateId++;
|
updateId++;
|
||||||
|
|
||||||
@ -441,7 +378,7 @@ public abstract class ClangHighlightController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateHighlightColor(ClangToken t) {
|
private void updateHighlightColor(ClangToken t) {
|
||||||
// set the color to the current combined value of both highlight types
|
// set the color to the current combined value of all highlight types
|
||||||
Color combinedColor = getCombinedColor(t);
|
Color combinedColor = getCombinedColor(t);
|
||||||
t.setHighlight(combinedColor);
|
t.setHighlight(combinedColor);
|
||||||
}
|
}
|
||||||
@ -469,7 +406,7 @@ public abstract class ClangHighlightController {
|
|||||||
// note: not sure whether we should always blend all colors or decide to allow some
|
// note: not sure whether we should always blend all colors or decide to allow some
|
||||||
// highlighters have precedence for highlighting
|
// highlighters have precedence for highlighting
|
||||||
|
|
||||||
HighlightToken primaryHl = primaryHighlightTokens.get(t);
|
HighlightToken primaryHl = contextHighlightTokens.get(t);
|
||||||
Color blendedHlColor = blendHighlighterColors(t);
|
Color blendedHlColor = blendHighlighterColors(t);
|
||||||
|
|
||||||
List<Color> allColors = new ArrayList<>();
|
List<Color> allColors = new ArrayList<>();
|
||||||
@ -506,12 +443,12 @@ public abstract class ClangHighlightController {
|
|||||||
return null; // not sure if this can happen
|
return null; // not sure if this can happen
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<ClangDecompilerHighlighter> global = getGlobalHighlighters();
|
Set<DecompilerHighlighter> global = getGlobalHighlighters();
|
||||||
Set<ClangDecompilerHighlighter> secondary = getSecondaryHighlighters(function);
|
Set<DecompilerHighlighter> secondary = getSecondaryHighlighters(function);
|
||||||
Iterable<ClangDecompilerHighlighter> it = CollectionUtils.asIterable(global, secondary);
|
Iterable<DecompilerHighlighter> it = CollectionUtils.asIterable(global, secondary);
|
||||||
Color lastColor = null;
|
Color lastColor = null;
|
||||||
for (ClangDecompilerHighlighter highlighter : it) {
|
for (DecompilerHighlighter highlighter : it) {
|
||||||
TokenHighlights highlights = highlighterHighlights.get(highlighter);
|
TokenHighlights highlights = userHighlights.get(highlighter);
|
||||||
HighlightToken hlToken = highlights.get(token);
|
HighlightToken hlToken = highlights.get(token);
|
||||||
if (hlToken == null) {
|
if (hlToken == null) {
|
||||||
continue;
|
continue;
|
||||||
@ -542,6 +479,24 @@ public abstract class ClangHighlightController {
|
|||||||
return highFunction.getFunction();
|
return highFunction.getFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void addPrimaryHighlightToTokensForBrace(ClangSyntaxToken token,
|
||||||
|
Color highlightColor) {
|
||||||
|
|
||||||
|
if (DecompilerUtils.isBrace(token)) {
|
||||||
|
highlightBrace(token, highlightColor);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void highlightBrace(ClangSyntaxToken startToken, Color highlightColor) {
|
||||||
|
|
||||||
|
ClangSyntaxToken matchingBrace = DecompilerUtils.getMatchingBrace(startToken);
|
||||||
|
if (matchingBrace != null) {
|
||||||
|
matchingBrace.setMatchingToken(true); // this is a signal to the painter
|
||||||
|
addPrimaryHighlights(Set.of(matchingBrace), highlightColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If input token is a parenthesis, highlight all tokens between it and its match
|
* If input token is a parenthesis, highlight all tokens between it and its match
|
||||||
* @param tok potential parenthesis token
|
* @param tok potential parenthesis token
|
||||||
@ -609,23 +564,6 @@ public abstract class ClangHighlightController {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBraceHighlight(ClangSyntaxToken token, Color highlightColor) {
|
|
||||||
|
|
||||||
if (DecompilerUtils.isBrace(token)) {
|
|
||||||
highlightBrace(token, highlightColor);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void highlightBrace(ClangSyntaxToken startToken, Color highlightColor) {
|
|
||||||
|
|
||||||
ClangSyntaxToken matchingBrace = DecompilerUtils.getMatchingBrace(startToken);
|
|
||||||
if (matchingBrace != null) {
|
|
||||||
matchingBrace.setMatchingToken(true); // this is a signal to the painter
|
|
||||||
addPrimaryHighlights(Set.of(matchingBrace), highlightColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addListener(ClangHighlightListener listener) {
|
public void addListener(ClangHighlightListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
@ -642,9 +580,21 @@ public abstract class ClangHighlightController {
|
|||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
listeners.clear();
|
listeners.clear();
|
||||||
primaryHighlightTokens.clear();
|
contextHighlightTokens.clear();
|
||||||
secondaryHighlighters.clear();
|
userHighlights.dispose();
|
||||||
secondaryHighlightersbyFunction.clear();
|
|
||||||
highlighterHighlights.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class GeneratedColorProvider implements ColorProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getColor(ClangToken token) {
|
||||||
|
return userHighlights.getSecondaryColor(token.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Generated Color Provider " + userHighlights.getAppliedColorsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,6 @@ import ghidra.program.model.pcode.HighFunction;
|
|||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
*
|
|
||||||
* Control the GUI layout for displaying tokenized C code
|
* Control the GUI layout for displaying tokenized C code
|
||||||
*/
|
*/
|
||||||
public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||||
@ -53,10 +51,10 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
private Field[] fieldList; // Array of fields comprising layout
|
private Field[] fieldList; // Array of fields comprising layout
|
||||||
private FontMetrics metrics;
|
private FontMetrics metrics;
|
||||||
private FieldHighlightFactory hlFactory;
|
private FieldHighlightFactory hlFactory;
|
||||||
private ArrayList<LayoutModelListener> listeners;
|
private List<LayoutModelListener> listeners;
|
||||||
private Color[] syntaxColor; // Foreground colors.
|
private Color[] syntaxColor; // Foreground colors.
|
||||||
private BigInteger numIndexes = BigInteger.ZERO;
|
private BigInteger numIndexes = BigInteger.ZERO;
|
||||||
private ArrayList<ClangLine> lines = new ArrayList<>();
|
private List<ClangLine> lines = new ArrayList<>();
|
||||||
|
|
||||||
private boolean showLineNumbers = true;
|
private boolean showLineNumbers = true;
|
||||||
|
|
||||||
@ -71,7 +69,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
buildLayouts(null, null, null, false);
|
buildLayouts(null, null, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<ClangLine> getLines() {
|
public List<ClangLine> getLines() {
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +246,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void splitToMaxWidthLines(ArrayList<String> res, String line) {
|
private void splitToMaxWidthLines(List<String> res, String line) {
|
||||||
int maxchar;
|
int maxchar;
|
||||||
if ((maxWidth == 0) || (indentWidth == 0)) {
|
if ((maxWidth == 0) || (indentWidth == 0)) {
|
||||||
maxchar = 40;
|
maxchar = 40;
|
||||||
@ -257,7 +255,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
maxchar = maxWidth / indentWidth;
|
maxchar = maxWidth / indentWidth;
|
||||||
}
|
}
|
||||||
String[] toklist = line.split("[ \t]+");
|
String[] toklist = line.split("[ \t]+");
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuilder buf = new StringBuilder();
|
||||||
int cursize = 0;
|
int cursize = 0;
|
||||||
boolean atleastone = false;
|
boolean atleastone = false;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -275,7 +273,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
res.add(finishLine);
|
res.add(finishLine);
|
||||||
cursize = 5;
|
cursize = 5;
|
||||||
atleastone = false;
|
atleastone = false;
|
||||||
buf = new StringBuffer();
|
buf = new StringBuilder();
|
||||||
buf.append(" ");
|
buf.append(" ");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -302,7 +300,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
return false; // No error message to add
|
return false; // No error message to add
|
||||||
}
|
}
|
||||||
String[] errlines_init = errmsg.split("[\n\r]+");
|
String[] errlines_init = errmsg.split("[\n\r]+");
|
||||||
ArrayList<String> errlines = new ArrayList<>();
|
List<String> errlines = new ArrayList<>();
|
||||||
for (String element : errlines_init) {
|
for (String element : errlines_init) {
|
||||||
splitToMaxWidthLines(errlines, element);
|
splitToMaxWidthLines(errlines, element);
|
||||||
}
|
}
|
||||||
@ -345,16 +343,16 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
java.util.function.Function<String, SearchMatch> matcher, String searchString,
|
java.util.function.Function<String, SearchMatch> matcher, String searchString,
|
||||||
FieldLocation currentLocation) {
|
FieldLocation currentLocation) {
|
||||||
|
|
||||||
int row = currentLocation.getIndex().intValue();
|
int startRow = currentLocation.getIndex().intValue();
|
||||||
for (int i = row; i < fieldList.length; i++) {
|
for (int row = startRow; row < fieldList.length; row++) {
|
||||||
ClangTextField field = (ClangTextField) fieldList[i];
|
ClangTextField field = (ClangTextField) fieldList[row];
|
||||||
String partialLine =
|
FieldLocation location = (row == startRow) ? currentLocation : null;
|
||||||
getTextLineFromOffset((i == row) ? currentLocation : null, field, true);
|
String lineText = getLineTextFromOffset(location, field, true);
|
||||||
SearchMatch match = matcher.apply(partialLine);
|
SearchMatch match = matcher.apply(lineText);
|
||||||
if (match == SearchMatch.NO_MATCH) {
|
if (match == SearchMatch.NO_MATCH) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (i == row) { // cursor is on this line
|
if (row == startRow) { // cursor is on this line
|
||||||
//
|
//
|
||||||
// The match start for all lines without the cursor will be relative to the start
|
// The match start for all lines without the cursor will be relative to the start
|
||||||
// of the line, which is 0. However, when searching on the row with the cursor,
|
// of the line, which is 0. However, when searching on the row with the cursor,
|
||||||
@ -362,13 +360,15 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
// compensate for the difference between the start of the line and the cursor.
|
// compensate for the difference between the start of the line and the cursor.
|
||||||
//
|
//
|
||||||
String fullLine = field.getText();
|
String fullLine = field.getText();
|
||||||
int cursorOffset = fullLine.length() - partialLine.length();
|
int cursorOffset = fullLine.length() - lineText.length();
|
||||||
match.start += cursorOffset;
|
match.start += cursorOffset;
|
||||||
match.end += cursorOffset;
|
match.end += cursorOffset;
|
||||||
}
|
}
|
||||||
FieldNumberColumnPair pair = getFieldIndexFromOffset(match.start, field);
|
|
||||||
FieldLocation fieldLocation =
|
// we use 0 here because currently there is only one field, which is the entire line
|
||||||
new FieldLocation(i, pair.getFieldNumber(), 0, pair.getColumn());
|
int fieldNum = 0;
|
||||||
|
int column = getScreenColumnFromOffset(match.start, field);
|
||||||
|
FieldLocation fieldLocation = new FieldLocation(row, fieldNum, 0, column);
|
||||||
|
|
||||||
return new FieldBasedSearchLocation(fieldLocation, match.start, match.end - 1,
|
return new FieldBasedSearchLocation(fieldLocation, match.start, match.end - 1,
|
||||||
searchString, true);
|
searchString, true);
|
||||||
@ -380,17 +380,19 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
java.util.function.Function<String, SearchMatch> matcher, String searchString,
|
java.util.function.Function<String, SearchMatch> matcher, String searchString,
|
||||||
FieldLocation currentLocation) {
|
FieldLocation currentLocation) {
|
||||||
|
|
||||||
int row = currentLocation.getIndex().intValue();
|
int startRow = currentLocation.getIndex().intValue();
|
||||||
for (int i = row; i >= 0; i--) {
|
for (int row = startRow; row >= 0; row--) {
|
||||||
ClangTextField field = (ClangTextField) fieldList[i];
|
ClangTextField field = (ClangTextField) fieldList[row];
|
||||||
String textLine =
|
FieldLocation location = (row == startRow) ? currentLocation : null;
|
||||||
getTextLineFromOffset((i == row) ? currentLocation : null, field, false);
|
String lineText = getLineTextFromOffset(location, field, false);
|
||||||
|
|
||||||
SearchMatch match = matcher.apply(textLine);
|
SearchMatch match = matcher.apply(lineText);
|
||||||
if (match != SearchMatch.NO_MATCH) {
|
if (match != SearchMatch.NO_MATCH) {
|
||||||
FieldNumberColumnPair pair = getFieldIndexFromOffset(match.start, field);
|
|
||||||
FieldLocation fieldLocation =
|
// we use 0 here because currently there is only one field, which is the entire line
|
||||||
new FieldLocation(i, pair.getFieldNumber(), 0, pair.getColumn());
|
int fieldNum = 0;
|
||||||
|
int column = getScreenColumnFromOffset(match.start, field);
|
||||||
|
FieldLocation fieldLocation = new FieldLocation(row, fieldNum, 0, column);
|
||||||
|
|
||||||
return new FieldBasedSearchLocation(fieldLocation, match.start, match.end - 1,
|
return new FieldBasedSearchLocation(fieldLocation, match.start, match.end - 1,
|
||||||
searchString, false);
|
searchString, false);
|
||||||
@ -483,7 +485,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
return findNextTokenGoingBackward(function, searchString, currentLocation);
|
return findNextTokenGoingBackward(function, searchString, currentLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTextLineFromOffset(FieldLocation location, ClangTextField textField,
|
private String getLineTextFromOffset(FieldLocation location, ClangTextField textField,
|
||||||
boolean forwardSearch) {
|
boolean forwardSearch) {
|
||||||
|
|
||||||
if (location == null) { // the cursor location is not on this line; use all of the text
|
if (location == null) { // the cursor location is not on this line; use all of the text
|
||||||
@ -494,32 +496,28 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
String partialText = textField.getText();
|
String lineText = textField.getText();
|
||||||
|
|
||||||
if (forwardSearch) {
|
if (forwardSearch) {
|
||||||
|
|
||||||
int nextCol = location.getCol();
|
int nextCol = location.getCol();
|
||||||
|
|
||||||
// protects against the location column being out of range (this can happen if we're
|
// protects against the location column being out of range (this can happen if we're
|
||||||
// searching forward and the cursor is past the last token)
|
// searching forward and the cursor is past the last token)
|
||||||
if (nextCol >= partialText.length()) {
|
if (nextCol >= lineText.length()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip a character to start the next search; this prevents matching the previous match
|
// skip a character to start the next search; this prevents matching the previous match
|
||||||
return partialText.substring(nextCol);
|
return lineText.substring(nextCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// backwards search
|
// backwards search
|
||||||
return partialText.substring(0, location.getCol());
|
return lineText.substring(0, location.getCol());
|
||||||
}
|
}
|
||||||
|
|
||||||
private FieldNumberColumnPair getFieldIndexFromOffset(int screenOffset,
|
private int getScreenColumnFromOffset(int textOffset, ClangTextField textField) {
|
||||||
ClangTextField textField) {
|
RowColLocation rowColLocation = textField.textOffsetToScreenLocation(textOffset);
|
||||||
RowColLocation rowColLocation = textField.textOffsetToScreenLocation(screenOffset);
|
return rowColLocation.col();
|
||||||
|
|
||||||
// we use 0 here because currently there is only one field, which is the entire line
|
|
||||||
return new FieldNumberColumnPair(0, rowColLocation.col());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SearchMatch {
|
private static class SearchMatch {
|
||||||
@ -565,27 +563,4 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||||||
public void flushChanges() {
|
public void flushChanges() {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
//==================================================================================================
|
|
||||||
// Inner Classes
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
private class FieldNumberColumnPair {
|
|
||||||
private final int fieldNumber;
|
|
||||||
private final int column;
|
|
||||||
|
|
||||||
FieldNumberColumnPair(int fieldNumber, int column) {
|
|
||||||
this.fieldNumber = fieldNumber;
|
|
||||||
this.column = column;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int getFieldNumber() {
|
|
||||||
return fieldNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getColumn() {
|
|
||||||
return column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ package ghidra.app.decompiler.component;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.field.*;
|
import docking.widgets.fieldpanel.field.*;
|
||||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
|
||||||
import docking.widgets.fieldpanel.support.FieldHighlightFactory;
|
import docking.widgets.fieldpanel.support.FieldHighlightFactory;
|
||||||
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
import ghidra.app.decompiler.ClangToken;
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
|
||||||
public class ClangTextField extends WrappingVerticalLayoutTextField {
|
public class ClangTextField extends WrappingVerticalLayoutTextField {
|
||||||
@ -123,4 +123,12 @@ public class ClangTextField extends WrappingVerticalLayoutTextField {
|
|||||||
public int getLineNumber() {
|
public int getLineNumber() {
|
||||||
return lineNumber;
|
return lineNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClangToken getFirstToken() {
|
||||||
|
return tokenList.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClangToken getLastToken() {
|
||||||
|
return tokenList.get(tokenList.size() - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
|
|
||||||
private FieldHighlightFactory hlFactory;
|
private FieldHighlightFactory hlFactory;
|
||||||
private ClangHighlightController highlightController;
|
private ClangHighlightController highlightController;
|
||||||
private Map<String, ClangDecompilerHighlighter> highlightersById = new HashMap<>();
|
private Map<String, DecompilerHighlighter> highlightersById = new HashMap<>();
|
||||||
private PendingHighlightUpdate pendingHighlightUpdate;
|
private PendingHighlightUpdate pendingHighlightUpdate;
|
||||||
private SwingUpdateManager highlighCursorUpdater = new SwingUpdateManager(() -> {
|
private SwingUpdateManager highlighCursorUpdater = new SwingUpdateManager(() -> {
|
||||||
if (pendingHighlightUpdate != null) {
|
if (pendingHighlightUpdate != null) {
|
||||||
@ -89,6 +89,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private ActiveMiddleMouse activeMiddleMouse;
|
||||||
private int middleMouseHighlightButton;
|
private int middleMouseHighlightButton;
|
||||||
private Color middleMouseHighlightColor;
|
private Color middleMouseHighlightColor;
|
||||||
private Color currentVariableHighlightColor;
|
private Color currentVariableHighlightColor;
|
||||||
@ -194,7 +195,14 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
return highlightController.getHighlighterHighlights(highligter);
|
return highlightController.getHighlighterHighlights(highligter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<ClangDecompilerHighlighter> getSecondaryHighlihgtersByFunction(Function function) {
|
public TokenHighlights getMiddleMouseHighlights() {
|
||||||
|
if (activeMiddleMouse != null) {
|
||||||
|
return activeMiddleMouse.getHighlights();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<DecompilerHighlighter> getSecondaryHighlihgtersByFunction(Function function) {
|
||||||
return highlightController.getSecondaryHighlighters(function);
|
return highlightController.getSecondaryHighlighters(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,31 +220,50 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addSecondaryHighlight(ClangToken token) {
|
public void addSecondaryHighlight(ClangToken token) {
|
||||||
ColorProvider cp = highlightController.getRandomColorProvider();
|
ColorProvider cp = highlightController.getGeneratedColorProvider();
|
||||||
addSecondaryHighlight(token.getText(), cp);
|
addSecondaryHighlight(token.getText(), cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSecondaryHighlight(ClangToken token, Color color) {
|
public void addSecondaryHighlight(ClangToken token, Color color) {
|
||||||
ColorProvider cp = t -> color;
|
ColorProvider cp = new DefaultColorProvider("User Secondary Highlight", color);
|
||||||
addSecondaryHighlight(token.getText(), cp);
|
addSecondaryHighlight(token.getText(), cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSecondaryHighlight(String tokenText, ColorProvider colorProvider) {
|
private void addSecondaryHighlight(String tokenText, ColorProvider colorProvider) {
|
||||||
NameTokenMatcher matcher = new NameTokenMatcher(tokenText, colorProvider);
|
NameTokenMatcher matcher = new NameTokenMatcher(tokenText, colorProvider);
|
||||||
ClangDecompilerHighlighter highlighter = createHighlighter(matcher);
|
DecompilerHighlighter highlighter = createHighlighter(matcher);
|
||||||
applySecondaryHighlights(highlighter);
|
applySecondaryHighlights(highlighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applySecondaryHighlights(ClangDecompilerHighlighter highlighter) {
|
private void applySecondaryHighlights(DecompilerHighlighter highlighter) {
|
||||||
Function function = decompileData.getFunction();
|
Function function = decompileData.getFunction();
|
||||||
highlightController.addSecondaryHighlighter(function, highlighter);
|
highlightController.addSecondaryHighlighter(function, highlighter);
|
||||||
highlighter.applyHighlights();
|
highlighter.applyHighlights();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void togglePrimaryHighlight(FieldLocation location, Field field, Color highlightColor) {
|
private void toggleMiddleMouseHighlight(FieldLocation location, Field field) {
|
||||||
ClangToken token = ((ClangTextField) field).getToken(location);
|
ClangToken token = ((ClangTextField) field).getToken(location);
|
||||||
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(token.getText());
|
ColorProvider cp = new MiddleMouseColorProvider();
|
||||||
highlightController.togglePrimaryHighlights(middleMouseHighlightColor, lazyTokens);
|
NameTokenMatcher matcher = new NameTokenMatcher(token.getText(), cp);
|
||||||
|
|
||||||
|
ActiveMiddleMouse previousMiddleMouse = activeMiddleMouse;
|
||||||
|
activeMiddleMouse = null;
|
||||||
|
|
||||||
|
if (previousMiddleMouse != null) {
|
||||||
|
|
||||||
|
// middle mousing always clears the last middle-mouse highlight
|
||||||
|
previousMiddleMouse.clear();
|
||||||
|
|
||||||
|
if (previousMiddleMouse.matches(token)) {
|
||||||
|
// middle mousing on the same token clears, but does not create a new highlight
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerHighlighter newMiddleMouseHighlighter = createHighlighter(matcher);
|
||||||
|
ActiveMiddleMouse newMiddleMouse = new ActiveMiddleMouse(token, newMiddleMouseHighlighter);
|
||||||
|
newMiddleMouse.apply();
|
||||||
|
activeMiddleMouse = newMiddleMouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addHighlighterHighlights(ClangDecompilerHighlighter highlighter,
|
void addHighlighterHighlights(ClangDecompilerHighlighter highlighter,
|
||||||
@ -248,14 +275,14 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
highlightController.removeHighlighterHighlights(highlighter);
|
highlightController.removeHighlighterHighlights(highlighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClangDecompilerHighlighter createHighlighter(CTokenHighlightMatcher tm) {
|
public DecompilerHighlighter createHighlighter(CTokenHighlightMatcher tm) {
|
||||||
UUID uuId = UUID.randomUUID();
|
UUID uuId = UUID.randomUUID();
|
||||||
String id = uuId.toString();
|
String id = uuId.toString();
|
||||||
return createHighlighter(id, tm);
|
return createHighlighter(id, tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClangDecompilerHighlighter createHighlighter(String id, CTokenHighlightMatcher tm) {
|
public DecompilerHighlighter createHighlighter(String id, CTokenHighlightMatcher tm) {
|
||||||
ClangDecompilerHighlighter currentHighlighter = highlightersById.get(id);
|
DecompilerHighlighter currentHighlighter = highlightersById.get(id);
|
||||||
if (currentHighlighter != null) {
|
if (currentHighlighter != null) {
|
||||||
currentHighlighter.dispose();
|
currentHighlighter.dispose();
|
||||||
}
|
}
|
||||||
@ -271,7 +298,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
}
|
}
|
||||||
|
|
||||||
void removeHighlighter(String id) {
|
void removeHighlighter(String id) {
|
||||||
ClangDecompilerHighlighter highlighter = highlightersById.remove(id);
|
DecompilerHighlighter highlighter = highlightersById.remove(id);
|
||||||
highlightController.removeHighlighter(highlighter);
|
highlightController.removeHighlighter(highlighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,11 +366,15 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
|
|
||||||
private void cloneGlobalHighlighters(DecompilerPanel sourcePanel) {
|
private void cloneGlobalHighlighters(DecompilerPanel sourcePanel) {
|
||||||
|
|
||||||
Set<ClangDecompilerHighlighter> globalHighlighters =
|
Set<DecompilerHighlighter> globalHighlighters =
|
||||||
sourcePanel.highlightController.getGlobalHighlighters();
|
sourcePanel.highlightController.getGlobalHighlighters();
|
||||||
for (ClangDecompilerHighlighter otherHighlighter : globalHighlighters) {
|
for (DecompilerHighlighter otherHighlighter : globalHighlighters) {
|
||||||
|
|
||||||
ClangDecompilerHighlighter newHighlighter = otherHighlighter.clone(this);
|
if (!(otherHighlighter instanceof ClangDecompilerHighlighter clangHighlighter)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerHighlighter newHighlighter = clangHighlighter.clone(this);
|
||||||
highlightersById.put(newHighlighter.getId(), newHighlighter);
|
highlightersById.put(newHighlighter.getId(), newHighlighter);
|
||||||
|
|
||||||
TokenHighlights otherHighlighterTokens =
|
TokenHighlights otherHighlighterTokens =
|
||||||
@ -374,15 +405,20 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
// clone will match the cloned decompiler.
|
// clone will match the cloned decompiler.
|
||||||
//
|
//
|
||||||
Function function = decompileData.getFunction();
|
Function function = decompileData.getFunction();
|
||||||
Set<ClangDecompilerHighlighter> secondaryHighlighters =
|
Set<DecompilerHighlighter> secondaryHighlighters =
|
||||||
sourcePanel.getSecondaryHighlihgtersByFunction(function);
|
sourcePanel.getSecondaryHighlihgtersByFunction(function);
|
||||||
|
|
||||||
//
|
//
|
||||||
// We do NOT clone the secondary highlighters. This allows the user the remove them
|
// We do NOT clone the secondary highlighters. This allows the user the remove them
|
||||||
// from the primary provider without effecting the cloned provider and vice versa.
|
// from the primary provider without effecting the cloned provider and vice versa.
|
||||||
//
|
//
|
||||||
for (ClangDecompilerHighlighter highlighter : secondaryHighlighters) {
|
for (DecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||||
ClangDecompilerHighlighter newHighlighter = highlighter.copy(this);
|
|
||||||
|
if (!(highlighter instanceof ClangDecompilerHighlighter clangHighlighter)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerHighlighter newHighlighter = clangHighlighter.copy(this);
|
||||||
highlightersById.put(newHighlighter.getId(), newHighlighter);
|
highlightersById.put(newHighlighter.getId(), newHighlighter);
|
||||||
applySecondaryHighlights(newHighlighter);
|
applySecondaryHighlights(newHighlighter);
|
||||||
}
|
}
|
||||||
@ -455,9 +491,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<ClangDecompilerHighlighter> globalHighlighters =
|
Set<DecompilerHighlighter> globalHighlighters = highlightController.getGlobalHighlighters();
|
||||||
highlightController.getGlobalHighlighters();
|
for (DecompilerHighlighter highlighter : globalHighlighters) {
|
||||||
for (ClangDecompilerHighlighter highlighter : globalHighlighters) {
|
|
||||||
highlighter.clearHighlights();
|
highlighter.clearHighlights();
|
||||||
highlighter.applyHighlights();
|
highlighter.applyHighlights();
|
||||||
}
|
}
|
||||||
@ -470,9 +505,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<ClangDecompilerHighlighter> secondaryHighlighters =
|
Set<DecompilerHighlighter> secondaryHighlighters =
|
||||||
getSecondaryHighlihgtersByFunction(function);
|
getSecondaryHighlihgtersByFunction(function);
|
||||||
for (ClangDecompilerHighlighter highlighter : secondaryHighlighters) {
|
for (DecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||||
highlighter.clearHighlights();
|
highlighter.clearHighlights();
|
||||||
highlighter.applyHighlights();
|
highlighter.applyHighlights();
|
||||||
}
|
}
|
||||||
@ -719,7 +754,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buttonState == middleMouseHighlightButton && clickCount == 1) {
|
if (buttonState == middleMouseHighlightButton && clickCount == 1) {
|
||||||
togglePrimaryHighlight(location, field, middleMouseHighlightColor);
|
toggleMiddleMouseHighlight(location, field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,19 +805,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
controller.goToFunction(function, newWindow);
|
controller.goToFunction(function, newWindow);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO no idea what this is supposed to be handling...someone doc this please
|
|
||||||
// String labelName = functionToken.getText();
|
|
||||||
// if (labelName.startsWith("func_0x")) {
|
|
||||||
// try {
|
|
||||||
// Address addr =
|
|
||||||
// decompileData.getFunction().getEntryPoint().getAddress(labelName.substring(7));
|
|
||||||
// controller.goToAddress(addr, newWindow);
|
|
||||||
// }
|
|
||||||
// catch (AddressFormatException e) {
|
|
||||||
// controller.goToLabel(labelName, newWindow);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryGoToLabel(ClangLabelToken token, boolean newWindow) {
|
private void tryGoToLabel(ClangLabelToken token, boolean newWindow) {
|
||||||
@ -1377,4 +1399,57 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MiddleMouseColorProvider implements ColorProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getColor(ClangToken token) {
|
||||||
|
return middleMouseHighlightColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Middle Mouse Color Provider " + middleMouseHighlightColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to track the current middle moused token.
|
||||||
|
*/
|
||||||
|
private class ActiveMiddleMouse {
|
||||||
|
|
||||||
|
private ClangToken token;
|
||||||
|
private DecompilerHighlighter highlighter;
|
||||||
|
|
||||||
|
ActiveMiddleMouse(ClangToken token, DecompilerHighlighter highlighter) {
|
||||||
|
this.token = token;
|
||||||
|
this.highlighter = highlighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenHighlights getHighlights() {
|
||||||
|
return highlightController.getHighlighterHighlights(highlighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerHighlighter getHighlighter() {
|
||||||
|
return highlighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean matches(ClangToken other) {
|
||||||
|
return token.getText().equals(other.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
highlightController.removeHighlighter(highlighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply() {
|
||||||
|
applySecondaryHighlights(highlighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Middle Mouse Token " + token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,10 @@ import ghidra.program.model.pcode.*;
|
|||||||
public class DecompilerUtils {
|
public class DecompilerUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gaither decompiler options from tool and program. If tool is null or does not provide
|
* Gather decompiler options from tool and program. If tool is null or does not provide
|
||||||
* a {@link OptionsService} provider only options stored within the program will be consumed.
|
* a {@link OptionsService} provider only options stored within the program will be consumed.
|
||||||
* @param serviceProvider plugin tool or service provider providing access to {@link OptionsService}
|
* @param serviceProvider plugin tool or service provider providing access to
|
||||||
|
* {@link OptionsService}
|
||||||
* @param program program
|
* @param program program
|
||||||
* @return decompiler options
|
* @return decompiler options
|
||||||
*/
|
*/
|
||||||
@ -281,7 +282,7 @@ public class DecompilerUtils {
|
|||||||
* @param function decompiled function
|
* @param function decompiled function
|
||||||
* @return true if {@code var} corresponds to existing auto {@code this} parameter, else false
|
* @return true if {@code var} corresponds to existing auto {@code this} parameter, else false
|
||||||
*/
|
*/
|
||||||
public static boolean testForAutoParameterThis(HighVariable var, Function function) {
|
public static boolean isThisParameter(HighVariable var, Function function) {
|
||||||
if (var instanceof HighParam) {
|
if (var instanceof HighParam) {
|
||||||
int slot = ((HighParam) var).getSlot();
|
int slot = ((HighParam) var).getSlot();
|
||||||
Parameter parameter = function.getParameter(slot);
|
Parameter parameter = function.getParameter(slot);
|
||||||
@ -626,7 +627,7 @@ public class DecompilerUtils {
|
|||||||
|
|
||||||
String destinationStart = label.getText() + ':';
|
String destinationStart = label.getText() + ':';
|
||||||
Address address = label.getMinAddress();
|
Address address = label.getMinAddress();
|
||||||
List<ClangToken> tokens = DecompilerUtils.getTokens(root, address);
|
List<ClangToken> tokens = getTokens(root, address);
|
||||||
for (ClangToken token : tokens) {
|
for (ClangToken token : tokens) {
|
||||||
if (isGoToStatement(token)) {
|
if (isGoToStatement(token)) {
|
||||||
continue; // ignore any goto statements
|
continue; // ignore any goto statements
|
||||||
@ -791,7 +792,7 @@ public class DecompilerUtils {
|
|||||||
* @param group is the token hierarchy
|
* @param group is the token hierarchy
|
||||||
* @return the array of ClangLine objects
|
* @return the array of ClangLine objects
|
||||||
*/
|
*/
|
||||||
public static ArrayList<ClangLine> toLines(ClangTokenGroup group) {
|
public static List<ClangLine> toLines(ClangTokenGroup group) {
|
||||||
|
|
||||||
List<ClangNode> alltoks = new ArrayList<>();
|
List<ClangNode> alltoks = new ArrayList<>();
|
||||||
group.flatten(alltoks);
|
group.flatten(alltoks);
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
/* ###
|
||||||
|
* 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 color provider that returns a specific color.
|
||||||
|
*/
|
||||||
|
public class DefaultColorProvider implements ColorProvider {
|
||||||
|
|
||||||
|
private Color color;
|
||||||
|
private String prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param prefix a descriptive prefix used in the {@link #toString()} method
|
||||||
|
* @param color the color
|
||||||
|
*/
|
||||||
|
DefaultColorProvider(String prefix, Color color) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color getColor(ClangToken token) {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return prefix + ' ' + color;
|
||||||
|
}
|
||||||
|
}
|
@ -50,7 +50,7 @@ public class LocationClangHighlightController extends ClangHighlightController {
|
|||||||
addPrimaryHighlight(tok, defaultHighlightColor);
|
addPrimaryHighlight(tok, defaultHighlightColor);
|
||||||
if (tok instanceof ClangSyntaxToken) {
|
if (tok instanceof ClangSyntaxToken) {
|
||||||
addPrimaryHighlightToTokensForParenthesis((ClangSyntaxToken) tok, defaultParenColor);
|
addPrimaryHighlightToTokensForParenthesis((ClangSyntaxToken) tok, defaultParenColor);
|
||||||
addBraceHighlight((ClangSyntaxToken) tok, defaultParenColor);
|
addPrimaryHighlightToTokensForBrace((ClangSyntaxToken) tok, defaultParenColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ package ghidra.app.decompiler.component;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
|
||||||
|
import generic.json.Json;
|
||||||
import ghidra.app.decompiler.CTokenHighlightMatcher;
|
import ghidra.app.decompiler.CTokenHighlightMatcher;
|
||||||
import ghidra.app.decompiler.ClangToken;
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
|
||||||
@ -40,4 +41,9 @@ class NameTokenMatcher implements CTokenHighlightMatcher {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Json.toString(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,6 @@ public class NullClangHighlightController extends ClangHighlightController {
|
|||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPrimaryHighlightedText() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPrimaryHighlights(ClangNode parentNode, ColorProvider colorProvider) {
|
public void addPrimaryHighlights(ClangNode parentNode, ColorProvider colorProvider) {
|
||||||
// stub
|
// stub
|
||||||
@ -51,7 +46,7 @@ public class NullClangHighlightController extends ClangHighlightController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addBraceHighlight(ClangSyntaxToken token, Color highlightColor) {
|
public void addPrimaryHighlightToTokensForBrace(ClangSyntaxToken token, Color highlightColor) {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import generic.theme.Gui;
|
|||||||
*/
|
*/
|
||||||
public class TokenHighlightColors {
|
public class TokenHighlightColors {
|
||||||
|
|
||||||
private Map<String, Color> colorsByName = new HashMap<>();
|
private Map<String, Color> colorsByText = new HashMap<>();
|
||||||
private List<Color> recentColors = new ArrayList<>();
|
private List<Color> recentColors = new ArrayList<>();
|
||||||
|
|
||||||
private Color generateColor() {
|
private Color generateColor() {
|
||||||
@ -42,15 +42,27 @@ public class TokenHighlightColors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Color getColor(String text) {
|
public Color getColor(String text) {
|
||||||
return colorsByName.computeIfAbsent(text, t -> generateColor());
|
return colorsByText.computeIfAbsent(text, t -> generateColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setColor(String text, Color color) {
|
public void setColor(String text, Color color) {
|
||||||
colorsByName.put(text, color);
|
colorsByText.put(text, color);
|
||||||
recentColors.add(color);
|
recentColors.add(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Color> getRecentColors() {
|
public List<Color> getRecentColors() {
|
||||||
return recentColors;
|
return recentColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAppliedColorsString() {
|
||||||
|
if (colorsByText.isEmpty()) {
|
||||||
|
return "No tokens highlighted";
|
||||||
|
}
|
||||||
|
return colorsByText.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getAppliedColorsString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,148 @@
|
|||||||
|
/* ###
|
||||||
|
* 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 org.apache.commons.collections4.map.LazyMap;
|
||||||
|
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.DecompilerHighlighter;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to manage and track Decompiler highlights created by the user via the UI or from a
|
||||||
|
* script. This class manages secondary and global highlights. For a description of these terms,
|
||||||
|
* see {@link ClangHighlightController}.
|
||||||
|
* <p>
|
||||||
|
* These highlights will remain until cleared explicitly by the user or a client API call.
|
||||||
|
* Contrastingly, context highlights are cleared as the user moves the cursor around the Decompiler
|
||||||
|
* display.
|
||||||
|
*/
|
||||||
|
public class UserHighlights {
|
||||||
|
|
||||||
|
private Map<Function, List<DecompilerHighlighter>> secondaryHighlightersByFunction =
|
||||||
|
LazyMap.lazyMap(new HashMap<>(), f -> new ArrayList<>());
|
||||||
|
|
||||||
|
// store the secondary highlighters here in addition to the map below so that we may discern
|
||||||
|
// between secondary highlights and highlight service highlights
|
||||||
|
private Set<DecompilerHighlighter> secondaryHighlighters = new HashSet<>();
|
||||||
|
|
||||||
|
// all highlighters, including secondary and global highlight service highlighters
|
||||||
|
private Map<DecompilerHighlighter, TokenHighlights> allHighlighterHighlights = new HashMap<>();
|
||||||
|
|
||||||
|
// color supplier for secondary highlights
|
||||||
|
private TokenHighlightColors secondaryHighlightColors = new TokenHighlightColors();
|
||||||
|
|
||||||
|
Color getSecondaryColor(String text) {
|
||||||
|
// Note: this call is used to generate colors for secondary highlighters that this API
|
||||||
|
// creates. Client highlighters will create their own colors.
|
||||||
|
return secondaryHighlightColors.getColor(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAppliedColorsString() {
|
||||||
|
return secondaryHighlightColors.getAppliedColorsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasSecondaryHighlights(Function function) {
|
||||||
|
return !secondaryHighlightersByFunction.get(function).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getSecondaryHighlight(ClangToken token) {
|
||||||
|
DecompilerHighlighter highlighter = getSecondaryHighlighter(token);
|
||||||
|
if (highlighter != null) {
|
||||||
|
TokenHighlights highlights = allHighlighterHighlights.get(highlighter);
|
||||||
|
HighlightToken hlToken = highlights.get(token);
|
||||||
|
return hlToken.getColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenHighlightColors getSecondaryHighlightColors() {
|
||||||
|
return secondaryHighlightColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<DecompilerHighlighter> getSecondaryHighlighters(Function function) {
|
||||||
|
return new HashSet<>(secondaryHighlightersByFunction.get(function));
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<DecompilerHighlighter> getGlobalHighlighters() {
|
||||||
|
Set<DecompilerHighlighter> allHighlighters = allHighlighterHighlights.keySet();
|
||||||
|
Set<DecompilerHighlighter> results = new HashSet<>(allHighlighters);
|
||||||
|
results.removeAll(secondaryHighlighters);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<DecompilerHighlighter> getSecondaryHighlightersByFunction(Function f) {
|
||||||
|
return secondaryHighlightersByFunction.get(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenHighlights getHighlights(DecompilerHighlighter highlighter) {
|
||||||
|
return allHighlighterHighlights.get(highlighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
DecompilerHighlighter getSecondaryHighlighter(ClangToken token) {
|
||||||
|
for (DecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||||
|
TokenHighlights highlights = allHighlighterHighlights.get(highlighter);
|
||||||
|
HighlightToken hlToken = highlights.get(token);
|
||||||
|
if (hlToken != null) {
|
||||||
|
return highlighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSecondaryHighlighter(Function function, DecompilerHighlighter highlighter) {
|
||||||
|
|
||||||
|
// Note: this highlighter has likely already been added the the this class, but has not
|
||||||
|
// yet been bound to the given function.
|
||||||
|
secondaryHighlightersByFunction.get(function).add(highlighter);
|
||||||
|
secondaryHighlighters.add(highlighter);
|
||||||
|
allHighlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This adds the given highlighter. This is for global and secondary highlights. Secondary
|
||||||
|
// highlights will be later registered to this class for the function they apply to.
|
||||||
|
TokenHighlights add(DecompilerHighlighter highlighter) {
|
||||||
|
allHighlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
|
||||||
|
return allHighlighterHighlights.get(highlighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(DecompilerHighlighter highlighter) {
|
||||||
|
allHighlighterHighlights.remove(highlighter);
|
||||||
|
secondaryHighlighters.remove(highlighter);
|
||||||
|
|
||||||
|
Collection<List<DecompilerHighlighter>> lists = secondaryHighlightersByFunction.values();
|
||||||
|
for (List<DecompilerHighlighter> highlighters : lists) {
|
||||||
|
if (highlighters.remove(highlighter)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenHighlights get(DecompilerHighlighter highlighter) {
|
||||||
|
return allHighlighterHighlights.get(highlighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
secondaryHighlighters.clear();
|
||||||
|
secondaryHighlightersByFunction.clear();
|
||||||
|
allHighlighterHighlights.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -120,7 +120,7 @@ public class FillOutStructureCmd extends BackgroundCommand<Program> {
|
|||||||
pointerDT = program.getDataTypeManager()
|
pointerDT = program.getDataTypeManager()
|
||||||
.addDataType(pointerDT, DataTypeConflictHandler.DEFAULT_HANDLER);
|
.addDataType(pointerDT, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||||
|
|
||||||
boolean isThisParam = DecompilerUtils.testForAutoParameterThis(var, function);
|
boolean isThisParam = DecompilerUtils.isThisParameter(var, function);
|
||||||
if (!isThisParam) {
|
if (!isThisParam) {
|
||||||
commitVariable(var, pointerDT, isThisParam);
|
commitVariable(var, pointerDT, isThisParam);
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ public class FillOutStructureHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (structDT == null) {
|
if (structDT == null) {
|
||||||
if (createClassIfNeeded && DecompilerUtils.testForAutoParameterThis(var, function)) {
|
if (createClassIfNeeded && DecompilerUtils.isThisParameter(var, function)) {
|
||||||
structDT = createUniqueClassNamespaceAndStructure(var, (int) size, function);
|
structDT = createUniqueClassNamespaceAndStructure(var, (int) size, function);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1016,6 +1016,13 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||||||
new RemoveAllSecondaryHighlightsAction();
|
new RemoveAllSecondaryHighlightsAction();
|
||||||
setGroupInfo(removeAllSecondadryHighlightsAction, highlightGroup, subGroupPosition++);
|
setGroupInfo(removeAllSecondadryHighlightsAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
PreviousHighlightedTokenAction previousHighlightedTokenAction =
|
||||||
|
new PreviousHighlightedTokenAction();
|
||||||
|
setGroupInfo(previousHighlightedTokenAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
|
NextHighlightedTokenAction nextHighlightedTokenAction = new NextHighlightedTokenAction();
|
||||||
|
setGroupInfo(nextHighlightedTokenAction, highlightGroup, subGroupPosition++);
|
||||||
|
|
||||||
String convertGroup = "7 - Convert Group";
|
String convertGroup = "7 - Convert Group";
|
||||||
subGroupPosition = 0;
|
subGroupPosition = 0;
|
||||||
RemoveEquateAction removeEquateAction = new RemoveEquateAction();
|
RemoveEquateAction removeEquateAction = new RemoveEquateAction();
|
||||||
@ -1122,6 +1129,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||||||
addLocalAction(setSecondaryHighlightColorChooserAction);
|
addLocalAction(setSecondaryHighlightColorChooserAction);
|
||||||
addLocalAction(removeSecondaryHighlightAction);
|
addLocalAction(removeSecondaryHighlightAction);
|
||||||
addLocalAction(removeAllSecondadryHighlightsAction);
|
addLocalAction(removeAllSecondadryHighlightsAction);
|
||||||
|
addLocalAction(nextHighlightedTokenAction);
|
||||||
|
addLocalAction(previousHighlightedTokenAction);
|
||||||
addLocalAction(convertBinaryAction);
|
addLocalAction(convertBinaryAction);
|
||||||
addLocalAction(convertDecAction);
|
addLocalAction(convertDecAction);
|
||||||
addLocalAction(convertFloatAction);
|
addLocalAction(convertFloatAction);
|
||||||
@ -1165,7 +1174,14 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||||||
private void setGroupInfo(DockingAction action, String group, int subGroupPosition) {
|
private void setGroupInfo(DockingAction action, String group, int subGroupPosition) {
|
||||||
MenuData popupMenuData = action.getPopupMenuData();
|
MenuData popupMenuData = action.getPopupMenuData();
|
||||||
popupMenuData.setMenuGroup(group);
|
popupMenuData.setMenuGroup(group);
|
||||||
popupMenuData.setMenuSubGroup(Integer.toString(subGroupPosition));
|
|
||||||
|
// Some groups have numbers reach double-digits. These will not compare correctly unless
|
||||||
|
// padded. Ensure all string numbers are at least 2 digits.
|
||||||
|
String numberString = Integer.toString(subGroupPosition);
|
||||||
|
if (numberString.length() == 1) {
|
||||||
|
numberString = '0' + numberString;
|
||||||
|
}
|
||||||
|
popupMenuData.setMenuSubGroup(numberString);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void graphServiceRemoved() {
|
private void graphServiceRemoved() {
|
||||||
|
@ -61,7 +61,7 @@ public class DecompilerStructureVariableAction extends CreateStructureVariableAc
|
|||||||
HighVariable var = tokenAtCursor.getHighVariable();
|
HighVariable var = tokenAtCursor.getHighVariable();
|
||||||
if (var != null && !(var instanceof HighConstant)) {
|
if (var != null && !(var instanceof HighConstant)) {
|
||||||
dt = var.getDataType();
|
dt = var.getDataType();
|
||||||
isThisParam = DecompilerUtils.testForAutoParameterThis(var, function);
|
isThisParam = DecompilerUtils.isThisParameter(var, function);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dt == null || dt.getLength() > maxPointerSize) {
|
if (dt == null || dt.getLength() > maxPointerSize) {
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import docking.action.KeyBindingData;
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.TokenIterator;
|
||||||
|
import ghidra.app.decompiler.component.*;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.app.util.HelpTopics;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action to navigate to the next token highlighted by the user via the middle-mouse.
|
||||||
|
*/
|
||||||
|
public class NextHighlightedTokenAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
|
public NextHighlightedTokenAction() {
|
||||||
|
super("Next Highlihted Token");
|
||||||
|
|
||||||
|
setPopupMenuData(new MenuData(new String[] { "Next Highlight" }, "Decompile"));
|
||||||
|
setKeyBindingData(new KeyBindingData("Ctrl period"));
|
||||||
|
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "GoToMiddleMouseHighlight"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
|
if (!context.hasRealFunction()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
TokenHighlights highlights = panel.getMiddleMouseHighlights();
|
||||||
|
if (highlights != null) {
|
||||||
|
return highlights.size() > 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||||
|
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
TokenHighlights highlights = panel.getMiddleMouseHighlights();
|
||||||
|
ClangToken cursorToken = context.getTokenAtCursor();
|
||||||
|
TokenIterator it = new TokenIterator(cursorToken, true);
|
||||||
|
it.next(); // ignore the current token
|
||||||
|
|
||||||
|
if (goToNexToken(panel, it, highlights)) {
|
||||||
|
return; // found another token in the current direction
|
||||||
|
}
|
||||||
|
|
||||||
|
// this means there are no more occurrences in the current direction; wrap the search
|
||||||
|
ClangToken firstToken = getFirstToken(panel);
|
||||||
|
it = new TokenIterator(firstToken, true);
|
||||||
|
goToNexToken(panel, it, highlights);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClangToken getFirstToken(DecompilerPanel panel) {
|
||||||
|
List<Field> fields = panel.getFields();
|
||||||
|
Field line = fields.get(0);
|
||||||
|
ClangTextField tf = (ClangTextField) line;
|
||||||
|
return tf.getFirstToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean goToNexToken(DecompilerPanel panel, TokenIterator it,
|
||||||
|
TokenHighlights highlights) {
|
||||||
|
|
||||||
|
while (it.hasNext()) {
|
||||||
|
ClangToken nextToken = it.next();
|
||||||
|
HighlightToken hlToken = highlights.get(nextToken);
|
||||||
|
if (hlToken == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClangToken token = hlToken.getToken();
|
||||||
|
panel.goToToken(token);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.decompile.actions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import docking.action.KeyBindingData;
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import docking.widgets.fieldpanel.field.Field;
|
||||||
|
import ghidra.app.decompiler.ClangToken;
|
||||||
|
import ghidra.app.decompiler.TokenIterator;
|
||||||
|
import ghidra.app.decompiler.component.*;
|
||||||
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
|
import ghidra.app.util.HelpTopics;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action to navigate to the previous token highlighted by the user via the middle-mouse.
|
||||||
|
*/
|
||||||
|
public class PreviousHighlightedTokenAction extends AbstractDecompilerAction {
|
||||||
|
|
||||||
|
public PreviousHighlightedTokenAction() {
|
||||||
|
super("Previous Highlighted Token");
|
||||||
|
|
||||||
|
setPopupMenuData(new MenuData(new String[] { "Previous Highlight" }, "Decompile"));
|
||||||
|
setKeyBindingData(new KeyBindingData("Ctrl comma"));
|
||||||
|
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "GoToMiddleMouseHighlight"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||||
|
if (!context.hasRealFunction()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
TokenHighlights highlights = panel.getMiddleMouseHighlights();
|
||||||
|
if (highlights != null) {
|
||||||
|
return highlights.size() > 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||||
|
|
||||||
|
DecompilerPanel panel = context.getDecompilerPanel();
|
||||||
|
TokenHighlights highlights = panel.getMiddleMouseHighlights();
|
||||||
|
ClangToken cursorToken = context.getTokenAtCursor();
|
||||||
|
TokenIterator it = new TokenIterator(cursorToken, false);
|
||||||
|
it.next(); // ignore the current token
|
||||||
|
|
||||||
|
if (goToNexToken(panel, it, highlights)) {
|
||||||
|
return; // found another token in the current direction
|
||||||
|
}
|
||||||
|
|
||||||
|
// this means there are no more occurrences in the current direction; wrap the search
|
||||||
|
ClangToken lastToken = getLastToken(panel);
|
||||||
|
it = new TokenIterator(lastToken, false);
|
||||||
|
goToNexToken(panel, it, highlights);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClangToken getLastToken(DecompilerPanel panel) {
|
||||||
|
List<Field> fields = panel.getFields();
|
||||||
|
int lastLine = fields.size();
|
||||||
|
Field line = fields.get(lastLine - 1);
|
||||||
|
ClangTextField tf = (ClangTextField) line;
|
||||||
|
return tf.getLastToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean goToNexToken(DecompilerPanel panel, TokenIterator it,
|
||||||
|
TokenHighlights highlights) {
|
||||||
|
|
||||||
|
while (it.hasNext()) {
|
||||||
|
ClangToken nextToken = it.next();
|
||||||
|
HighlightToken hlToken = highlights.get(nextToken);
|
||||||
|
if (hlToken == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClangToken token = hlToken.getToken();
|
||||||
|
panel.goToToken(token);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -70,4 +70,9 @@ public class SliceHighlightColorProvider implements ColorProvider {
|
|||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Slice Color Provider " + hlColor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ import ghidra.app.plugin.core.decompile.actions.*;
|
|||||||
import ghidra.app.util.AddEditDialog;
|
import ghidra.app.util.AddEditDialog;
|
||||||
import ghidra.framework.options.ToolOptions;
|
import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class DecompilerClangTest extends AbstractDecompilerTest {
|
public class DecompilerClangTest extends AbstractDecompilerTest {
|
||||||
|
|
||||||
@ -383,29 +384,32 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||||||
setDecompilerLocation(line, charPosition);
|
setDecompilerLocation(line, charPosition);
|
||||||
|
|
||||||
ClangToken token = getToken();
|
ClangToken token = getToken();
|
||||||
String text = token.getText();
|
String printfText = token.getText();
|
||||||
assertEquals("_printf", text);
|
assertEquals("_printf", printfText);
|
||||||
|
|
||||||
highlight();
|
highlight(); // "printf" is secondary highlighted
|
||||||
|
|
||||||
// 5:30 "a->name"
|
// 5:30 "a->name"
|
||||||
line = 5;
|
line = 5;
|
||||||
charPosition = 38;
|
charPosition = 38;
|
||||||
setDecompilerLocation(line, charPosition);
|
setDecompilerLocation(line, charPosition);
|
||||||
ClangToken token2 = getToken();
|
ClangToken token2 = getToken();
|
||||||
String text2 = token2.getText();
|
String nameText = token2.getText();
|
||||||
assertEquals("name", text2);
|
assertEquals("name", nameText);
|
||||||
|
|
||||||
Color color2 = highlight();
|
Color color2 = highlight(); // "name" is secondary highlighted
|
||||||
|
|
||||||
// 5:2 "_printf"
|
// 5:2 "_printf"
|
||||||
line = 5;
|
line = 5;
|
||||||
charPosition = 2;
|
charPosition = 2;
|
||||||
setDecompilerLocation(line, charPosition);
|
setDecompilerLocation(line, charPosition);
|
||||||
removeSecondaryHighlight();
|
|
||||||
|
|
||||||
assertNoFieldsSecondaryHighlighted(text);
|
Msg.debug(this, "test - remove");
|
||||||
assertAllFieldsHighlighted(text2, color2);
|
|
||||||
|
removeSecondaryHighlight(); // remove "printf" highlight
|
||||||
|
|
||||||
|
assertNoFieldsSecondaryHighlighted(printfText);
|
||||||
|
assertAllFieldsHighlighted(nameText, color2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -812,7 +816,106 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSecondaryHighlighting_MiddleMouseDoesNotClearSecondaryHighlight() {
|
public void testSecondaryHighlighting_MiddleMouse_SecondaryHighlight() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
The middle mouse is a secondary highlight so that it persists as the user clicks around.
|
||||||
|
|
||||||
|
Middle mousing on an already middle moused highlight should clear the highlight. Middle
|
||||||
|
mousing on a new token should clear the original highlight and highlight the new token.
|
||||||
|
|
||||||
|
Decomp of '_call_structure_A':
|
||||||
|
|
||||||
|
1|
|
||||||
|
2| void _call_structure_A(A *a)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| _printf("call_structure_A: %s\n",a->name);
|
||||||
|
6| _printf("call_structure_A: %s\n",(a->b).name);
|
||||||
|
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| }
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
decompile("100000d60"); // '_call_structure_A'
|
||||||
|
|
||||||
|
// 5:2 "_printf"
|
||||||
|
int line = 5;
|
||||||
|
int charPosition = 2;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
|
||||||
|
ClangToken token = getToken();
|
||||||
|
String tokenText = token.getText();
|
||||||
|
assertEquals("_printf", tokenText);
|
||||||
|
|
||||||
|
middleMouse();
|
||||||
|
assertCombinedHighlightColor(token);
|
||||||
|
|
||||||
|
middleMouse();
|
||||||
|
assertNoFieldsSecondaryHighlighted(tokenText);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecondaryHighlighting_MiddleMouse_SecondaryHighlight_NewToken() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
The middle mouse is a secondary highlight so that it persists as the user clicks around.
|
||||||
|
|
||||||
|
Middle mousing on an already middle moused highlight should clear the highlight. Middle
|
||||||
|
mousing on a new token should clear the original highlight and highlight the new token.
|
||||||
|
|
||||||
|
Decomp of '_call_structure_A':
|
||||||
|
|
||||||
|
1|
|
||||||
|
2| void _call_structure_A(A *a)
|
||||||
|
3|
|
||||||
|
4| {
|
||||||
|
5| _printf("call_structure_A: %s\n",a->name);
|
||||||
|
6| _printf("call_structure_A: %s\n",(a->b).name);
|
||||||
|
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| }
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
decompile("100000d60"); // '_call_structure_A'
|
||||||
|
|
||||||
|
// 5:2 "_printf"
|
||||||
|
int line = 5;
|
||||||
|
int charPosition = 2;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
|
||||||
|
ClangToken token = getToken();
|
||||||
|
String tokenText = token.getText();
|
||||||
|
assertEquals("_printf", tokenText);
|
||||||
|
|
||||||
|
middleMouse();
|
||||||
|
assertCombinedHighlightColor(token);
|
||||||
|
|
||||||
|
// 5:30 "a->name"
|
||||||
|
line = 5;
|
||||||
|
charPosition = 38;
|
||||||
|
setDecompilerLocation(line, charPosition);
|
||||||
|
ClangToken token2 = getToken();
|
||||||
|
String text2 = token2.getText();
|
||||||
|
assertEquals("name", text2);
|
||||||
|
|
||||||
|
middleMouse();
|
||||||
|
assertNoFieldsSecondaryHighlighted(tokenText);
|
||||||
|
assertCombinedHighlightColor(token2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecondaryHighlighting_MiddleMouseDoesNotClearSecondaryHighlight_ExistingHighlight() {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@ -850,7 +953,9 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||||||
assertCombinedHighlightColor(token);
|
assertCombinedHighlightColor(token);
|
||||||
|
|
||||||
middleMouse();
|
middleMouse();
|
||||||
assertAllFieldsHighlighted(tokenText, color);
|
ClangToken cursorToken = getToken(provider);
|
||||||
|
Predicate<ClangToken> ignores = t -> t == cursorToken;
|
||||||
|
assertAllFieldsHighlighted(tokenText, color, ignores);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user