mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch 'origin/GP-1-dragonmacher-function-color-fix--SQUASHED'
This commit is contained in:
commit
c7aa190b40
13 changed files with 669 additions and 290 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,49 +238,19 @@ 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) {
|
||||
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();
|
||||
|
@ -325,14 +273,56 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
Equate equate = equateTable.getEquate(minAddress, opIndex, scalar.getValue());
|
||||
if (equate != null) {
|
||||
return new EquateOperandFieldLocation(program, minAddress, refAddr,
|
||||
equate.getDisplayName(), equate, opIndex, subOpIndex,
|
||||
translatedLocation.col());
|
||||
equate.getDisplayName(), equate, opIndex, subOpIndex, dataCol);
|
||||
}
|
||||
}
|
||||
return new OperandFieldLocation(program, minAddress, data.getComponentPath(), refAddr,
|
||||
codeUnitFormat.getDataValueRepresentationString(data), 0, col);
|
||||
codeUnitFormat.getDataValueRepresentationString(data), 0, screenCol);
|
||||
}
|
||||
return null;
|
||||
|
||||
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) {
|
||||
|
@ -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();
|
||||
}
|
||||
else {
|
||||
boolean underline = isUnderlined(inst, opIndex, opRepList.isPrimaryReferenceHidden());
|
||||
for (; subOpIndex < opRepList.size(); subOpIndex++) {
|
||||
characterOffset = addElement(inst, elements, opRepList.get(subOpIndex), underline,
|
||||
opIndex, subOpIndex, characterOffset);
|
||||
if (opInfo.isInvalid()) {
|
||||
addOperandErrorElement(opInfo, results);
|
||||
return;
|
||||
}
|
||||
|
||||
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 boolean isIndirectReference(List<?> elements) {
|
||||
if (elements.size() != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private int createSeparatorFieldElement(Instruction instruction, int separatorIndex,
|
||||
int opIndex, int subOpIndex, int characterOffset, List<OperandFieldElement> elements) {
|
||||
String separator = instruction.getSeparator(separatorIndex);
|
||||
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,15 +768,40 @@ 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) {
|
||||
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());
|
||||
}
|
||||
return separatorAttributes;
|
||||
|
||||
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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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());
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue