diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DecompilerCodeComparisonView.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DecompilerCodeComparisonView.java index b41262e1c9..48b99836ee 100644 --- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DecompilerCodeComparisonView.java +++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DecompilerCodeComparisonView.java @@ -241,9 +241,8 @@ public class DecompilerCodeComparisonView extends CodeComparisonView { private void linkHighlightControllers() { DiffClangHighlightController left = cDisplays.get(LEFT).getHighlightController(); DiffClangHighlightController right = cDisplays.get(RIGHT).getHighlightController(); - left.addListener(right); - right.addListener(left); - + left.setTokenChangedListener(right); + right.setTokenChangedListener(left); } @Override diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DiffClangHighlightController.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DiffClangHighlightController.java index c02cd40f59..d1045f9692 100755 --- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DiffClangHighlightController.java +++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DiffClangHighlightController.java @@ -4,9 +4,9 @@ * 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. @@ -17,18 +17,16 @@ package ghidra.features.codecompare.decompile; import java.awt.Color; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import docking.widgets.EventTrigger; import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.support.FieldLocation; -import ghidra.app.decompiler.ClangSyntaxToken; -import ghidra.app.decompiler.ClangToken; +import ghidra.app.decompiler.*; import ghidra.app.decompiler.component.*; import ghidra.features.codecompare.graphanalysis.TokenBin; -import ghidra.util.ColorUtils; -import ghidra.util.SystemUtilities; -import util.CollectionUtils; /** * Class to handle Function Difference highlights for a decompiled function. @@ -40,202 +38,251 @@ public class DiffClangHighlightController extends LocationClangHighlightControll private Set diffTokenSet = new HashSet<>(); private ClangToken locationToken; private TokenBin locationTokenBin; - private List highlightBins; - private List listenerList = new ArrayList<>(); - private TokenBin matchingTokenBin; + private List allTokenBins; private DecompilerCodeComparisonOptions comparisonOptions; + private DiffClangHighlightListener listener = new DummyListener(); + + private DiffTokenHighlighter diffColorHighlighter; + private BasicTokenHighlighter currentTokenHighlighter; + + // highlights the token in this highlighter for the selected token in the other highlighter + private BasicTokenHighlighter matchingTokenHighlighter; public DiffClangHighlightController(DecompilerCodeComparisonOptions comparisonOptions) { this.comparisonOptions = comparisonOptions; } - public void clearDiffHighlights() { - doClearDiffHighlights(); - notifyListeners(); - } - - private void doClearDiffHighlights() { - ClangToken[] array = diffTokenSet.toArray(new ClangToken[diffTokenSet.size()]); - for (ClangToken clangToken : array) { - clearDiffHighlight(clangToken); - } - } - - private void clearDiffHighlight(ClangToken clangToken) { - Color highlight = clangToken.getHighlight(); - if (highlight != null && highlight.equals(comparisonOptions.getDiffHighlightColor())) { - clangToken.setHighlight(null); - } - diffTokenSet.remove(clangToken); - } - - private void clearNonDiffHighlight(ClangToken clangToken) { - if (diffTokenSet.contains(clangToken)) { - clangToken.setHighlight(comparisonOptions.getDiffHighlightColor()); - } - else { - clangToken.setHighlight(null); - } - if (clangToken.isMatchingToken()) { - clangToken.setMatchingToken(false); - } - } - public void setDiffHighlights(List highlightBins, Set tokenSet) { - this.highlightBins = highlightBins; - doClearDiffHighlights(); - for (ClangToken clangToken : tokenSet) { - clangToken.setHighlight(comparisonOptions.getDiffHighlightColor()); - diffTokenSet.add(clangToken); + this.allTokenBins = highlightBins; + + clearDiffHighlights(); + + if (!tokenSet.isEmpty()) { + Color color = comparisonOptions.getDiffHighlightColor(); + diffColorHighlighter = new DiffTokenHighlighter(new ArrayList<>(tokenSet), color); + diffColorHighlighter.applyHighlights(); } notifyListeners(); } @Override - public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) { + public void fieldLocationChanged(FieldLocation location, Field field, + EventTrigger trigger) { - if (!(field instanceof ClangTextField)) { + if (!(field instanceof ClangTextField textField)) { return; } - // Get the token for the location so we can highlight its token bin. - // Also we will use it when notifying the other panel to highlight. - ClangToken tok = ((ClangTextField) field).getToken(location); - if (SystemUtilities.isEqual(locationToken, tok)) { - return; // Current location's token hasn't changed. + // Get the token for the location so we can highlight its token bin. Also we will use it + // when notifying the other panel to highlight. + ClangToken tok = textField.getToken(location); + if (Objects.equals(locationToken, tok)) { + return; // current location's token hasn't changed } - // Undo any highlight of the previous matching tokenBin. - if (matchingTokenBin != null && matchingTokenBin.getMatch() != null) { - clearTokenBinHighlight(matchingTokenBin.getMatch()); - matchingTokenBin = null; - } - - clearCurrentLocationHighlight(); - clearPrimaryHighlights(); - addPrimaryHighlight(tok, defaultHighlightColor); - if (tok instanceof ClangSyntaxToken) { - List tokens = addPrimaryHighlightToTokensForParenthesis( - (ClangSyntaxToken) tok, defaultParenColor); - reHighlightDiffs(tokens); - addPrimaryHighlightToTokensForBrace((ClangSyntaxToken) tok, defaultParenColor); + clearCurrentLocationHighlight(); + clearMatchingTokenBin(); + + highlightTokensBetweenParens(tok); + highlightCurrentLocationToken(tok); + + listener.locationTokenChanged(locationTokenBin); + } + + private void highlightTokensBetweenParens(ClangToken tok) { + + if (!(tok instanceof ClangSyntaxToken syntaxToken)) { + return; } + addPrimaryHighlightToTokensForParenthesis(syntaxToken, defaultParenColor); + addPrimaryHighlightToTokensForBrace(syntaxToken, defaultParenColor); + } + + private void highlightCurrentLocationToken(ClangToken tok) { + TokenBin tokenBin = null; - if (tok != null) { - Color highlightColor = comparisonOptions.getFocusedTokenIneligibleHighlightColor(); // Don't know - if (highlightBins != null) { - tokenBin = TokenBin.getBinContainingToken(highlightBins, tok); - if (tokenBin != null) { - if (tokenBin.getMatch() != null) { - highlightColor = comparisonOptions.getFocusedTokenMatchHighlightColor(); - } - else if (tokenBin.getMatch() == null) { - highlightColor = comparisonOptions.getFocusedTokenUnmatchedHighlightColor(); - } - else { - // All the tokens that didn't fall into the "has a match" or "no match" - // categories above are in a single token bin. - // We don't want all these highlighted at the same time, so set the - // tokenBin to null. By doing this, only the current token gets highlighted. - tokenBin = null; - } - } - } - locationToken = tok; - locationTokenBin = tokenBin; - if (tokenBin == null) { - addPrimaryHighlight(tok, highlightColor); + if (tok != null && allTokenBins != null) { + tokenBin = TokenBin.getBinContainingToken(allTokenBins, tok); + } + + Color binHlColor = comparisonOptions.getFocusedTokenIneligibleHighlightColor(); + if (tokenBin != null) { + if (tokenBin.getMatch() != null) { + binHlColor = comparisonOptions.getFocusedTokenMatchHighlightColor(); } else { - addTokenBinHighlight(tokenBin, highlightColor); + binHlColor = comparisonOptions.getFocusedTokenUnmatchedHighlightColor(); } } - // Notify other decompiler panel highlight controller we have a new location token. - for (DiffClangHighlightListener listener : listenerList) { - listener.locationTokenChanged(tok, tokenBin); + locationToken = tok; + locationTokenBin = tokenBin; + + List tokens = List.of(); + if (tokenBin != null) { + tokens = toList(tokenBin); + } + else if (tok != null) { + tokens = List.of(tok); + } + + installCurrentTokenHighlighter(tokens, binHlColor); + refreshDiffHighlightsForCurrentLocationChange(); + } + + /* + * The diff highlighter is smart enough to ignore the token at the current location. We have to + * kick it when the location changes so it will update the highlights. + */ + private void refreshDiffHighlightsForCurrentLocationChange() { + if (diffColorHighlighter != null) { + diffColorHighlighter.clearHighlights(); + diffColorHighlighter.applyHighlights(); } } - private void reHighlightDiffs(List tokenList) { - Color averageColor = - ColorUtils.blend(defaultParenColor, comparisonOptions.getDiffHighlightColor(), 0.5); - for (ClangToken clangToken : tokenList) { - if (diffTokenSet.contains(clangToken)) { - clangToken.setHighlight(averageColor); - } - } + private static List toList(TokenBin tokens) { + return StreamSupport.stream(tokens.spliterator(), false).collect(Collectors.toList()); } private void clearCurrentLocationHighlight() { - if (locationTokenBin != null) { - clearTokenBinHighlight(locationTokenBin); - locationTokenBin = null; - locationToken = null; + + if (currentTokenHighlighter != null) { + currentTokenHighlighter.dispose(); + currentTokenHighlighter = null; } - if (locationToken != null) { - clearNonDiffHighlight(locationToken); - locationToken = null; + + locationTokenBin = null; + locationToken = null; + } + + private void clearDiffHighlights() { + + if (diffColorHighlighter != null) { + diffColorHighlighter.dispose(); + diffColorHighlighter = null; + } + + diffTokenSet.clear(); + } + + private void clearMatchingTokenBin() { + if (matchingTokenHighlighter != null) { + matchingTokenHighlighter.dispose(); + matchingTokenHighlighter = null; } } - private void addTokenBinHighlight(TokenBin tokenBin, Color highlightColor) { - for (ClangToken token : tokenBin) { - addPrimaryHighlight(token, highlightColor); + private void installCurrentTokenHighlighter(List tokens, Color highlightColor) { + if (tokens.isEmpty()) { + return; } + + currentTokenHighlighter = new BasicTokenHighlighter(tokens, highlightColor); + currentTokenHighlighter.applyHighlights(); } - private void clearTokenBinHighlight(TokenBin tokenBin) { - for (ClangToken token : tokenBin) { - clearNonDiffHighlight(token); + private void installMatchingTokenBinHighlighter(TokenBin tokenBin, Color highlightColor) { + + clearMatchingTokenBin(); + + if (tokenBin == null) { + return; } + + matchingTokenHighlighter = new BasicTokenHighlighter(tokenBin, highlightColor); + matchingTokenHighlighter.applyHighlights(); } - private void doClearHighlights(TokenHighlights tokens) { - List clangTokens = - CollectionUtils.asStream(tokens).map(ht -> ht.getToken()).collect(Collectors.toList()); - for (ClangToken clangToken : clangTokens) { - clearNonDiffHighlight(clangToken); - } - tokens.clear(); - notifyListeners(); + public void setTokenChangedListener(DiffClangHighlightListener listener) { + this.listener = listener == null ? new DummyListener() : listener; } @Override - public void clearPrimaryHighlights() { - doClearHighlights(getPrimaryHighlights()); - } - - public boolean addListener(DiffClangHighlightListener listener) { - return listenerList.add(listener); - } - - public boolean removeListener(DiffClangHighlightListener listener) { - return listenerList.remove(listener); - } - - @Override - public void locationTokenChanged(ClangToken tok, TokenBin tokenBin) { + public void locationTokenChanged(TokenBin tokenBin) { clearCurrentLocationHighlight(); + refreshDiffHighlightsForCurrentLocationChange(); // The token Changed in our other matching DiffClangHighlightController - highlightMatchingToken(tok, tokenBin); + if (tokenBin != null) { + TokenBin match = tokenBin.getMatch(); + Color color = comparisonOptions.getFocusedTokenMatchHighlightColor(); + installMatchingTokenBinHighlighter(match, color); + } } - private void highlightMatchingToken(ClangToken tok, TokenBin tokenBin) { - // Undo any highlight of the previous matching tokenBin. - if (matchingTokenBin != null && matchingTokenBin.getMatch() != null) { - clearTokenBinHighlight(matchingTokenBin.getMatch()); +//================================================================================================= +// Inner Classes +//================================================================================================= + + /** + * Highlights a given set of tokens with the given color. + */ + private class BasicTokenHighlighter implements DecompilerHighlighter { + + private String id; + protected List tokens; + private Color color; + + BasicTokenHighlighter(TokenBin tokens, Color color) { + this(toList(tokens), color); } - // Highlight the new matching tokenBin. - if (tokenBin != null && tokenBin.getMatch() != null) { - addTokenBinHighlight(tokenBin.getMatch(), - comparisonOptions.getFocusedTokenMatchHighlightColor()); + BasicTokenHighlighter(List tokens, Color color) { + this.color = color; + UUID uuId = UUID.randomUUID(); + this.id = uuId.toString(); + this.tokens = tokens; } - matchingTokenBin = tokenBin; + // subclass overrides this method + protected List getCurrentTokens() { + return tokens; + } + + @Override + public void applyHighlights() { + Supplier> tokenSupplier = this::getCurrentTokens; + ColorProvider cp = t -> color; + addHighlighterHighlights(this, tokenSupplier, cp); + } + + @Override + public void clearHighlights() { + removeHighlighterHighlights(this); + } + + @Override + public void dispose() { + removeHighlighterHighlights(this); + } + + @Override + public String getId() { + return id; + } + } + + private class DiffTokenHighlighter extends BasicTokenHighlighter { + + DiffTokenHighlighter(List tokens, Color color) { + super(tokens, color); + } + + @Override + protected List getCurrentTokens() { + // ignore the selected token so that it paints with its own color and does not get + // blended with this highlighter's color + return tokens.stream().filter(t -> t != locationToken).collect(Collectors.toList()); + } + } + + private class DummyListener implements DiffClangHighlightListener { + @Override + public void locationTokenChanged(TokenBin tokenBin) { + // stub + } } } diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DiffClangHighlightListener.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DiffClangHighlightListener.java index 3aeae375c7..5af5ca1b3e 100755 --- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DiffClangHighlightListener.java +++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/DiffClangHighlightListener.java @@ -4,9 +4,9 @@ * 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. @@ -15,16 +15,14 @@ */ package ghidra.features.codecompare.decompile; -import ghidra.app.decompiler.ClangToken; import ghidra.features.codecompare.graphanalysis.TokenBin; public interface DiffClangHighlightListener { /** * Notifier that the current location changed to the specified token. - * @param tok the token * @param tokenBin the bin which contains the token. Otherwise, null. */ - public void locationTokenChanged(ClangToken tok, TokenBin tokenBin); + public void locationTokenChanged(TokenBin tokenBin); } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangCommentToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangCommentToken.java index 7a6a1e2a63..ae1b209ccd 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangCommentToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangCommentToken.java @@ -4,9 +4,9 @@ * 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. @@ -33,7 +33,6 @@ public class ClangCommentToken extends ClangToken { newToken.setText(text); newToken.setLineParent(source.getLineParent()); newToken.setSyntaxType(source.getSyntaxType()); - newToken.setHighlight(source.getHighlight()); newToken.srcaddr = source.srcaddr; return newToken; } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangNode.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangNode.java index a34602d08f..7dfa336f8c 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangNode.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangNode.java @@ -4,9 +4,9 @@ * 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. @@ -15,7 +15,6 @@ */ package ghidra.app.decompiler; -import java.awt.Color; import java.util.List; import ghidra.program.model.address.Address; @@ -45,12 +44,6 @@ public interface ClangNode { */ public Address getMaxAddress(); - /** - * Set a highlighting background color for all text elements - * @param c is the color to set - */ - public void setHighlight(Color c); - /** * Return the number of immediate groupings this text breaks up into * @return the number of child groupings diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java index e33ae50844..bd63a3987b 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangToken.java @@ -18,7 +18,6 @@ package ghidra.app.decompiler; import static ghidra.program.model.pcode.AttributeId.*; import static ghidra.program.model.pcode.ElementId.*; -import java.awt.Color; import java.util.Iterator; import java.util.List; @@ -49,13 +48,11 @@ public class ClangToken implements ClangNode { private ClangLine lineparent; private String text; private int syntax_type; - private Color highlight; // Color to highlight with or null if no highlight private boolean matchingToken; public ClangToken(ClangNode par) { parent = par; text = null; - highlight = null; syntax_type = DEFAULT_COLOR; lineparent = null; } @@ -63,14 +60,12 @@ public class ClangToken implements ClangNode { public ClangToken(ClangNode par, String txt) { parent = par; text = txt; - highlight = null; syntax_type = DEFAULT_COLOR; } public ClangToken(ClangNode par, String txt, int color) { parent = par; text = txt; - highlight = null; syntax_type = color; } @@ -123,19 +118,6 @@ public class ClangToken implements ClangNode { return null; } - @Override - public void setHighlight(Color val) { - highlight = val; - } - - /** - * Get the background highlight color used to render this token, or null if not highlighted - * @return the Color or null - */ - public Color getHighlight() { - return highlight; - } - /** * Set whether or not additional "matching" highlighting is applied to this token. * Currently this means a bounding box is drawn around the token. diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTokenGroup.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTokenGroup.java index aa22cc498a..b962c13450 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTokenGroup.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangTokenGroup.java @@ -4,9 +4,9 @@ * 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. @@ -17,7 +17,6 @@ package ghidra.app.decompiler; import static ghidra.program.model.pcode.ElementId.*; -import java.awt.Color; import java.util.*; import java.util.stream.Stream; @@ -97,13 +96,6 @@ public class ClangTokenGroup implements ClangNode, Iterable { return parent.getClangFunction(); } - @Override - public void setHighlight(Color val) { - for (ClangNode element : tokgroup) { - element.setHighlight(val); - } - } - @Override public void flatten(List list) { for (ClangNode element : tokgroup) { @@ -168,9 +160,7 @@ public class ClangTokenGroup implements ClangNode, Iterable { public String toString() { String lastTokenStr = null; StringBuffer buffer = new StringBuffer(); - Iterator iter = tokgroup.iterator(); - while (iter.hasNext()) { - ClangNode node = iter.next(); + for (ClangNode node : tokgroup) { String tokenStr = node.toString(); if (tokenStr.length() == 0) { continue; diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangFieldElement.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangFieldElement.java index 7ac8a4ebfe..41b409689c 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangFieldElement.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangFieldElement.java @@ -4,9 +4,9 @@ * 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. @@ -27,9 +27,12 @@ import ghidra.app.decompiler.ClangToken; public class ClangFieldElement extends AbstractTextFieldElement { private final ClangToken token; + private ClangHighlightController hlController; - public ClangFieldElement(ClangToken token, AttributedString as, int col) { + public ClangFieldElement(ClangHighlightController hlController, ClangToken token, + AttributedString as, int col) { super(as, 0, col); + this.hlController = hlController; this.token = token; } @@ -39,7 +42,7 @@ public class ClangFieldElement extends AbstractTextFieldElement { @Override public void paint(JComponent c, Graphics g, int x, int y) { - Color highlightColor = token.getHighlight(); + Color highlightColor = hlController.getCombinedColor(token); if (highlightColor != null) { g.setColor(highlightColor); g.fillRect(x, y - getHeightAbove(), getStringWidth(), @@ -63,12 +66,12 @@ public class ClangFieldElement extends AbstractTextFieldElement { if (as == attributedString) { return this; } - return new ClangFieldElement(token, as, column + start); + return new ClangFieldElement(hlController, token, as, column + start); } @Override public FieldElement replaceAll(char[] targets, char replacement) { - return new ClangFieldElement(token, attributedString.replaceAll(targets, replacement), - column); + AttributedString as = attributedString.replaceAll(targets, replacement); + return new ClangFieldElement(hlController, token, as, column); } } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangHighlightController.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangHighlightController.java index a1ae82005f..2a20953268 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangHighlightController.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangHighlightController.java @@ -30,6 +30,7 @@ import ghidra.program.model.pcode.HighFunction; import ghidra.program.model.pcode.PcodeOp; import ghidra.util.ColorUtils; import util.CollectionUtils; +import utility.function.Dummy; /** * Class to handle highlights for a decompiled function. @@ -86,7 +87,6 @@ public abstract class ClangHighlightController { // arbitrary value chosen by guessing; this can be changed if needed private int maxColorBlendSize = 5; - private boolean isRebuilding; private List listeners = new ArrayList<>(); @@ -169,33 +169,19 @@ public abstract class ClangHighlightController { // finished. This allows all highlights to calculate their matches without the color // blending affecting performance. // - isRebuilding = true; Set service = getServiceHighlighters(); Set secondary = getSecondaryHighlighters(function); Iterable it = CollectionUtils.asIterable(service, secondary); - try { - for (DecompilerHighlighter highlighter : it) { - highlighter.clearHighlights(); - highlighter.applyHighlights(); - } - } - finally { - isRebuilding = false; + for (DecompilerHighlighter highlighter : it) { + highlighter.clearHighlights(); + highlighter.applyHighlights(); } // gather all highlighted tokens and then update their color - Set allTokens = new HashSet<>(); it = CollectionUtils.asIterable(service, secondary); for (DecompilerHighlighter highlighter : it) { - TokenHighlights hlTokens = userHighlights.add(highlighter); - for (HighlightToken hlToken : hlTokens) { - allTokens.add(hlToken.getToken()); - } - } - - for (ClangToken token : allTokens) { - updateHighlightColor(token); + userHighlights.add(highlighter); } } @@ -238,7 +224,6 @@ public abstract class ClangHighlightController { public void clearPrimaryHighlights() { Consumer clearAll = token -> { token.setMatchingToken(false); - updateHighlightColor(token); }; doClearHighlights(contextHighlightTokens, clearAll); @@ -297,7 +282,7 @@ public abstract class ClangHighlightController { for (DecompilerHighlighter highlighter : highlighters) { TokenHighlights highlights = userHighlights.getHighlights(highlighter); - Consumer clearHighlight = token -> updateHighlightColor(token); + Consumer clearHighlight = Dummy.consumer(); doClearHighlights(highlights, clearHighlight); } highlighters.clear(); @@ -333,7 +318,7 @@ public abstract class ClangHighlightController { return; } - Consumer clearHighlight = token -> updateHighlightColor(token); + Consumer clearHighlight = Dummy.consumer(); doClearHighlights(highlighterTokens, clearHighlight); notifyListeners(); } @@ -417,18 +402,6 @@ public abstract class ClangHighlightController { // store the actual requested color currentHighlights.add(new HighlightToken(clangToken, highlightColor)); - updateHighlightColor(clangToken); - } - - private void updateHighlightColor(ClangToken t) { - - if (isRebuilding) { - return; - } - - // set the color to the current combined value of all highlight types - Color combinedColor = getCombinedColor(t); - t.setHighlight(combinedColor); } private void add(Set colors, HighlightToken hlToken) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java index eee2fc7222..03f58d9d8e 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/ClangLayoutController.java @@ -183,20 +183,21 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener { FieldElement[] elements = new FieldElement[tokens.size()]; int columnPosition = 0; + Program program = decompilerPanel.getProgram(); + ClangHighlightController hlController = decompilerPanel.getHighlightController(); for (int i = 0; i < tokens.size(); ++i) { ClangToken token = tokens.get(i); Color color = getTokenColor(token); if (token instanceof ClangCommentToken) { AttributedString prototype = new AttributedString("prototype", color, metrics); - Program program = decompilerPanel.getProgram(); elements[i] = CommentUtils.parseTextForAnnotations(token.getText(), program, prototype, 0); columnPosition += elements[i].length(); } else { AttributedString as = new AttributedString(token.getText(), color, metrics); - elements[i] = new ClangFieldElement(token, as, columnPosition); + elements[i] = new ClangFieldElement(hlController, token, as, columnPosition); columnPosition += as.length(); } } diff --git a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/decompiler/component/DecompilerClangTest.java b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/decompiler/component/DecompilerClangTest.java index c249fc1601..ecf3e429ad 100644 --- a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/decompiler/component/DecompilerClangTest.java +++ b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/decompiler/component/DecompilerClangTest.java @@ -2048,7 +2048,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest { private void assertCombinedHighlightColor(ClangToken token) { Color combinedColor = getCombinedHighlightColor(token); - Color actual = token.getHighlight(); + Color actual = getHighlight(provider, token); assertEquals(combinedColor, actual); } @@ -2139,7 +2139,8 @@ public class DecompilerClangTest extends AbstractDecompilerTest { DecompilerController controller = provider.getController(); DecompilerPanel panel = controller.getDecompilerPanel(); Color hlColor = panel.getCurrentVariableHighlightColor(); - assertEquals(hlColor, token.getHighlight()); + Color actual = getHighlight(provider, token); + assertEquals(hlColor, actual); } private void rename(String newName) { @@ -2265,7 +2266,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest { // test the token under the cursor directly, as that may have a combined highlight applied Color combinedColor = getCombinedHighlightColor(theProvider, cursorToken); ColorMatcher cm = new ColorMatcher(color, combinedColor); - Color actual = cursorToken.getHighlight(); + Color actual = getHighlight(theProvider, cursorToken); assertTrue("Token is not highlighted: '" + cursorToken + "'" + "\n\texpected: " + cm + "; found: " + toString(actual), cm.matches(actual)); } @@ -2283,7 +2284,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest { // test the token under the cursor directly, as that may have a combined highlight applied Color combinedColor = getCombinedHighlightColor(token); ColorMatcher cm = new ColorMatcher(color, combinedColor); - Color actual = token.getHighlight(); + Color actual = getHighlight(provider, token); String tokenString = token.toString() + " at line " + token.getLineParent().getLineNumber(); assertTrue("Token is not highlighted: '" + tokenString + "'" + "\n\texpected: " + cm + "; found: " + toString(actual), cm.matches(actual)); @@ -2330,7 +2331,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest { continue; } - Color actual = token.getHighlight(); + Color actual = getHighlight(provider, token); assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm + "; found: " + toString(actual), cm.matches(actual)); } @@ -2357,7 +2358,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest { continue; } - Color actual = token.getHighlight(); + Color actual = getHighlight(theProvider, token); assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm + "; found: " + toString(actual), cm.matches(actual)); } @@ -2378,7 +2379,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest { assertAllFieldsHighlighted(theProvider, name, cm, ignores); // test the token under the cursor directly, as that may have a combined highlight applied - Color actual = token.getHighlight(); + Color actual = getHighlight(theProvider, token); assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm + "; found: " + toString(actual), cm.matches(actual)); } @@ -2394,7 +2395,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest { continue; } - Color actual = otherToken.getHighlight(); + Color actual = getHighlight(theProvider, otherToken); assertTrue("Token is not highlighted: '" + otherToken + "'" + "\n\texpected: " + colorMatcher + "; found: " + toString(actual), colorMatcher.matches(actual)); } @@ -2411,7 +2412,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest { continue; } - Color actual = otherToken.getHighlight(); + Color actual = getHighlight(theProvider, otherToken); Color combinedColor = getCombinedHighlightColor(otherToken); ColorMatcher combinedColorMatcher = colorMatcher.with(combinedColor); assertTrue( @@ -2421,6 +2422,12 @@ public class DecompilerClangTest extends AbstractDecompilerTest { } } + private Color getHighlight(DecompilerProvider theProvider, ClangToken token) { + DecompilerPanel panel = theProvider.getController().getDecompilerPanel(); + ClangHighlightController highlightController = panel.getHighlightController(); + return highlightController.getCombinedColor(token); + } + private String toString(Color c) { if (c == null) { return "Color{null}";