GP-1556 - Added support for searching for structure fields by offset

This commit is contained in:
dragonmacher 2022-03-25 09:43:50 -04:00
parent 883f5a687a
commit 812ea4fe1e
45 changed files with 1461 additions and 840 deletions

View file

@ -58,7 +58,7 @@
references to the address 0040767d.</P>
<BLOCKQUOTE>
<P><IMG alt="" src="../../shared/tip.png">You can also <A href="#Data_Types">show
<P><IMG alt="" src="images/tip.png">You can also <A href="#Data_Types">show
references to data types</A> from the Data Type Manager. In this case, all locations where
the selected data type is applied will be highlighted.</P>
</BLOCKQUOTE>
@ -73,11 +73,11 @@
<LI>Select whichever of the following is available from the popup menu:
<UL>
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Show References to</B>
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Show References to</B>
from the popup menu.</LI>
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Find References to</B>
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Find References to</B>
from the popup menu.</LI>
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Find Uses of</B>
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Find Uses of</B>
from the popup menu.</LI>
</UL>
</OL>
@ -134,12 +134,12 @@
</P>
<BLOCKQUOTE>
<P>
<IMG alt="" src="../../shared/tip.png">
<IMG alt="" src="images/tip.png">
To instead make a Program Selection Highlight, use the select button mentioned above.
Then, click from the menu bar <B>Select
<img src="../../shared/arrow.gif" alt="-&gt;" border="0">
<img src="images/arrow.gif" alt="-&gt;" border="0">
Program Highlight
<img src="../../shared/arrow.gif" alt="-&gt;" border="0">
<img src="images/arrow.gif" alt="-&gt;" border="0">
Entire Selection
</B>
</P>
@ -167,7 +167,7 @@
the decompiled function will be shown.
</P>
<P><A name="Make_Selection"></A><IMG alt="" src="../../shared/tip.png"> You can make a
<P><A name="Make_Selection"></A><IMG alt="" src="images/tip.png"> You can make a
selection in the Code Browser from the entries in the table:</P>
<OL>
@ -198,16 +198,26 @@
the action will display all places that function is applied.</P>
<BLOCKQUOTE>
<P>
<IMG border="0" src="images/tip.png" alt=""> When searching for references to a field of
a composite data type via the popup menu in the Data Type Manager,
you can search by name or by offset within the parent data type. This is useful when
the field you seek does not have a name.
</P>
</BLOCKQUOTE>
<BLOCKQUOTE>
<P>
<IMG border="0" src="../../shared/note.png" alt="">
<IMG border="0" src="images/note.png" alt="">
<A name="Data_Type_Discovery"></A>
By default, finding uses of
data types will search not only for applied data types, but also will perform
dynamic discovery of data types using the <CODE>Data Type Reference Finder</CODE>
service. This causes the search to be slower, but also reports many more type
uses. To disable the dynamic searching, use the
<B>Search</B><img src="../../shared/arrow.gif" alt="-&gt;" border="0">
<B>Search</B><img src="images/arrow.gif" alt="-&gt;" border="0">
<B>Dynamic Data Type Discovery</B>
<A href="help/topics/Tool/ToolOptions_Dialog.htm">tool option</A>.
</P>
@ -221,7 +231,7 @@
<OL start="1" type="1">
<LI>Right-mouse anywhere on the code unit*</LI>
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Show References to
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Show References to
Address </B> from the popup menu.</LI>
</OL>
@ -240,7 +250,7 @@
<BLOCKQUOTE>
<P>
<IMG border="0" src="../../shared/note.png" alt=""> This action will show only direct
<IMG border="0" src="images/note.png" alt=""> This action will show only direct
references to the current code unit. No other special reference finding will take place.
</P>
</BLOCKQUOTE>
@ -250,7 +260,7 @@
<BLOCKQUOTE>
<P>
<IMG border="0" src="../../shared/tip.png" alt=""> <I>see <A href=
<IMG border="0" src="images/tip.png" alt=""> <I>see <A href=
"help/topics/DockingWindows/Docking_Windows.htm#Renaming_Windows">Docking Windows - Renaming
Windows</A></I>
</P>

View file

@ -25,9 +25,10 @@ import docking.DockingUtils;
import docking.action.*;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.services.FieldMatcher;
import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.*;
import ghidra.util.*;
public abstract class AbstractFindReferencesDataTypeAction extends DockingAction {
@ -89,7 +90,40 @@ public abstract class AbstractFindReferencesDataTypeAction extends DockingAction
DataType dataType = getDataType(context);
DataType baseDataType = ReferenceUtils.getBaseDataType(dataType);
String field = getDataTypeField(baseDataType);
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(baseDataType, field));
Swing.runLater(() -> doFindDataTypeUsage(service, baseDataType, field));
}
private void doFindDataTypeUsage(FindAppliedDataTypesService service, DataType dt,
String field) {
if (field == null) {
// no field specified; search for all uses of the given type
service.findAndDisplayAppliedDataTypeAddresses(dt);
return;
}
if (dt instanceof Structure) {
Integer offset = getOffsetForDeafaultFieldName((Structure) dt, field);
if (offset != null) {
// The user has picked a field by it's default name. In this case we need to
// search by offset to ensure we find code that does not use the default name.
FieldMatcher fieldMatcher = new FieldMatcher(dt, offset);
service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher);
return;
}
}
service.findAndDisplayAppliedDataTypeAddresses(dt, field);
}
private Integer getOffsetForDeafaultFieldName(Structure structure, String fieldName) {
DataTypeComponent[] components = structure.getComponents();
for (DataTypeComponent dtc : components) {
String defaultName = dtc.getDefaultFieldName();
if (fieldName.equals(defaultName)) {
return dtc.getOffset();
}
}
return null;
}
}

View file

@ -53,8 +53,8 @@ public class FindDataTypesBySizeAction extends DockingAction {
@Override
public void actionPerformed(ActionContext context) {
NumberRangeInputDialog inputDialog =
new NumberRangeInputDialog(getName(), "Size(s)");
NumberRangeInputDialog inputDialog = new NumberRangeInputDialog(getName(), "Size(s)");
inputDialog.setHelpLocation(getHelpLocation());
if (!inputDialog.show()) {
return;
}

View file

@ -32,6 +32,7 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.services.FieldMatcher;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
@ -98,15 +99,27 @@ public class FindReferencesToFieldAction extends DockingAction {
choices = ((Enum) dt).getNames();
}
String userChoice = OptionDialog.showInputChoiceDialog(null, "Choose Field",
"Find uses of '" + dt.getName() + "' field", choices, null,
OptionDialog.QUESTION_MESSAGE);
String message = "Find uses of '" + dt.getName() + "' field by name or offset";
String userChoice = OptionDialog.showEditableInputChoiceDialog(null, "Choose Field",
message, choices, null, OptionDialog.QUESTION_MESSAGE);
if (userChoice == null) {
return;
}
Swing.runLater(
() -> service.findAndDisplayAppliedDataTypeAddresses(dt, userChoice));
FieldMatcher fieldMatcher;
Long longChoice = parseInt(userChoice);
if (longChoice != null) {
fieldMatcher = new FieldMatcher(dt, longChoice.intValue());
}
else {
fieldMatcher = new FieldMatcher(dt, userChoice);
}
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher));
}
private Long parseInt(String s) {
return NumericUtilities.parseNumber(s, null);
}
private String[] getCompisiteFieldNames(Composite composite) {

View file

@ -15,6 +15,7 @@
*/
package ghidra.app.plugin.core.navigation;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.data.DataType;
/**
@ -25,7 +26,7 @@ public interface FindAppliedDataTypesService {
/**
* Tells this service to find all places where the given datatype is applied <b>and</b> will
* display the results of the search.
*
*
* @param dataType The datatype which to base the search upon.
*/
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType);
@ -33,9 +34,23 @@ public interface FindAppliedDataTypesService {
/**
* Tells this service to find all places where the given datatype is applied <b>and</b> will
* display the results of the search.
*
*
* @param dataType The datatype which to base the search upon.
* @param fieldName the sub-field for which to search
*/
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName);
/**
* Tells this service to find all places where the given datatype is applied <b>and</b> will
* display the results of the search.
* <p>
* The supplied field matcher will be used to restrict matches to the given field. The matcher
* may be 'empty', supplying only the data type for which to search. In this case, all uses
* of the type will be matched, regardless of field.
*
* @param dataType The datatype which to base the search upon.
* @param fieldMatcher the field matcher.
*/
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType,
FieldMatcher fieldMatcher);
}

