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.

+ +

Template Display Options

+
+

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.

+

XREFs Field

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ProgramTextWriter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ProgramTextWriter.java index ae4fc3dc12..04a4fe2231 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ProgramTextWriter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ProgramTextWriter.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import generic.theme.GThemeDefaults.Colors.Messages; import ghidra.app.util.DisplayableEol; +import ghidra.app.util.template.TemplateSimplifier; import ghidra.framework.plugintool.ServiceProvider; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; @@ -85,7 +86,8 @@ class ProgramTextWriter { writer.print(""); writer.println("
");
 		}
-
+		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); + } + +}