Merge remote-tracking branch 'origin/GP-1-dragonmacher-function-color-fix--SQUASHED'

This commit is contained in:
Ryan Kurtz 2025-07-11 11:58:14 -04:00
commit c7aa190b40
13 changed files with 669 additions and 290 deletions

View file

@ -40,7 +40,7 @@ color.fg.listing.comment.pre = color.palette.indigo
color.fg.listing.comment.offcut = color.fg.error
color.fg.listing.ref.bad = color.fg.error
color.fg.listing.ext.entrypoint = color.palette.magenta
color.fg.listing.ext.ref.resolved = color.palette.darkorange
color.fg.listing.ext.ref.resolved = color.palette.teal
color.fg.listing.ext.ref.unresolved = color.fg.listing.ref.bad
color.fg.listing.fieldname = color.fg

View file

@ -382,10 +382,10 @@ public class SymbolInspector implements OptionsChangeListener {
else if (isVariableSymbol(s)) {
if (s.getSymbolType() == SymbolType.PARAMETER) {
Function function = (Function) s.getParentNamespace();
return function.hasCustomVariableStorage() ? OptionsGui.PARAMETER_CUSTOM
: OptionsGui.PARAMETER_DYNAMIC;
return function.hasCustomVariableStorage() ? OptionsGui.FUN_PARAM_CUSTOM
: OptionsGui.FUN_PARAM_DYNAMIC;
}
return OptionsGui.VARIABLE;
return OptionsGui.FUN_VARIABLE;
}
else if (isPrimarySymbol(s)) {
return OptionsGui.LABELS_PRIMARY;

View file

@ -81,9 +81,9 @@ public abstract class AbstractVariableFieldFactory extends FieldFactory {
parameterFieldOptions = new ParameterFieldOptions[2];
parameterFieldOptions[CUSTOM_PARAM_INDEX] =
new ParameterFieldOptions(OptionsGui.PARAMETER_CUSTOM);
new ParameterFieldOptions(OptionsGui.FUN_PARAM_CUSTOM);
parameterFieldOptions[DYNAMIC_PARAM_INDEX] =
new ParameterFieldOptions(OptionsGui.PARAMETER_DYNAMIC);
new ParameterFieldOptions(OptionsGui.FUN_PARAM_DYNAMIC);
for (int i = 0; i < 2; i++) {
parameterFieldOptions[i].style =
@ -122,11 +122,16 @@ public abstract class AbstractVariableFieldFactory extends FieldFactory {
}
protected Color getColor(Variable var) {
if (var instanceof Parameter) {
if (var instanceof Parameter param) {
if (param.isAutoParameter()) {
return FunctionColors.PARAM_AUTO;
}
return var.getFunction().hasCustomVariableStorage() ? FunctionColors.PARAM_CUSTOM
: FunctionColors.PARAM_DYNAMIC;
}
return FunctionColors.PARAM;
return FunctionColors.VARIABLE;
}
protected FontMetrics getMetrics(Variable var) {

View file

@ -16,6 +16,7 @@
package ghidra.app.util.viewer.field;
import java.awt.Color;
import java.awt.FontMetrics;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
@ -23,17 +24,10 @@ import java.util.List;
import javax.swing.Icon;
import javax.swing.event.ChangeListener;
import docking.widgets.fieldpanel.field.AbstractTextFieldElement;
import docking.widgets.fieldpanel.field.AttributedString;
import docking.widgets.fieldpanel.field.CompositeFieldElement;
import docking.widgets.fieldpanel.field.FieldElement;
import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.fieldpanel.support.FieldUtils;
import docking.widgets.fieldpanel.support.RowColLocation;
import docking.widgets.fieldpanel.field.*;
import docking.widgets.fieldpanel.support.*;
import ghidra.GhidraOptions;
import ghidra.app.util.ColorAndStyle;
import ghidra.app.util.ListingHighlightProvider;
import ghidra.app.util.SymbolInspector;
import ghidra.app.util.*;
import ghidra.app.util.viewer.field.ListingColors.FunctionColors;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.options.OptionsGui;
@ -41,30 +35,14 @@ import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataImage;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.data.Playable;
import ghidra.program.model.data.Union;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.LabelString;
import ghidra.program.model.listing.OperandRepresentationList;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.Variable;
import ghidra.program.model.listing.VariableOffset;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.LabelString.LabelType;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.Equate;
import ghidra.program.model.symbol.EquateTable;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceManager;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.util.EquateOperandFieldLocation;
import ghidra.program.util.OperandFieldLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.program.model.symbol.*;
import ghidra.program.util.*;
import ghidra.util.HelpLocation;
/**
@ -260,81 +238,93 @@ abstract class OperandFieldHelper extends FieldFactory {
int subOpIndex = element.getOperandSubIndex();
RowColLocation translatedLocation = btf.screenToDataLocation(row, col);
int dataCol = translatedLocation.col();
if (obj instanceof Instruction) {
Instruction inst = (Instruction) obj;
OperandRepresentationList operandRepresentationList =
codeUnitFormat.getOperandRepresentationList(inst, opIndex);
String repStr = "<UNSUPPORTED>";
Address refAddr = null;
VariableOffset variableOffset = null;
Program program = inst.getProgram();
if (operandRepresentationList == null) {
return new OperandFieldLocation(program, inst.getMinAddress(), variableOffset,
refAddr, repStr, opIndex, subOpIndex, translatedLocation.col());
}
repStr = operandRepresentationList.toString();
if (subOpIndex >= 0 && operandRepresentationList.size() > subOpIndex) {
Object rep = operandRepresentationList.get(subOpIndex);
if (rep instanceof Address) {
refAddr = (Address) rep;
}
else {
int extendedRefIndex = repStr.indexOf("=>");
if (extendedRefIndex < 0 || translatedLocation.col() <= extendedRefIndex) {
// only get variable offset if extended reference was not clicked on
variableOffset = getVariableOffset(rep);
}
refAddr = inst.getAddress(opIndex);
if (refAddr == null) {
// Check for inferred variable reference
refAddr = getVariableStorageAddress(inst, operandRepresentationList,
element.getText());
}
if (rep instanceof Equate) {
Equate equate = (Equate) rep;
return new EquateOperandFieldLocation(program, inst.getMinAddress(),
refAddr, repStr, equate, opIndex, subOpIndex, translatedLocation.col());
}
}
}
return new OperandFieldLocation(program, inst.getMinAddress(), variableOffset, refAddr,
repStr, opIndex, subOpIndex, translatedLocation.col());
return createInstructionLocation(obj, element, opIndex, subOpIndex, dataCol);
}
else if (obj instanceof Data) {
Data data = (Data) obj;
Address refAddr = null;
Program program = data.getProgram();
ReferenceManager referenceManager = program.getReferenceManager();
Address minAddress = data.getMinAddress();
Reference primaryReference = referenceManager.getPrimaryReferenceFrom(minAddress, 0);
Object value = data.getValue();
if (primaryReference != null) {
refAddr = primaryReference.getToAddress();
}
else {
if (value instanceof Address) {
refAddr = (Address) value;
}
}
if (value instanceof Scalar) {
Scalar scalar = (Scalar) value;
EquateTable equateTable = program.getEquateTable();
Equate equate = equateTable.getEquate(minAddress, opIndex, scalar.getValue());
if (equate != null) {
return new EquateOperandFieldLocation(program, minAddress, refAddr,
equate.getDisplayName(), equate, opIndex, subOpIndex,
translatedLocation.col());
}
}
return new OperandFieldLocation(program, minAddress, data.getComponentPath(), refAddr,
codeUnitFormat.getDataValueRepresentationString(data), 0, col);
return createDataLocation(obj, opIndex, subOpIndex, dataCol, col);
}
return null;
}
private ProgramLocation createDataLocation(Object obj, int opIndex, int subOpIndex,
int dataCol, int screenCol) {
Data data = (Data) obj;
Address refAddr = null;
Program program = data.getProgram();
ReferenceManager referenceManager = program.getReferenceManager();
Address minAddress = data.getMinAddress();
Reference primaryReference = referenceManager.getPrimaryReferenceFrom(minAddress, 0);
Object value = data.getValue();
if (primaryReference != null) {
refAddr = primaryReference.getToAddress();
}
else {
if (value instanceof Address) {
refAddr = (Address) value;
}
}
if (value instanceof Scalar) {
Scalar scalar = (Scalar) value;
EquateTable equateTable = program.getEquateTable();
Equate equate = equateTable.getEquate(minAddress, opIndex, scalar.getValue());
if (equate != null) {
return new EquateOperandFieldLocation(program, minAddress, refAddr,
equate.getDisplayName(), equate, opIndex, subOpIndex, dataCol);
}
}
return new OperandFieldLocation(program, minAddress, data.getComponentPath(), refAddr,
codeUnitFormat.getDataValueRepresentationString(data), 0, screenCol);
}
private ProgramLocation createInstructionLocation(Object obj, OperandFieldElement element,
int opIndex, int subOpIndex, int dataCol) {
Instruction inst = (Instruction) obj;
OperandRepresentationList opRepList =
codeUnitFormat.getOperandRepresentationList(inst, opIndex);
Address minAddress = inst.getMinAddress();
Address refAddr = null;
VariableOffset variableOffset = null;
Program program = inst.getProgram();
if (opRepList == null) {
return new OperandFieldLocation(program, minAddress, variableOffset,
refAddr, "<UNSUPPORTED>", opIndex, subOpIndex, dataCol);
}
String repStr = opRepList.toString();
if (subOpIndex >= 0 && opRepList.size() > subOpIndex) {
Object rep = opRepList.get(subOpIndex);
if (rep instanceof Address) {
refAddr = (Address) rep;
}
else {
int extendedRefIndex = repStr.indexOf("=>");
if (extendedRefIndex < 0 || dataCol <= extendedRefIndex) {
// only get variable offset if extended reference was not clicked on
variableOffset = getVariableOffset(rep);
}
refAddr = inst.getAddress(opIndex);
if (refAddr == null) {
// Check for inferred variable reference
refAddr = getVariableStorageAddress(inst, opRepList, element.getText());
}
if (rep instanceof Equate) {
Equate equate = (Equate) rep;
return new EquateOperandFieldLocation(program, minAddress,
refAddr, repStr, equate, opIndex, subOpIndex, dataCol);
}
}
}
return new OperandFieldLocation(program, minAddress, variableOffset, refAddr,
repStr, opIndex, subOpIndex, dataCol);
}
private VariableOffset getVariableOffset(Object representation) {
if (representation instanceof VariableOffset) {
return (VariableOffset) representation;
@ -463,33 +453,23 @@ abstract class OperandFieldHelper extends FieldFactory {
return null;
}
List<OperandFieldElement> elements = new ArrayList<>();
int characterOffset = createSeparatorFieldElement(inst, 0, 0, 0, 0, elements);
OpFieldResults results = new OpFieldResults(proxy);
OperandFieldElement separator = createLeadingSeparatorElement(inst);
if (separator != null) {
results.add(separator);
}
for (int opIndex = 0; opIndex < numOperands; opIndex++) {
OperandRepresentationList operandRepresentationList =
codeUnitFormat.getOperandRepresentationList(inst, opIndex);
characterOffset = addElementsForOperand(inst, elements, opIndex,
operandRepresentationList, characterOffset);
characterOffset = 0;
OpInfo opInfo = new OpInfo(inst, opIndex);
addOperandElements(opInfo, results);
results.resetCharacterOffset();
}
// There may be operands with no representation objects, so we don't want to create a
// composite field element.
if (elements.isEmpty()) {
// If this is an operand with no representation objects, don't create a visual element
if (results.isEmpty()) {
return null;
}
if (wrapOnSemicolon) {
List<FieldElement> lines = breakIntoLines(elements);
if (lines.size() == 1) {
return ListingTextField.createSingleLineTextField(this, proxy, lines.get(0),
startX + varWidth, width, hlProvider);
}
return ListingTextField.createMultilineTextField(this, proxy, lines, startX, width,
hlProvider);
}
return ListingTextField.createSingleLineTextField(this, proxy,
new CompositeFieldElement(elements), startX + varWidth, width, hlProvider);
return results.createListingTextField(varWidth);
}
private List<FieldElement> breakIntoLines(List<OperandFieldElement> elements) {
@ -518,80 +498,208 @@ abstract class OperandFieldHelper extends FieldFactory {
return fieldElements;
}
private int addElementsForOperand(Instruction inst, List<OperandFieldElement> elements,
int opIndex, OperandRepresentationList opRepList, int characterOffset) {
private void addOperandElements(OpInfo opInfo, OpFieldResults results) {
int subOpIndex = 0;
if (opRepList == null || opRepList.hasError()) {
AttributedString as =
new AttributedString(opRepList != null ? opRepList.toString() : "<UNSUPPORTED>",
badRefAttributes.colorAttribute, getMetrics(badRefAttributes.styleAttribute),
false, ListingColors.UNDERLINE);
elements.add(new OperandFieldElement(as, opIndex, subOpIndex, characterOffset));
characterOffset += as.length();
if (opInfo.isInvalid()) {
addOperandErrorElement(opInfo, results);
return;
}
else {
boolean underline = isUnderlined(inst, opIndex, opRepList.isPrimaryReferenceHidden());
for (; subOpIndex < opRepList.size(); subOpIndex++) {
characterOffset = addElement(inst, elements, opRepList.get(subOpIndex), underline,
opIndex, subOpIndex, characterOffset);
}
for (; subOpIndex < opInfo.getRepCount(); subOpIndex++) {
Object opElement = opInfo.getRepElement(subOpIndex);
addOperandElement(opInfo, results, opElement, subOpIndex);
}
// add in any separator after this operand
return createSeparatorFieldElement(inst, opIndex + 1, opIndex, subOpIndex - 1,
characterOffset, elements);
int separatorIndex = opInfo.opIndex + 1;
OperandFieldElement separator =
createSeparatorElement(opInfo, results, subOpIndex - 1, separatorIndex);
if (separator != null) {
results.add(separator);
}
}
private int addElements(Instruction inst, List<OperandFieldElement> elements, List<?> objList,
int opIndex, int subOpIndex, boolean underline, int characterOffset) {
for (Object element : objList) {
characterOffset = addElement(inst, elements, element, underline, opIndex, subOpIndex,
characterOffset);
private void addOperandErrorElement(OpInfo opInfo, OpFieldResults results) {
FontMetrics metrics = getMetrics(badRefAttributes.styleAttribute);
String text = opInfo.getRepText();
AttributedString as =
new AttributedString(text, badRefAttributes.colorAttribute, metrics,
false, ListingColors.UNDERLINE);
results.add(new OperandFieldElement(as, opInfo.opIndex, 0, results.characterOffset));
// add in any separator after this operand
int separatorIndex = opInfo.opIndex + 1;
OperandFieldElement separator = createSeparatorElement(opInfo, results, -1, separatorIndex);
if (separator != null) {
results.add(separator);
}
return characterOffset;
}
private int addElement(Instruction inst, List<OperandFieldElement> elements, Object opElem,
boolean underline, int opIndex, int subOpIndex, int characterOffset) {
private void addOperandSubElements(OpInfo opInfo, OpFieldResults results,
List<?> elements, int subOpIndex) {
if (opElem instanceof VariableOffset) {
List<Object> objList = ((VariableOffset) opElem).getObjects();
return addElements(inst, elements, objList, opIndex, subOpIndex, underline,
characterOffset);
// Special case: '[->Lib::Foo]'
// Most references will paint the label color, typically blue. For external function calls,
// the color will change depending on whether the external function is linked. Handle this
// case specially so that the arrow text will be the same color as the library name.
if (isIndirectReference(elements)) {
addIndirectReference(opInfo, results, elements, subOpIndex);
return;
}
if (opElem instanceof List) {
return addElements(inst, elements, (List<?>) opElem, opIndex, subOpIndex, underline,
characterOffset);
for (Object element : elements) {
addOperandElement(opInfo, results, element, subOpIndex);
}
ColorStyleAttributes attributes = getOpAttributes(opElem, inst, opIndex);
AttributedString as = new AttributedString(opElem.toString(), attributes.colorAttribute,
getMetrics(attributes.styleAttribute), underline, ListingColors.UNDERLINE);
elements.add(new OperandFieldElement(as, opIndex, subOpIndex, characterOffset));
if (wrapOnSemicolon && opElem instanceof Character c && c == ';') {
elements.add(LINE_BREAK);
}
return characterOffset + as.length();
}
private int createSeparatorFieldElement(Instruction instruction, int separatorIndex,
int opIndex, int subOpIndex, int characterOffset, List<OperandFieldElement> elements) {
String separator = instruction.getSeparator(separatorIndex);
private boolean isIndirectReference(List<?> elements) {
if (elements.size() != 2) {
return false;
}
Object object = elements.get(0);
String text = object.toString();
if (!text.equals(CodeUnitFormat.EXTENDED_INDIRECT_REFERENCE_DELIMITER)) {
return false;
}
Object element = elements.get(1);
return element instanceof LabelString;
}
private void addIndirectReference(OpInfo opInfo, OpFieldResults results,
List<?> elements, int subOpIndex) {
Object opElement = elements.get(1);
ColorStyleAttributes attributes = getOpAttributes(opInfo, opElement);
boolean underline = opInfo.isUnderline();
Object pointer = elements.get(0).toString();
String text = pointer + opElement.toString();
FontMetrics metrics = getMetrics(attributes.styleAttribute);
AttributedString as = new AttributedString(text, attributes.colorAttribute,
metrics, underline, ListingColors.UNDERLINE);
results.add(
new OperandFieldElement(as, opInfo.opIndex, subOpIndex, results.characterOffset));
}
private void addOperandElement(OpInfo opInfo, OpFieldResults results, Object opElement,
int subOpIndex) {
if (opElement instanceof VariableOffset variableOffset) {
addVariableElement(opInfo, results, variableOffset, subOpIndex);
return;
}
if (opElement instanceof List list) {
addOperandSubElements(opInfo, results, list, subOpIndex);
return;
}
ColorStyleAttributes attributes = getOpAttributes(opInfo, opElement);
addOperandElementWithAttributes(opInfo, results, opElement, subOpIndex, attributes);
}
private void addOperandElementWithAttributes(OpInfo opInfo, OpFieldResults results,
Object opElement, int subOpIndex, ColorStyleAttributes attributes) {
boolean underline = opInfo.isUnderline();
FontMetrics metrics = getMetrics(attributes.styleAttribute);
AttributedString as = new AttributedString(opElement.toString(), attributes.colorAttribute,
metrics, underline, ListingColors.UNDERLINE);
results.add(
new OperandFieldElement(as, opInfo.opIndex, subOpIndex, results.characterOffset));
if (wrapOnSemicolon && opElement instanceof Character c && c == ';') {
results.addLineBreak();
}
}
private void addVariableElement(OpInfo opInfo, OpFieldResults results,
VariableOffset variableOffset, int subOpIndex) {
Color color = FunctionColors.VARIABLE;
Variable variable = variableOffset.getVariable();
if (variable instanceof Parameter param) {
if (param.isAutoParameter()) {
Object opElement = opInfo.getRepElement(subOpIndex);
addAutoParameterElement(opInfo, results, param, opElement, subOpIndex);
return;
}
Function f = variable.getFunction();
color = f.hasCustomVariableStorage() ? FunctionColors.PARAM_CUSTOM
: FunctionColors.PARAM_DYNAMIC;
}
List<Object> objects = variableOffset.getObjects();
addVariableOffsetElements(opInfo, results, objects, subOpIndex, color);
}
private void addVariableOffsetElements(OpInfo opInfo, OpFieldResults results,
List<?> elements, int subOpIndex, Color color) {
ColorStyleAttributes attributes = variableRefAttributes.with(color);
boolean underline = opInfo.isUnderline();
for (Object element : elements) {
FontMetrics metrics = getMetrics(attributes.styleAttribute);
AttributedString as =
new AttributedString(element.toString(), attributes.colorAttribute,
metrics, underline, ListingColors.UNDERLINE);
results.add(
new OperandFieldElement(as, opInfo.opIndex, subOpIndex, results.characterOffset));
}
}
private void addAutoParameterElement(OpInfo opInfo, OpFieldResults results, Parameter parameter,
Object opElement, int subOpIndex) {
Color color = FunctionColors.PARAM_AUTO;
int variableStyle = variableRefAttributes.styleAttribute;
boolean underline = opInfo.isUnderline();
FontMetrics metrics = getMetrics(variableStyle);
AttributedString as = new AttributedString(opElement.toString(), color, metrics, underline,
ListingColors.UNDERLINE);
results.add(
new OperandFieldElement(as, opInfo.opIndex, subOpIndex, results.characterOffset));
}
private OperandFieldElement createSeparatorElement(OpInfo opInfo, OpFieldResults results,
int subOpIndex, int separatorIndex) {
String separator = opInfo.getSeparator(separatorIndex);
if (separator == null) {
return characterOffset;
return null;
}
if (spaceAfterSeparator) {
separator += " ";
}
AttributedString as = new AttributedString(separator, separatorAttributes.colorAttribute,
getMetrics(separatorAttributes.styleAttribute));
OperandFieldElement fieldElement =
new OperandFieldElement(as, opIndex, subOpIndex, characterOffset);
elements.add(fieldElement);
return characterOffset + fieldElement.length();
return new OperandFieldElement(as, opInfo.opIndex, subOpIndex, results.characterOffset);
}
private OperandFieldElement createLeadingSeparatorElement(Instruction instruction) {
String separator = instruction.getSeparator(0);
if (separator == null) {
return null;
}
if (spaceAfterSeparator) {
separator += " ";
}
AttributedString as = new AttributedString(separator, separatorAttributes.colorAttribute,
getMetrics(separatorAttributes.styleAttribute));
return new OperandFieldElement(as, 0, 0, 0);
}
private boolean isUnderlined(CodeUnit codeUnit, int opIndex, boolean primaryReferenceHidden) {
@ -636,10 +744,10 @@ abstract class OperandFieldHelper extends FieldFactory {
return attributes;
}
private ColorStyleAttributes getOpAttributes(Object opObject, Instruction inst, int opIndex) {
private ColorStyleAttributes getOpAttributes(OpInfo opInfo, Object opObject) {
if (opObject instanceof String) {
return getOpAttributes(inst, opIndex, inst.getProgram());
return getOpAttributes(opInfo.inst, opInfo.opIndex, opInfo.getProgram());
}
if (opObject instanceof Register) {
return registerAttributes;
@ -660,17 +768,42 @@ abstract class OperandFieldHelper extends FieldFactory {
}
return badRefAttributes;
}
if (opObject instanceof LabelString) {
LabelString label = (LabelString) opObject;
LabelString.LabelType labelType = label.getLabelType();
if (labelType == LabelString.LabelType.VARIABLE) {
return variableRefAttributes;
}
return getOpAttributes(inst, opIndex, inst.getProgram());
if (opObject instanceof LabelString labelString) {
return getLabelStringAttributes(labelString, opInfo.inst, opInfo.opIndex);
}
return separatorAttributes;
}
private ColorStyleAttributes getLabelStringAttributes(LabelString opObject, Instruction inst,
int opIndex) {
LabelString label = opObject;
LabelType labelType = label.getLabelType();
if (labelType == LabelType.VARIABLE) {
return variableRefAttributes;
}
if (labelType == LabelType.EXTERNAL) {
Symbol symbol = label.getSymbol();
ColorStyleAttributes attributes = getExternalSymbolAttributes(symbol);
if (attributes != null) {
return attributes;
}
}
return getOpAttributes(inst, opIndex, inst.getProgram());
}
private ColorStyleAttributes getExternalSymbolAttributes(Symbol symbol) {
ColorAndStyle c = inspector.getColorAndStyle(symbol);
if (c != null) {
ColorStyleAttributes newAttributes = new ColorStyleAttributes();
newAttributes.colorAttribute = c.getColor();
newAttributes.styleAttribute = c.getStyle();
return newAttributes;
}
return null;
}
private ColorStyleAttributes getRefAttributes(CodeUnit cu, int opIndex, Program p) {
ReferenceManager refMgr = p.getReferenceManager();
@ -739,10 +872,7 @@ abstract class OperandFieldHelper extends FieldFactory {
return addressAttributes;
}
/**
* Called when the fonts are first initialized or when one of the options
* changes. It looks up all the color settings and resets the its values.
*/
// Called when the fonts are first initialized or when one of the options
private void setOptions(Options options) {
separatorAttributes.colorAttribute = ListingColors.SEPARATOR;
@ -757,7 +887,7 @@ abstract class OperandFieldHelper extends FieldFactory {
scalarAttributes.styleAttribute =
options.getInt(OptionsGui.CONSTANT.getStyleOptionName(), -1);
variableRefAttributes.styleAttribute =
options.getInt(OptionsGui.VARIABLE.getStyleOptionName(), -1);
options.getInt(OptionsGui.FUN_VARIABLE.getStyleOptionName(), -1);
addressAttributes.styleAttribute =
options.getInt(OptionsGui.ADDRESS.getStyleOptionName(), -1);
badRefAttributes.styleAttribute =
@ -771,6 +901,13 @@ abstract class OperandFieldHelper extends FieldFactory {
private class ColorStyleAttributes {
private Color colorAttribute;
private int styleAttribute;
public ColorStyleAttributes with(Color color) {
ColorStyleAttributes attrs = new ColorStyleAttributes();
attrs.styleAttribute = styleAttribute;
attrs.colorAttribute = color;
return attrs;
}
}
static class OperandFieldElement extends AbstractTextFieldElement {
@ -791,9 +928,6 @@ abstract class OperandFieldHelper extends FieldFactory {
return row;
}
/**
* @see docking.widgets.fieldpanel.field.FieldElement#substring(int, int)
*/
@Override
public FieldElement substring(int start, int end) {
AttributedString as = attributedString.substring(start, end);
@ -803,13 +937,106 @@ abstract class OperandFieldHelper extends FieldFactory {
return new OperandFieldElement(as, row, operandSubIndex, column + start);
}
/**
* @see docking.widgets.fieldpanel.field.FieldElement#replaceAll(char[], char)
*/
@Override
public FieldElement replaceAll(char[] targets, char replacement) {
return new OperandFieldElement(attributedString.replaceAll(targets, replacement), row,
operandSubIndex, column);
}
}
/**
* Simple object to pass between methods for accumulating results and tracking character offset.
*/
private class OpFieldResults {
private int characterOffset;
private List<OperandFieldElement> elements = new ArrayList<>();
private ProxyObj<?> proxy;
OpFieldResults(ProxyObj<?> proxy) {
this.proxy = proxy;
}
void add(OperandFieldElement element) {
elements.add(element);
characterOffset += element.length();
}
void addLineBreak() {
elements.add(LINE_BREAK);
}
boolean isEmpty() {
return elements.isEmpty();
}
void resetCharacterOffset() {
characterOffset = 0;
}
ListingTextField createListingTextField(int varWidth) {
FieldFactory factory = OperandFieldHelper.this;
if (wrapOnSemicolon) {
List<FieldElement> lines = breakIntoLines(elements);
if (lines.size() == 1) {
return ListingTextField.createSingleLineTextField(factory, proxy,
lines.get(0), startX + varWidth, width, hlProvider);
}
return ListingTextField.createMultilineTextField(factory, proxy, lines, startX,
width,
hlProvider);
}
return ListingTextField.createSingleLineTextField(factory, proxy,
new CompositeFieldElement(elements), startX + varWidth, width, hlProvider);
}
}
/**
* Simple object to contain data and methods related to the instruction being processed and its
* operand representations.
*/
private class OpInfo {
private Instruction inst;
private int opIndex;
private OperandRepresentationList opRepList;
OpInfo(Instruction inst, int opIndex) {
this.inst = inst;
this.opIndex = opIndex;
this.opRepList = codeUnitFormat.getOperandRepresentationList(inst, opIndex);
}
boolean isInvalid() {
return opRepList == null || opRepList.hasError();
}
boolean isUnderline() {
return isUnderlined(inst, opIndex, opRepList.isPrimaryReferenceHidden());
}
int getRepCount() {
return opRepList.size();
}
Object getRepElement(int subOpIndex) {
return opRepList.get(subOpIndex);
}
String getRepText() {
return opRepList != null ? opRepList.toString() : "<UNSUPPORTED>";
}
String getSeparator(int separatorIndex) {
return inst.getSeparator(separatorIndex);
}
Program getProgram() {
return inst.getProgram();
}
}
}

View file

@ -45,7 +45,7 @@ public class VariableNameFieldFactory extends AbstractVariableFieldFactory {
/**
* Constructor
* @param model the model that the field belongs to.
* @param hsProvider the HightLightStringProvider.
* @param hlProvider the HightLightStringProvider.
* @param displayOptions the Options for display properties.
* @param fieldOptions the Options for field specific properties.
*/
@ -72,9 +72,6 @@ public class VariableNameFieldFactory extends AbstractVariableFieldFactory {
width, hlProvider);
}
/**
* @see ghidra.app.util.viewer.field.FieldFactory#getProgramLocation(int, int, ghidra.app.util.viewer.field.ListingField)
*/
@Override
public ProgramLocation getProgramLocation(int row, int col, ListingField bf) {
ProxyObj<?> proxy = bf.getProxy();
@ -88,9 +85,6 @@ public class VariableNameFieldFactory extends AbstractVariableFieldFactory {
return null;
}
/**
* @see ghidra.app.util.viewer.field.FieldFactory#getFieldLocation(ghidra.app.util.viewer.field.ListingField, BigInteger, int, ghidra.program.util.ProgramLocation)
*/
@Override
public FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
ProgramLocation loc) {
@ -111,9 +105,6 @@ public class VariableNameFieldFactory extends AbstractVariableFieldFactory {
return null;
}
/**
* @see ghidra.app.util.viewer.field.FieldFactory#acceptsType(int, java.lang.Class)
*/
@Override
public boolean acceptsType(int category, Class<?> proxyObjectClass) {
if (!Variable.class.isAssignableFrom(proxyObjectClass)) {

View file

@ -40,6 +40,7 @@ import generic.theme.GThemeDefaults.Colors.Palette;
import ghidra.GhidraOptions;
import ghidra.app.util.viewer.field.ListingColors;
import ghidra.app.util.viewer.field.ListingColors.*;
import ghidra.util.ColorUtils;
/**
* Class for displaying and manipulating field colors and fonts.
@ -65,6 +66,9 @@ public class OptionsGui extends JPanel {
public static final ScreenElement FUN_CALL_FIXUP = new ScreenElement("Function Call-Fixup", FunctionColors.CALL_FIXUP);
public static final ScreenElement FUN_NAME = new ScreenElement("Function Name", FunctionColors.NAME);
public static final ScreenElement FUN_PARAMS = new ScreenElement("Function Parameters", FunctionColors.PARAM);
public static final ScreenElement FUN_PARAM_CUSTOM = new ScreenElement("Function Parameter, Custom Storage", FunctionColors.PARAM_CUSTOM);
public static final ScreenElement FUN_PARAM_DYNAMIC = new ScreenElement("Function Parameter, Dynamic Storage", FunctionColors.PARAM_DYNAMIC);
public static final ScreenElement FUN_VARIABLE = new ScreenElement("Function Variable", FunctionColors.VARIABLE);
public static final ScreenElement FUN_TAG = new ScreenElement("Function Tag", FunctionColors.TAG);
public static final ScreenElement FUN_AUTO_PARAMS = new ScreenElement("Function Auto-Parameters", FunctionColors.PARAM_AUTO);
public static final ScreenElement FUN_RET_TYPE = new ScreenElement("Function Return Type", FunctionColors.RETURN_TYPE);
@ -73,7 +77,7 @@ public class OptionsGui extends JPanel {
public static final ScreenElement LABELS_LOCAL = new ScreenElement("Labels, Local", LabelColors.LOCAL);
public static final ScreenElement MNEMONIC = new ScreenElement("Mnemonic", MnemonicColors.NORMAL);
public static final ScreenElement MNEMONIC_OVERRIDE = new ScreenElement("Mnemonic, Override", MnemonicColors.OVERRIDE);
public static final ScreenElement MNEMONIC_UNIMPL = new ScreenElement("Unimplemented Mnemonic", MnemonicColors.UNIMPLEMENTED);
public static final ScreenElement MNEMONIC_UNIMPL = new ScreenElement("Mnemonic, Unimplemented ", MnemonicColors.UNIMPLEMENTED);
public static final ScreenElement FLOW_ARROW_ACTIVE = new ScreenElement("Flow Arrow, Active", FlowArrowColors.ACTIVE);
public static final ScreenElement FLOW_ARROW_NON_ACTIVE = new ScreenElement("Flow Arrow, Not Active", FlowArrowColors.INACTIVE);
public static final ScreenElement FLOW_ARROW_SELECTED = new ScreenElement("Flow Arrow, Selected", FlowArrowColors.SELECTED);
@ -83,9 +87,7 @@ public class OptionsGui extends JPanel {
public static final ScreenElement COMMENT_POST = new ScreenElement("Comment, Post", "Post-Comment", CommentColors.POST);
public static final ScreenElement COMMENT_PRE = new ScreenElement("Comment, Pre", "Pre-Comment", CommentColors.PRE);
public static final ScreenElement SEPARATOR = new ScreenElement("Separator", ListingColors.SEPARATOR);
public static final ScreenElement VARIABLE = new ScreenElement("Variable", FunctionColors.VARIABLE);
public static final ScreenElement PARAMETER_CUSTOM = new ScreenElement("Parameter, Custom Storage", FunctionColors.PARAM_CUSTOM);
public static final ScreenElement PARAMETER_DYNAMIC = new ScreenElement("Parameter, Dynamic Storage", FunctionColors.PARAM_DYNAMIC);
public static final ScreenElement SEPARATOR_LINE = new ScreenElement("Separator Line", Colors.BACKGROUND);
public static final ScreenElement XREF = new ScreenElement("XRef", XrefColors.DEFAULT);
public static final ScreenElement XREF_OFFCUT = new ScreenElement("XRef, Offcut", XrefColors.OFFCUT);
public static final ScreenElement XREF_READ = new ScreenElement("XRef Read", XrefColors.READ);
@ -98,19 +100,22 @@ public class OptionsGui extends JPanel {
public static final ScreenElement PCODE_RAW_VARNODE = new ScreenElement("P-code Raw Varnode", PcodeColors.VARNODE);
public static final ScreenElement PCODE_USEROP = new ScreenElement("P-code Userop", PcodeColors.USEROP);
static ScreenElement[] elements = {
ADDRESS,
BACKGROUND, BAD_REF_ADDR, BYTES,
COMMENT_AUTO, COMMENT_EOL, COMMENT_PLATE, COMMENT_POST, COMMENT_PRE, COMMENT_REPEATABLE,
COMMENT_REF_REPEAT, CONSTANT,
ENTRY_POINT, EXT_REF_RESOLVED, EXT_REF_UNRESOLVED,
FIELD_NAME, FLOW_ARROW_ACTIVE, FLOW_ARROW_NON_ACTIVE, FLOW_ARROW_SELECTED,
FUN_NAME, FUN_PARAMS, FUN_AUTO_PARAMS, FUN_PARAM_DYNAMIC, FUN_PARAM_CUSTOM, FUN_VARIABLE,
FUN_RET_TYPE, FUN_CALL_FIXUP, FUN_TAG,
LABELS_LOCAL, LABELS_NON_PRIMARY, LABELS_PRIMARY, LABELS_UNREFD,
MNEMONIC, MNEMONIC_OVERRIDE, MNEMONIC_UNIMPL,
PCODE_LINE_LABEL, PCODE_ADDR_SPACE, PCODE_RAW_VARNODE, PCODE_USEROP,
REGISTERS, SEPARATOR, UNDERLINE,
XREF, XREF_OFFCUT, XREF_READ, XREF_WRITE, XREF_OTHER };
//@formatter:on
static ScreenElement[] elements =
{ ADDRESS, BACKGROUND, BAD_REF_ADDR, BYTES, COMMENT_AUTO, COMMENT_EOL, COMMENT_PLATE,
COMMENT_POST, COMMENT_PRE, COMMENT_REPEATABLE, COMMENT_REF_REPEAT, CONSTANT,
ENTRY_POINT, EXT_REF_RESOLVED, EXT_REF_UNRESOLVED, FIELD_NAME, FLOW_ARROW_ACTIVE,
FLOW_ARROW_NON_ACTIVE, FLOW_ARROW_SELECTED, FUN_CALL_FIXUP, FUN_NAME, FUN_PARAMS,
FUN_AUTO_PARAMS, FUN_RET_TYPE, FUN_TAG, LABELS_LOCAL, LABELS_NON_PRIMARY,
LABELS_PRIMARY, LABELS_UNREFD, MNEMONIC, MNEMONIC_OVERRIDE, PARAMETER_CUSTOM,
PARAMETER_DYNAMIC, PCODE_LINE_LABEL, PCODE_ADDR_SPACE, PCODE_RAW_VARNODE, PCODE_USEROP,
REGISTERS, SEPARATOR, UNDERLINE, MNEMONIC_UNIMPL, VARIABLE, XREF, XREF_OFFCUT,
XREF_READ, XREF_WRITE, XREF_OTHER };
private Map<Integer, FontMetrics> metricsMap = new HashMap<>();
private JList<ScreenElement> namesList;
@ -167,28 +172,31 @@ public class OptionsGui extends JPanel {
}
else {
setSelectedIndex(index);
setSelectedFieldElement(index);
}
});
setSelectedIndex(0);
colorChooser.getSelectionModel().addChangeListener(e -> {
Color c = colorChooser.getColor();
Color oldColor = elements[selectedIndex].getColor();
elements[selectedIndex].setColor(c);
colorPanel.setBackground(c);
genLayouts();
fieldPanel.setBackgroundColor(BACKGROUND.getColor());
enableApply();
if (!ColorUtils.hasSameRgb(oldColor, c)) {
enableApply();
}
});
ActionListener styleListener = e -> {
updateStyle();
genLayouts();
fieldPanel.setBackgroundColor(BACKGROUND.getColor());
enableApply();
};
ActionListener familyListener = e -> {
updateFonts();
genLayouts();
fieldPanel.setBackgroundColor(BACKGROUND.getColor());
enableApply();
};
@ -217,7 +225,7 @@ public class OptionsGui extends JPanel {
}
/**
* callback for when the selected display field changes.
* Callback for when the selected display field changes.
*
* @param index the index in the JList of the selected field.
*/
@ -243,6 +251,26 @@ public class OptionsGui extends JPanel {
}
}
private void setSelectedFieldElement(int index) {
ListModel<ScreenElement> model = namesList.getModel();
ScreenElement screenElement = model.getElementAt(index);
SimpleLayoutModel layoutModel = (SimpleLayoutModel) fieldPanel.getLayoutModel();
FieldSelection selection = layoutModel.getFieldSelection(screenElement);
if (selection == null) {
fieldPanel.clearSelection();
return;
}
FieldRange range = selection.getFieldRange(0);
FieldLocation loc = range.getStart();
fieldPanel.setCursorPosition(loc.getIndex(), loc.getFieldNum(), loc.getRow(), loc.getCol());
fieldPanel.setSelection(selection);
fieldPanel.scrollTo(loc);
fieldPanel.center(loc);
}
public void setBaseFont(Font font) {
baseFont = font;
baseMetrics = getFontMetrics(font);
@ -515,6 +543,8 @@ public class OptionsGui extends JPanel {
lb.add(".........", SEPARATOR);
list.add(lb.getLayout());
list.add(blankLine());
lb = new LayoutBuilder(1);
lb.add(" ", null);
lb.add("sprintf", LABELS_NON_PRIMARY);
@ -557,6 +587,8 @@ public class OptionsGui extends JPanel {
lb.add(".........", SEPARATOR);
list.add(lb.getLayout());
list.add(blankLine());
lb = new LayoutBuilder(2);
lb.add(" ", null);
lb.add("LAB2000", LABELS_PRIMARY);
@ -610,6 +642,9 @@ public class OptionsGui extends JPanel {
lb.add("33", CONSTANT);
list.add(lb.getLayout());
list.add(blankLine());
list.add(blankLine());
lb = new LayoutBuilder(1);
lb.add(" ", null);
lb.add("// This is a function comment", COMMENT_EOL);
@ -639,30 +674,30 @@ public class OptionsGui extends JPanel {
lb = new LayoutBuilder(3);
lb.add(" ", null);
lb.add("12 ", PARAMETER_DYNAMIC);
lb.add("DWord ", PARAMETER_DYNAMIC);
lb.add("param_12 ", PARAMETER_DYNAMIC);
lb.add("12 ", FUN_PARAM_DYNAMIC);
lb.add("DWord ", FUN_PARAM_DYNAMIC);
lb.add("param_12 ", FUN_PARAM_DYNAMIC);
list.add(lb.getLayout());
lb = new LayoutBuilder(3);
lb.add(" ", null);
lb.add("8 ", PARAMETER_CUSTOM);
lb.add("DWord ", PARAMETER_CUSTOM);
lb.add("param_8 ", PARAMETER_CUSTOM);
lb.add("8 ", FUN_PARAM_CUSTOM);
lb.add("DWord ", FUN_PARAM_CUSTOM);
lb.add("param_8 ", FUN_PARAM_CUSTOM);
list.add(lb.getLayout());
lb = new LayoutBuilder(3);
lb.add(" ", null);
lb.add("4 ", PARAMETER_CUSTOM);
lb.add("Word ", PARAMETER_CUSTOM);
lb.add("param_4 ", PARAMETER_CUSTOM);
lb.add("4 ", FUN_PARAM_CUSTOM);
lb.add("Word ", FUN_PARAM_CUSTOM);
lb.add("param_4 ", FUN_PARAM_CUSTOM);
list.add(lb.getLayout());
lb = new LayoutBuilder(3);
lb.add(" ", null);
lb.add("-4 ", VARIABLE);
lb.add("Float ", VARIABLE);
lb.add("local_4 ", VARIABLE);
lb.add("-4 ", FUN_VARIABLE);
lb.add("Float ", FUN_VARIABLE);
lb.add("local_4 ", FUN_VARIABLE);
list.add(lb.getLayout());
lb = new LayoutBuilder(2);
@ -689,8 +724,14 @@ public class OptionsGui extends JPanel {
}
}
private Layout blankLine() {
LayoutBuilder lb = new LayoutBuilder(1);
lb.add(" ", SEPARATOR_LINE);
return lb.getLayout();
}
/**
* updates the style of the field at the selected index.
* Updates the style of the field at the selected index.
*/
private void updateStyle() {
if (customCheckbox.isSelected()) {
@ -839,12 +880,75 @@ public class OptionsGui extends JPanel {
return BigInteger.valueOf(layouts.length);
}
FieldSelection getFieldSelection(ScreenElement element) {
List<FieldRange> ranges = getAllRanges(element);
if (ranges.isEmpty()) {
return null;
}
FieldSelection selection = new FieldSelection();
for (FieldRange r : ranges) {
selection.addRange(r);
}
return selection;
}
// finds all places this element is used; consecutive fields will be combined
List<FieldRange> getAllRanges(ScreenElement element) {
List<FieldRange> ranges = new ArrayList<>();
for (int index = 0; index < layouts.length; index++) {
SingleRowLayout rowLayout = (SingleRowLayout) layouts[index];
getRangesForRow(rowLayout, element, index, ranges);
}
return ranges;
}
private void getRangesForRow(SingleRowLayout layout, ScreenElement element, int index,
List<FieldRange> ranges) {
FieldRange lastRange = null;
int n = layout.getNumFields();
for (int i = 0; i < n; i++) {
ScreenElementTextField field = (ScreenElementTextField) layout.getField(i);
ScreenElement fieldElement = field.getScreenElement();
String text = field.getText();
if (element != fieldElement) {
lastRange = null;
continue;
}
int start = 0;
if (lastRange == null) {
start = getNonWhitespaceStart(text);
}
lastRange = new FieldRange(
new FieldLocation(index, i, 0, start),
new FieldLocation(index, i, 0, start + text.trim().length()));
ranges.add(lastRange);
}
}
}
private int getNonWhitespaceStart(String text) {
int start = 0;
for (int j = 0; j < text.length(); j++) {
char c = text.charAt(j);
if (!Character.isWhitespace(c)) {
break;
}
start++;
}
return start;
}
/**
* Class to create the layouts for the preview panel.
*/
class LayoutBuilder {
private class LayoutBuilder {
private ClippingTextField[] fields;
int startPos;
int fieldNum;

View file

@ -1251,8 +1251,8 @@ public class CodeUnitFormat {
}
result = addBlockName(program, toAddress, result, refBlock, withBlockName);
LabelType labelType = (toSymbol != null && toSymbol.isExternal()) ? LabelString.EXTERNAL
: LabelString.CODE_LABEL;
LabelType labelType = (toSymbol != null && toSymbol.isExternal()) ? LabelType.EXTERNAL
: LabelType.CODE_LABEL;
LabelString label = new LabelString(result, labelType);
// Apply extended pointer markup if needed
@ -1291,8 +1291,8 @@ public class CodeUnitFormat {
Symbol symbol = program.getSymbolTable().getSymbol(referencesFrom[0]);
if (symbol != null && !symbol.isDynamic()) {
String result = getSymbolLabelString(program, symbol, ref.getFromAddress());
return new LabelString(result,
symbol.isExternal() ? LabelString.EXTERNAL : LabelString.CODE_LABEL);
return new LabelString(result, symbol,
symbol.isExternal() ? LabelType.EXTERNAL : LabelType.CODE_LABEL);
}
return null;
}

View file

@ -12,6 +12,7 @@ color.fg.decompiler.type = color.palette.blue
color.fg.decompiler.parameter = color.palette.purple
color.fg.decompiler.global = color.palette.darkcyan
color.fg.decompiler.special = color.palette.crimson
color.fg.decompiler.external.function = color.palette.fuchsia
color.bg.decompiler.current.variable = color.palette.highlight.transparent.yellow

View file

@ -26,6 +26,7 @@ import docking.widgets.fieldpanel.field.*;
import docking.widgets.fieldpanel.listener.IndexMapper;
import docking.widgets.fieldpanel.listener.LayoutModelListener;
import docking.widgets.fieldpanel.support.*;
import generic.theme.GColor;
import ghidra.app.decompiler.*;
import ghidra.app.util.SymbolInspector;
import ghidra.app.util.viewer.field.CommentUtils;
@ -41,6 +42,9 @@ import ghidra.util.UndefinedFunction;
*/
public class ClangLayoutController implements LayoutModel, LayoutModelListener {
private static GColor COLOR_EXTERNAL_FUNCTION =
new GColor("color.fg.decompiler.external.function");
private int maxWidth;
private int indentWidth;
private SymbolInspector symbolInspector;
@ -202,22 +206,44 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
private Color getTokenColor(ClangToken token) {
Color tokenColor = syntaxColor[token.getSyntaxType()];
if (token instanceof ClangFuncNameToken clangFunctionToken) {
Program program = decompilerPanel.getProgram();
Function function = DecompilerUtils.getFunction(program, clangFunctionToken);
if (isValidFunction(function)) {
Symbol symbol = function.getSymbol();
tokenColor = symbolInspector.getColor(symbol);
return getFunctionColor(function);
}
}
Color tokenColor = syntaxColor[token.getSyntaxType()];
if (tokenColor != null) {
return tokenColor;
}
return syntaxColor[ClangToken.ERROR_COLOR];
}
private Color getFunctionColor(Function function) {
Symbol symbol = function.getSymbol();
// For now we have decided that any external function, linked or not, will be one color, as
// this makes it easy for the user to identify external function calls. Other functions will
// be colored according to the SymbolInspector. If we use the SymbolInspector for all
// colors, then some of the color values will be very close to some of the colors used by
// the Decompiler. For example, non-linked external functions default to red and linked
// external functions default to green.
if (function.isExternal()) {
return COLOR_EXTERNAL_FUNCTION;
}
if (function.isThunk()) {
Function thunkedFunction = function.getThunkedFunction(true);
if (thunkedFunction.isExternal()) {
return COLOR_EXTERNAL_FUNCTION;
}
}
return symbolInspector.getColor(symbol);
}
private boolean isValidFunction(Function f) {
return f != null && !(f instanceof UndefinedFunction);
}

View file

@ -86,7 +86,7 @@ public class FieldLocation implements Comparable<FieldLocation> {
* @param index the index of the layout containing the location
* @param fieldNum the index of the field in the layout containing the location
* @param row the text row in the field containing the location.
* @param col the character position the row containing the location.
* @param col the character position in the row containing the location.
*/
public FieldLocation(int index, int fieldNum, int row, int col) {
this(BigInteger.valueOf(index), fieldNum, row, col);

View file

@ -18,6 +18,8 @@ package ghidra.util;
import java.awt.Color;
import java.util.Comparator;
import generic.theme.GColor;
public class ColorUtils {
private static final int MAX_COLOR_VALUE = 255;
@ -348,6 +350,26 @@ public class ColorUtils {
return new Color(red, green, blue, alpha);
}
/**
* Returns true if both colors are not null and have the same RGB value. This is useful to
* compare colors that may have different classes, such as {@link Color} and {@link GColor}.
*
* @param c1 the first color
* @param c2 the second color
* @return true if the colors have the same RGB value
*/
public static boolean hasSameRgb(Color c1, Color c2) {
int rgb1 = 0;
int rgb2 = 0;
if (c1 != null) {
rgb1 = c1.getRGB();
}
if (c2 != null) {
rgb2 = c2.getRGB();
}
return rgb1 == rgb2;
}
/**
* Blender of colors
*/

View file

@ -15,22 +15,33 @@
*/
package ghidra.program.model.listing;
import ghidra.program.model.symbol.Symbol;
public class LabelString {
public enum LabelType { CODE_LABEL, VARIABLE, EXTERNAL }
public static final LabelType CODE_LABEL = LabelType.CODE_LABEL;
public static final LabelType VARIABLE = LabelType.VARIABLE;
public static final LabelType EXTERNAL = LabelType.EXTERNAL;
public enum LabelType {
CODE_LABEL, VARIABLE, EXTERNAL
}
private final String label;
private final LabelType type;
private Symbol symbol;
public LabelString(String label, LabelType type) {
this.label = label;
this.type = type;
}
public LabelString(String label, Symbol symbol, LabelType type) {
this.label = label;
this.symbol = symbol;
this.type = type;
}
public Symbol getSymbol() {
return symbol;
}
@Override
public String toString() {
return label;

View file

@ -15,12 +15,12 @@
*/
package ghidra.program.model.listing;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.LabelString.LabelType;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.util.SystemUtilities;
@ -43,7 +43,7 @@ public class VariableOffset {
/**
* Constructor for an implied variable reference.
* @param variable function variable
* @param var function variable
* @param offset offset into variable
* @param indirect if true and variable data-type is a pointer, the offset
* is relative to underlying data-type of the pointer-type. This should generally be
@ -51,8 +51,8 @@ public class VariableOffset {
* whereas it would be false for stack-references.
* @param dataAccess true if content of variable is being read and/or written
*/
public VariableOffset(Variable variable, long offset, boolean indirect, boolean dataAccess) {
this.variable = variable;
public VariableOffset(Variable var, long offset, boolean indirect, boolean dataAccess) {
this.variable = Objects.requireNonNull(var, "Variable reference not bound to a variable");
this.offset = offset;
this.indirect = indirect;
this.dataAccess = dataAccess;
@ -65,20 +65,14 @@ public class VariableOffset {
*/
public VariableOffset(Reference ref, Variable var) {
indirect = false;
variable = var;
if (variable == null) {
throw new IllegalArgumentException("Variable reference not bound to a variable");
}
this.indirect = false;
this.variable = Objects.requireNonNull(var, "Variable reference not bound to a variable");
RefType rt = ref.getReferenceType();
dataAccess = rt.isRead() || rt.isWrite();
this.dataAccess = rt.isRead() || rt.isWrite();
if (ref instanceof StackReference && variable.isStackVariable()) {
offset = variable.getStackOffset();
offset = ((StackReference) ref).getStackOffset() - variable.getStackOffset();
this.offset = ((StackReference) ref).getStackOffset() - variable.getStackOffset();
}
}
/**
@ -94,6 +88,7 @@ public class VariableOffset {
/**
* Sets the original replaced sub-operand Register.
* @param reg the register
*/
public void setReplacedElement(Register reg) {
replacedElement = reg;
@ -107,9 +102,6 @@ public class VariableOffset {
return replacedElement;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
@ -204,7 +196,7 @@ public class VariableOffset {
}
List<Object> list = new ArrayList<>();
list.add(new LabelString(name.toString(), LabelString.VARIABLE));
list.add(new LabelString(name.toString(), LabelType.VARIABLE));
if (absOffset != 0 || scalarAdjustment != 0) {
long adjustedOffset = (offset < 0 ? -absOffset : absOffset) + scalarAdjustment;