diff --git a/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm b/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm index d951ac960f..d534c02f17 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm @@ -936,6 +936,33 @@
Selection Color - Set the Browser Selection color.
+ ++Max Template Depth - Sets the depth to display nested templates. A + depth of 0 completely simplifies the entire template, while a depth of 1 will + show 1 level of templates.For example, if the name was "foo<char,bar<int, dog<char>>", + a nesting depth of 0 would display "foo<>" and an nesting depth of 1 would display + "foo<char, bar<>>". +
+ +Max Template Length - This is the maximum length any template string will display. + If the template string exceeds this length, the middle part of the template will be replaced + with "..." to get the string down to the minimum length. For example, the string + "foo<abcdefghijklmnopqrstuvwxyz,0123456789>" would display something like "foo<abcd...6789>" + if the max length was set to 10. Note that this restriction is applied AFTER + any simplifications from the nesting depth. +
+ +Min Template Length - This is the minimum length of a template before template + simplification is applied. In other words, if the template string is less than this length, + then the template will not be simplified. This is done so that simple templates such as + "foo<char>" are not simplified to "foo<>", even if the template nesting depth is set to 0. +
+ +Simplify Templated Names - This turns the entire templating simplification feature + on or off. If this is off, none of the other option have any effect.
+
");
}
-
+ TemplateSimplifier simplifier = new TemplateSimplifier();
+ simplifier.setEnabled(false);
CodeUnitFormatOptions formatOptions = new CodeUnitFormatOptions(
options.isShowBlockNameInOperands() ? CodeUnitFormatOptions.ShowBlockName.NON_LOCAL
: CodeUnitFormatOptions.ShowBlockName.NEVER,
@@ -95,7 +97,8 @@ class ProgramTextWriter {
true, // include extended reference markup
true, // include scalar adjustment
true, // include library names in namespace
- true // follow referenced pointers
+ true, // follow referenced pointers
+ simplifier // disabled simplifier
);
CodeUnitFormat cuFormat = new CodeUnitFormat(formatOptions);
@@ -628,9 +631,9 @@ class ProgramTextWriter {
if (options.isHTML()) {
Reference ref =
cu.getProgram()
- .getReferenceManager()
- .getPrimaryReferenceFrom(cuAddress,
- i);
+ .getReferenceManager()
+ .getPrimaryReferenceFrom(cuAddress,
+ i);
addReferenceLinkedText(ref, opReps[i], true);
}
else {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/template/TemplateSimplifier.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/template/TemplateSimplifier.java
new file mode 100644
index 0000000000..d438fa2893
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/template/TemplateSimplifier.java
@@ -0,0 +1,300 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.template;
+
+import ghidra.GhidraOptions;
+import ghidra.framework.options.Options;
+import ghidra.framework.options.ToolOptions;
+import ghidra.util.HelpLocation;
+
+/**
+ * Class for simplify names with template data. This class can be used with tool options or
+ * as a stand alone configurable simplifier.
+ */
+public class TemplateSimplifier {
+ public static final String SUB_OPTION_NAME = "Templates";
+
+ public static final String SIMPLIFY_TEMPLATES_OPTION =
+ SUB_OPTION_NAME + ".Simplify Templated Names";
+ public static final String TEMPLATE_NESTING_DEPTH_OPTION =
+ SUB_OPTION_NAME + ".Max Template Depth";
+ public static final String MAX_TEMPLATE_LENGTH_OPTION =
+ SUB_OPTION_NAME + ".Max Template Length";
+ public static final String MIN_TEMPLATE_LENGTH_OPTION =
+ SUB_OPTION_NAME + ".Min Template Length";
+
+ public static final String SIMPLY_TEMPLATES_DESCRIPTION =
+ "Determines whether to diplay templated names in a simplified form.";
+ public static final String TEMPLATE_NESTING_DEPTH_DESCRIPTION =
+ "Maximum template depth to display when simplify templated names.";
+ public static final String MAX_TEMPLATE_LENGTH_DESCRIPTION =
+ "Maximum number of characters to display in a template before truncating the name in the middle.";
+ public static final String MIN_TEMPLATE_LENGTH_DESCRIPTION =
+ "Minumum size of template to be simplified";
+
+ private boolean doSimplify = true;
+ private int templateNestingDepth = 0;
+ private int maxTemplateLength = 20;
+ private int minTemplateLength = 10;
+
+ /**
+ * Constructor to use for a TemplateSimplifier that doesn't use values from ToolOptions
+ */
+ public TemplateSimplifier() {
+ // constructs using standard simplifying options.
+ }
+
+ /**
+ * Constructor to use for a TemplateSimplifier that operates using the current values in
+ * the tool options
+ * @param fieldOptions the "Listing Field" options
+ */
+ public TemplateSimplifier(ToolOptions fieldOptions) {
+ checkForCorrectOptions(fieldOptions);
+ ensureRegistered(fieldOptions);
+ loadOptions(fieldOptions);
+
+ }
+
+ /**
+ * Sets the template nesting depth to be simplified. A depth of 0 simplifies the entire
+ * template portion of the name (everything in between <>). A depth of 1 leaves one level of
+ * template information
+ * @param depth the nesting depth
+ */
+ public void setNestingDepth(int depth) {
+ this.templateNestingDepth = depth;
+ }
+
+ /**
+ * Returns the nesting depth for simplification
+ * @return the nesting depth for simplification
+ */
+ public int getNestingDepth() {
+ return templateNestingDepth;
+ }
+
+ /**
+ * Sets the maximum length do display the template portion. If, after any nesting,
+ * simplification, the resulting template string is longer that the max length, the middle
+ * portion will be replaced with "..." to reduce the template string to the given max length.
+ * @param maxLength the max length of a template to display
+ */
+ public void setMaxTemplateLength(int maxLength) {
+ this.maxTemplateLength = maxLength;
+ }
+
+ /**
+ * Gets the maximum length that a template will display.
+ * @return the maximum length that a template will display
+ */
+ public int getMaxTemplateLength() {
+ return maxTemplateLength;
+ }
+
+ /**
+ * Sets if this TemplateSimplifier is enabled. If disabled, the {@link #simplify(String)}
+ * method will return the input string.
+ * @param doSimplify true to do simplification, false to do nothing
+ */
+ public void setEnabled(boolean doSimplify) {
+ this.doSimplify = doSimplify;
+ }
+
+ /**
+ * Returns if this TemplateSimplifier is enabled.
+ * @return if this TemplateSimplifier is enabled
+ */
+ public boolean isEnabled() {
+ return doSimplify;
+ }
+
+ /**
+ * Sets the minimum length for a template string to be simplified. In other words, template
+ * strings less than this length will not be changed.
+ * @param minLength the minimum length to simplify
+ */
+ public void setMinimumTemplateLength(int minLength) {
+ this.minTemplateLength = minLength;
+ }
+
+ /**
+ * Returns the minimum length of a template string that will be simplified.
+ * @return the minimum length of a template string that will be simplified.
+ */
+ public int getMinimumTemplateLength() {
+ return minTemplateLength;
+ }
+
+ /**
+ * Simplifies any template string in the given input base on the current simplification
+ * settings.
+ * @param input the input string to be simplified
+ * @return a simplified string
+ */
+ public String simplify(String input) {
+ if (!doSimplify) {
+ return input;
+ }
+ return doSimplify(input, templateNestingDepth);
+ }
+
+ /**
+ * Reloads the current simplification settings from the given field options
+ * @param fieldOptions the options to retrieve the simplification settings.
+ */
+ public void reloadFromOptions(ToolOptions fieldOptions) {
+ checkForCorrectOptions(fieldOptions);
+ loadOptions(fieldOptions);
+ }
+
+ /**
+ * Notification that options have changed
+ * @param options the options object that has changed values
+ * @param optionName the name of the options that changed
+ * @param oldValue the old value for the option that changed
+ * @param newValue the new value for the option that changed
+ * @return true if the option that changed was a template simplification option
+ */
+ public boolean fieldOptionsChanged(Options options, String optionName, Object oldValue,
+ Object newValue) {
+ if (optionName.equals(SIMPLIFY_TEMPLATES_OPTION)) {
+ doSimplify = (Boolean) newValue;
+ return true;
+ }
+ if (optionName.equals(TEMPLATE_NESTING_DEPTH_OPTION)) {
+ templateNestingDepth = (Integer) newValue;
+ return true;
+ }
+ if (optionName.equals(MAX_TEMPLATE_LENGTH_OPTION)) {
+ maxTemplateLength = (Integer) newValue;
+ return true;
+ }
+ if (optionName.equals(MIN_TEMPLATE_LENGTH_OPTION)) {
+ minTemplateLength = (Integer) newValue;
+ return true;
+ }
+
+ return false;
+ }
+
+ private String doSimplify(String input, int depth) {
+ StringBuilder builder = new StringBuilder();
+ int pos = 0;
+ TemplateString ts;
+ while ((ts = findTemplateString(input, pos)) != null) {
+ builder.append(input.substring(pos, ts.start));
+ String template = ts.getTemplate();
+ if (depth == 0) {
+ builder.append("<");
+ if (template.length() <= minTemplateLength) {
+ builder.append(template);
+ }
+ builder.append(">");
+ }
+ else {
+ builder.append("<");
+ String simplifiedTemplate = doSimplify(template, depth - 1);
+ if (simplifiedTemplate.length() > maxTemplateLength) {
+ simplifiedTemplate = middleTruncate(template);
+ }
+ builder.append(simplifiedTemplate);
+ builder.append(">");
+ }
+ pos = ts.end + 1;
+ }
+ builder.append(input.substring(pos));
+ return builder.toString();
+ }
+
+ private String middleTruncate(String input) {
+ int partSize = maxTemplateLength / 2;
+ return input.substring(0, partSize) + "..." + input.substring(input.length() - partSize);
+ }
+
+ private static TemplateString findTemplateString(String input, int pos) {
+ for (int i = pos; i < input.length(); i++) {
+ char c = input.charAt(i);
+ if (c == '<') {
+ int end = findMatchingEnd(input, i + 1);
+ if (end > i) {
+ return new TemplateString(input, i, end);
+ }
+ }
+ }
+ return null;
+ }
+
+ private static int findMatchingEnd(String input, int start) {
+ int depth = 0;
+ for (int i = start; i < input.length(); i++) {
+ char c = input.charAt(i);
+ if (c == '>') {
+ if (depth == 0) {
+ return i;
+ }
+ depth--;
+ }
+ else if (c == '<') {
+ depth++;
+ }
+ }
+ return -1;
+ }
+
+ private record TemplateString(String input, int start, int end) {
+ String getTemplate() {
+ return input.substring(start + 1, end); // don't include the enclosing <> chars
+ }
+ }
+
+ private void loadOptions(ToolOptions options) {
+ doSimplify = options.getBoolean(SIMPLIFY_TEMPLATES_OPTION, doSimplify);
+ templateNestingDepth = options.getInt(TEMPLATE_NESTING_DEPTH_OPTION, templateNestingDepth);
+ maxTemplateLength = options.getInt(MAX_TEMPLATE_LENGTH_OPTION, maxTemplateLength);
+ minTemplateLength = options.getInt(MIN_TEMPLATE_LENGTH_OPTION, minTemplateLength);
+ }
+
+ private void checkForCorrectOptions(ToolOptions fieldOptions) {
+ if (!GhidraOptions.CATEGORY_BROWSER_FIELDS.equals(fieldOptions.getName())) {
+ throw new IllegalArgumentException(
+ "Expected options named \"" + GhidraOptions.CATEGORY_BROWSER_FIELDS + "\", not \"" +
+ fieldOptions.getName() + "\"");
+ }
+ }
+
+ private void ensureRegistered(Options options) {
+ if (options.isRegistered(SIMPLIFY_TEMPLATES_OPTION)) {
+ return;
+ }
+
+ HelpLocation help = new HelpLocation("CodeBrowserPlugin", "Template Display Options");
+
+ options.getOptions(SUB_OPTION_NAME).setOptionsHelpLocation(help);
+
+ options.registerOption(SIMPLIFY_TEMPLATES_OPTION, doSimplify, help,
+ SIMPLY_TEMPLATES_DESCRIPTION);
+
+ options.registerOption(TEMPLATE_NESTING_DEPTH_OPTION, templateNestingDepth, help,
+ TEMPLATE_NESTING_DEPTH_DESCRIPTION);
+
+ options.registerOption(MAX_TEMPLATE_LENGTH_OPTION, maxTemplateLength, help,
+ MAX_TEMPLATE_LENGTH_DESCRIPTION);
+
+ options.registerOption(MIN_TEMPLATE_LENGTH_OPTION, minTemplateLength, help,
+ MIN_TEMPLATE_LENGTH_DESCRIPTION);
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BrowserCodeUnitFormatOptions.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BrowserCodeUnitFormatOptions.java
index c2c52f4c31..9b0993ece7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BrowserCodeUnitFormatOptions.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BrowserCodeUnitFormatOptions.java
@@ -20,6 +20,7 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import ghidra.GhidraOptions;
+import ghidra.app.util.template.TemplateSimplifier;
import ghidra.framework.options.*;
import ghidra.program.model.listing.CodeUnitFormatOptions;
import ghidra.util.HelpLocation;
@@ -103,7 +104,7 @@ public class BrowserCodeUnitFormatOptions extends CodeUnitFormatOptions
BrowserCodeUnitFormatOptions(ToolOptions fieldOptions, boolean autoUpdate) {
this.fieldOptions = fieldOptions;
this.displayOptions = new OptionsBasedDataTypeDisplayOptions(fieldOptions);
-
+ templateSimplifier = new TemplateSimplifier(fieldOptions);
boolean exists = fieldOptions.isRegistered(NAMESPACE_OPTIONS);
if (!exists) {
@@ -144,7 +145,10 @@ public class BrowserCodeUnitFormatOptions extends CodeUnitFormatOptions
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue,
Object newValue) {
- if (optionName.equals(GhidraOptions.SHOW_BLOCK_NAME_OPTION) ||
+ if (templateSimplifier.fieldOptionsChanged(options, optionName, oldValue, newValue)) {
+ notifyListeners();
+ }
+ else if (optionName.equals(GhidraOptions.SHOW_BLOCK_NAME_OPTION) ||
optionName.equals(REGISTER_VARIABLE_MARKUP_OPTION) ||
optionName.equals(STACK_VARIABLE_MARKUP_OPTION) ||
optionName.equals(INFERRED_VARIABLE_MARKUP_OPTION) ||
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldFactory.java
index 4785a87c67..fdb75bf461 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldFactory.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FieldFactory.java
@@ -21,6 +21,7 @@ import java.math.BigInteger;
import docking.widgets.fieldpanel.support.FieldLocation;
import generic.theme.Gui;
import ghidra.app.util.HighlightProvider;
+import ghidra.app.util.template.TemplateSimplifier;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.framework.options.Options;
@@ -54,6 +55,7 @@ public abstract class FieldFactory implements ExtensionPoint {
protected String colorOptionName;
protected String styleOptionName;
+ private TemplateSimplifier templateSimplifier;
/**
* Base constructor
@@ -72,14 +74,14 @@ public abstract class FieldFactory implements ExtensionPoint {
styleOptionName = name + " Style";
width = 100;
-
+ templateSimplifier = model.getFormatManager().getTemplateSimplifier();
initDisplayOptions(displayOptions);
initFieldOptions(fieldOptions);
}
protected void initFieldOptions(Options fieldOptions) {
fieldOptions.getOptions(name)
- .setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", name));
+ .setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", name));
}
protected void initDisplayOptions(Options displayOptions) {
@@ -314,4 +316,8 @@ public abstract class FieldFactory implements ExtensionPoint {
fontMetrics[i] = Toolkit.getDefaultToolkit().getFontMetrics(font);
}
}
+
+ protected String simplifyTemplates(String input) {
+ return templateSimplifier.simplify(input);
+ }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionSignatureFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionSignatureFieldFactory.java
index 5925bb905a..77bda1c845 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionSignatureFieldFactory.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/FunctionSignatureFieldFactory.java
@@ -59,7 +59,7 @@ public class FunctionSignatureFieldFactory extends FieldFactory {
* @param fieldOptions the Options for field specific properties.
*/
public FunctionSignatureFieldFactory(FieldFormatModel model, HighlightProvider hlProvider,
- Options displayOptions, Options fieldOptions) {
+ ToolOptions displayOptions, ToolOptions fieldOptions) {
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
fieldOptions.registerOption(DISPLAY_NAMESPACE, false, null,
@@ -100,7 +100,7 @@ public class FunctionSignatureFieldFactory extends FieldFactory {
elementIndex++;
}
- // noreturn
+ // no return
if (function.hasNoReturn()) {
as = new AttributedString(Function.NORETURN + " ", FunctionColors.RETURN_TYPE,
getMetrics());
@@ -110,7 +110,9 @@ public class FunctionSignatureFieldFactory extends FieldFactory {
}
// return type
- as = new AttributedString(function.getReturn().getFormalDataType().getDisplayName() + " ",
+ String returnTypeName = function.getReturn().getFormalDataType().getDisplayName();
+ returnTypeName = simplifyTemplates(returnTypeName);
+ as = new AttributedString(returnTypeName + " ",
FunctionColors.RETURN_TYPE, getMetrics());
textElements.add(new FunctionReturnTypeFieldElement(as, elementIndex, 0, startCol));
startCol += as.length();
@@ -126,14 +128,14 @@ public class FunctionSignatureFieldFactory extends FieldFactory {
as = new AttributedString(callingConvention + " ", FunctionColors.RETURN_TYPE,
getMetrics());
textElements
- .add(new FunctionCallingConventionFieldElement(as, elementIndex, 0, startCol));
+ .add(new FunctionCallingConventionFieldElement(as, elementIndex, 0, startCol));
startCol += as.length();
elementIndex++;
}
// function name
- as = new AttributedString(function.getName(displayFunctionScope),
- getFunctionNameColor(function), getMetrics());
+ String functionName = simplifyTemplates(function.getName(displayFunctionScope));
+ as = new AttributedString(functionName, getFunctionNameColor(function), getMetrics());
textElements.add(new FunctionNameFieldElement(as, elementIndex, 0, startCol));
startCol += as.length();
elementIndex++;
@@ -159,15 +161,15 @@ public class FunctionSignatureFieldFactory extends FieldFactory {
Color pcolor =
params[i].isAutoParameter() ? FunctionColors.PARAM_AUTO : FunctionColors.PARAM;
- String text = params[i].getFormalDataType().getDisplayName() + " ";
- as = new AttributedString(text, pcolor, getMetrics());
+ String dtName = simplifyTemplates(params[i].getFormalDataType().getDisplayName() + " ");
+ as = new AttributedString(dtName, pcolor, getMetrics());
textElements.add(
new FunctionParameterFieldElement(as, elementIndex, paramOffset, startCol, i));
startCol += as.length();
paramOffset += as.length();
elementIndex++;
- text = params[i].getName();
+ String text = params[i].getName();
as = new AttributedString(text, pcolor, getMetrics());
textElements.add(
new FunctionParameterNameFieldElement(as, elementIndex, paramOffset, startCol, i));
@@ -381,7 +383,6 @@ public class FunctionSignatureFieldFactory extends FieldFactory {
@Override
public void fieldOptionsChanged(Options options, String optionName, Object oldValue,
Object newValue) {
-
if (optionName.equals(DISPLAY_NAMESPACE)) {
displayFunctionScope = ((Boolean) newValue).booleanValue();
model.update();
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java
index 1d9e1bf669..09b596932c 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/LabelFieldFactory.java
@@ -250,27 +250,26 @@ public class LabelFieldFactory extends FieldFactory {
private String checkLabelString(Symbol symbol, Program program) {
if (!displayLocalNamespace && !displayNonLocalNamespace) {
- return symbol.getName(); // no namespaces being shown
+ return simplifyTemplates(symbol.getName()); // no namespaces being shown
}
Namespace addressNamespace = program.getSymbolTable().getNamespace(symbol.getAddress());
Namespace symbolNamespace = symbol.getParentNamespace();
boolean isLocal = symbolNamespace.equals(addressNamespace);
if (!isLocal) {
- return symbol.getName(displayNonLocalNamespace);
+ return simplifyTemplates(symbol.getName(displayNonLocalNamespace));
}
// O.K., we ARE a local namespace, how to display it?
if (!displayLocalNamespace) {
- return symbol.getName();
+ return simplifyTemplates(symbol.getName());
}
// use the namespace name or a custom, user-defined value
if (useLocalPrefixOverride) {
- return localPrefixText + symbol.getName(false);
+ return simplifyTemplates(localPrefixText + symbol.getName(false));
}
- return symbol.getName(true);
-
+ return simplifyTemplates(symbol.getName(true));
}
private List getOffcutReferenceAddress(CodeUnit cu) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java
index b3803a1919..b0b61c3fe3 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/OperandFieldHelper.java
@@ -491,6 +491,7 @@ abstract class OperandFieldHelper extends FieldFactory {
}
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));
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ThunkedFunctionFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ThunkedFunctionFieldFactory.java
index 9757eacf84..47b2694eb9 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ThunkedFunctionFieldFactory.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ThunkedFunctionFieldFactory.java
@@ -25,7 +25,6 @@ import ghidra.app.util.HighlightProvider;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.proxy.FunctionProxy;
import ghidra.app.util.viewer.proxy.ProxyObj;
-import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Library;
@@ -56,7 +55,7 @@ public class ThunkedFunctionFieldFactory extends FieldFactory {
* @param fieldOptions the Options for field specific properties.
*/
public ThunkedFunctionFieldFactory(FieldFormatModel model, HighlightProvider hlProvider,
- ToolOptions displayOptions, Options fieldOptions) {
+ ToolOptions displayOptions, ToolOptions fieldOptions) {
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
}
@@ -96,8 +95,9 @@ public class ThunkedFunctionFieldFactory extends FieldFactory {
as = new AttributedString("Thunked-Function: ", ListingColors.SEPARATOR, getMetrics());
textElements.add(new TextFieldElement(as, elementIndex++, 0));
- as = new AttributedString(thunkedFunction.getName(true),
- getThunkedFunctionNameColor(thunkedFunction), getMetrics());
+ String functionName = simplifyTemplates(thunkedFunction.getName(true));
+ as = new AttributedString(functionName, getThunkedFunctionNameColor(thunkedFunction),
+ getMetrics());
textElements.add(new TextFieldElement(as, elementIndex++, 0));
return ListingTextField.createSingleLineTextField(this, proxy,
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableTypeFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableTypeFieldFactory.java
index bc3ea8a29c..97962c8ded 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableTypeFieldFactory.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableTypeFieldFactory.java
@@ -23,7 +23,6 @@ import ghidra.app.util.HighlightProvider;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.app.util.viewer.proxy.VariableProxy;
-import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Parameter;
@@ -52,7 +51,7 @@ public class VariableTypeFieldFactory extends AbstractVariableFieldFactory {
* @param fieldOptions the Options for field specific properties.
*/
private VariableTypeFieldFactory(FieldFormatModel model, HighlightProvider hlProvider,
- Options displayOptions, Options fieldOptions) {
+ ToolOptions displayOptions, ToolOptions fieldOptions) {
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
}
@@ -65,24 +64,31 @@ public class VariableTypeFieldFactory extends AbstractVariableFieldFactory {
if (!enabled || !(obj instanceof Variable)) {
return null;
}
- Variable sv = (Variable) obj;
+ Variable variable = (Variable) obj;
DataType dt;
- if (sv instanceof Parameter) {
- dt = ((Parameter) sv).getFormalDataType();
+ if (variable instanceof Parameter) {
+ dt = ((Parameter) variable).getFormalDataType();
}
else {
- dt = sv.getDataType();
+ dt = variable.getDataType();
}
- String dtName = (dt != null) ? dt.getDisplayName() : null;
-
+ String dtName = getDataTypeName(dt);
AttributedString as =
- new AttributedString((dtName != null) ? dtName : "", getColor(sv), getMetrics(sv));
+ new AttributedString(dtName, getColor(variable), getMetrics(variable));
FieldElement field = new TextFieldElement(as, 0, 0);
return ListingTextField.createSingleLineTextField(this, proxy, field, startX + varWidth,
width, hlProvider);
}
+ private String getDataTypeName(DataType dt) {
+ if (dt == null) {
+ return "";
+ }
+ String dtName = dt.getName();
+ return dtName == null ? "" : simplifyTemplates(dtName);
+ }
+
/**
* @see ghidra.app.util.viewer.field.FieldFactory#getProgramLocation(int, int, ghidra.app.util.viewer.field.ListingField)
*/
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java
index d3f80efabb..ec5757367d 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/format/FormatManager.java
@@ -23,6 +23,7 @@ import org.jdom.Element;
import docking.widgets.fieldpanel.support.Highlight;
import ghidra.app.util.HighlightProvider;
+import ghidra.app.util.template.TemplateSimplifier;
import ghidra.app.util.viewer.field.*;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.ServiceProvider;
@@ -67,6 +68,7 @@ public class FormatManager implements OptionsChangeListener {
private ServiceProvider serviceProvider;
private int arrayValuesPerLine = 1;
private boolean groupArrayElements = true;
+ TemplateSimplifier templateSimplifier;
// NOTE: Unused custom format code was removed. The custom format code last existed in
// commit #204e7892bf2f110ebb05ca4beee3fe5b397f88c9.
@@ -80,6 +82,7 @@ public class FormatManager implements OptionsChangeListener {
public FormatManager(ToolOptions displayOptions, ToolOptions fieldOptions) {
this.fieldOptions = fieldOptions;
this.displayOptions = displayOptions;
+ this.templateSimplifier = new TemplateSimplifier(fieldOptions);
highlightProvider = new MultipleHighlighterProvider();
getFactorys();
for (int i = 0; i < NUM_MODELS; i++) {
@@ -153,7 +156,7 @@ public class FormatManager implements OptionsChangeListener {
/**
* Adds a listener to be notified when a format changes.
*
- * @param listener the listener to be added.
+ * @param listener the listener to be added
*/
public void addFormatModelListener(FormatModelListener listener) {
formatListeners.add(listener);
@@ -171,6 +174,7 @@ public class FormatManager implements OptionsChangeListener {
/**
* Returns the total number of model in the format manager.
+ * @return the total number of model in the format manager
*/
public int getNumModels() {
return NUM_MODELS;
@@ -180,27 +184,31 @@ public class FormatManager implements OptionsChangeListener {
* Returns the format model for the given index.
*
* @param index the index of the format model to return.
+ * @return the format model for the given index
*/
public FieldFormatModel getModel(int index) {
return models[index];
}
/**
- * Returns the format model for the address break (divider)
+ * Returns the format model for the address break (divider).
+ * @return the format model for the address break (divider)
*/
public FieldFormatModel getDividerModel() {
return models[FieldFormatModel.DIVIDER];
}
/**
- * Returns the format model for the plate field
+ * Returns the format model for the plate field.
+ * @return the format model for the plate field
*/
public FieldFormatModel getPlateFormat() {
return models[FieldFormatModel.PLATE];
}
/**
- * Returns the format model for the function signature
+ * Returns the format model for the function signature.
+ * @return the format model for the function signature
*/
public FieldFormatModel getFunctionFormat() {
return models[FieldFormatModel.FUNCTION];
@@ -208,6 +216,7 @@ public class FormatManager implements OptionsChangeListener {
/**
* Returns the format model for the function variables.
+ * @return the format model for the function variables
*/
public FieldFormatModel getFunctionVarFormat() {
return models[FieldFormatModel.FUNCTION_VARS];
@@ -215,6 +224,7 @@ public class FormatManager implements OptionsChangeListener {
/**
* Returns the format model for a code unit.
+ * @return the format model for a code unit
*/
public FieldFormatModel getCodeUnitFormat() {
return models[FieldFormatModel.INSTRUCTION_OR_DATA];
@@ -261,6 +271,7 @@ public class FormatManager implements OptionsChangeListener {
/**
* Returns the Options used for display properties.
+ * @return the Options used for display properties.
*/
public ToolOptions getDisplayOptions() {
return displayOptions;
@@ -268,11 +279,20 @@ public class FormatManager implements OptionsChangeListener {
/**
* Returns the Options used for field specific properties.
+ * @return the Options used for field specific properties
*/
public ToolOptions getFieldOptions() {
return fieldOptions;
}
+ /**
+ * Returns the template simplifier.
+ * @return the template simplifier.
+ */
+ public TemplateSimplifier getTemplateSimplifier() {
+ return templateSimplifier;
+ }
+
/**
* Notifies listeners that the given model has changed.
*
@@ -852,6 +872,7 @@ public class FormatManager implements OptionsChangeListener {
}
}
else if (options == fieldOptions) {
+ templateSimplifier.fieldOptionsChanged(options, name, oldValue, newValue);
for (int i = 0; i < NUM_MODELS; i++) {
models[i].fieldOptionsChanged(options, name, oldValue, newValue);
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java b/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java
index a99e432b3a..df05e2c304 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormat.java
@@ -322,17 +322,19 @@ public class CodeUnitFormat {
}
else if (options.includeInferredVariableMarkup) {
boolean isRead = isRead(reg, instr);
- Variable regVar = program.getFunctionManager().getReferencedVariable(
- instr.getMinAddress(), reg.getAddress(), reg.getMinimumByteSize(), isRead);
+ Variable regVar = program.getFunctionManager()
+ .getReferencedVariable(
+ instr.getMinAddress(), reg.getAddress(), reg.getMinimumByteSize(), isRead);
if (regVar != null) {
// TODO: If register appears more than once, how can we distinguish read vs. write occurrence in operands
if (isRead && isWritten(reg, instr) && !hasRegisterWriteReference(instr, reg) &&
instr.getRegister(opIndex) != null) {
// If register both read and written and there are no write references for this instruction
// see if there is only one reference to choose from - if not we can't determine how to markup
- Variable regWriteVar = program.getFunctionManager().getReferencedVariable(
- instr.getMinAddress(), reg.getAddress(), reg.getMinimumByteSize(),
- false);
+ Variable regWriteVar = program.getFunctionManager()
+ .getReferencedVariable(
+ instr.getMinAddress(), reg.getAddress(), reg.getMinimumByteSize(),
+ false);
if (regWriteVar != regVar) {
continue; // TODO: tough case - not which operand is read vs. write!
}
@@ -630,8 +632,10 @@ public class CodeUnitFormat {
}
Variable regVar =
- instr.getProgram().getFunctionManager().getReferencedVariable(instr.getMinAddress(),
- associatedRegister.getAddress(), associatedRegister.getMinimumByteSize(), true);
+ instr.getProgram()
+ .getFunctionManager()
+ .getReferencedVariable(instr.getMinAddress(),
+ associatedRegister.getAddress(), associatedRegister.getMinimumByteSize(), true);
if (regVar == null) {
return false;
}
@@ -1151,8 +1155,10 @@ public class CodeUnitFormat {
public String getReferenceRepresentationString(CodeUnit fromCodeUnit, Reference ref) {
// NOTE: The isRead param is false since it really only pertains to register references which should
// generally only correspond to writes
- Variable refVar = fromCodeUnit.getProgram().getFunctionManager().getReferencedVariable(
- fromCodeUnit.getMinAddress(), ref.getToAddress(), 0, false);
+ Variable refVar = fromCodeUnit.getProgram()
+ .getFunctionManager()
+ .getReferencedVariable(
+ fromCodeUnit.getMinAddress(), ref.getToAddress(), 0, false);
Object repObj = getReferenceRepresentation(fromCodeUnit, ref, refVar);
return repObj != null ? repObj.toString() : null;
}
@@ -1367,7 +1373,13 @@ public class CodeUnitFormat {
}
}
String name = symbol.getName();
- return addNamespace(program, symbol.getParentNamespace(), name, markupAddress);
+ String displayName =
+ addNamespace(program, symbol.getParentNamespace(), name, markupAddress);
+ return simplifyTemplate(displayName);
+ }
+
+ private String simplifyTemplate(String name) {
+ return options.simplifyTemplate(name);
}
private boolean isStringData(CodeUnit cu) {
@@ -1379,7 +1391,7 @@ public class CodeUnitFormat {
private String getLabelStringForStringData(Data data, Symbol symbol) {
if (!symbol.isDynamic()) {
- return symbol.getName();
+ return options.simplifyTemplate(symbol.getName());
}
DataType dataType = data.getBaseDataType();
@@ -1440,7 +1452,7 @@ public class CodeUnitFormat {
Symbol containingSymbol = program.getSymbolTable().getPrimarySymbol(instructionAddress);
if (containingSymbol != null) {
- return containingSymbol.getName() + PLUS + diff;
+ return options.simplifyTemplate(containingSymbol.getName()) + PLUS + diff;
}
return getDefaultOffcutString(offsym, instruction, diff, false);
}
@@ -1464,10 +1476,11 @@ public class CodeUnitFormat {
protected String getDefaultOffcutString(Symbol symbol, CodeUnit cu, long diff,
boolean decorate) {
+ String name = options.simplifyTemplate(symbol.getName());
if (decorate) {
- return symbol.getName() + ' ' + '(' + cu.getMinAddress() + PLUS + diff + ')';
+ return name + ' ' + '(' + cu.getMinAddress() + PLUS + diff + ')';
}
- return symbol.getName();
+ return name;
}
/**
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormatOptions.java b/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormatOptions.java
index acd5b9f546..b7b118ac80 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormatOptions.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/program/model/listing/CodeUnitFormatOptions.java
@@ -15,6 +15,7 @@
*/
package ghidra.program.model.listing;
+import ghidra.app.util.template.TemplateSimplifier;
import ghidra.program.model.data.DataTypeDisplayOptions;
public class CodeUnitFormatOptions {
@@ -80,9 +81,11 @@ public class CodeUnitFormatOptions {
protected volatile boolean showOffcutInfo = true;
protected DataTypeDisplayOptions displayOptions = DataTypeDisplayOptions.DEFAULT;
+ protected TemplateSimplifier templateSimplifier;
public CodeUnitFormatOptions() {
// use default options;
+ templateSimplifier = new TemplateSimplifier();
}
/**
@@ -93,6 +96,7 @@ public class CodeUnitFormatOptions {
public CodeUnitFormatOptions(ShowBlockName showBlockName, ShowNamespace showNamespace) {
this.showBlockName = showBlockName;
this.showNamespace = showNamespace;
+ templateSimplifier = new TemplateSimplifier();
}
/**
@@ -119,7 +123,7 @@ public class CodeUnitFormatOptions {
String localPrefixOverride, boolean doRegVariableMarkup, boolean doStackVariableMarkup,
boolean includeInferredVariableMarkup, boolean alwaysShowPrimaryReference,
boolean includeScalarReferenceAdjustment, boolean showLibraryInNamespace,
- boolean followReferencedPointers) {
+ boolean followReferencedPointers, TemplateSimplifier templateSimplifier) {
this.showBlockName = showBlockName;
this.showNamespace = showNamespace;
this.showLibraryInNamespace = showLibraryInNamespace;
@@ -130,6 +134,7 @@ public class CodeUnitFormatOptions {
this.alwaysShowPrimaryReference = alwaysShowPrimaryReference;
this.followReferencedPointers = followReferencedPointers;
this.includeScalarReferenceAdjustment = includeScalarReferenceAdjustment;
+ this.templateSimplifier = templateSimplifier;
}
/**
@@ -139,4 +144,8 @@ public class CodeUnitFormatOptions {
public ShowBlockName getShowBlockNameOption() {
return showBlockName;
}
+
+ public String simplifyTemplate(String name) {
+ return templateSimplifier.simplify(name);
+ }
}
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/template/TemplateSimplifierTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/template/TemplateSimplifierTest.java
new file mode 100644
index 0000000000..36ce4690fd
--- /dev/null
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/template/TemplateSimplifierTest.java
@@ -0,0 +1,115 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.template;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import generic.test.AbstractGenericTest;
+import ghidra.framework.options.ToolOptions;
+
+public class TemplateSimplifierTest extends AbstractGenericTest {
+
+ private TemplateSimplifier simplifier = new TemplateSimplifier();
+
+ @Before
+ public void setUp() {
+ simplifier.setMinimumTemplateLength(0);
+ simplifier.setNestingDepth(0);
+ simplifier.setEnabled(true);
+ }
+
+ @Test
+ public void testSimplifyTemplates() {
+ assertEquals("bob<>", simplify("bob"));
+ assertEquals("bob<>", simplify("bob>"));
+ assertEquals("bob", simplify("bob"));
+ assertEquals("bob<>", simplify("bob>>"));
+ }
+
+ @Test
+ public void testSimplifyTemplatesDepth1() {
+ simplifier.setNestingDepth(1);
+ assertEquals("bob", simplify("bob"));
+ assertEquals("bob>", simplify("bob>"));
+ assertEquals("bob", simplify("bob"));
+ assertEquals("bob>", simplify("bob>>"));
+ }
+
+ @Test
+ public void testSimplifyTemplatesDepth2() {
+ simplifier.setNestingDepth(2);
+ assertEquals("bob", simplify("bob"));
+ assertEquals("bob>", simplify("bob>"));
+ assertEquals("bob", simplify("bob"));
+ assertEquals("bob>>", simplify("bob>>"));
+ }
+
+ @Test
+ public void testStripTemplatesWithMaxSize() {
+ simplifier.setMaxTemplateLength(10);
+ simplifier.setNestingDepth(5);
+ assertEquals("bob", simplify("bob"));
+ }
+
+ @Test
+ public void testStripTemplatesWithMaxSizeAndNestedTemplates() {
+ simplifier.setMaxTemplateLength(10);
+ simplifier.setNestingDepth(5);
+ assertEquals("bob>",
+ simplify("bob>"));
+ }
+
+ @Test
+ public void testMinSizeToSimplify() {
+ simplifier.setMinimumTemplateLength(5);
+ assertEquals("bob", simplify("bob"));
+ assertEquals("bob<>", simplify("bob"));
+ }
+
+ @Test
+ public void testOptionsGetRegistered() {
+ ToolOptions options = new ToolOptions("Listing Fields");
+ simplifier = new TemplateSimplifier(options);
+
+ assertTrue(options.isRegistered(TemplateSimplifier.SIMPLIFY_TEMPLATES_OPTION));
+ assertTrue(options.isRegistered(TemplateSimplifier.MAX_TEMPLATE_LENGTH_OPTION));
+ assertTrue(options.isRegistered(TemplateSimplifier.TEMPLATE_NESTING_DEPTH_OPTION));
+ assertTrue(options.isRegistered(TemplateSimplifier.MIN_TEMPLATE_LENGTH_OPTION));
+ }
+
+ @Test
+ public void testReadsOptions() {
+ ToolOptions options = new ToolOptions("Listing Fields");
+ options.setBoolean(TemplateSimplifier.SIMPLIFY_TEMPLATES_OPTION, false);
+ options.setInt(TemplateSimplifier.MAX_TEMPLATE_LENGTH_OPTION, 33);
+ options.setInt(TemplateSimplifier.TEMPLATE_NESTING_DEPTH_OPTION, 3);
+ options.setInt(TemplateSimplifier.MIN_TEMPLATE_LENGTH_OPTION, 7);
+ simplifier = new TemplateSimplifier(options);
+
+ assertEquals(false, simplifier.isEnabled());
+ assertEquals(33, simplifier.getMaxTemplateLength());
+ assertEquals(3, simplifier.getNestingDepth());
+ assertEquals(7, simplifier.getMinimumTemplateLength());
+ }
+
+ private String simplify(String in) {
+ return simplifier.simplify(in);
+ }
+
+}