Merge remote-tracking branch

'origin/GP-1435-dragonmacher-decompiler-highlighter-service--SQUASHED'
(Closes #2313)
This commit is contained in:
Ryan Kurtz 2021-11-17 12:59:37 -05:00
commit 8d8d32262d
34 changed files with 2014 additions and 527 deletions

View File

@ -0,0 +1,45 @@
/* ###
* 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;
import java.awt.Color;
/**
* The interface that clients must define to create a {@link DecompilerHighlighter}
*
* <p>Every function decompiled will trigger this matcher to get called. The order of method
* calls is: {@link #start(ClangNode)}, repeated calls to {@link #getTokenHighlight(ClangToken)}
* and then {@link #end()}.
*
* @see DecompilerHighlightService
*/
public interface CTokenHighlightMatcher {
public default void start(ClangNode root) {
// stub; provided for clients that may wish to work from the root
}
public default void end() {
// stub; provided for clients that may wish to perform cleanup when highlighting is finished
}
/**
* The basic method clients must implement to determine if a token should be highlighted.
* Returning a non-null Color will trigger the given token to be highlighted.
* @param token the token
* @return the highlight color or null
*/
public Color getTokenHighlight(ClangToken token);
}

View File

@ -251,7 +251,7 @@ public class ClangToken implements ClangNode {
/**
* Get the high-level variable associate with this
* token or null otherwise
* @return HighVariable
* @return HighVariable
*/
public HighVariable getHighVariable() {
if (Parent() instanceof ClangVariableDecl) {

View File

@ -0,0 +1,54 @@
/* ###
* 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;
/**
* A service that allows clients to create highlights in the form of background colors for
* {@link ClangToken}s in the Decompiler UI.
*
* <p>Note: highlights apply to a full token and not strings of text. To highlight a token, you
* create an instance of the {@link CTokenHighlightMatcher} to pass to one of the
* {@link #createHighlighter(String, CTokenHighlightMatcher)} methods of this interface.
*
* <p>There is no limit to the number of highlighters that may be installed. If multiple
* highlights overlap, then their colors will be blended.
*/
public interface DecompilerHighlightService {
/**
* Creates a highlighter that will use the given matcher to create highlights as functions
* get decompiled.
* @param tm the matcher
* @return the new highlighter
*/
public DecompilerHighlighter createHighlighter(CTokenHighlightMatcher tm);
/**
* A version of {@link #createHighlighter(String, CTokenHighlightMatcher)} that allows clients
* to specify an ID. This ID will be used to ensure that any existing highlighters with that
* ID will be removed before creating a new highlighter.
*
* <p>This method is convenient for scripts, since a script cannot hold on to any created
* highlighters between repeated script executions. A good value for script writers to use
* is the name of their script class.
*
* @param id the ID
* @param tm the matcher
* @return the new highlighter
*/
public DecompilerHighlighter createHighlighter(String id, CTokenHighlightMatcher tm);
}

View File

@ -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;
/**
* The highlighter interface passed to clients of the {@link DecompilerHighlightService}.
*
* <p>The expected workflow for this class is: create the highlighter, clients
* will request highlights via {@link #applyHighlights()}, clients will clear highlights via
* {@link #clearHighlights()} and the highlighter may be removed via {@link #dispose()}.
*/
public interface DecompilerHighlighter {
/**
* Call this method when you wish to apply your highlights.
*/
public void applyHighlights();
/**
* Call this method when you wish to remove your highlights.
*/
public void clearHighlights();
/**
* Call this method to remove your highlighter from the Decompiler.
*/
public void dispose();
/**
* Returns the ID used by this highlighter. This will either be generated by this API or
* supplied by the client.
* @return the ID
* @see DecompilerHighlightService#createHighlighter(String, CTokenHighlightMatcher)
*/
public String getId();
}

View File

@ -0,0 +1,161 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.decompiler.component;
import java.awt.Color;
import java.util.*;
import java.util.function.Supplier;
import ghidra.app.decompiler.*;
/**
* The implementation of {@link DecompilerHighlighter}. This will get created by the
* Decompiler and then handed to clients that use the {@link DecompilerHighlightService}. This
* is also used internally for 'secondary highlights'.
*
* <p>This class may be {@link #clone() cloned} or {@link #copy(DecompilerPanel) copied} as
* needed when the user creates a snapshot. Highlight service highlighters will be cloned;
* secondary highlighters will be copied. Cloning allows this class to delegate highlighting
* and cleanup for clones. Contrastingly, copying allows the secondary highlights to operate
* independently.
*/
class ClangDecompilerHighlighter implements DecompilerHighlighter {
protected String id;
private DecompilerPanel decompilerPanel;
private CTokenHighlightMatcher matcher;
private Set<ClangDecompilerHighlighter> clones = new HashSet<>();
ClangDecompilerHighlighter(DecompilerPanel panel, CTokenHighlightMatcher matcher) {
UUID uuId = UUID.randomUUID();
this.id = uuId.toString();
this.decompilerPanel = panel;
this.matcher = matcher;
}
ClangDecompilerHighlighter(String id, DecompilerPanel panel, CTokenHighlightMatcher matcher) {
this.id = id;
this.decompilerPanel = panel;
this.matcher = matcher;
}
/**
* Create a clone of this highlighter and tracks the clone
* @param panel the panel
* @return the highlighter
*/
ClangDecompilerHighlighter clone(DecompilerPanel panel) {
// note: we re-use the ID to make tracking easier
ClangDecompilerHighlighter clone = new ClangDecompilerHighlighter(id, panel, matcher);
clones.add(clone);
return clone;
}
/**
* Creates a copy of this highlighter that is not tracked by this highlighter
* @param panel the panel
* @return the highlighter
*/
ClangDecompilerHighlighter copy(DecompilerPanel panel) {
return new ClangDecompilerHighlighter(panel, matcher);
}
@Override
public void applyHighlights() {
if (decompilerPanel == null) {
return; // disposed
}
clearHighlights();
ClangLayoutController layoutModel =
(ClangLayoutController) decompilerPanel.getLayoutModel();
ClangTokenGroup root = layoutModel.getRoot();
Map<ClangToken, Color> highlights = new HashMap<>();
try {
matcher.start(root);
gatherHighlights(root, highlights);
}
finally {
matcher.end();
}
Supplier<? extends Collection<ClangToken>> tokens = () -> highlights.keySet();
ColorProvider colorProvider = t -> highlights.get(t);
decompilerPanel.addHighlighterHighlights(this, tokens, colorProvider);
clones.forEach(c -> c.applyHighlights());
}
private void gatherHighlights(ClangTokenGroup root, Map<ClangToken, Color> results) {
int n = root.numChildren();
for (int i = 0; i < n; ++i) {
ClangNode child = root.Child(i);
getHighlight(child, results);
if (child instanceof ClangTokenGroup) {
gatherHighlights(((ClangTokenGroup) child), results);
}
}
}
private void getHighlight(ClangNode node, Map<ClangToken, Color> results) {
if (node instanceof ClangTokenGroup) {
return;
}
ClangToken token = (ClangToken) node;
Color color = matcher.getTokenHighlight(token);
if (color != null) {
results.put(token, color);
}
}
@Override
public void clearHighlights() {
if (decompilerPanel == null) {
return; // disposed
}
decompilerPanel.removeHighlighterHighlights(this);
clones.forEach(c -> c.clearHighlights());
}
@Override
public void dispose() {
if (decompilerPanel == null) {
return; // disposed
}
clearHighlights();
decompilerPanel.removeHighlighter(id);
decompilerPanel = null;
clones.forEach(c -> c.dispose());
}
@Override
public String getId() {
return id;
}
@Override
public String toString() {
return super.toString() + ' ' + id;
}
}

View File

@ -17,14 +17,16 @@ package ghidra.app.decompiler.component;
import java.awt.Color;
import java.util.*;
import java.util.function.Function;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.apache.commons.collections4.map.LazyMap;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.util.ColorUtils;
import util.CollectionUtils;
@ -33,17 +35,30 @@ import util.CollectionUtils;
* Class to handle highlights for a decompiled function.
*
* <p>This class does not painting directly. Rather, this class tracks the currently highlighted
* tokens and then sets the highlight color on the token when it is highlighted and clears the
* tokens and then sets the highlight color on the token when it is highlighted and clears the
* highlight color when the highlight is removed.
*
* <p>This class maintains the notion of 'primary' highlights and 'secondary' highlights.
* Primary highlights are considered transient and get cleared whenever the location changes.
* Secondary highlights will stay until they are manually cleared by a user action. Primary
* highlights happen when the user clicks around the Decompiler. They show state such as the
* current field, impact of a variable (via a slicing action), or related syntax (such as
* matching braces). Secondary highlights are triggered by the user to show all occurrences of
* a particular variable. Further, the user can apply multiple secondary highlights at the
* same time, with different colors for each highlight.
* <p>This class maintains the following types of highlights:
* <UL>
* <LI>Primary 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
* current field, impact of a variable (via a slicing action), or related syntax (such as
* matching braces)
* </LI>
* <LI>Secondary Highlights - triggered by the user to show all occurrences of a particular
* variable; they will stay until they are manually cleared by a user action. The user can \
* apply multiple secondary highlights at the same time, with different colors for each
* highlight.
* <B>These highlights apply to the function in use when the highlight is created. Thus,
* each function has a unique set of highlights that is maintained between decompilation.</B>
* </LI>
* <LI>Global Highlights - triggered by clients of the {@link DecompilerHighlightService}; they
* will stay until the client of the service clears the highlight.
* <B>These highlights apply to every function that is decompiler.</B>
* </LI>
* </UL>
*
* <p>When multiple highlights overlap, their colors will be blended.
*/
public abstract class ClangHighlightController {
@ -56,14 +71,23 @@ public abstract class ClangHighlightController {
return c;
}
// Note: Most of the methods in this class were extracted from the ClangLayoutController class
// and the DecompilerPanel class.
protected Color defaultHighlightColor = DEFAULT_HIGHLIGHT_COLOR;
protected Color defaultParenColor = DEFAULT_HIGHLIGHT_COLOR;
private TokenHighlights primaryHighlightTokens = new TokenHighlights();
private TokenHighlights secondaryHighlightTokens = new TokenHighlights();
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();
/**
@ -80,7 +104,22 @@ public abstract class ClangHighlightController {
defaultHighlightColor = c;
}
public String getHighlightedText() {
/**
* Returns the color provider used by this class to generate colors. The initial color
* selection is random. Repeated calls to get a color for the same token will return the same
* color.
* @return the color provider
*/
public ColorProvider getRandomColorProvider() {
return token -> secondaryHighlightColors.getColor(token.getText());
}
/**
* 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();
@ -88,20 +127,78 @@ public abstract class ClangHighlightController {
return null;
}
/**
* An value that is updated every time a new highlight is added. This allows clients to
* determine if a buffered update request is still valid.
* @return the value
*/
public long getUpdateId() {
return updateId;
}
public boolean hasPrimaryHighlight(ClangToken token) {
return primaryHighlightTokens.contains(token);
}
public boolean hasSecondaryHighlight(ClangToken token) {
return getSecondaryHighlight(token) != null;
}
public boolean hasSecondaryHighlights() {
return !secondaryHighlighters.isEmpty();
}
public Color getSecondaryHighlight(ClangToken token) {
DecompilerHighlighter highlighter = getSecondaryHighlighter(token);
if (highlighter != null) {
TokenHighlights highlights = highlighterHighlights.get(highlighter);
HighlightToken hlToken = highlights.get(token);
return hlToken.getColor();
}
return null;
}
public TokenHighlightColors getSecondaryHighlightColors() {
return secondaryHighlightColors;
}
public TokenHighlights getPrimaryHighlightedTokens() {
public TokenHighlights getPrimaryHighlights() {
return primaryHighlightTokens;
}
public TokenHighlights getSecondaryHighlightedTokens() {
return secondaryHighlightTokens;
/**
* Returns all secondary highlighters for the given function. This allows clients to update
* the secondary highlight state of a given function without affecting highlights applied to
* other functions.
* @param function the function
* @return the highlighters
*/
public Set<ClangDecompilerHighlighter> getSecondaryHighlightersByFunction(Function function) {
return new HashSet<>(secondaryHighlightersbyFunction.get(function));
}
/**
* Returns all global highlighters installed in this controller. The global highlighters apply
* to all functions. This is in contrast to secondary highlighters, which are
* function-specific.
* @return the highlighters
*/
public Set<ClangDecompilerHighlighter> getGlobalHighlighters() {
Set<ClangDecompilerHighlighter> allHighlighters = highlighterHighlights.keySet();
Set<ClangDecompilerHighlighter> results = new HashSet<>(allHighlighters);
results.removeAll(secondaryHighlighters);
return results;
}
/**
* Gets all highlights for the given highlighter.
* @param highlighter the highlighter
* @return the highlights
* @see #getPrimaryHighlights()
*/
public TokenHighlights getHighlighterHighlights(DecompilerHighlighter highlighter) {
return highlighterHighlights.get(highlighter);
}
/**
@ -131,28 +228,36 @@ public abstract class ClangHighlightController {
}
public void clearPrimaryHighlights() {
doClearHighlights(primaryHighlightTokens);
Consumer<ClangToken> clearAll = token -> {
token.setMatchingToken(false);
updateHighlightColor(token);
};
doClearHighlights(primaryHighlightTokens, clearAll);
notifyListeners();
}
public void clearAllHighlights() {
doClearHighlights(primaryHighlightTokens);
doClearHighlights(secondaryHighlightTokens);
notifyListeners();
}
private void doClearHighlights(TokenHighlights tokenHighlights) {
private void doClearHighlights(TokenHighlights tokenHighlights, Consumer<ClangToken> clearer) {
Iterator<HighlightToken> it = tokenHighlights.iterator();
while (it.hasNext()) {
HighlightToken highlight = it.next();
// must remove the highlight before calling the clearer as that may call back into the
// TokenHighlights we are clearing
it.remove();
ClangToken token = highlight.getToken();
token.setMatchingToken(false);
updateHighlightColor(token);
clearer.accept(token);
}
tokenHighlights.clear();
}
/**
* Toggles the primary highlight state of the given set of tokens. If the given tokens do not
* all have the same highlight state (highlights on or off), then the highlights will be
* cleared. If all tokens are not highlighted, then they will all become highlighted.
*
* @param hlColor the highlight color
* @param tokens the tokens
*/
public void togglePrimaryHighlights(Color hlColor, Supplier<List<ClangToken>> tokens) {
boolean isAllHighlighted = true;
@ -175,67 +280,105 @@ public abstract class ClangHighlightController {
addPrimaryHighlights(tokens, hlColor);
}
public boolean hasPrimaryHighlight(ClangToken token) {
return primaryHighlightTokens.contains(token);
}
public boolean hasSecondaryHighlight(ClangToken token) {
return secondaryHighlightTokens.contains(token);
}
public Set<HighlightToken> getSecondaryHighlightsByFunction(
ghidra.program.model.listing.Function f) {
Set<HighlightToken> highlights = secondaryHighlightTokens.getHighlightsByFunction(f);
return highlights;
}
public void removeSecondaryHighlights(ghidra.program.model.listing.Function f) {
Set<HighlightToken> oldHighlights = secondaryHighlightTokens.removeHighlightsByFunction(f);
for (HighlightToken hl : oldHighlights) {
ClangToken token = hl.getToken();
updateHighlightColor(token);
/**
* Removes all secondary highlights for the given function
* @param f the function
*/
public void removeSecondaryHighlights(Function f) {
List<ClangDecompilerHighlighter> highlighters = secondaryHighlightersbyFunction.get(f);
for (ClangDecompilerHighlighter highlighter : highlighters) {
TokenHighlights highlights = highlighterHighlights.get(highlighter);
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token);
doClearHighlights(highlights, clearHighlight);
}
notifyListeners();
}
/**
* Removes all secondary highlights for the given token
* @param token the token
* @see #removeSecondaryHighlights(Function)
*/
public void removeSecondaryHighlights(ClangToken token) {
secondaryHighlightTokens.remove(token);
}
public void removeSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens) {
for (ClangToken clangToken : tokens.get()) {
secondaryHighlightTokens.remove(clangToken);
updateHighlightColor(clangToken);
DecompilerHighlighter highlighter = getSecondaryHighlighter(token);
if (highlighter != null) {
highlighter.dispose(); // this will call removeHighlighterHighlights()
}
notifyListeners();
}
public void addSecondaryHighlights(String tokenText,
Supplier<? extends Collection<ClangToken>> tokens) {
Color highlightColor = secondaryHighlightColors.getColor(tokenText);
addSecondaryHighlights(tokens, highlightColor);
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 addSecondaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
Color hlColor) {
Function<ClangToken, Color> colorProvider = token -> hlColor;
addTokensToHighlights(tokens.get(), colorProvider, secondaryHighlightTokens);
public void removeHighlighter(DecompilerHighlighter highlighter) {
removeHighlighterHighlights(highlighter);
secondaryHighlighters.remove(highlighter);
highlighterHighlights.remove(highlighter);
}
public void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
/**
* Removes all highlights for this highlighter across all functions
* @param highlighter the highlighter
*/
public void removeHighlighterHighlights(DecompilerHighlighter highlighter) {
TokenHighlights highlighterTokens = highlighterHighlights.get(highlighter);
if (highlighterTokens == null) {
return;
}
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token);
doClearHighlights(highlighterTokens, clearHighlight);
notifyListeners();
}
/**
* Adds the given secondary highlighter, but does not create any highlights. All secondary
* highlighters pertain to a given function.
* @param function the function
* @param highlighter the highlighter
*/
public void addSecondaryHighlighter(Function function, ClangDecompilerHighlighter 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());
}
/**
* Adds the given highlighter, but does not create any highlights
* @param highlighter the highlighter
*/
public void addHighlighter(ClangDecompilerHighlighter highlighter) {
highlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
}
public void addHighlighterHighlights(ClangDecompilerHighlighter highlighter,
Supplier<? extends Collection<ClangToken>> tokens,
ColorProvider colorProvider) {
Objects.requireNonNull(highlighter);
TokenHighlights highlighterTokens =
highlighterHighlights.computeIfAbsent(highlighter, k -> new TokenHighlights());
addTokensToHighlights(tokens.get(), colorProvider, highlighterTokens);
}
private void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
Color hlColor) {
Function<ClangToken, Color> colorProvider = token -> hlColor;
ColorProvider colorProvider = token -> hlColor;
addTokensToHighlights(tokens.get(), colorProvider, primaryHighlightTokens);
}
public void addPrimaryHighlights(ClangNode parentNode,
TokenHighlightColorProvider colorProvider) {
Set<ClangToken> tokens = new HashSet<>();
gatherAllTokens(parentNode, tokens);
addTokensToHighlights(tokens, colorProvider::getColor, primaryHighlightTokens);
}
public void addPrimaryHighlights(ClangNode parentNode, Set<PcodeOp> ops, Color hlColor) {
addPrimaryHighlights(parentNode, token -> {
@ -244,18 +387,25 @@ public abstract class ClangHighlightController {
});
}
public void addPrimaryHighlights(ClangNode parentNode, ColorProvider colorProvider) {
Set<ClangToken> tokens = new HashSet<>();
gatherAllTokens(parentNode, tokens);
addTokensToHighlights(tokens, colorProvider::getColor, primaryHighlightTokens);
}
private void addPrimaryHighlights(Collection<ClangToken> tokens, Color hlColor) {
Function<ClangToken, Color> colorProvider = token -> hlColor;
ColorProvider colorProvider = token -> hlColor;
addTokensToHighlights(tokens, colorProvider, primaryHighlightTokens);
}
private void addTokensToHighlights(Collection<ClangToken> tokens,
Function<ClangToken, Color> colorProvider, TokenHighlights currentHighlights) {
ColorProvider colorProvider, TokenHighlights currentHighlights) {
updateId++;
for (ClangToken clangToken : tokens) {
Color color = colorProvider.apply(clangToken);
Color color = colorProvider.getColor(clangToken);
doAddHighlight(clangToken, color, currentHighlights);
}
notifyListeners();
@ -283,25 +433,79 @@ public abstract class ClangHighlightController {
t.setHighlight(combinedColor);
}
private void add(List<Color> colors, HighlightToken hlToken) {
if (hlToken != null) {
colors.add(hlToken.getColor());
}
}
private void add(List<Color> colors, Color c) {
if (c != null) {
colors.add(c);
}
}
/**
* Returns the current highlight color for the given token, based upon all known highlights,
* primary, secondary and highlighters
* @param t the token
* @return the color
*/
public Color getCombinedColor(ClangToken t) {
// note: not sure whether we should always blend all colors or decide to allow some
// highlighters have precedence for highlighting
HighlightToken primaryHl = primaryHighlightTokens.get(t);
HighlightToken secondaryHl = secondaryHighlightTokens.get(t);
Color primary = primaryHl == null ? null : primaryHl.getColor();
Color secondary = secondaryHl == null ? null : secondaryHl.getColor();
Color blendedHlColor = blendHighlighterColors(t);
if (primary == null) {
if (secondary == null) {
return null;
List<Color> allColors = new ArrayList<>();
add(allColors, primaryHl);
add(allColors, blendedHlColor);
Color blended = blend(allColors);
return blended;
}
public Color blend(List<Color> colors) {
if (colors.isEmpty()) {
return null;
}
if (colors.size() == 1) {
return CollectionUtils.any(colors);
}
Color lastColor = colors.get(0);
for (int i = 1; i < colors.size(); i++) {
Color nextColor = colors.get(i);
lastColor = ColorUtils.blend(lastColor, nextColor, .8f);
}
return lastColor;
}
private Color blendHighlighterColors(ClangToken token) {
Color lastColor = null;
Collection<TokenHighlights> allHighlights = highlighterHighlights.values();
for (TokenHighlights highlights : allHighlights) {
HighlightToken hlToken = highlights.get(token);
if (hlToken == null) {
continue;
}
Color nextColor = hlToken.getColor();
if (lastColor != null) {
lastColor = ColorUtils.blend(lastColor, nextColor, .8f);
}
else {
lastColor = nextColor;
}
return secondary;
}
if (secondary == null) {
return primary;
}
return ColorUtils.blend(primary, secondary, .8f);
return lastColor;
}
/**
@ -372,7 +576,7 @@ public abstract class ClangHighlightController {
return results;
}
public void addHighlightBrace(ClangSyntaxToken token, Color highlightColor) {
public void addBraceHighlight(ClangSyntaxToken token, Color highlightColor) {
if (DecompilerUtils.isBrace(token)) {
highlightBrace(token, highlightColor);
@ -402,4 +606,12 @@ public abstract class ClangHighlightController {
listener.tokenHighlightsChanged();
}
}
public void dispose() {
listeners.clear();
primaryHighlightTokens.clear();
secondaryHighlighters.clear();
secondaryHighlightersbyFunction.clear();
highlighterHighlights.clear();
}
}

View File

@ -13,16 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.decompile.actions;
package ghidra.app.decompiler.component;
import java.awt.Color;
import java.util.function.Function;
import ghidra.app.decompiler.ClangToken;
/**
* Provides highlight color for the given token
* Functional interface to allow us to map a token to a color.
*
* <p>This class allows us to avoid the namespace conflicts of Java's Function and Ghidra's
* Function since we can declare a {@code ColorProvider} as a parameter to methods instead of
* a {@link Function}.
*/
public interface TokenHighlightColorProvider {
public interface ColorProvider {
/**
* Returns a color for the given token

View File

@ -65,6 +65,7 @@ public class DecompilerController {
//==================================================================================================
// Methods call by the provider
//==================================================================================================
/**
* Called by the provider when the provider is disposed. Once dispose is called, it should
* never be used again.

View File

@ -20,9 +20,7 @@ import java.awt.event.MouseEvent;
import java.math.BigInteger;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.swing.JComponent;
import javax.swing.JPanel;
@ -43,7 +41,6 @@ import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
@ -53,7 +50,6 @@ import ghidra.program.util.ProgramSelection;
import ghidra.util.*;
import ghidra.util.bean.field.AnnotatedTextFieldElement;
import ghidra.util.task.SwingUpdateManager;
import util.CollectionUtils;
/**
* Class to handle the display of a decompiled function
@ -74,6 +70,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
private HighlightFactory hlFactory;
private ClangHighlightController highlightController;
private Map<String, ClangDecompilerHighlighter> highlightersById = new HashMap<>();
private PendingHighlightUpdate pendingHighlightUpdate;
private SwingUpdateManager highlighCursorUpdater = new SwingUpdateManager(() -> {
if (pendingHighlightUpdate != null) {
@ -144,58 +141,66 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
return fieldPanel;
}
public void applySecondaryHighlights(Map<String, Color> highlightsByName) {
Set<Entry<String, Color>> entries = highlightsByName.entrySet();
for (Entry<String, Color> entry : entries) {
String tokenName = entry.getKey();
Color color = entry.getValue();
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenName);
highlightController.addSecondaryHighlights(lazyTokens, color);
}
}
//==================================================================================================
// Highlight Methods
//==================================================================================================
public TokenHighlightColors getSecondaryHighlightColors() {
return highlightController.getSecondaryHighlightColors();
}
public TokenHighlights getSecondaryHighlightedTokens() {
return highlightController.getSecondaryHighlightedTokens();
public boolean hasSecondaryHighlights() {
return highlightController.hasSecondaryHighlights();
}
public boolean hasSecondaryHighlight(ClangToken token) {
return highlightController.hasSecondaryHighlight(token);
}
public Color getSecondaryHighlight(ClangToken token) {
return highlightController.getSecondaryHighlight(token);
}
public TokenHighlights getHighlights(DecompilerHighlighter highligter) {
return highlightController.getHighlighterHighlights(highligter);
}
private Set<ClangDecompilerHighlighter> getSecondaryHighlihgtersByFunction(Function function) {
return highlightController.getSecondaryHighlightersByFunction(function);
}
/**
* Removes all secondary highlights for the current function
*/
public void removeSecondaryHighlights() {
Function function = controller.getFunction();
highlightController.removeSecondaryHighlights(function);
}
public void removeSecondaryHighlight(ClangToken token) {
removeSecondaryHighlight(token.getText());
}
private void removeSecondaryHighlight(String tokenText) {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenText);
highlightController.removeSecondaryHighlights(lazyTokens);
highlightController.removeSecondaryHighlights(token);
}
public void addSecondaryHighlight(ClangToken token) {
String tokenText = token.getText();
addSecondaryHighlight(tokenText);
}
private void addSecondaryHighlight(String tokenText) {
Supplier<List<ClangToken>> lazyTokens = () -> {
return findTokensByName(tokenText);
};
highlightController.addSecondaryHighlights(tokenText, lazyTokens);
ColorProvider cp = highlightController.getRandomColorProvider();
addSecondaryHighlight(token.getText(), cp);
}
public void addSecondaryHighlight(ClangToken token, Color color) {
addSecondaryHighlight(token.getText(), color);
ColorProvider cp = t -> color;
addSecondaryHighlight(token.getText(), cp);
}
private void addSecondaryHighlight(String tokenText, Color color) {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(tokenText);
highlightController.addSecondaryHighlights(lazyTokens, color);
private void addSecondaryHighlight(String tokenText, ColorProvider colorProvider) {
NameTokenMatcher matcher = new NameTokenMatcher(tokenText, colorProvider);
ClangDecompilerHighlighter highlighter = createHighlighter(matcher);
applySecondaryHighlights(highlighter);
}
private void applySecondaryHighlights(ClangDecompilerHighlighter highlighter) {
Function function = decompileData.getFunction();
highlightController.addSecondaryHighlighter(function, highlighter);
highlighter.applyHighlights();
}
private void togglePrimaryHighlight(FieldLocation location, Field field, Color highlightColor) {
@ -204,6 +209,156 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
highlightController.togglePrimaryHighlights(middleMouseHighlightColor, lazyTokens);
}
void addHighlighterHighlights(ClangDecompilerHighlighter highlighter,
Supplier<? extends Collection<ClangToken>> tokens, ColorProvider colorProvider) {
highlightController.addHighlighterHighlights(highlighter, tokens, colorProvider);
}
void removeHighlighterHighlights(DecompilerHighlighter highlighter) {
highlightController.removeHighlighterHighlights(highlighter);
}
public ClangDecompilerHighlighter createHighlighter(CTokenHighlightMatcher tm) {
UUID uuId = UUID.randomUUID();
String id = uuId.toString();
return createHighlighter(id, tm);
}
public ClangDecompilerHighlighter createHighlighter(String id, CTokenHighlightMatcher tm) {
ClangDecompilerHighlighter currentHighlighter = highlightersById.get(id);
if (currentHighlighter != null) {
currentHighlighter.dispose();
}
ClangDecompilerHighlighter newHighlighter = new ClangDecompilerHighlighter(id, this, tm);
highlightersById.put(id, newHighlighter);
highlightController.addHighlighter(newHighlighter);
return newHighlighter;
}
public DecompilerHighlighter getHighlighter(String id) {
return highlightersById.get(id);
}
void removeHighlighter(String id) {
ClangDecompilerHighlighter highlighter = highlightersById.remove(id);
highlightController.removeHighlighter(highlighter);
}
public void clearPrimaryHighlights() {
highlightController.clearPrimaryHighlights();
}
public void addHighlights(Set<Varnode> varnodes, ColorProvider colorProvider) {
ClangTokenGroup root = layoutMgr.getRoot();
highlightController.addPrimaryHighlights(root, colorProvider);
}
public void addHighlights(Set<PcodeOp> ops, Color hlColor) {
ClangTokenGroup root = layoutMgr.getRoot();
highlightController.addPrimaryHighlights(root, ops, hlColor);
}
public String getHighlightedText() {
return highlightController.getPrimaryHighlightedText();
}
public void setHighlightController(ClangHighlightController highlightController) {
if (this.highlightController != null) {
this.highlightController.removeListener(this);
}
this.highlightController = ClangHighlightController.dummyIfNull(highlightController);
highlightController.setHighlightColor(currentVariableHighlightColor);
highlightController.addListener(this);
}
public ClangHighlightController getHighlightController() {
return highlightController;
}
@Override
public void tokenHighlightsChanged() {
repaint();
}
/**
* This is function is used to alert the panel that a token was renamed.
* If the token that is being renamed had a secondary highlight, we must re-apply the highlight
* to the new token.
*
* @param token the token being renamed
* @param newName the new name of the token
*/
public void tokenRenamed(ClangToken token, String newName) {
Color hlColor = highlightController.getSecondaryHighlight(token);
if (hlColor == null) {
return; // not highlighted
}
// remove the old highlighter
highlightController.removeSecondaryHighlights(token);
controller.doWhenNotBusy(() -> {
addSecondaryHighlight(newName, t -> hlColor);
});
}
private void cloneGlobalHighlighters(DecompilerPanel sourcePanel) {
Set<ClangDecompilerHighlighter> allHighlighters =
sourcePanel.highlightController.getGlobalHighlighters();
for (ClangDecompilerHighlighter otherHighlighter : allHighlighters) {
ClangDecompilerHighlighter newHighlighter = otherHighlighter.clone(this);
highlightersById.put(newHighlighter.getId(), newHighlighter);
TokenHighlights otherHighlighterTokens =
sourcePanel.highlightController.getHighlighterHighlights(otherHighlighter);
if (otherHighlighterTokens == null || otherHighlighterTokens.isEmpty()) {
// The highlighter has been created but no highlights have been applied. It is up
// to the client to apply the highlights. The new highlighter will respond to the
// client request if the later apply the highlights.
continue;
}
newHighlighter.applyHighlights();
}
}
/**
* Called by the provider to clone all highlights in the source panel and apply them to this
* panel
* @param sourcePanel the panel that was cloned
*/
public void cloneHighlights(DecompilerPanel sourcePanel) {
cloneGlobalHighlighters(sourcePanel);
//
// Keep only those secondary highlighters for the current function. This ensures that the
// clone will match the cloned decompiler.
//
Function function = decompileData.getFunction();
Set<ClangDecompilerHighlighter> secondaryHighlighters =
sourcePanel.getSecondaryHighlihgtersByFunction(function);
//
// 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.
//
for (ClangDecompilerHighlighter highlighter : secondaryHighlighters) {
ClangDecompilerHighlighter newHighlighter = highlighter.copy(this);
highlightersById.put(newHighlighter.getId(), newHighlighter);
applySecondaryHighlights(newHighlighter);
}
}
//==================================================================================================
// End Highlight Methods
//==================================================================================================
@Override
public void setBackground(Color bg) {
originalBackgroundColor = bg;
@ -256,6 +411,21 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
currentSearchLocation = null;
reapplySecondaryHighlights();
reapplyHighlighterHighlights();
}
private void reapplyHighlighterHighlights() {
Function function = decompileData.getFunction();
if (function == null) {
return;
}
Collection<ClangDecompilerHighlighter> values = highlightersById.values();
for (ClangDecompilerHighlighter highlighter : values) {
highlighter.clearHighlights();
highlighter.applyHighlights();
}
}
private void reapplySecondaryHighlights() {
@ -265,26 +435,11 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
return;
}
// The existing highlights are based on the previously generated tokens, which no longer
// exist. Use those tokens to highlight the current tokens, which are conceptually the
// same tokens.
Set<HighlightToken> oldHighlights =
highlightController.getSecondaryHighlightsByFunction(function);
//@formatter:off
Map<String, List<ClangToken>> tokensByName =
CollectionUtils.asStream(oldHighlights)
.map(ht -> ht.getToken())
.collect(Collectors.groupingBy(t -> t.getText()))
;
//@formatter:on
Set<Entry<String, List<ClangToken>>> entries = tokensByName.entrySet();
for (Entry<String, List<ClangToken>> entry : entries) {
String name = entry.getKey();
List<ClangToken> oldTokens = entry.getValue();
highlightController.removeSecondaryHighlights(() -> oldTokens);
addSecondaryHighlight(name);
Set<ClangDecompilerHighlighter> secondaryHighlighters =
getSecondaryHighlihgtersByFunction(function);
for (ClangDecompilerHighlighter highlighter : secondaryHighlighters) {
highlighter.clearHighlights();
highlighter.applyHighlights();
}
}
@ -332,9 +487,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
// to go to an actual token, since multiple tokens can share an address, we woudln't know
// which token is best.)
//
// Note: at the time of this writing, not all fields have an address value. For
// Note: at the time of this writing, not all fields have an address value. For
// example, the ClangFuncNameToken, does not have an address. (It seems that most
// of the tokens in the function signature do not have an address, which can
// of the tokens in the function signature do not have an address, which can
// probably be fixed.) So, to deal with this oddity, we will have some special
// case code below.
//
@ -389,7 +544,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
/**
* Put cursor on first token in the list
* @param tokens the tokens to search for
* @param tokens the tokens to search for
*/
private void goToBeginningOfLine(List<ClangToken> tokens) {
if (tokens.isEmpty()) {
@ -450,8 +605,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
/**
* Translate Ghidra address to decompiler address. Functions within an overlay space are
* decompiled in their physical space, therefore decompiler results refer to the
* Translate Ghidra address to decompiler address. Functions within an overlay space are
* decompiled in their physical space, therefore decompiler results refer to the
* functions underlying .physical space
*
* @param addr the Ghidra address
@ -470,7 +625,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
/**
* Translate Ghidra address set to decompiler address set. Functions within an overlay
* Translate Ghidra address set to decompiler address set. Functions within an overlay
* space are decompiled in their physical space, therefore decompiler results
* refer to the functions underlying .physical space
*
@ -533,7 +688,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
layoutMgr = null;
decompilerHoverProvider.dispose();
highlighCursorUpdater.dispose();
highlightController.clearAllHighlights();
highlightController.dispose();
highlightersById.clear();
}
private FontMetrics getFontMetrics(DecompileOptions decompileOptions) {
@ -542,7 +698,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
/**
* Passing false signals to disallow navigating to new functions from within the panel by
* Passing false signals to disallow navigating to new functions from within the panel by
* using the mouse.
* @param enabled false disabled mouse function navigation
*/
@ -626,17 +782,17 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
// 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);
}
}
// 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) {
@ -855,10 +1011,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
return SPECIAL_COLOR_DEF;
}
public String getHighlightedText() {
return highlightController.getHighlightedText();
}
public FieldLocation getCursorPosition() {
return fieldPanel.getCursorLocation();
}
@ -933,22 +1085,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
return decompilerHoverProvider.isShowing();
}
public void clearPrimaryHighlights() {
highlightController.clearPrimaryHighlights();
}
public void addVarnodeHighlights(Set<Varnode> varnodes,
TokenHighlightColorProvider colorProvider) {
ClangTokenGroup root = layoutMgr.getRoot();
highlightController.addPrimaryHighlights(root, colorProvider);
}
public void addPcodeOpHighlights(Set<PcodeOp> ops, Color hlColor) {
ClangTokenGroup root = layoutMgr.getRoot();
highlightController.addPrimaryHighlights(root, ops, hlColor);
}
public List<ClangToken> findTokensByName(String name) {
List<ClangToken> tokens = new ArrayList<>();
doFindTokensByName(tokens, layoutMgr.getRoot(), name);
@ -957,7 +1093,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
private void doFindTokensByName(List<ClangToken> tokens, ClangTokenGroup group, String name) {
// TODO is it possible that two or more different variable tokens share the same name?
for (int i = 0; i < group.numChildren(); ++i) {
ClangNode child = group.Child(i);
if (child instanceof ClangTokenGroup) {
@ -1006,54 +1141,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
highlightController.setHighlightColor(currentVariableHighlightColor);
}
public void setHighlightController(ClangHighlightController highlightController) {
if (this.highlightController != null) {
this.highlightController.removeListener(this);
}
this.highlightController = ClangHighlightController.dummyIfNull(highlightController);
highlightController.setHighlightColor(currentVariableHighlightColor);
highlightController.addListener(this);
}
@Override
public void tokenHighlightsChanged() {
repaint();
}
/**
* This is function is used to alert the panel that a token was renamed.
* If the token that is being renamed had a secondary highlight, we must re-apply the highlight
* to the new token.
*
* @param token the token being renamed
* @param newName the new name of the token
*/
public void tokenRenamed(ClangToken token, String newName) {
if (!highlightController.hasSecondaryHighlight(token)) {
return;
}
TokenHighlightColors colors = highlightController.getSecondaryHighlightColors();
String oldName = token.getText();
Color hlColor = colors.getColor(oldName);
highlightController.removeSecondaryHighlights(token);
controller.doWhenNotBusy(() -> {
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(newName);
highlightController.addSecondaryHighlights(lazyTokens, hlColor);
});
}
public ClangHighlightController getHighlightController() {
return highlightController;
}
//==================================================================================================
// Inner Classes
//==================================================================================================
//==================================================================================================
private class SearchHighlightFactory implements HighlightFactory {
@ -1138,10 +1228,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
/**
* Moves this field panel to the given line and column. Further, this navigation will
* fire an event to the rest of the tool. (This is in contrast to a field panel
* <code>goTo</code>, which we use to simply move the cursor, but not trigger an
* tool-level navigation event.)
* <code>goTo</code>, which we use to simply move the cursor, but not trigger an
* tool-level navigation event.)
*
* @param lineNumber the line number
* @param lineNumber the line number
* @param column the column within the line
*/
void navigateTo(int lineNumber, int column) {
@ -1171,7 +1261,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
void doUpdate() {
// Note: don't send this buffered cursor change highlight if some other highlight
// has been applied. Otherwise, this highlight would overwrite the last
// has been applied. Otherwise, this highlight would overwrite the last
// applied highlight.
long lastUpdateId = highlightController.getUpdateId();
if (updateId == lastUpdateId) {

View File

@ -43,7 +43,7 @@ public class LocationClangHighlightController extends ClangHighlightController {
addPrimaryHighlight(tok, defaultHighlightColor);
if (tok instanceof ClangSyntaxToken) {
addPrimaryHighlightToTokensForParenthesis((ClangSyntaxToken) tok, defaultParenColor);
addHighlightBrace((ClangSyntaxToken) tok, defaultParenColor);
addBraceHighlight((ClangSyntaxToken) tok, defaultParenColor);
}
}
}

View File

@ -0,0 +1,43 @@
/* ###
* 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.CTokenHighlightMatcher;
import ghidra.app.decompiler.ClangToken;
/**
* Matcher used for secondary highlights in the Decompiler.
*/
class NameTokenMatcher implements CTokenHighlightMatcher {
private ColorProvider colorProvider;
private String name;
NameTokenMatcher(String name, ColorProvider colorProvider) {
this.name = name;
this.colorProvider = colorProvider;
}
@Override
public Color getTokenHighlight(ClangToken token) {
if (name.equals(token.getText())) {
return colorProvider.getColor(token);
}
return null;
}
}

View File

@ -16,15 +16,13 @@
package ghidra.app.decompiler.component;
import java.awt.Color;
import java.util.Collection;
import java.util.Set;
import java.util.function.Supplier;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.actions.TokenHighlightColorProvider;
import ghidra.app.decompiler.ClangNode;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.program.model.pcode.PcodeOp;
/**
@ -38,13 +36,12 @@ public class NullClangHighlightController extends ClangHighlightController {
}
@Override
public String getHighlightedText() {
public String getPrimaryHighlightedText() {
return null;
}
@Override
public void addPrimaryHighlights(ClangNode parentNode,
TokenHighlightColorProvider colorProvider) {
public void addPrimaryHighlights(ClangNode parentNode, ColorProvider colorProvider) {
// stub
}
@ -54,18 +51,7 @@ public class NullClangHighlightController extends ClangHighlightController {
}
@Override
public void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
Color highlightColor) {
// stub
}
@Override
public void clearAllHighlights() {
// stub
}
@Override
public void addHighlightBrace(ClangSyntaxToken token, Color highlightColor) {
public void addBraceHighlight(ClangSyntaxToken token, Color highlightColor) {
// stub
}

View File

@ -17,15 +17,12 @@ package ghidra.app.decompiler.component;
import java.awt.Color;
import java.util.*;
import java.util.Map.Entry;
import ghidra.app.decompiler.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.HighFunction;
import ghidra.app.decompiler.ClangToken;
/**
* A simple class to manage {@link HighlightToken}s used to create highlights in the Decompiler.
* This class allows clients to access highlights either by a {@link ClangToken} or a
* This class allows clients to access highlights either by a {@link ClangToken} or a
* {@link HighlightToken}.
*/
public class TokenHighlights implements Iterable<HighlightToken> {
@ -52,19 +49,6 @@ public class TokenHighlights implements Iterable<HighlightToken> {
return new TokenKey(t);
}
private Function getFunction(ClangToken t) {
ClangFunction cFunction = t.getClangFunction();
if (cFunction == null) {
return null;
}
HighFunction highFunction = cFunction.getHighFunction();
if (highFunction == null) {
return null;
}
return highFunction.getFunction();
}
/**
* Returns true if there are not highlights
* @return true if there are not highlights
@ -98,23 +82,6 @@ public class TokenHighlights implements Iterable<HighlightToken> {
return highlightsByToken.get(getKey(t));
}
/**
* Returns all highlights for the given function
*
* @param f the function
* @return the highlights
*/
public Set<HighlightToken> getHighlightsByFunction(Function f) {
Set<HighlightToken> results = new HashSet<>();
Set<TokenKey> keys = getHighlightKeys(f);
for (TokenKey key : keys) {
HighlightToken hl = highlightsByToken.get(key);
results.add(hl);
}
return results;
}
/**
* Returns true if this class has a highlight for the given token
* @param t the token
@ -139,39 +106,6 @@ public class TokenHighlights implements Iterable<HighlightToken> {
highlightsByToken.remove(getKey(t));
}
/**
* Removes all highlights associated with the given function
*
* @param function the function
* @return the removed highlights; empty if no highlights existed
*/
public Set<HighlightToken> removeHighlightsByFunction(Function function) {
Set<HighlightToken> oldHighlights = new HashSet<>();
Set<TokenKey> keys = getHighlightKeys(function);
for (TokenKey key : keys) {
HighlightToken hl = highlightsByToken.remove(key);
oldHighlights.add(hl);
}
return oldHighlights;
}
private Set<TokenKey> getHighlightKeys(Function function) {
Set<TokenKey> results = new HashSet<>();
Set<Entry<TokenKey, HighlightToken>> entries = highlightsByToken.entrySet();
for (Entry<TokenKey, HighlightToken> entry : entries) {
HighlightToken highlight = entry.getValue();
ClangToken token = highlight.getToken();
Function tokenFunction = getFunction(token);
if (function.equals(tokenFunction)) {
results.add(entry.getKey());
}
}
return results;
}
@Override
public Iterator<HighlightToken> iterator() {
return highlightsByToken.values().iterator();
@ -181,80 +115,4 @@ public class TokenHighlights implements Iterable<HighlightToken> {
public String toString() {
return highlightsByToken.values().toString();
}
//==================================================================================================
// Inner Classes
//==================================================================================================
// a key that allows us to equate tokens that are not the same instance
private class TokenKey {
private ClangToken token;
TokenKey(ClangToken token) {
this.token = Objects.requireNonNull(token);
}
public TokenKey(HighlightToken t) {
this(t.getToken());
}
@Override
public int hashCode() {
String text = token.getText();
return text == null ? 0 : text.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
ClangToken otherToken = ((TokenKey) obj).token;
if (token.getClass() != otherToken.getClass()) {
return false;
}
if (!Objects.equals(token.getText(), otherToken.getText())) {
return false;
}
ClangLine lineParent = token.getLineParent();
ClangLine otherLineParent = otherToken.getLineParent();
if (!sameLines(lineParent, otherLineParent)) {
return false;
}
if (lineParent == null) {
return false;
}
int positionInLine = lineParent.indexOfToken(token);
int otherPositionInLine = otherLineParent.indexOfToken(otherToken);
return positionInLine == otherPositionInLine;
}
private boolean sameLines(ClangLine l1, ClangLine l2) {
if (l1 == null) {
if (l2 != null) {
return false;
}
return true;
}
else if (l2 == null) {
return false;
}
return l1.getLineNumber() == l2.getLineNumber();
}
@Override
public String toString() {
return token.toString();
}
}
}

View File

@ -0,0 +1,93 @@
/* ###
* 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.util.Objects;
import ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.ClangToken;
// a key that allows us to equate tokens that are not the same instance
class TokenKey {
private ClangToken token;
TokenKey(ClangToken token) {
this.token = Objects.requireNonNull(token);
}
public TokenKey(HighlightToken t) {
this(t.getToken());
}
@Override
public int hashCode() {
String text = token.getText();
return text == null ? 0 : text.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
ClangToken otherToken = ((TokenKey) obj).token;
if (token.getClass() != otherToken.getClass()) {
return false;
}
if (!Objects.equals(token.getText(), otherToken.getText())) {
return false;
}
ClangLine lineParent = token.getLineParent();
ClangLine otherLineParent = otherToken.getLineParent();
if (!sameLines(lineParent, otherLineParent)) {
return false;
}
if (lineParent == null) {
return false;
}
int positionInLine = lineParent.indexOfToken(token);
int otherPositionInLine = otherLineParent.indexOfToken(otherToken);
return positionInLine == otherPositionInLine;
}
private boolean sameLines(ClangLine l1, ClangLine l2) {
if (l1 == null) {
if (l2 != null) {
return false;
}
return true;
}
else if (l2 == null) {
return false;
}
return l1.getLineNumber() == l2.getLineNumber();
}
@Override
public String toString() {
return token.toString();
}
}

View File

@ -20,6 +20,8 @@ import java.util.*;
import org.jdom.Element;
import ghidra.app.CorePluginPackage;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.DecompilerHighlightService;
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames;
@ -49,6 +51,7 @@ import ghidra.util.task.SwingUpdateManager;
GoToService.class, NavigationHistoryService.class, ClipboardService.class,
DataTypeManagerService.class /*, ProgramManager.class */
},
servicesProvided = { DecompilerHighlightService.class },
eventsConsumed = {
ProgramActivatedPluginEvent.class, ProgramOpenedPluginEvent.class,
ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class,
@ -81,6 +84,12 @@ public class DecompilePlugin extends Plugin {
disconnectedProviders = new ArrayList<>();
connectedProvider = new PrimaryDecompilerProvider(this);
registerServices();
}
private void registerServices() {
registerServiceProvided(DecompilerHighlightService.class, connectedProvider);
}
@Override
@ -200,15 +209,18 @@ public class DecompilePlugin extends Plugin {
}
}
void handleTokenRenamed(ClangToken tokenAtCursor, String newName) {
connectedProvider.handleTokenRenamed(tokenAtCursor, newName);
for (DecompilerProvider provider : disconnectedProviders) {
provider.handleTokenRenamed(tokenAtCursor, newName);
}
}
private void removeProvider(DecompilerProvider provider) {
tool.removeComponentProvider(provider);
provider.dispose();
}
/**
* Process the plugin event; delegates the processing to the
* byte block.
*/
@Override
public void processEvent(PluginEvent event) {
if (event instanceof ProgramClosedPluginEvent) {

View File

@ -15,7 +15,6 @@
*/
package ghidra.app.plugin.core.decompile;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.math.BigInteger;
@ -55,11 +54,13 @@ import resources.ResourceManager;
import utility.function.Callback;
public class DecompilerProvider extends NavigatableComponentProviderAdapter
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler {
final static String OPTIONS_TITLE = "Decompiler";
implements DomainObjectListener, OptionsChangeListener, DecompilerCallbackHandler,
DecompilerHighlightService {
private static Icon REFRESH_ICON = Icons.REFRESH_ICON;
static final ImageIcon C_SOURCE_ICON =
private static final String OPTIONS_TITLE = "Decompiler";
private static final Icon REFRESH_ICON = Icons.REFRESH_ICON;
private static final ImageIcon C_SOURCE_ICON =
ResourceManager.loadImage("images/decompileFunction.gif");
private DockingAction graphASTControlFlowAction;
@ -264,6 +265,20 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
tool.toFront(this);
}
//==================================================================================================
// DecompilerHighlightService interface methods
//==================================================================================================
@Override
public DecompilerHighlighter createHighlighter(CTokenHighlightMatcher tm) {
return getDecompilerPanel().createHighlighter(tm);
}
@Override
public DecompilerHighlighter createHighlighter(String id, CTokenHighlightMatcher tm) {
return getDecompilerPanel().createHighlighter(id, tm);
}
//==================================================================================================
// DomainObjectListener methods
//==================================================================================================
@ -360,6 +375,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
if (clipboardService != null) {
clipboardService.deRegisterClipboardContentProvider(clipboardProvider);
}
controller.dispose();
program = null;
currentLocation = null;
@ -486,7 +502,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
decompilerPanel.setCursorPosition(location);
}
DecompilerController getController() {
public DecompilerController getController() {
return controller;
}
@ -644,6 +660,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
return controller.getDecompilerPanel();
}
// snapshot callback
public void cloneWindow() {
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
@ -656,16 +673,13 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
// Any change in the HighlightTokens should be delivered to the new panel
DecompilerPanel myPanel = getDecompilerPanel();
TokenHighlights myHighlights = myPanel.getSecondaryHighlightedTokens();
newProvider.setLocation(currentLocation, myPanel.getViewerPosition());
// transfer any state after the new decompiler is initialized
DecompilerPanel newPanel = newProvider.getDecompilerPanel();
Map<String, Color> highlightsByName = myHighlights.copyHighlightsByName();
newProvider.doWheNotBusy(() -> {
newPanel.setViewerPosition(myViewPosition);
newPanel.applySecondaryHighlights(highlightsByName);
newPanel.cloneHighlights(myPanel);
});
});
}
@ -1073,4 +1087,12 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
public void programClosed(Program closedProgram) {
controller.programClosed(closedProgram);
}
public void tokenRenamed(ClangToken tokenAtCursor, String newName) {
plugin.handleTokenRenamed(tokenAtCursor, newName);
}
void handleTokenRenamed(ClangToken tokenAtCursor, String newName) {
controller.getDecompilerPanel().tokenRenamed(tokenAtCursor, newName);
}
}

View File

@ -16,7 +16,7 @@
package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.TokenHighlights;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
@ -35,17 +35,12 @@ public abstract class AbstractSetSecondaryHighlightAction extends AbstractDecomp
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor == null) {
ClangToken token = context.getTokenAtCursor();
if (token == null) {
return false;
}
TokenHighlights highlightedTokens =
context.getDecompilerPanel().getSecondaryHighlightedTokens();
if (highlightedTokens.contains(tokenAtCursor)) {
return false; // already highlighted
}
return true;
DecompilerPanel panel = context.getDecompilerPanel();
return !panel.hasSecondaryHighlight(token);
}
}

View File

@ -60,7 +60,7 @@ public class BackwardsSliceAction extends AbstractDecompilerAction {
Set<Varnode> backwardSlice = DecompilerUtils.getBackwardSlice(varnode);
SliceHighlightColorProvider colorProvider =
new SliceHighlightColorProvider(decompilerPanel, backwardSlice, varnode, op);
decompilerPanel.addVarnodeHighlights(backwardSlice, colorProvider);
decompilerPanel.addHighlights(backwardSlice, colorProvider);
}
}

View File

@ -55,7 +55,7 @@ public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction {
}
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
decompilerPanel.clearPrimaryHighlights();
decompilerPanel.addPcodeOpHighlights(backwardSlice,
decompilerPanel.addHighlights(backwardSlice,
decompilerPanel.getCurrentVariableHighlightColor());
}
}

View File

@ -57,7 +57,7 @@ public class ForwardSliceAction extends AbstractDecompilerAction {
SliceHighlightColorProvider colorProvider =
new SliceHighlightColorProvider(decompilerPanel, forwardSlice, varnode, op);
decompilerPanel.addVarnodeHighlights(forwardSlice, colorProvider);
decompilerPanel.addHighlights(forwardSlice, colorProvider);
}
}

View File

@ -55,7 +55,7 @@ public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction {
}
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
decompilerPanel.clearPrimaryHighlights();
decompilerPanel.addPcodeOpHighlights(forwardSlice,
decompilerPanel.addHighlights(forwardSlice,
decompilerPanel.getCurrentVariableHighlightColor());
}

View File

@ -57,7 +57,7 @@ public class HighlightDefinedUseAction extends AbstractDecompilerAction {
PcodeOp op = varnode.getDef();
SliceHighlightColorProvider colorProvider =
new SliceHighlightColorProvider(decompilerPanel, varnodes, varnode, op);
decompilerPanel.addVarnodeHighlights(varnodes, colorProvider);
decompilerPanel.addHighlights(varnodes, colorProvider);
}
}

View File

@ -80,7 +80,7 @@ public class IsolateVariableAction extends AbstractDecompilerAction {
HighSymbol highSymbol = tokenAtCursor.getHighVariable().getSymbol();
IsolateVariableTask newVariableTask =
new IsolateVariableTask(context.getTool(), context.getProgram(),
context.getDecompilerPanel(), tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
context.getComponentProvider(), tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
newVariableTask.runTask(false);
}

View File

@ -16,7 +16,7 @@
package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerProvider;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function;
@ -37,9 +37,9 @@ public class IsolateVariableTask extends RenameTask {
private boolean nameIsReserved;
private boolean instanceIsMapped;
public IsolateVariableTask(PluginTool tool, Program program, DecompilerPanel panel,
public IsolateVariableTask(PluginTool tool, Program program, DecompilerProvider provider,
ClangToken token, HighSymbol sym, SourceType st) {
super(tool, program, panel, token, "");
super(tool, program, provider, token, "");
highSymbol = sym;
highFunction = highSymbol.getHighFunction();
function = highFunction.getFunction();

View File

@ -16,7 +16,8 @@
package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.decompiler.component.*;
import ghidra.app.decompiler.component.ClangHighlightController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
@ -45,8 +46,7 @@ public class RemoveAllSecondaryHighlightsAction extends AbstractDecompilerAction
}
DecompilerPanel panel = context.getDecompilerPanel();
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
return !highlightedTokens.isEmpty();
return panel.hasSecondaryHighlights();
}
@Override

View File

@ -17,7 +17,8 @@ package ghidra.app.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*;
import ghidra.app.decompiler.component.ClangHighlightController;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;
@ -51,8 +52,7 @@ public class RemoveSecondaryHighlightAction extends AbstractDecompilerAction {
}
DecompilerPanel panel = context.getDecompilerPanel();
TokenHighlights highlightedTokens = panel.getSecondaryHighlightedTokens();
return highlightedTokens.contains(token);
return panel.hasSecondaryHighlight(token);
}
@Override

View File

@ -58,22 +58,23 @@ public class RenameFieldAction extends AbstractDecompilerAction {
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
PluginTool tool = context.getTool();
final ClangToken tokenAtCursor = context.getTokenAtCursor();
ClangToken tokenAtCursor = context.getTokenAtCursor();
Structure dt = getStructDataType(tokenAtCursor);
if (dt == null) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Could not find structure datatype");
return;
}
int offset = ((ClangFieldToken) tokenAtCursor).getOffset();
if (offset < 0 || offset >= dt.getLength()) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed",
"Could not resolve field within structure");
return;
}
RenameStructureFieldTask nameTask =
new RenameStructureFieldTask(tool, context.getProgram(), context.getDecompilerPanel(),
new RenameStructureFieldTask(tool, context.getProgram(), context.getComponentProvider(),
tokenAtCursor, dt, offset);
nameTask.runTask(true);
}

View File

@ -81,8 +81,7 @@ public class RenameLocalAction extends AbstractDecompilerAction {
HighSymbol highSymbol = findHighSymbolFromToken(tokenAtCursor, context.getHighFunction());
RenameVariableTask nameTask = new RenameVariableTask(tool, context.getProgram(),
context.getDecompilerPanel(),
tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
context.getComponentProvider(), tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
nameTask.runTask(true);
}

View File

@ -16,7 +16,7 @@
package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerProvider;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
@ -29,9 +29,9 @@ public class RenameStructureFieldTask extends RenameTask {
private Structure structure;
public int offset;
public RenameStructureFieldTask(PluginTool tool, Program program, DecompilerPanel panel,
public RenameStructureFieldTask(PluginTool tool, Program program, DecompilerProvider provider,
ClangToken token, Structure structure, int offset) {
super(tool, program, panel, token, token.getText());
super(tool, program, provider, token, token.getText());
this.structure = structure;
this.offset = offset;
}

View File

@ -15,10 +15,12 @@
*/
package ghidra.app.plugin.core.decompile.actions;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.dialogs.InputDialog;
import docking.widgets.dialogs.InputDialogListener;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerProvider;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
@ -37,37 +39,39 @@ public abstract class RenameTask {
protected String errorMsg = null; // Error to return if isValid returns false
protected PluginTool tool;
protected Program program;
protected DecompilerPanel decompilerPanel;
protected DecompilerProvider provider;
protected ClangToken tokenAtCursor;
public RenameTask(PluginTool tool, Program program, DecompilerPanel panel, ClangToken token,
String old) {
public RenameTask(PluginTool tool, Program program, DecompilerProvider provider,
ClangToken token, String old) {
this.tool = tool;
this.program = program;
this.decompilerPanel = panel;
this.provider = provider;
this.tokenAtCursor = token;
oldName = old;
}
public abstract String getTransactionName();
public abstract boolean isValid(String newNm);
public abstract void commit() throws DuplicateNameException, InvalidInputException;
public String getNewName() { return newName; }
public String getNewName() {
return newName;
}
/**
* Bring up a dialog that is initialized with the old name, and allows the user to select a new name
* @param oldNameIsCancel is true if the user keeping/entering the old name is considered a cancel
* @return true unless the user canceled
*/
private boolean runDialog(boolean oldNameIsCancel) {
private boolean showDialog(boolean oldNameIsCancel) {
InputDialogListener listener = new InputDialogListener() {
@Override
public boolean inputIsValid(InputDialog dialog) {
String name = dialog.getValue();
if ((name==null)||(name.length()==0)) {
if (StringUtils.isBlank(name)) {
dialog.setStatusText("Cannot have empty name");
return false;
}
@ -82,21 +86,21 @@ public abstract class RenameTask {
return res;
}
};
String label = "Rename " + oldName + ":";
InputDialog renameVarDialog = new InputDialog( getTransactionName(),
new String[]{ label }, new String[]{ oldName }, listener );
tool.showDialog(renameVarDialog);
if (renameVarDialog.isCanceled()) {
return false;
}
InputDialog renameVarDialog = new InputDialog(getTransactionName(),
new String[] { label }, new String[] { oldName }, listener);
tool.showDialog(renameVarDialog);
if (renameVarDialog.isCanceled()) {
return false;
}
if (oldNameIsCancel && newName.equals(oldName)) {
return false;
}
return true;
return true;
}
/**
@ -104,23 +108,28 @@ public abstract class RenameTask {
* @param oldNameIsCancel is true if the user entering/keeping the old name is considered a cancel
*/
public void runTask(boolean oldNameIsCancel) {
boolean dialogres = runDialog(oldNameIsCancel);
if (dialogres) {
int transaction = program.startTransaction(getTransactionName());
boolean commit = false;
try {
commit();
commit = true;
}
catch (DuplicateNameException e) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed", e.getMessage());
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed", e.getMessage());
}
finally {
program.endTransaction(transaction, commit);
decompilerPanel.tokenRenamed(tokenAtCursor, getNewName());
boolean result = showDialog(oldNameIsCancel);
if (!result) {
return;
}
int tx = program.startTransaction(getTransactionName());
boolean commit = false;
try {
commit();
commit = true;
}
catch (DuplicateNameException e) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed", e.getMessage());
}
catch (InvalidInputException e) {
Msg.showError(this, tool.getToolFrame(), "Rename Failed", e.getMessage());
}
finally {
program.endTransaction(tx, commit);
if (commit) {
provider.tokenRenamed(tokenAtCursor, getNewName());
}
}

View File

@ -16,7 +16,7 @@
package ghidra.app.plugin.core.decompile.actions;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.plugin.core.decompile.DecompilerProvider;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
@ -35,9 +35,9 @@ public class RenameVariableTask extends RenameTask {
private SourceType srctype; // Desired source type for the variable being renamed
private SourceType signatureSrcType; // Signature source type of the function (which will be preserved)
public RenameVariableTask(PluginTool tool, Program program, DecompilerPanel panel,
public RenameVariableTask(PluginTool tool, Program program, DecompilerProvider provider,
ClangToken token, HighSymbol sym, SourceType st) {
super(tool, program, panel, token, sym.getName());
super(tool, program, provider, token, sym.getName());
highSymbol = sym;
exactSpot = token.getVarnode();
hfunction = sym.getHighFunction();

View File

@ -19,8 +19,7 @@ import java.awt.Color;
import java.util.Set;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.decompiler.component.*;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
@ -30,7 +29,7 @@ import ghidra.program.model.pcode.Varnode;
* @see ForwardSliceAction
* @see BackwardsSliceAction
*/
public class SliceHighlightColorProvider implements TokenHighlightColorProvider {
public class SliceHighlightColorProvider implements ColorProvider {
private Set<Varnode> varnodes;
private Varnode specialVn;

View File

@ -84,7 +84,7 @@ public class HighSymbolTest extends AbstractDecompilerTest {
private void renameVariable(HighSymbol highSymbol, ClangToken tokenAtCursor, String newName) {
RenameVariableTask rename =
new RenameVariableTask(provider.getTool(), highSymbol.getProgram(),
provider.getDecompilerPanel(), tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
provider, tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
assertTrue(rename.isValid(newName));
modifyProgram(p -> {
rename.commit();
@ -94,7 +94,7 @@ public class HighSymbolTest extends AbstractDecompilerTest {
private void isolateVariable(HighSymbol highSymbol, ClangToken tokenAtCursor, String newName) {
IsolateVariableTask isolate = new IsolateVariableTask(provider.getTool(), program,
provider.getDecompilerPanel(), tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
provider, tokenAtCursor, highSymbol, SourceType.USER_DEFINED);
assertTrue(isolate.isValid(newName));
modifyProgram(p -> {
isolate.commit();