mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
Merge remote-tracking branch
'origin/GP-1435-dragonmacher-decompiler-highlighter-service--SQUASHED' (Closes #2313)
This commit is contained in:
commit
8d8d32262d
@ -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);
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public class BackwardsSliceToPCodeOpsAction extends AbstractDecompilerAction {
|
||||
}
|
||||
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
|
||||
decompilerPanel.clearPrimaryHighlights();
|
||||
decompilerPanel.addPcodeOpHighlights(backwardSlice,
|
||||
decompilerPanel.addHighlights(backwardSlice,
|
||||
decompilerPanel.getCurrentVariableHighlightColor());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public class ForwardSliceToPCodeOpsAction extends AbstractDecompilerAction {
|
||||
}
|
||||
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
|
||||
decompilerPanel.clearPrimaryHighlights();
|
||||
decompilerPanel.addPcodeOpHighlights(forwardSlice,
|
||||
decompilerPanel.addHighlights(forwardSlice,
|
||||
decompilerPanel.getCurrentVariableHighlightColor());
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user