View file

@ -35,8 +35,8 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* A location descriptor that should be extended by location descriptor implementations that
* are based upon data types.
* A location descriptor that should be extended by location descriptor implementations that are
* based upon data types.
*/
abstract class DataTypeLocationDescriptor extends LocationDescriptor {
@ -70,18 +70,28 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
findDataTypeReferences(accumulator, monitor);
}
/** The original data type that this location descriptor describes */
/**
* The original data type that this location descriptor describes
* @return the type
*/
protected abstract DataType getSourceDataType();
/** Generates the label for the results window */
/**
* Generates the label for the results window
* @return the label
*/
protected abstract String generateLabel();
/** Returns the name of the data type, for example, 'Foo' or 'Foo.bar.baz' */
/**
* Returns the name of the data type, for example, 'Foo' or 'Foo.bar.baz'
* @return the name
*/
protected abstract String getDataTypeName();
/**
/**
* The base data type that this location descriptor describes (this may be the same as the
* original data type.
* @return the type.
*/
protected DataType getBaseDataType() {
return getSourceDataType(); // by default these two values are the same
@ -91,7 +101,7 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
TaskMonitor monitor) throws CancelledException {
DataType currentDataType = getDataType();
ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, null, program,
ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, program,
useDynamicSearching, monitor);
}
@ -177,8 +187,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
List<Variable> allVariables = ReferenceUtils.getVariables(function, true);
for (Variable variable : allVariables) {
DataType variableDataType = variable.getDataType();
if (ReferenceUtils.getBaseDataType(variableDataType).isEquivalent(
currentDataType)) {
if (ReferenceUtils.getBaseDataType(variableDataType)
.isEquivalent(currentDataType)) {
return true;
}
}
@ -266,8 +276,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
}
// check for pointer names
else if (label.endsWith("*") && label.startsWith(paramName)) {
// see if we need to chop off some '*'s, as we may have searched for a pointer to a
// pointer and have found a match against a simple pointer and thus the display may
// see if we need to chop off some '*'s, as we may have searched for a pointer to a
// pointer and have found a match against a simple pointer and thus the display may
// not match our label
if (paramParts.length == 1) {
return paramName; // not a full declaration, just the name

View file

@ -20,6 +20,7 @@ import java.awt.Color;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.fieldpanel.support.Highlight;
import ghidra.app.services.FieldMatcher;
import ghidra.app.util.viewer.field.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Data;
@ -34,34 +35,39 @@ import ghidra.util.task.TaskMonitor;
*/
public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeLocationDescriptor {
private String typeAndFieldName;
private String fieldName;
// this is either "Foo.bar" or "Foo offset 1"
private String typeDisplayString;
private FieldMatcher fieldMatcher;
public GenericCompositeDataTypeLocationDescriptor(
GenericCompositeDataTypeProgramLocation location, Program program) {
super(location, program, location.getDataType());
this.fieldName = location.getFieldName();
this.typeAndFieldName = getDataTypeName() + '.' + fieldName;
this.fieldMatcher = location.getFieldMatcher();
this.typeDisplayString = fieldMatcher.getDisplayText();
label = generateLabel();
}
@Override
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
throws CancelledException {
ReferenceUtils.findDataTypeReferences(accumulator, getDataType(), fieldName, program,
ReferenceUtils.findDataTypeFieldReferences(accumulator, fieldMatcher, program,
useDynamicSearching, monitor);
}
@Override
public String getTypeName() {
return super.getTypeName() + "." + fieldName;
// not sure why this code was doing this and not using our variable; oversight?
// return super.getTypeName() + "." + fieldName;
return typeDisplayString;
}
// implemented to ignore the location being provided, since this is a 'dummy' type class
@Override
protected String generateLabel() {
return "\"" + originalDataType.getName() + "." + fieldName + "\" (DataType)";
// changed this; keep old code around in case an edge case appears
// return "\"" + originalDataType.getName() + "." + fieldName + "\" (DataType)";
return "\"" + typeDisplayString + "\" (DataType)";
}
// Overridden to perform a simple check against data types, since the program locations are
@ -79,7 +85,7 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
GenericCompositeDataTypeLocationDescriptor otherDescriptor =
(GenericCompositeDataTypeLocationDescriptor) obj;
return getDataType().equals(otherDescriptor.getDataType()) &&
fieldName.equals(otherDescriptor.fieldName);
fieldMatcher.equals(otherDescriptor.fieldMatcher);
}
@Override
@ -113,14 +119,15 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
// Not sure how to get the correct part of the text. This is a hack for now.
int offset = StringUtils.indexOfIgnoreCase(text, typeAndFieldName, 0);
int offset = StringUtils.indexOfIgnoreCase(text, typeDisplayString, 0);
if (offset != -1) {
return new Highlight[] {
new Highlight(offset, offset + typeAndFieldName.length() - 1, highlightColor) };
return new Highlight[] { new Highlight(offset,
offset + typeDisplayString.length() - 1, highlightColor) };
}
}
else if (FieldNameFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
String fieldName = fieldMatcher.getFieldName();
if (text.equalsIgnoreCase(fieldName)) {
return new Highlight[] { new Highlight(0, text.length(), highlightColor) };
}

View file

@ -17,28 +17,39 @@ package ghidra.app.plugin.core.navigation.locationreferences;
import java.util.Objects;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.AssertException;
/**
* A class to signal that the ProgramLocation is used for data types and is not really
* connected to the listing. This is a subclass is designed for data types that have fields, such
* as {@link Composite} types and {@link Enum} types.
*
*
* @see GenericCompositeDataTypeLocationDescriptor
*/
public class GenericCompositeDataTypeProgramLocation extends GenericDataTypeProgramLocation {
private String fieldName;
private FieldMatcher fieldMatcher;
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, String fieldName) {
this(program, dataType, new FieldMatcher(dataType, fieldName));
}
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType,
FieldMatcher fieldMatcher) {
super(program, dataType);
this.fieldName = Objects.requireNonNull(fieldName);
this.fieldMatcher = Objects.requireNonNull(fieldMatcher);
// sanity check
if (!Objects.equals(dataType, fieldMatcher.getDataType())) {
throw new AssertException("Data type does not match the FieldMatcher type");
}
}
public String getFieldName() {
return fieldName;
public FieldMatcher getFieldMatcher() {
return fieldMatcher;
}
}

View file

@ -260,10 +260,10 @@ public abstract class LocationDescriptor {
}
/**
* Returns a generic {@link ProgramLocation} based upon the <tt>program</tt> and
* <tt>homeAddress</tt> of this <tt>LocationDescriptor</tt>. Subclasses should override this
* Returns a generic {@link ProgramLocation} based upon the <tt>program</tt> and
* <tt>homeAddress</tt> of this <tt>LocationDescriptor</tt>. Subclasses should override this
* method to return more specific addresses.
*
*
* @return a generic ProgramLocation.
*/
ProgramLocation getHomeLocation() {
@ -329,9 +329,9 @@ public abstract class LocationDescriptor {
TaskMonitor monitor) throws CancelledException;
/**
* Returns a descriptive category name for this location descriptor. This is used for
* display in a popup menu.
*
* Returns a descriptive category name for this location descriptor. This is used for
* display in a popup menu.
*
* @return a descriptive category name for this location descriptor
*/
public String getTypeName() {
@ -355,8 +355,8 @@ public abstract class LocationDescriptor {
/**
* Gets all location references for the given descriptor, loading them if not already loaded.
*
* @param accumulator the datastructure into which will be placed a collection of
*
* @param accumulator the datastructure into which will be placed a collection of
* location references that reference the location this descriptor is representing.
* @param monitor A monitor to report progress or cancel the gathering of addresses.
* @param reload True signals to perform a new search for reference addresses; false will
@ -370,10 +370,10 @@ public abstract class LocationDescriptor {
}
/**
* When true, the search algorithm will use dynamic searching when possible, which is to
* not only find references that are already created, but to also use external tools to
* locate potential references.
*
* When true, the search algorithm will use dynamic searching when possible, which is to
* not only find references that are already created, but to also use external tools to
* locate potential references.
*
* @param useDynamicSearching true to perform dynamic searching
*/
void setUseDynamicSearching(boolean useDynamicSearching) {
@ -384,7 +384,7 @@ public abstract class LocationDescriptor {
* Sets a listener on this descriptor that will be notified when the references contained
* in this descriptor may no longer be accurate. For example, the listener will be called
* when an undo or redo is performed in Ghidra.
* @param modelChangeListener The listener to add.
* @param modelFreshnessListener The listener to add.
*/
void setModelFreshnessListener(ChangeListener modelFreshnessListener) {
this.modelFreshnessListener = modelFreshnessListener;

View file

@ -99,7 +99,7 @@ public class LocationReference implements Comparable<LocationReference> {
* Returns the address where the item described by this object is used. For example, for
* data types, the address is where a data type is applied; for references, this value is the
* <tt>from</tt> address.
*
*
* @return the address where the item described by this object is used.
*/
public Address getLocationOfUse() {
@ -110,7 +110,7 @@ public class LocationReference implements Comparable<LocationReference> {
* Returns the context associated with this location. This could be a String that highlights
* what part of a function signature the location matches or a line from the Decompiler
* that matches.
*
*
* @return the context
*/
@ -118,7 +118,7 @@ public class LocationReference implements Comparable<LocationReference> {
* Returns the context associated with this location. The context may be a simple plain string
* or may be String that highlights part of a function signature the location matches or
* a line from the Decompiler that matches.
*
*
* @return the context
*/
public LocationReferenceContext getContext() {
@ -169,12 +169,7 @@ public class LocationReference implements Comparable<LocationReference> {
if (!context.equals(other.context)) {
return false;
}
if (locationOfUseAddress == null) {
if (other.locationOfUseAddress != null) {
return false;
}
}
else if (!locationOfUseAddress.equals(other.locationOfUseAddress)) {
if (!Objects.equals(locationOfUseAddress, other.locationOfUseAddress)) {
return false;
}
return refType.equals(other.refType);
@ -194,7 +189,7 @@ public class LocationReference implements Comparable<LocationReference> {
"\taddress: " + locationOfUseAddress + ",\n" +
((refType.equals("")) ? "" : "\trefType: " + refType + ",\n") +
"\tisOffcut: " + isOffcutReference + ",\n" +
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + ",") +
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + "\n") +
"}";
//@formatter:off
}

View file

@ -22,7 +22,7 @@ import ghidra.util.HTMLUtilities;
/**
* A class to hold context representation for {@link LocationReference}s.
*
*
* @see LocationReferenceContextBuilder
*/
public class LocationReferenceContext {
@ -107,7 +107,7 @@ public class LocationReferenceContext {
/**
* Returns any sub-strings of this context's overall text that match client-defined input
*
*
* See the {@link LocationReferenceContextBuilder} for how to define matching text pieces
* @return the matching strings
*/
@ -123,7 +123,7 @@ public class LocationReferenceContext {
@Override
public String toString() {
return Json.toString(this);
return getPlainText();
}
/**

View file

@ -26,8 +26,7 @@ import ghidra.app.events.ProgramClosedPluginEvent;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.services.GoToService;
import ghidra.app.services.ProgramManager;
import ghidra.app.services.*;
import ghidra.app.util.XReferenceUtils;
import ghidra.app.util.query.TableService;
import ghidra.framework.options.ToolOptions;
@ -211,8 +210,7 @@ public class LocationReferencesPlugin extends Plugin
}
private void disposeProviderList() {
for (int i = 0; i < providerList.size(); i++) {
LocationReferencesProvider provider = providerList.get(i);
for (LocationReferencesProvider provider : providerList) {
provider.dispose();
}
providerList.clear();
@ -261,8 +259,8 @@ public class LocationReferencesPlugin extends Plugin
}
protected void programClosed(Program program) {
for (Iterator<LocationReferencesProvider> iterator =
providerList.iterator(); iterator.hasNext();) {
for (Iterator<LocationReferencesProvider> iterator = providerList.iterator(); iterator
.hasNext();) {
LocationReferencesProvider provider = iterator.next();
if (provider.getProgram() == program) {
provider.dispose();
@ -293,11 +291,19 @@ public class LocationReferencesPlugin extends Plugin
@Override
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType) {
findAndDisplayAppliedDataTypeAddresses(dataType, null);
findAndDisplayAppliedDataTypeAddresses(dataType, (FieldMatcher) null);
}
@Override
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName) {
FieldMatcher matcher = new FieldMatcher(dataType, fieldName);
findAndDisplayAppliedDataTypeAddresses(dataType, matcher);
}
@Override
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType,
FieldMatcher fieldMatcher) {
ProgramManager programManagerService = tool.getService(ProgramManager.class);
GoToService goToService = tool.getService(GoToService.class);
Program program = programManagerService.getCurrentProgram();
@ -308,9 +314,9 @@ public class LocationReferencesPlugin extends Plugin
}
ProgramLocation genericLocation;
if (fieldName != null) {
if (fieldMatcher != null) {
genericLocation =
new GenericCompositeDataTypeProgramLocation(program, dataType, fieldName);
new GenericCompositeDataTypeProgramLocation(program, dataType, fieldMatcher);
}
else {
genericLocation = new GenericDataTypeProgramLocation(program, dataType);

View file

@ -20,11 +20,11 @@ import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Predicate;
import ghidra.app.services.DataTypeReference;
import ghidra.app.services.DataTypeReferenceFinder;
import ghidra.app.services.*;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Array;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.program.util.*;
@ -162,20 +162,24 @@ public final class ReferenceUtils {
/**
* Returns all references (locations) that use the given datatype.
* <br>
* <b>Note: </b> This method call may take a long time, as it must search all of the
* data within the program and may also perform long running tasks, like decompiling every
* function in the program.
* <br>
* @param accumulator the results storage
* <p>
* <b>Note: </b> This method call may take a long time, as it must search all of the data
* within the program and may also perform long running tasks, like decompiling every function
* in the program.
*
* @param accumulator the results storage.
* @param dataType The datatype for which to find references.
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be
* a {@link Composite} to search for a field
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be a
* {@link Composite} to search for a field.
* @param program The program from within which to find references.
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
* dummy monitor will be used.
* @throws CancelledException if the monitor is cancelled
* dummy monitor will be used.
* @throws CancelledException if the monitor is cancelled.
* @deprecated use {@link #findDataTypeFieldReferences(Accumulator, FieldMatcher, Program,
* boolean, TaskMonitor)}.
* @Deprecated(since = "10.2")
*/
@Deprecated
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
DataType dataType, String fieldName, Program program, TaskMonitor monitor)
throws CancelledException {
@ -184,24 +188,26 @@ public final class ReferenceUtils {
/**
* Returns all references (locations) that use the given datatype.
* <br>
* <b>Note: </b> This method call may take a long time, as it must search all of the
* data within the program and may also perform long running tasks, like decompiling every
* function in the program.
* <br>
* @param accumulator the results storage
* <p>
* <b>Note: </b> This method call may take a long time, as it must search all of the data
* within the program and may also perform long running tasks, like decompiling every function
* in the program.
*
* @param accumulator the results storage.
* @param dataType The datatype for which to find references.
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be
* a {@link Composite} to search for a field
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be a
* {@link Composite} to search for a field.
* @param program The program from within which to find references.
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service
* will be used to search for data types that are not applied in memory.
* Using the service will be slower, but will recover type usage that could
* not be found by examining the Listing.
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
* search for data types that are not applied in memory. Using the service will be slower, but
* will recover type usage that could not be found by examining the Listing.
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
* dummy monitor will be used.
* @throws CancelledException if the monitor is cancelled
* dummy monitor will be used.
* @throws CancelledException if the monitor is cancelled.
* @deprecated use {@link #findDataTypeFieldReferences(Accumulator, FieldMatcher, Program,
* boolean, TaskMonitor)}.
*/
@Deprecated(since = "10.2")
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
DataType dataType, String fieldName, Program program, boolean discoverTypes,
TaskMonitor monitor) throws CancelledException {
@ -209,12 +215,72 @@ public final class ReferenceUtils {
// Note: none of the params can be null, but this one gets used much later, so check now
Objects.requireNonNull(dataType, () -> "Data Type cannot be null");
if (monitor == null) {
monitor = TaskMonitor.DUMMY;
}
FieldMatcher fieldMatcher = new FieldMatcher(dataType, fieldName);
doFindDataTypeReferences(accumulator, fieldMatcher, program, discoverTypes, monitor);
}
/**
* Returns all references (locations) that use the given datatype.
* <p>
* <b>Note: </b> This method call may take a long time, as it must search all of the data
* within the program and may also perform long running tasks, like decompiling every function
* in the program.
*
* @param accumulator the results storage.
* @param dataType The datatype for which to find references.
* @param program The program from within which to find references.
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
* search for data types that are not applied in memory. Using the service will be slower, but
* will recover type usage that could not be found by examining the Listing.
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
* dummy monitor will be used.
* @throws CancelledException if the monitor is cancelled.
*/
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
DataType dataType, Program program, boolean discoverTypes, TaskMonitor monitor)
throws CancelledException {
doFindDataTypeReferences(accumulator, new FieldMatcher(dataType), program, discoverTypes,
monitor);
}
/**
* Returns all references (locations) that use the given datatype.
* <p>
* <b>Note: </b> This method call may take a long time, as it must search all of the data
* within the program and may also perform long running tasks, like decompiling every function
* in the program.
* <p>
* The supplied field matcher will be used to restrict matches to the given field. The matcher
* may be 'empty', supplying only the data type for which to search. In this case, all uses
* of the type will be matched, regardless of field.
*
* @param accumulator the results storage.
* @param fieldMatcher the field matcher.
* @param program The program from within which to find references.
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
* search for data types that are not applied in memory. Using the service will be slower, but
* will recover type usage that could not be found by examining the Listing.
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
* dummy monitor will be used.
* @throws CancelledException if the monitor is cancelled.
*/
public static void findDataTypeFieldReferences(Accumulator<LocationReference> accumulator,
FieldMatcher fieldMatcher, Program program, boolean discoverTypes, TaskMonitor monitor)
throws CancelledException {
Objects.requireNonNull(fieldMatcher, "FieldMatcher cannot be null");
doFindDataTypeReferences(accumulator, fieldMatcher, program, discoverTypes, monitor);
}
private static void doFindDataTypeReferences(Accumulator<LocationReference> accumulator,
FieldMatcher fieldMatcher, Program program, boolean discoverTypes, TaskMonitor monitor)
throws CancelledException {
monitor = TaskMonitor.dummyIfNull(monitor);
DataType dataType = fieldMatcher.getDataType();
Listing listing = program.getListing();
long dataCount = listing.getNumDefinedData();
int functionCount = program.getFunctionManager().getFunctionCount();
int totalCount = (int) dataCount + functionCount;
@ -226,9 +292,10 @@ public final class ReferenceUtils {
// the code.
Accumulator<LocationReference> asSet = asSet(accumulator);
if (fieldName == null) {
// It only makes sense to search here when we do not have a field
if (fieldMatcher.isIgnored()) {
//
// It only makes sense to search here when we do not have a field to match
//
boolean localsOnly = discoverTypes;
FunctionIterator iterator = listing.getFunctions(false);
findDataTypeMatchesInFunctionHeaders(asSet, iterator, dataType, localsOnly, monitor);
@ -244,10 +311,11 @@ public final class ReferenceUtils {
boolean matches = dataTypesMatch(dataType, baseType);
return matches;
};
findDataTypeMatchesInDefinedData(asSet, program, dataMatcher, fieldName, monitor);
findDataTypeMatchesInDefinedData(asSet, program, dataMatcher, fieldMatcher, monitor);
if (discoverTypes) {
findDataTypeMatchesOutsideOfListing(asSet, program, dataType, fieldName, monitor);
findDataTypeMatchesOutsideOfListing(asSet, program, dataType, fieldMatcher, monitor);
}
monitor.checkCanceled();
@ -265,7 +333,7 @@ public final class ReferenceUtils {
private static void findDataTypeMatchesOutsideOfListing(
Accumulator<LocationReference> accumulator, Program program, DataType dataType,
String fieldName, TaskMonitor monitor) throws CancelledException {
FieldMatcher fieldMatcher, TaskMonitor monitor) throws CancelledException {
List<DataTypeReferenceFinder> finders =
ClassSearcher.getInstances(DataTypeReferenceFinder.class);
@ -284,12 +352,7 @@ public final class ReferenceUtils {
}
for (DataTypeReferenceFinder finder : finders) {
if (fieldName == null) {
finder.findReferences(program, dataType, callback, monitor);
}
else {
finder.findReferences(program, dataType, fieldName, callback, monitor);
}
finder.findReferences(program, fieldMatcher, callback, monitor);
}
}
@ -617,15 +680,15 @@ public final class ReferenceUtils {
* some digging to see what is buried at the given address.
*/
private static LocationDescriptor createDataMemberLocationDescriptor(
OperandFieldLocation location, Address refAddress) {
OperandFieldLocation location, Address address) {
// TODO we don't support data types on external addresses; this could change in the future
if (refAddress.isExternalAddress()) {
// Note: we don't support data types on external addresses; this could change in the future
if (address.isExternalAddress()) {
return null;
}
Program program = location.getProgram();
Data outermostData = getDataContainingAddress(program, refAddress);
Data outermostData = getDataContainingAddress(program, address);
if (outermostData == null) {
// no data
return null;
@ -634,12 +697,12 @@ public final class ReferenceUtils {
String fieldPath = getFieldPath(location);
if (!fieldPath.contains(".")) {
// no field reference, so don't create a structure member reference, but just
// a generic data type reference
// no field reference, so don't create a structure member reference, but just a generic
// data type reference
DataType type = outermostData.getDataType();
if (type == DataType.DEFAULT || Undefined.isUndefined(type)) {
// nobody wants to search for undefined usage; too many (this is the case
// where the user is not on an actual data type)
// nobody wants to search for undefined usage; too many (this is the case where the
// user is not on an actual data type)
return null;
}
@ -650,15 +713,15 @@ public final class ReferenceUtils {
String fieldName = getFieldName(location);
Address parentAddress = outermostData.getMinAddress();
int componentAddress = (int) refAddress.subtract(parentAddress);
int componentAddress = (int) address.subtract(parentAddress);
Data subData = outermostData.getComponentContaining(componentAddress);
if (subData != null) {
int[] componentPath = subData.getComponentPath();
FieldNameFieldLocation fieldLocation =
new FieldNameFieldLocation(program, refAddress, componentPath, fieldName, 0);
new FieldNameFieldLocation(program, address, componentPath, fieldName, 0);
LocationDescriptor descriptor =
createSubDataMemberLocationDescriptor(program, refAddress, fieldLocation, subData);
createSubDataMemberLocationDescriptor(program, address, fieldLocation, subData);
return descriptor;
}
@ -667,18 +730,41 @@ public final class ReferenceUtils {
//
DataType dt = outermostData.getDataType();
if (dt instanceof Union) {
AddressFieldLocation addressLocation = new AddressFieldLocation(program, refAddress,
new int[] { 0 }, refAddress.toString(), 0);
AddressFieldLocation addressLocation =
new AddressFieldLocation(program, address, new int[] { 0 }, address.toString(), 0);
return new UnionLocationDescriptor(addressLocation, program);
}
return null;
}
private static GenericDataTypeLocationDescriptor createGenericDataTypeLocationDescriptor(
OperandFieldLocation location) {
Data data = getDataAt(location);
if (data == null) {
return null;
}
DataType dt = data.getDataType();
if (dt instanceof Enum) {
String enumText = location.getOperandRepresentation();
GenericCompositeDataTypeProgramLocation genericLocation =
new GenericCompositeDataTypeProgramLocation(location.getProgram(), dt, enumText);
return new GenericCompositeDataTypeLocationDescriptor(genericLocation,
location.getProgram());
}
GenericDataTypeProgramLocation genericLocation =
new GenericDataTypeProgramLocation(location.getProgram(), dt);
return new GenericDataTypeLocationDescriptor(genericLocation, location.getProgram(), dt);
}
/*
* Creates a location descriptor using the String display markup and type information
* found inside of the VariableOffset object.
*
*
* This method differs from createDataMemberLocationDescriptor() in that this method
* will create locations that represent DataTypes that are not applied in memory.
*/
@ -813,9 +899,15 @@ public final class ReferenceUtils {
private static LocationDescriptor createOperandLocationDescriptor(
OperandFieldLocation location) {
// this is the 'to' address
Address refAddress = getReferenceAddress(location);
if (refAddress == null) {
return null; // no reference and no variable-offset
//
// No reference address for this location. Try to create a generic data descriptor.
//
GenericDataTypeLocationDescriptor descriptor =
createGenericDataTypeLocationDescriptor(location);
return descriptor;
}
Address operandAddress = location.getAddress();
@ -824,20 +916,18 @@ public final class ReferenceUtils {
ReferenceManager referenceManager = program.getReferenceManager();
Reference reference =
referenceManager.getReference(operandAddress, refAddress, operandIndex);
if (reference == null) {
// Prefer using the reference, for consistency. Without that, the
// VariableOffset object contains markup and type information we can use.
// Having a VariableOffset without a reference occurs when a
// register variable reference is inferred during instruction operand formatting.
// Prefer using the reference, for consistency. Without that, the VariableOffset
// object contains markup and type information we can use. Having a VariableOffset
// without a reference occurs when a register variable reference is inferred during
// instruction operand formatting.
VariableOffset variableOffset = location.getVariableOffset();
return createGenericDataTypeLocationDescriptor(program, variableOffset);
}
// note: not sure why we are ignoring external references. It seems like that is
// a thing you may want to find refs to. If you figure it out, update this
// comment.
// note: not sure why we are ignoring external references. It seems like that is a thing
// you may want to find refs to. If you figure it out, update this comment.
// if (reference.isExternalReference()) {
// return null;
// }
@ -930,17 +1020,17 @@ public final class ReferenceUtils {
/**
* Searches defined data for types that match, according to the given predicate.
*
*
* @param accumulator the results accumulator
* @param program the program
* @param dataMatcher the predicate that determines a successful match
* @param fieldName the optional field name for which to search
* @param fieldMatcher the field matcher; will be ignored if it contains null values
* @param monitor the task monitor used to track progress and cancel the work
* @throws CancelledException if the operation was cancelled
*/
public static void findDataTypeMatchesInDefinedData(Accumulator<LocationReference> accumulator,
Program program, Predicate<Data> dataMatcher, String fieldName, TaskMonitor monitor)
throws CancelledException {
Program program, Predicate<Data> dataMatcher, FieldMatcher fieldMatcher,
TaskMonitor monitor) throws CancelledException {
Listing listing = program.getListing();
DataIterator dataIter = listing.getDefinedData(true);
@ -948,39 +1038,43 @@ public final class ReferenceUtils {
monitor.checkCanceled();
Data data = dataIter.next();
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, data, fieldName,
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, data, fieldMatcher,
dataMatcher, monitor);
monitor.incrementProgress(1);
}
}
private static LocationReference createReferenceFromDefinedData(Data data, String fieldName) {
private static LocationReference createReferenceFromDefinedData(Data data,
FieldMatcher fieldMatcher) {
Address dataAddress = data.getMinAddress();
if (fieldName == null) {
// no field--include the hit
if (fieldMatcher.isIgnored()) {
// no field to match; include the hit
return new LocationReference(dataAddress, data.getPathName());
}
DataTypeComponent component = getDataTypeComponent(data, fieldName);
if (component == null) {
// this implies the given data does not contain our field--do not include the hit
return null;
}
// Note: just check the current type; we may have to unroll it, looking for pointers
// along the way if this is not sufficient
DataType dt = data.getDataType();
if (dt instanceof Pointer) {
// For defined data, do not include pointer types when we have a field name. A
// pointer to the base composite type is not a direct usage of the given field.
// pointer to the base composite type is not a direct usage of the given field.find
return null;
}
DataType baseDt = getBaseDataType(data.getDataType());
if (matchesEnumField(data, baseDt, fieldMatcher)) {
return new LocationReference(dataAddress, fieldMatcher.getDisplayText());
}
DataTypeComponent component = getDataTypeComponent(baseDt, fieldMatcher);
if (component == null) {
return null; // not in composite
}
Address componentAddress;
try {
componentAddress = dataAddress.addNoWrap(component.getOffset());
return new LocationReference(componentAddress, data.getPathName() + "." + fieldName);
return new LocationReference(componentAddress,
data.getPathName() + "." + fieldMatcher.getFieldName());
}
catch (AddressOverflowException e) {
// shouldn't happen
@ -990,33 +1084,58 @@ public final class ReferenceUtils {
return null;
}
private static DataTypeComponent getDataTypeComponent(Data data, String fieldName) {
DataType dt = getBaseDataType(data.getDataType());
if (!(dt instanceof Composite)) {
Msg.debug(ReferenceUtils.class,
"Somehow searched for a field name on a Data Type that is not a Composite");
return null;
private static boolean matchesEnumField(Data data, DataType dt, FieldMatcher matcher) {
if (!(dt instanceof Enum)) {
return false;
}
Composite c = (Composite) dt;
DataTypeComponent[] components = c.getDefinedComponents();
for (DataTypeComponent component : components) {
if (SystemUtilities.isEqual(component.getFieldName(), fieldName)) {
return component;
Enum enumm = (Enum) dt;
List<String> names = getEnumNames(data, enumm);
for (String name : names) {
long value = enumm.getValue(name);
if (matcher.matches(name, (int) value)) {
return true;
}
}
return false;
}
// Note: sometimes this happens if the user searches on an array element field, which
// exists only in the Listing markup
private static List<String> getEnumNames(Data data, Enum enumm) {
String enumEntryName = data.getDefaultValueRepresentation();
List<String> enumNames = new ArrayList<>(List.of(enumm.getNames()));
if (enumNames.contains(enumEntryName)) {
return List.of(enumEntryName);
}
if (!enumEntryName.contains(" | ")) {
return Collections.emptyList();
}
// not a big fan of using this delimiter check, but will do so for now until there is
// a better way, such as asking the enum to do this work for us
String[] names = enumEntryName.split(" \\| ");
return List.of(names);
}
private static DataTypeComponent getDataTypeComponent(DataType dt, FieldMatcher matcher) {
if (dt instanceof Composite) {
Composite c = (Composite) dt;
DataTypeComponent[] components = c.getDefinedComponents();
for (DataTypeComponent component : components) {
if (matcher.matches(component.getFieldName(), component.getOffset())) {
return component;
}
}
}
return null;
}
private static void getMatchingDataTypesReferencesFromDataAndSubData(
Accumulator<LocationReference> accumulator, Data data, String fieldName,
Accumulator<LocationReference> accumulator, Data data, FieldMatcher fieldMatcher,
Predicate<Data> dataMatcher, TaskMonitor monitor) throws CancelledException {
if (dataMatcher.test(data)) {
getMatchingDataTypesReferencesFromData(accumulator, data, fieldName, monitor);
getMatchingDataTypesReferencesFromData(accumulator, data, fieldMatcher, monitor);
}
// We know that arrays are all the same element; we decided to just mark the beginning.
@ -1031,16 +1150,16 @@ public final class ReferenceUtils {
monitor.checkCanceled();
Data subData = data.getComponent(i);
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, subData, fieldName,
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, subData, fieldMatcher,
dataMatcher, monitor);
}
}
private static void getMatchingDataTypesReferencesFromData(
Accumulator<LocationReference> accumulator, Data data, String fieldName,
Accumulator<LocationReference> accumulator, Data data, FieldMatcher fieldMatcher,
TaskMonitor monitor) throws CancelledException {
LocationReference ref = createReferenceFromDefinedData(data, fieldName);
LocationReference ref = createReferenceFromDefinedData(data, fieldMatcher);
if (ref == null) {
return;
}
@ -1058,12 +1177,13 @@ public final class ReferenceUtils {
Consumer<Reference> referenceConsumer = reference -> {
Address toAddress = reference.getToAddress();
if (fieldName == null) {
if (fieldMatcher.isIgnored()) {
// no field to match; use the data address
accumulator.add(new LocationReference(reference, isOffcut(program, toAddress)));
return;
}
// only add the reference if it is directly to the field
// have a field match; only add the reference if it is directly to the field
if (toAddress.equals(dataAddress)) {
accumulator.add(new LocationReference(reference, false));
}
@ -1113,7 +1233,6 @@ public final class ReferenceUtils {
}
private static boolean dataTypesMatch(DataType searchType, DataType possibleType) {
if (isBuiltIn(searchType)) {
Class<? extends DataType> clazz = searchType.getClass();
return clazz.equals(possibleType.getClass());
@ -1132,8 +1251,8 @@ public final class ReferenceUtils {
return false;
}
if (DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID.equals(
sourceArchive.getSourceArchiveID())) {
if (DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID
.equals(sourceArchive.getSourceArchiveID())) {
return true;
}
return false;
@ -1141,7 +1260,7 @@ public final class ReferenceUtils {
/**
* Returns all references to the given variable
*
*
* @param accumulator the results accumulator
* @param program the program
* @param variable the variable
@ -1153,8 +1272,8 @@ public final class ReferenceUtils {
ReferenceManager referenceManager = program.getReferenceManager();
Reference[] variableRefsTo = referenceManager.getReferencesTo(variable);
for (Reference ref : variableRefsTo) {
accumulator.add(
new LocationReference(ref, !ref.getToAddress().equals(variableAddress)));
accumulator
.add(new LocationReference(ref, !ref.getToAddress().equals(variableAddress)));
}
}

View file

@ -40,12 +40,10 @@ class UnionLocationDescriptor extends DataTypeLocationDescriptor {
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
throws CancelledException {
// Note: we pass null for the 'fieldName', as we have no way of disambiguating which
// Note: we don't use 'fieldName' of the union, as we have no way of disambiguating which
// field a reference will point to. So, grab all references.
String fieldName = null;
ReferenceUtils.findDataTypeReferences(accumulator, union, fieldName, program,
useDynamicSearching, monitor);
ReferenceUtils.findDataTypeReferences(accumulator, union, program, useDynamicSearching,
monitor);
}
@Override

View file

@ -38,7 +38,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
* <p>
* Note that this operation is multi-threaded and that results will be delivered as they
* are found via the <code>callback</code>.
*
*
* @param program the program to search
* @param dataType the type for which to search
* @param callback the callback to be called when a reference is found
@ -46,8 +46,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
* @throws CancelledException if the operation was cancelled
*/
public void findReferences(Program program, DataType dataType,
Consumer<DataTypeReference> callback,
TaskMonitor monitor) throws CancelledException;
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
/**
* Finds references in the current program to specific field of the given {@link Composite} type
@ -55,14 +54,34 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
* <p>
* Note that this operation is multi-threaded and that results will be delivered as they
* are found via the <code>callback</code>.
*
*
* @param program the program to search
* @param dataType the type containing the field for which to search
* @param fieldName the name of the composite's field for which to search
* @param fieldName the name of the composite's field for which to search; may be null
* @param callback the callback to be called when a reference is found
* @param monitor the monitor that allows for progress and cancellation
* @throws CancelledException if the operation was cancelled
*/
public void findReferences(Program program, DataType dataType, String fieldName,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
/**
* Finds references in the current program to specific field of the given {@link Composite} type
* in a manner appropriate with the given implementation.
* <p>
* The supplied field matcher will be used to restrict matches to the given field. The matcher
* may be 'empty', supplying only the data type for which to search. In this case, all uses
* of the type will be matched, regardless of field.
* <p>
* Note that this operation is multi-threaded and that results will be delivered as they
* are found via the <code>callback</code>.
*
* @param program the program to search
* @param fieldMatcher the field matcher to use for matching types
* @param callback the callback to be called when a reference is found
* @param monitor the monitor that allows for progress and cancellation
* @throws CancelledException if the operation was cancelled
*/
public void findReferences(Program program, FieldMatcher fieldMatcher,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
}

View file

@ -0,0 +1,185 @@
/* ###
* 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.services;
import java.util.Objects;
import ghidra.program.model.data.*;
import ghidra.util.datastruct.SortedRangeList;
/**
* This class allows clients to match on multiple field attributes, such as name and offset
* within a parent data type.
* <p>
* Use {@link #FieldMatcher(DataType)} as an 'empty' or 'ignored' field matcher to signal that any
* field match is considered value.
*/
public class FieldMatcher {
private String fieldName;
private SortedRangeList fieldOffsets = new SortedRangeList();
private DataType dataType;
/**
* Creates an 'empty' matcher that can be used to signal no specific field or offset match
* is required.
* @param dataType the non-null data type.
*/
public FieldMatcher(DataType dataType) {
this.dataType = Objects.requireNonNull(dataType);
}
public FieldMatcher(DataType dataType, String fieldName) {
this.dataType = Objects.requireNonNull(dataType);
this.fieldName = fieldName;
}
public FieldMatcher(DataType dataType, int offset) {
this.dataType = Objects.requireNonNull(dataType);
fieldOffsets.addRange(offset, offset);
}
/**
* Signals that no specific field match is required.
* @return true if no field or offset has been specified.
*/
public boolean isIgnored() {
return fieldName == null && fieldOffsets.isEmpty();
}
public boolean matches(String dtFieldName, int dtOffset) {
if (isIgnored()) {
return true; // an empty matcher signals to match all fields
}
if (fieldName != null) {
if (Objects.equals(fieldName, dtFieldName)) {
return true;
}
}
if (fieldOffsets.contains(dtOffset)) {
return true;
}
return false;
}
/**
* Returns a display text for this field matcher, for example, {@code Foo.bar}.
* @return the display text
*/
public String getDisplayText() {
if (fieldName != null) {
return dataType.getName() + '.' + fieldName;
}
if (!fieldOffsets.isEmpty()) {
String compositeFieldName = generateCompositeFieldNameByOffset();
if (compositeFieldName != null) {
return compositeFieldName;
}
return dataType.getName() + " at " + fieldOffsets.toString();
}
return dataType.getName();
}
private String generateCompositeFieldNameByOffset() {
long n = fieldOffsets.getNumValues();
if (n != 1) {
return null;
}
int offset = fieldOffsets.getMin();
if (dataType instanceof Structure) {
Structure structure = (Structure) dataType;
DataTypeComponent dtc = structure.getComponentContaining(offset);
if (dtc != null) {
String name = dtc.getFieldName();
if (name != null) {
return name;
}
return dtc.getDefaultFieldName();
}
}
else if (dataType instanceof Composite) {
Composite composite = (Composite) dataType;
DataTypeComponent[] components = composite.getComponents();
for (DataTypeComponent dtc : components) {
int dtcOffset = dtc.getOffset();
if (dtcOffset == offset) {
return dtc.getFieldName();
}
}
}
return null;
}
public DataType getDataType() {
return dataType;
}
/**
* Returns the field name given to this matcher or will attempt to generate a default field
* name using the given data type and offset.
* @return the field name or null
*/
public String getFieldName() {
if (fieldName != null) {
return fieldName;
}
return generateCompositeFieldNameByOffset();
}
@Override
public String toString() {
return getDisplayText();
}
@Override
public int hashCode() {
return Objects.hash(dataType, fieldName, fieldOffsets);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
FieldMatcher other = (FieldMatcher) obj;
if (!Objects.equals(dataType, other.dataType)) {
return false;
}
if (!Objects.equals(fieldName, other.fieldName)) {
return false;
}
if (!Objects.equals(fieldOffsets, other.fieldOffsets)) {
return false;
}
return true;
}
}

View file

@ -79,8 +79,8 @@ public abstract class FieldFactory implements ExtensionPoint {
this.displayOptions = displayOptions;
initDisplayOptions();
fieldOptions.getOptions(name).setOptionsHelpLocation(
new HelpLocation("CodeBrowserPlugin", name));
fieldOptions.getOptions(name)
.setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", name));
}
protected void initDisplayOptions() {
@ -141,6 +141,7 @@ public abstract class FieldFactory implements ExtensionPoint {
* @param highlightProvider the HightLightProvider.
* @param options the Options for display properties.
* @param fieldOptions the Options for field specific properties.
* @return the factory
*/
public abstract FieldFactory newInstance(FieldFormatModel formatModel,
HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions);
@ -185,6 +186,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Returns the Field name.
* @return the name.
*/
public String getFieldName() {
return name;
@ -192,14 +194,15 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Returns the default field color.
* @return the color.
*/
public Color getDefaultColor() {
return Color.BLACK;
}
/**
* Returns the starting x position for the fields generated by this
* factory.
* Returns the starting x position for the fields generated by this factory.
* @return the start x.
*/
public int getStartX() {
return startX;
@ -207,6 +210,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Sets the starting x position for the fields generated by this factory.
* @param x the x position.
*/
public void setStartX(int x) {
startX = x;
@ -214,6 +218,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Returns the width of the fields generated by this factory.
* @return the width.
*/
public int getWidth() {
return width;
@ -221,6 +226,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Sets the width of the fields generated by this factory.
* @param w the width.
*/
public void setWidth(int w) {
width = w;
@ -228,6 +234,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Returns the FieldModel that this factory belongs to.
* @return the model.
*/
public FieldFormatModel getFieldModel() {
return model;
@ -235,6 +242,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Returns true if this FieldFactory is currently enabled to generate Fields.
* @return true if enabled.
*/
public boolean isEnabled() {
return enabled;
@ -261,12 +269,14 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Return a FieldLocation that corresponds to the given index, fieldNum, and ProgramLocation
* IF and ONLY IF the given programLocation is the type generated by this class's
* {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each FieldFactory
* should generate and process a unique ProgramLocation class.
* {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each
* FieldFactory should generate and process a unique ProgramLocation class.
*
* @param bf the ListingField at the current cursor.
* @param index the line index (corresponds to an address)
* @param fieldNum the index of field within the layout to try and get a FieldLocation.
* @param loc the ProgramLocation to be converted into a FieldLocation.
* @return the location.
*/
public abstract FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
ProgramLocation loc);
@ -276,6 +286,7 @@ public abstract class FieldFactory implements ExtensionPoint {
* @param row the row within this field
* @param col the col on the given row within this field.
* @param bf the ListingField containing the cursor.
* @return the location.
*/
public abstract ProgramLocation getProgramLocation(int row, int col, ListingField bf);
@ -313,6 +324,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Returns a description of the fields generated by this factory.
* @return the text.
*/
public String getFieldText() {
return name;
@ -320,14 +332,12 @@ public abstract class FieldFactory implements ExtensionPoint {
/**
* Returns the font metrics used by this field factory
* @return the metrics.
*/
public FontMetrics getMetrics() {
return getMetrics(style);
}
/**
* @return Returns the metrics.
*/
protected FontMetrics getMetrics(int fontStyle) {
if (fontStyle == -1) {
return defaultMetrics;
@ -335,8 +345,7 @@ public abstract class FieldFactory implements ExtensionPoint {
return fontMetrics[fontStyle];
}
@SuppressWarnings("deprecation")
// we know
@SuppressWarnings("deprecation") // we know
private void setMetrics(Font newFont) {
defaultMetrics = Toolkit.getDefaultToolkit().getFontMetrics(newFont);
for (int i = 0; i < fontMetrics.length; i++) {

View file

@ -57,7 +57,7 @@ abstract class OperandFieldHelper extends FieldFactory {
private final static String SPACE_AFTER_SEPARATOR_OPTION =
GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Add Space After Separator";
public static enum UNDERLINE_CHOICE {
public enum UNDERLINE_CHOICE {
Hidden, All, None
}
@ -183,12 +183,6 @@ abstract class OperandFieldHelper extends FieldFactory {
return null;
}
/**
* Returns the FactoryField for the given object at index index.
*
* @param obj the object whose properties should be displayed.
* @param varWidth the amount of variable width spacing for any fields before this one.
*/
ListingField getField(Object obj, ProxyObj<?> proxy, int varWidth) {
if (!enabled) {
return null;
@ -222,10 +216,10 @@ abstract class OperandFieldHelper extends FieldFactory {
ListingTextField btf = (ListingTextField) lf;
FieldElement fieldElement = btf.getFieldElement(row, col);
if (!(fieldElement instanceof OperandFieldElement)) {
return null;
}
OperandFieldElement element = (OperandFieldElement) fieldElement;
int opIndex = element.getOperandIndex();
int subOpIndex = element.getOperandSubIndex();
@ -276,11 +270,10 @@ abstract class OperandFieldHelper extends FieldFactory {
else if (obj instanceof Data) {
Data data = (Data) obj;
Address refAddr = null;
Reference primaryReference =
data.getProgram()
.getReferenceManager()
.getPrimaryReferenceFrom(
data.getMinAddress(), 0);
Program program = data.getProgram();
ReferenceManager referenceManager = program.getReferenceManager();
Address minAddress = data.getMinAddress();
Reference primaryReference = referenceManager.getPrimaryReferenceFrom(minAddress, 0);
Object value = data.getValue();
if (primaryReference != null) {
refAddr = primaryReference.getToAddress();
@ -291,20 +284,18 @@ abstract class OperandFieldHelper extends FieldFactory {
}
}
Program program = data.getProgram();
if (value instanceof Scalar) {
Scalar scalar = (Scalar) value;
Equate equate = program.getEquateTable()
.getEquate(data.getMinAddress(), opIndex,
scalar.getValue());
EquateTable equateTable = program.getEquateTable();
Equate equate = equateTable.getEquate(minAddress, opIndex, scalar.getValue());
if (equate != null) {
return new EquateOperandFieldLocation(program, data.getMinAddress(), refAddr,
return new EquateOperandFieldLocation(program, minAddress, refAddr,
equate.getDisplayName(), equate, opIndex, subOpIndex,
translatedLocation.col());
}
}
return new OperandFieldLocation(program, data.getMinAddress(), data.getComponentPath(),
refAddr, codeUnitFormat.getDataValueRepresentationString(data), 0, col);
return new OperandFieldLocation(program, minAddress, data.getComponentPath(), refAddr,
codeUnitFormat.getDataValueRepresentationString(data), 0, col);
}
return null;
}
@ -477,9 +468,9 @@ abstract class OperandFieldHelper extends FieldFactory {
private int addElements(Instruction inst, List<OperandFieldElement> elements, List<?> objList,
int opIndex, int subOpIndex, boolean underline, int characterOffset) {
for (int i = 0; i < objList.size(); i++) {
characterOffset = addElement(inst, elements, objList.get(i), underline, opIndex,
subOpIndex, characterOffset);
for (Object element : objList) {
characterOffset = addElement(inst, elements, element, underline, opIndex, subOpIndex,
characterOffset);
}
return characterOffset;
}

View file

@ -30,7 +30,7 @@ import ghidra.util.classfinder.ClassSearcher;
/**
* Generates data value Fields for data subcomponents.
* <P>
* <P>
* This field is not meant to be loaded by the {@link ClassSearcher}, hence the X in the name.
*/
public class SubDataFieldFactory extends OperandFieldFactory {
@ -43,16 +43,10 @@ public class SubDataFieldFactory extends OperandFieldFactory {
* @param path the component path for the data
*/
public SubDataFieldFactory(String name, int[] path) {
super();
this.componentPath = path;
this.name = name;
}
/**
* Constructor
* @param provider The FieldProvider object that serves as the SubDataFieldFactory factory.
* @param model The Field model that will use this Address factory.
*/
private SubDataFieldFactory(String name, int[] componentPath, FieldFormatModel model,
HighlightProvider hlProvider, ToolOptions displayOptions, ToolOptions fieldOptions) {
super(model, hlProvider, displayOptions, fieldOptions);
@ -60,12 +54,6 @@ public class SubDataFieldFactory extends OperandFieldFactory {
this.componentPath = componentPath;
}
/**
* Returns the FactoryField for the given object at index index.
* @param varWidth the amount of variable width spacing for any fields
* before this one.
* @param proxy the object whose properties should be displayed.
*/
@Override
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
Object obj = proxy.getObject();
@ -138,8 +126,8 @@ public class SubDataFieldFactory extends OperandFieldFactory {
@Override
public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider,
ToolOptions displayOptions, ToolOptions fieldOptions) {
return new SubDataFieldFactory(name, componentPath, formatModel, provider, displayOptions,
ToolOptions options, ToolOptions fieldOptions) {
return new SubDataFieldFactory(name, componentPath, formatModel, provider, options,
fieldOptions);
}

View file

@ -48,8 +48,7 @@ import ghidra.util.exception.AssertException;
import ghidra.util.exception.RollbackException;
import junit.framework.AssertionFailedError;
import utility.application.ApplicationLayout;
import utility.function.ExceptionalCallback;
import utility.function.ExceptionalFunction;
import utility.function.*;
public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDockingTest {
@ -68,8 +67,6 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
private static Language Z80_LANGUAGE;
public AbstractGhidraHeadlessIntegrationTest() {
super();
// Ensure that all error messages do NOT use a gui popup, and instead are routed to the
// console.
setErrorGUIEnabled(false);
@ -167,7 +164,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* Run a command against the specified program within a transaction.
* The transaction will be committed unless the command throws a RollbackException.
*
*
* @param program the program
* @param cmd the command to apply
* @return result of command applyTo method
@ -199,7 +196,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* Provides a convenient method for modifying the current program, handling the transaction
* logic.
*
*
* @param p the program
* @param c the code to execute
* @see #modifyProgram(Program, ExceptionalCallback)
@ -222,11 +219,41 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
}
}
/**
* Provides a convenient method for modifying the current program, handling the transaction
* logic and returning a result.
* @param <T> the return type
* @param <E> the exception type
* @param p the program
* @param s the code to execute
* @return the supplier's return value
* @see #modifyProgram(Program, ExceptionalCallback)
* @see #modifyProgram(Program, ExceptionalFunction)
*/
public static <T, E extends Exception> T tx(Program p, ExceptionalSupplier<T, E> s) {
int txId = p.startTransaction("Test - Function in Transaction");
boolean commit = true;
try {
T t = s.get();
p.flushEvents();
waitForSwing();
return t;
}
catch (Exception e) {
commit = false;
failWithException("Exception modifying program '" + p.getName() + "'", e);
}
finally {
p.endTransaction(txId, commit);
}
return null;
}
/**
* Provides a convenient method for modifying the current program, handling the transaction
* logic. This method is calls {@link #tx(Program, ExceptionalCallback)}, but helps with
* semantics.
*
*
* @param p the program
* @param c the code to execute
* @see #modifyProgram(Program, ExceptionalFunction)
@ -238,7 +265,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* Provides a convenient method for modifying the current program, handling the transaction
* logic and returning a new item as a result
*
*
* @param program the program
* @param f the function for modifying the program and creating the desired result
* @return the result
@ -439,7 +466,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* Returns the global symbol with the given name if and only if it is the only
* global symbol with that name.
*
*
* @param program the program to search.
* @param name the name of the global symbol to find.
* @return the global symbol with the given name if and only if it is the only one.
@ -451,7 +478,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* Returns the symbol in the given namespace with the given name if and only if it is the only
* symbol in that namespace with that name.
*
*
* @param program the program to search.
* @param name the name of the symbol to find.
* @param namespace the parent namespace; may be null
@ -468,14 +495,14 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* A convenience method that allows you to open the given program in a default tool,
* navigating to the given address.
*
*
* <P>Note: this is a blocking operation. Your test will not proceed while this method is
* sleeping.
*
*
* <P><B>Do not leave this call in your test when committing changes.</B>
* @param p the program
* @param address the address
*
*
* @throws Exception if there is an issue create a {@link TestEnv}
*/
public void debugProgramInTool(Program p, String address) throws Exception {
@ -512,7 +539,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* Waits for a launched script to complete by using the given listener.
*
*
* @param listener the listener used to track script progress
* @param timeoutMS the max time to wait; failing if exceeded
*/
@ -534,7 +561,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/**
* Replaces the given implementations of the provided service class with the given class.
*
*
* @param tool the tool whose services to update (optional)
* @param service the service to override
* @param replacement the new version of the service

View file

@ -44,6 +44,7 @@ import ghidra.program.util.AddressFieldLocation;
import ghidra.program.util.FieldNameFieldLocation;
import ghidra.test.AbstractProgramBasedTest;
import ghidra.test.ClassicSampleX86ProgramBuilder;
import ghidra.util.Msg;
import ghidra.util.datastruct.Accumulator;
import ghidra.util.datastruct.ListAccumulator;
import ghidra.util.exception.CancelledException;
@ -51,7 +52,7 @@ import ghidra.util.table.GhidraTable;
import ghidra.util.task.TaskMonitor;
/**
* A base class for use by tests that exercise various types of
* A base class for use by tests that exercise various types of
* {@link LocationDescriptor}.
*/
public abstract class AbstractLocationReferencesTest extends AbstractProgramBasedTest {
@ -98,7 +99,7 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
//
// Arrays/Structures
//
//
DataType type = new IntegerDataType();
DataType pointer = new PointerDataType(type);
ArrayDataType array = new ArrayDataType(pointer, 4, pointer.getLength());
@ -150,7 +151,7 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
private void doGoToDataNameFieldAt(Address a, int[] path) {
openData(a);
// note: the path here is
// note: the path here is
FieldNameFieldLocation location = new FieldNameFieldLocation(program, a, path, "name", 0);
ProgramLocationPluginEvent event =
new ProgramLocationPluginEvent("Test", location, program);
@ -318,7 +319,10 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
protected void assertResultCount(int expected) {
List<Address> referenceAddresses = getResultAddresses();
assertEquals(expected, referenceAddresses.size());
if (referenceAddresses.size() != expected) {
Msg.debug(this, "Result addresses found: " + referenceAddresses);
fail("Incorrect number of results; see console");
}
}
protected void assertResultCount(String msg, int expected) {

View file

@ -15,13 +15,16 @@
*/
package ghidra.app.plugin.core.navigation.locationreferences;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import ghidra.app.cmd.data.CreateDataCmd;
import ghidra.app.util.viewer.field.FieldNameFieldFactory;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.util.ProgramLocation;
public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTest {
@ -38,4 +41,61 @@ public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTes
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
}
@Test
public void testFindStructureField_UnnamedDefaultField() {
// apply a structure with unnamed fields
Structure struct = (Structure) getDt("/MyStruct");
Address address = addr(0x01005560);
assertTrue(applyCmd(program, new CreateDataCmd(address, struct)));
openData(0x01005560);
goTo(addr(0x01005560), FieldNameFieldFactory.FIELD_NAME, 1);
ProgramLocation location = codeBrowser.getCurrentLocation();
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
}
@Test
public void testFindEnumByMember() {
//
// This test searches for usage of an enum field. We will add two different enum field
// uses to make sure we only find the one for which we are searching.
//
Enum enoom = createEnum();
Address otherAddress = addr(0x01008014); // 0x1 ONE; this also has references
assertTrue(applyCmd(program, new CreateDataCmd(otherAddress, enoom)));
// this is the address will will use to search
Address address = addr(0x01008019); // 0x0 ZERO
assertTrue(applyCmd(program, new CreateDataCmd(address, enoom)));
goTo(address, "Operands", 1);
search();
assertResultCount(1);
}
private DataType getDt(String path) {
DataTypeManager dtm = program.getDataTypeManager();
DataType dataType = dtm.getDataType(path);
return dataType;
}
private Enum createEnum() {
return tx(program, () -> {
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
Enum dt = new EnumDataType("TestEnum", 1);
dt.add("ZERO", 0);
dt.add("ONE", 1);
dt.add("TWO", 2);
return (Enum) dtm.addDataType(dt, null);
});
}
}

View file

@ -15,8 +15,7 @@
*/
package ghidra.app.plugin.core.navigation.locationreferences;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.*;
@ -44,7 +43,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
int parameterColumn = 1;
goTo(address, "Function Signature", parameterColumn);
// change the return type
// change the return type
DataType dataType = setReturnTypeToByte(address);
search();
@ -176,7 +175,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
@Test
public void testLabelLocationDescriptor() throws Exception {
// 010039fe - LAB_010039fe
// 010039fe - LAB_010039fe
Address address = addr(0x010039fe);
int column = 3;
goTo(address, "Label", column);
@ -231,19 +230,6 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
assertThat(descriptor, is(instanceOf(AddressLocationDescriptor.class)));
}
@Test
public void testFieldNameLocationDescriptor_StructureFieldName_ArrayInStructure()
throws Exception {
openData(0x01005540);
goTo(addr(0x01005541), FieldNameFieldFactory.FIELD_NAME, 1);
ProgramLocation location = codeBrowser.getCurrentLocation();
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
}
@Test
public void testFieldNameLocationDescriptor_StructureInArray() throws Exception {
@ -261,7 +247,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
@Test
public void testFindReferencesToFunctionDefinitionDataTypeFromService() throws Exception {
//
//
// For this test we will have to create a FunctionDefinitionData type that matches
// that of an existing function
//
@ -312,7 +298,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
//
// Dynamic data types should show all references to the the outermost data, including
// offcut.
// offcut.
//
// go to an unused address
@ -379,11 +365,9 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
assertContains(results, from1, from2, from3, stringAddr);
}
//==================================================================================================
// Private Methods
//==================================================================================================
//==================================================================================================
private void createString_CallStructure(String addressString) throws Exception {
// String