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> references to the address 0040767d.</P>
<BLOCKQUOTE> <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 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> the selected data type is applied will be highlighted.</P>
</BLOCKQUOTE> </BLOCKQUOTE>
@ -73,11 +73,11 @@
<LI>Select whichever of the following is available from the popup menu: <LI>Select whichever of the following is available from the popup menu:
<UL> <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> 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> 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> from the popup menu.</LI>
</UL> </UL>
</OL> </OL>
@ -134,12 +134,12 @@
</P> </P>
<BLOCKQUOTE> <BLOCKQUOTE>
<P> <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. To instead make a Program Selection Highlight, use the select button mentioned above.
Then, click from the menu bar <B>Select 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 Program Highlight
<img src="../../shared/arrow.gif" alt="-&gt;" border="0"> <img src="images/arrow.gif" alt="-&gt;" border="0">
Entire Selection Entire Selection
</B> </B>
</P> </P>
@ -167,7 +167,7 @@
the decompiled function will be shown. the decompiled function will be shown.
</P> </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> selection in the Code Browser from the entries in the table:</P>
<OL> <OL>
@ -198,16 +198,26 @@
the action will display all places that function is applied.</P> 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> <BLOCKQUOTE>
<P> <P>
<IMG border="0" src="../../shared/note.png" alt=""> <IMG border="0" src="images/note.png" alt="">
<A name="Data_Type_Discovery"></A> <A name="Data_Type_Discovery"></A>
By default, finding uses of By default, finding uses of
data types will search not only for applied data types, but also will perform 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> 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 service. This causes the search to be slower, but also reports many more type
uses. To disable the dynamic searching, use the 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> <B>Dynamic Data Type Discovery</B>
<A href="help/topics/Tool/ToolOptions_Dialog.htm">tool option</A>. <A href="help/topics/Tool/ToolOptions_Dialog.htm">tool option</A>.
</P> </P>
@ -221,7 +231,7 @@
<OL start="1" type="1"> <OL start="1" type="1">
<LI>Right-mouse anywhere on the code unit*</LI> <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> Address </B> from the popup menu.</LI>
</OL> </OL>
@ -240,7 +250,7 @@
<BLOCKQUOTE> <BLOCKQUOTE>
<P> <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. references to the current code unit. No other special reference finding will take place.
</P> </P>
</BLOCKQUOTE> </BLOCKQUOTE>
@ -250,7 +260,7 @@
<BLOCKQUOTE> <BLOCKQUOTE>
<P> <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 "help/topics/DockingWindows/Docking_Windows.htm#Renaming_Windows">Docking Windows - Renaming
Windows</A></I> Windows</A></I>
</P> </P>

View file

@ -25,9 +25,10 @@ import docking.DockingUtils;
import docking.action.*; import docking.action.*;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.services.FieldMatcher;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.*;
import ghidra.util.*; import ghidra.util.*;
public abstract class AbstractFindReferencesDataTypeAction extends DockingAction { public abstract class AbstractFindReferencesDataTypeAction extends DockingAction {
@ -89,7 +90,40 @@ public abstract class AbstractFindReferencesDataTypeAction extends DockingAction
DataType dataType = getDataType(context); DataType dataType = getDataType(context);
DataType baseDataType = ReferenceUtils.getBaseDataType(dataType); DataType baseDataType = ReferenceUtils.getBaseDataType(dataType);
String field = getDataTypeField(baseDataType); 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 @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
NumberRangeInputDialog inputDialog = NumberRangeInputDialog inputDialog = new NumberRangeInputDialog(getName(), "Size(s)");
new NumberRangeInputDialog(getName(), "Size(s)"); inputDialog.setHelpLocation(getHelpLocation());
if (!inputDialog.show()) { if (!inputDialog.show()) {
return; 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.DataTypesActionContext;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.services.FieldMatcher;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
@ -98,15 +99,27 @@ public class FindReferencesToFieldAction extends DockingAction {
choices = ((Enum) dt).getNames(); choices = ((Enum) dt).getNames();
} }
String userChoice = OptionDialog.showInputChoiceDialog(null, "Choose Field", String message = "Find uses of '" + dt.getName() + "' field by name or offset";
"Find uses of '" + dt.getName() + "' field", choices, null, String userChoice = OptionDialog.showEditableInputChoiceDialog(null, "Choose Field",
OptionDialog.QUESTION_MESSAGE); message, choices, null, OptionDialog.QUESTION_MESSAGE);
if (userChoice == null) { if (userChoice == null) {
return; return;
} }
Swing.runLater( FieldMatcher fieldMatcher;
() -> service.findAndDisplayAppliedDataTypeAddresses(dt, userChoice)); 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) { private String[] getCompisiteFieldNames(Composite composite) {

View file

@ -15,6 +15,7 @@
*/ */
package ghidra.app.plugin.core.navigation; package ghidra.app.plugin.core.navigation;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.data.DataType; 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 * Tells this service to find all places where the given datatype is applied <b>and</b> will
* display the results of the search. * display the results of the search.
* *
* @param dataType The datatype which to base the search upon. * @param dataType The datatype which to base the search upon.
*/ */
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType); 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 * Tells this service to find all places where the given datatype is applied <b>and</b> will
* display the results of the search. * display the results of the search.
* *
* @param dataType The datatype which to base the search upon. * @param dataType The datatype which to base the search upon.
* @param fieldName the sub-field for which to search * @param fieldName the sub-field for which to search
*/ */
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName); 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; import ghidra.util.task.TaskMonitor;
/** /**
* A location descriptor that should be extended by location descriptor implementations that * A location descriptor that should be extended by location descriptor implementations that are
* are based upon data types. * based upon data types.
*/ */
abstract class DataTypeLocationDescriptor extends LocationDescriptor { abstract class DataTypeLocationDescriptor extends LocationDescriptor {
@ -70,18 +70,28 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
findDataTypeReferences(accumulator, monitor); 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(); 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(); 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(); protected abstract String getDataTypeName();
/** /**
* The base data type that this location descriptor describes (this may be the same as the * The base data type that this location descriptor describes (this may be the same as the
* original data type. * original data type.
* @return the type.
*/ */
protected DataType getBaseDataType() { protected DataType getBaseDataType() {
return getSourceDataType(); // by default these two values are the same return getSourceDataType(); // by default these two values are the same
@ -91,7 +101,7 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
TaskMonitor monitor) throws CancelledException { TaskMonitor monitor) throws CancelledException {
DataType currentDataType = getDataType(); DataType currentDataType = getDataType();
ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, null, program, ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, program,
useDynamicSearching, monitor); useDynamicSearching, monitor);
} }
@ -177,8 +187,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
List<Variable> allVariables = ReferenceUtils.getVariables(function, true); List<Variable> allVariables = ReferenceUtils.getVariables(function, true);
for (Variable variable : allVariables) { for (Variable variable : allVariables) {
DataType variableDataType = variable.getDataType(); DataType variableDataType = variable.getDataType();
if (ReferenceUtils.getBaseDataType(variableDataType).isEquivalent( if (ReferenceUtils.getBaseDataType(variableDataType)
currentDataType)) { .isEquivalent(currentDataType)) {
return true; return true;
} }
} }
@ -266,8 +276,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
} }
// check for pointer names // check for pointer names
else if (label.endsWith("*") && label.startsWith(paramName)) { 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 // 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 // pointer and have found a match against a simple pointer and thus the display may
// not match our label // not match our label
if (paramParts.length == 1) { if (paramParts.length == 1) {
return paramName; // not a full declaration, just the name 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 org.apache.commons.lang3.StringUtils;
import docking.widgets.fieldpanel.support.Highlight; import docking.widgets.fieldpanel.support.Highlight;
import ghidra.app.services.FieldMatcher;
import ghidra.app.util.viewer.field.*; import ghidra.app.util.viewer.field.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Data;
@ -34,34 +35,39 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeLocationDescriptor { public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeLocationDescriptor {
private String typeAndFieldName; // this is either "Foo.bar" or "Foo offset 1"
private String fieldName; private String typeDisplayString;
private FieldMatcher fieldMatcher;
public GenericCompositeDataTypeLocationDescriptor( public GenericCompositeDataTypeLocationDescriptor(
GenericCompositeDataTypeProgramLocation location, Program program) { GenericCompositeDataTypeProgramLocation location, Program program) {
super(location, program, location.getDataType()); super(location, program, location.getDataType());
this.fieldName = location.getFieldName(); this.fieldMatcher = location.getFieldMatcher();
this.typeAndFieldName = getDataTypeName() + '.' + fieldName; this.typeDisplayString = fieldMatcher.getDisplayText();
label = generateLabel(); label = generateLabel();
} }
@Override @Override
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor) protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
ReferenceUtils.findDataTypeReferences(accumulator, getDataType(), fieldName, program, ReferenceUtils.findDataTypeFieldReferences(accumulator, fieldMatcher, program,
useDynamicSearching, monitor); useDynamicSearching, monitor);
} }
@Override @Override
public String getTypeName() { 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 // implemented to ignore the location being provided, since this is a 'dummy' type class
@Override @Override
protected String generateLabel() { 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 // 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 otherDescriptor =
(GenericCompositeDataTypeLocationDescriptor) obj; (GenericCompositeDataTypeLocationDescriptor) obj;
return getDataType().equals(otherDescriptor.getDataType()) && return getDataType().equals(otherDescriptor.getDataType()) &&
fieldName.equals(otherDescriptor.fieldName); fieldMatcher.equals(otherDescriptor.fieldMatcher);
} }
@Override @Override
@ -113,14 +119,15 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) { else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
// Not sure how to get the correct part of the text. This is a hack for now. // 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) { if (offset != -1) {
return new Highlight[] { return new Highlight[] { new Highlight(offset,
new Highlight(offset, offset + typeAndFieldName.length() - 1, highlightColor) }; offset + typeDisplayString.length() - 1, highlightColor) };
} }
} }
else if (FieldNameFieldFactory.class.isAssignableFrom(fieldFactoryClass)) { else if (FieldNameFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
String fieldName = fieldMatcher.getFieldName();
if (text.equalsIgnoreCase(fieldName)) { if (text.equalsIgnoreCase(fieldName)) {
return new Highlight[] { new Highlight(0, text.length(), highlightColor) }; 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 java.util.Objects;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.data.Composite; import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program; 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 * 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 * 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. * as {@link Composite} types and {@link Enum} types.
* *
* @see GenericCompositeDataTypeLocationDescriptor * @see GenericCompositeDataTypeLocationDescriptor
*/ */
public class GenericCompositeDataTypeProgramLocation extends GenericDataTypeProgramLocation { public class GenericCompositeDataTypeProgramLocation extends GenericDataTypeProgramLocation {
private String fieldName; private FieldMatcher fieldMatcher;
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, String fieldName) { GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, String fieldName) {
this(program, dataType, new FieldMatcher(dataType, fieldName));
}
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType,
FieldMatcher fieldMatcher) {
super(program, dataType); 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() { public FieldMatcher getFieldMatcher() {
return fieldName; return fieldMatcher;
} }
} }

View file

@ -260,10 +260,10 @@ public abstract class LocationDescriptor {
} }
/** /**
* Returns a generic {@link ProgramLocation} based upon the <tt>program</tt> and * Returns a generic {@link ProgramLocation} based upon the <tt>program</tt> and
* <tt>homeAddress</tt> of this <tt>LocationDescriptor</tt>. Subclasses should override this * <tt>homeAddress</tt> of this <tt>LocationDescriptor</tt>. Subclasses should override this
* method to return more specific addresses. * method to return more specific addresses.
* *
* @return a generic ProgramLocation. * @return a generic ProgramLocation.
*/ */
ProgramLocation getHomeLocation() { ProgramLocation getHomeLocation() {
@ -329,9 +329,9 @@ public abstract class LocationDescriptor {
TaskMonitor monitor) throws CancelledException; TaskMonitor monitor) throws CancelledException;
/** /**
* Returns a descriptive category name for this location descriptor. This is used for * Returns a descriptive category name for this location descriptor. This is used for
* display in a popup menu. * display in a popup menu.
* *
* @return a descriptive category name for this location descriptor * @return a descriptive category name for this location descriptor
*/ */
public String getTypeName() { 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. * 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. * location references that reference the location this descriptor is representing.
* @param monitor A monitor to report progress or cancel the gathering of addresses. * @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 * @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 * 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 * not only find references that are already created, but to also use external tools to
* locate potential references. * locate potential references.
* *
* @param useDynamicSearching true to perform dynamic searching * @param useDynamicSearching true to perform dynamic searching
*/ */
void setUseDynamicSearching(boolean useDynamicSearching) { 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 * 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 * in this descriptor may no longer be accurate. For example, the listener will be called
* when an undo or redo is performed in Ghidra. * 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) { void setModelFreshnessListener(ChangeListener modelFreshnessListener) {
this.modelFreshnessListener = 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 * 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 * data types, the address is where a data type is applied; for references, this value is the
* <tt>from</tt> address. * <tt>from</tt> address.
* *
* @return the address where the item described by this object is used. * @return the address where the item described by this object is used.
*/ */
public Address getLocationOfUse() { 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 * 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 * what part of a function signature the location matches or a line from the Decompiler
* that matches. * that matches.
* *
* @return the context * @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 * 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 * or may be String that highlights part of a function signature the location matches or
* a line from the Decompiler that matches. * a line from the Decompiler that matches.
* *
* @return the context * @return the context
*/ */
public LocationReferenceContext getContext() { public LocationReferenceContext getContext() {
@ -169,12 +169,7 @@ public class LocationReference implements Comparable<LocationReference> {
if (!context.equals(other.context)) { if (!context.equals(other.context)) {
return false; return false;
} }
if (locationOfUseAddress == null) { if (!Objects.equals(locationOfUseAddress, other.locationOfUseAddress)) {
if (other.locationOfUseAddress != null) {
return false;
}
}
else if (!locationOfUseAddress.equals(other.locationOfUseAddress)) {
return false; return false;
} }
return refType.equals(other.refType); return refType.equals(other.refType);
@ -194,7 +189,7 @@ public class LocationReference implements Comparable<LocationReference> {
"\taddress: " + locationOfUseAddress + ",\n" + "\taddress: " + locationOfUseAddress + ",\n" +
((refType.equals("")) ? "" : "\trefType: " + refType + ",\n") + ((refType.equals("")) ? "" : "\trefType: " + refType + ",\n") +
"\tisOffcut: " + isOffcutReference + ",\n" + "\tisOffcut: " + isOffcutReference + ",\n" +
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + ",") + ((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + "\n") +
"}"; "}";
//@formatter:off //@formatter:off
} }

View file

@ -22,7 +22,7 @@ import ghidra.util.HTMLUtilities;
/** /**
* A class to hold context representation for {@link LocationReference}s. * A class to hold context representation for {@link LocationReference}s.
* *
* @see LocationReferenceContextBuilder * @see LocationReferenceContextBuilder
*/ */
public class LocationReferenceContext { 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 * 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 * See the {@link LocationReferenceContextBuilder} for how to define matching text pieces
* @return the matching strings * @return the matching strings
*/ */
@ -123,7 +123,7 @@ public class LocationReferenceContext {
@Override @Override
public String toString() { 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.nav.Navigatable;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.services.GoToService; import ghidra.app.services.*;
import ghidra.app.services.ProgramManager;
import ghidra.app.util.XReferenceUtils; import ghidra.app.util.XReferenceUtils;
import ghidra.app.util.query.TableService; import ghidra.app.util.query.TableService;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
@ -211,8 +210,7 @@ public class LocationReferencesPlugin extends Plugin
} }
private void disposeProviderList() { private void disposeProviderList() {
for (int i = 0; i < providerList.size(); i++) { for (LocationReferencesProvider provider : providerList) {
LocationReferencesProvider provider = providerList.get(i);
provider.dispose(); provider.dispose();
} }
providerList.clear(); providerList.clear();
@ -261,8 +259,8 @@ public class LocationReferencesPlugin extends Plugin
} }
protected void programClosed(Program program) { protected void programClosed(Program program) {
for (Iterator<LocationReferencesProvider> iterator = for (Iterator<LocationReferencesProvider> iterator = providerList.iterator(); iterator
providerList.iterator(); iterator.hasNext();) { .hasNext();) {
LocationReferencesProvider provider = iterator.next(); LocationReferencesProvider provider = iterator.next();
if (provider.getProgram() == program) { if (provider.getProgram() == program) {
provider.dispose(); provider.dispose();
@ -293,11 +291,19 @@ public class LocationReferencesPlugin extends Plugin
@Override @Override
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType) { public void findAndDisplayAppliedDataTypeAddresses(DataType dataType) {
findAndDisplayAppliedDataTypeAddresses(dataType, null); findAndDisplayAppliedDataTypeAddresses(dataType, (FieldMatcher) null);
} }
@Override @Override
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName) { 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); ProgramManager programManagerService = tool.getService(ProgramManager.class);
GoToService goToService = tool.getService(GoToService.class); GoToService goToService = tool.getService(GoToService.class);
Program program = programManagerService.getCurrentProgram(); Program program = programManagerService.getCurrentProgram();
@ -308,9 +314,9 @@ public class LocationReferencesPlugin extends Plugin
} }
ProgramLocation genericLocation; ProgramLocation genericLocation;
if (fieldName != null) { if (fieldMatcher != null) {
genericLocation = genericLocation =
new GenericCompositeDataTypeProgramLocation(program, dataType, fieldName); new GenericCompositeDataTypeProgramLocation(program, dataType, fieldMatcher);
} }
else { else {
genericLocation = new GenericDataTypeProgramLocation(program, dataType); genericLocation = new GenericDataTypeProgramLocation(program, dataType);

View file

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

View file

@ -38,7 +38,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
* <p> * <p>
* Note that this operation is multi-threaded and that results will be delivered as they * Note that this operation is multi-threaded and that results will be delivered as they
* are found via the <code>callback</code>. * are found via the <code>callback</code>.
* *
* @param program the program to search * @param program the program to search
* @param dataType the type for which to search * @param dataType the type for which to search
* @param callback the callback to be called when a reference is found * @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 * @throws CancelledException if the operation was cancelled
*/ */
public void findReferences(Program program, DataType dataType, public void findReferences(Program program, DataType dataType,
Consumer<DataTypeReference> callback, Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
TaskMonitor monitor) throws CancelledException;
/** /**
* Finds references in the current program to specific field of the given {@link Composite} type * 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> * <p>
* Note that this operation is multi-threaded and that results will be delivered as they * Note that this operation is multi-threaded and that results will be delivered as they
* are found via the <code>callback</code>. * are found via the <code>callback</code>.
* *
* @param program the program to search * @param program the program to search
* @param dataType the type containing the field for which 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 callback the callback to be called when a reference is found
* @param monitor the monitor that allows for progress and cancellation * @param monitor the monitor that allows for progress and cancellation
* @throws CancelledException if the operation was cancelled * @throws CancelledException if the operation was cancelled
*/ */
public void findReferences(Program program, DataType dataType, String fieldName, public void findReferences(Program program, DataType dataType, String fieldName,
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
* 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; this.displayOptions = displayOptions;
initDisplayOptions(); initDisplayOptions();
fieldOptions.getOptions(name).setOptionsHelpLocation( fieldOptions.getOptions(name)
new HelpLocation("CodeBrowserPlugin", name)); .setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", name));
} }
protected void initDisplayOptions() { protected void initDisplayOptions() {
@ -141,6 +141,7 @@ public abstract class FieldFactory implements ExtensionPoint {
* @param highlightProvider the HightLightProvider. * @param highlightProvider the HightLightProvider.
* @param options the Options for display properties. * @param options the Options for display properties.
* @param fieldOptions the Options for field specific properties. * @param fieldOptions the Options for field specific properties.
* @return the factory
*/ */
public abstract FieldFactory newInstance(FieldFormatModel formatModel, public abstract FieldFactory newInstance(FieldFormatModel formatModel,
HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions); HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions);
@ -185,6 +186,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Returns the Field name. * Returns the Field name.
* @return the name.
*/ */
public String getFieldName() { public String getFieldName() {
return name; return name;
@ -192,14 +194,15 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Returns the default field color. * Returns the default field color.
* @return the color.
*/ */
public Color getDefaultColor() { public Color getDefaultColor() {
return Color.BLACK; return Color.BLACK;
} }
/** /**
* Returns the starting x position for the fields generated by this * Returns the starting x position for the fields generated by this factory.
* factory. * @return the start x.
*/ */
public int getStartX() { public int getStartX() {
return startX; return startX;
@ -207,6 +210,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Sets the starting x position for the fields generated by this factory. * Sets the starting x position for the fields generated by this factory.
* @param x the x position.
*/ */
public void setStartX(int x) { public void setStartX(int x) {
startX = x; startX = x;
@ -214,6 +218,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Returns the width of the fields generated by this factory. * Returns the width of the fields generated by this factory.
* @return the width.
*/ */
public int getWidth() { public int getWidth() {
return width; return width;
@ -221,6 +226,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Sets the width of the fields generated by this factory. * Sets the width of the fields generated by this factory.
* @param w the width.
*/ */
public void setWidth(int w) { public void setWidth(int w) {
width = w; width = w;
@ -228,6 +234,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Returns the FieldModel that this factory belongs to. * Returns the FieldModel that this factory belongs to.
* @return the model.
*/ */
public FieldFormatModel getFieldModel() { public FieldFormatModel getFieldModel() {
return model; return model;
@ -235,6 +242,7 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Returns true if this FieldFactory is currently enabled to generate Fields. * Returns true if this FieldFactory is currently enabled to generate Fields.
* @return true if enabled.
*/ */
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
@ -261,12 +269,14 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Return a FieldLocation that corresponds to the given index, fieldNum, and ProgramLocation * 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 * IF and ONLY IF the given programLocation is the type generated by this class's
* {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each FieldFactory * {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each
* should generate and process a unique ProgramLocation class. * FieldFactory should generate and process a unique ProgramLocation class.
*
* @param bf the ListingField at the current cursor. * @param bf the ListingField at the current cursor.
* @param index the line index (corresponds to an address) * @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 fieldNum the index of field within the layout to try and get a FieldLocation.
* @param loc the ProgramLocation to be converted into 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, public abstract FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
ProgramLocation loc); ProgramLocation loc);
@ -276,6 +286,7 @@ public abstract class FieldFactory implements ExtensionPoint {
* @param row the row within this field * @param row the row within this field
* @param col the col on the given row within this field. * @param col the col on the given row within this field.
* @param bf the ListingField containing the cursor. * @param bf the ListingField containing the cursor.
* @return the location.
*/ */
public abstract ProgramLocation getProgramLocation(int row, int col, ListingField bf); 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. * Returns a description of the fields generated by this factory.
* @return the text.
*/ */
public String getFieldText() { public String getFieldText() {
return name; return name;
@ -320,14 +332,12 @@ public abstract class FieldFactory implements ExtensionPoint {
/** /**
* Returns the font metrics used by this field factory * Returns the font metrics used by this field factory
* @return the metrics.
*/ */
public FontMetrics getMetrics() { public FontMetrics getMetrics() {
return getMetrics(style); return getMetrics(style);
} }
/**
* @return Returns the metrics.
*/
protected FontMetrics getMetrics(int fontStyle) { protected FontMetrics getMetrics(int fontStyle) {
if (fontStyle == -1) { if (fontStyle == -1) {
return defaultMetrics; return defaultMetrics;
@ -335,8 +345,7 @@ public abstract class FieldFactory implements ExtensionPoint {
return fontMetrics[fontStyle]; return fontMetrics[fontStyle];
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation") // we know
// we know
private void setMetrics(Font newFont) { private void setMetrics(Font newFont) {
defaultMetrics = Toolkit.getDefaultToolkit().getFontMetrics(newFont); defaultMetrics = Toolkit.getDefaultToolkit().getFontMetrics(newFont);
for (int i = 0; i < fontMetrics.length; i++) { 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 = private final static String SPACE_AFTER_SEPARATOR_OPTION =
GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Add Space After Separator"; GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Add Space After Separator";
public static enum UNDERLINE_CHOICE { public enum UNDERLINE_CHOICE {
Hidden, All, None Hidden, All, None
} }
@ -183,12 +183,6 @@ abstract class OperandFieldHelper extends FieldFactory {
return null; 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) { ListingField getField(Object obj, ProxyObj<?> proxy, int varWidth) {
if (!enabled) { if (!enabled) {
return null; return null;
@ -222,10 +216,10 @@ abstract class OperandFieldHelper extends FieldFactory {
ListingTextField btf = (ListingTextField) lf; ListingTextField btf = (ListingTextField) lf;
FieldElement fieldElement = btf.getFieldElement(row, col); FieldElement fieldElement = btf.getFieldElement(row, col);
if (!(fieldElement instanceof OperandFieldElement)) { if (!(fieldElement instanceof OperandFieldElement)) {
return null; return null;
} }
OperandFieldElement element = (OperandFieldElement) fieldElement; OperandFieldElement element = (OperandFieldElement) fieldElement;
int opIndex = element.getOperandIndex(); int opIndex = element.getOperandIndex();
int subOpIndex = element.getOperandSubIndex(); int subOpIndex = element.getOperandSubIndex();
@ -276,11 +270,10 @@ abstract class OperandFieldHelper extends FieldFactory {
else if (obj instanceof Data) { else if (obj instanceof Data) {
Data data = (Data) obj; Data data = (Data) obj;
Address refAddr = null; Address refAddr = null;
Reference primaryReference = Program program = data.getProgram();
data.getProgram() ReferenceManager referenceManager = program.getReferenceManager();
.getReferenceManager() Address minAddress = data.getMinAddress();
.getPrimaryReferenceFrom( Reference primaryReference = referenceManager.getPrimaryReferenceFrom(minAddress, 0);
data.getMinAddress(), 0);
Object value = data.getValue(); Object value = data.getValue();
if (primaryReference != null) { if (primaryReference != null) {
refAddr = primaryReference.getToAddress(); refAddr = primaryReference.getToAddress();
@ -291,20 +284,18 @@ abstract class OperandFieldHelper extends FieldFactory {
} }
} }
Program program = data.getProgram();
if (value instanceof Scalar) { if (value instanceof Scalar) {
Scalar scalar = (Scalar) value; Scalar scalar = (Scalar) value;
Equate equate = program.getEquateTable() EquateTable equateTable = program.getEquateTable();
.getEquate(data.getMinAddress(), opIndex, Equate equate = equateTable.getEquate(minAddress, opIndex, scalar.getValue());
scalar.getValue());
if (equate != null) { if (equate != null) {
return new EquateOperandFieldLocation(program, data.getMinAddress(), refAddr, return new EquateOperandFieldLocation(program, minAddress, refAddr,
equate.getDisplayName(), equate, opIndex, subOpIndex, equate.getDisplayName(), equate, opIndex, subOpIndex,
translatedLocation.col()); translatedLocation.col());
} }
} }
return new OperandFieldLocation(program, data.getMinAddress(), data.getComponentPath(), return new OperandFieldLocation(program, minAddress, data.getComponentPath(), refAddr,
refAddr, codeUnitFormat.getDataValueRepresentationString(data), 0, col); codeUnitFormat.getDataValueRepresentationString(data), 0, col);
} }
return null; return null;
} }
@ -477,9 +468,9 @@ abstract class OperandFieldHelper extends FieldFactory {
private int addElements(Instruction inst, List<OperandFieldElement> elements, List<?> objList, private int addElements(Instruction inst, List<OperandFieldElement> elements, List<?> objList,
int opIndex, int subOpIndex, boolean underline, int characterOffset) { int opIndex, int subOpIndex, boolean underline, int characterOffset) {
for (int i = 0; i < objList.size(); i++) { for (Object element : objList) {
characterOffset = addElement(inst, elements, objList.get(i), underline, opIndex, characterOffset = addElement(inst, elements, element, underline, opIndex, subOpIndex,
subOpIndex, characterOffset); characterOffset);
} }
return characterOffset; return characterOffset;
} }

View file

@ -30,7 +30,7 @@ import ghidra.util.classfinder.ClassSearcher;
/** /**
* Generates data value Fields for data subcomponents. * 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. * This field is not meant to be loaded by the {@link ClassSearcher}, hence the X in the name.
*/ */
public class SubDataFieldFactory extends OperandFieldFactory { public class SubDataFieldFactory extends OperandFieldFactory {
@ -43,16 +43,10 @@ public class SubDataFieldFactory extends OperandFieldFactory {
* @param path the component path for the data * @param path the component path for the data
*/ */
public SubDataFieldFactory(String name, int[] path) { public SubDataFieldFactory(String name, int[] path) {
super();
this.componentPath = path; this.componentPath = path;
this.name = name; 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, private SubDataFieldFactory(String name, int[] componentPath, FieldFormatModel model,
HighlightProvider hlProvider, ToolOptions displayOptions, ToolOptions fieldOptions) { HighlightProvider hlProvider, ToolOptions displayOptions, ToolOptions fieldOptions) {
super(model, hlProvider, displayOptions, fieldOptions); super(model, hlProvider, displayOptions, fieldOptions);
@ -60,12 +54,6 @@ public class SubDataFieldFactory extends OperandFieldFactory {
this.componentPath = componentPath; 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 @Override
public ListingField getField(ProxyObj<?> proxy, int varWidth) { public ListingField getField(ProxyObj<?> proxy, int varWidth) {
Object obj = proxy.getObject(); Object obj = proxy.getObject();
@ -138,8 +126,8 @@ public class SubDataFieldFactory extends OperandFieldFactory {
@Override @Override
public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider, public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider,
ToolOptions displayOptions, ToolOptions fieldOptions) { ToolOptions options, ToolOptions fieldOptions) {
return new SubDataFieldFactory(name, componentPath, formatModel, provider, displayOptions, return new SubDataFieldFactory(name, componentPath, formatModel, provider, options,
fieldOptions); fieldOptions);
} }

View file

@ -48,8 +48,7 @@ import ghidra.util.exception.AssertException;
import ghidra.util.exception.RollbackException; import ghidra.util.exception.RollbackException;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import utility.application.ApplicationLayout; import utility.application.ApplicationLayout;
import utility.function.ExceptionalCallback; import utility.function.*;
import utility.function.ExceptionalFunction;
public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDockingTest { public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDockingTest {
@ -68,8 +67,6 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
private static Language Z80_LANGUAGE; private static Language Z80_LANGUAGE;
public AbstractGhidraHeadlessIntegrationTest() { public AbstractGhidraHeadlessIntegrationTest() {
super();
// Ensure that all error messages do NOT use a gui popup, and instead are routed to the // Ensure that all error messages do NOT use a gui popup, and instead are routed to the
// console. // console.
setErrorGUIEnabled(false); setErrorGUIEnabled(false);
@ -167,7 +164,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
/** /**
* Run a command against the specified program within a transaction. * Run a command against the specified program within a transaction.
* The transaction will be committed unless the command throws a RollbackException. * The transaction will be committed unless the command throws a RollbackException.
* *
* @param program the program * @param program the program
* @param cmd the command to apply * @param cmd the command to apply
* @return result of command applyTo method * @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 * Provides a convenient method for modifying the current program, handling the transaction
* logic. * logic.
* *
* @param p the program * @param p the program
* @param c the code to execute * @param c the code to execute
* @see #modifyProgram(Program, ExceptionalCallback) * @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 * Provides a convenient method for modifying the current program, handling the transaction
* logic. This method is calls {@link #tx(Program, ExceptionalCallback)}, but helps with * logic. This method is calls {@link #tx(Program, ExceptionalCallback)}, but helps with
* semantics. * semantics.
* *
* @param p the program * @param p the program
* @param c the code to execute * @param c the code to execute
* @see #modifyProgram(Program, ExceptionalFunction) * @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 * Provides a convenient method for modifying the current program, handling the transaction
* logic and returning a new item as a result * logic and returning a new item as a result
* *
* @param program the program * @param program the program
* @param f the function for modifying the program and creating the desired result * @param f the function for modifying the program and creating the desired result
* @return the 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 * Returns the global symbol with the given name if and only if it is the only
* global symbol with that name. * global symbol with that name.
* *
* @param program the program to search. * @param program the program to search.
* @param name the name of the global symbol to find. * @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. * @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 * 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. * symbol in that namespace with that name.
* *
* @param program the program to search. * @param program the program to search.
* @param name the name of the symbol to find. * @param name the name of the symbol to find.
* @param namespace the parent namespace; may be null * @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, * A convenience method that allows you to open the given program in a default tool,
* navigating to the given address. * navigating to the given address.
* *
* <P>Note: this is a blocking operation. Your test will not proceed while this method is * <P>Note: this is a blocking operation. Your test will not proceed while this method is
* sleeping. * sleeping.
* *
* <P><B>Do not leave this call in your test when committing changes.</B> * <P><B>Do not leave this call in your test when committing changes.</B>
* @param p the program * @param p the program
* @param address the address * @param address the address
* *
* @throws Exception if there is an issue create a {@link TestEnv} * @throws Exception if there is an issue create a {@link TestEnv}
*/ */
public void debugProgramInTool(Program p, String address) throws Exception { 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. * Waits for a launched script to complete by using the given listener.
* *
* @param listener the listener used to track script progress * @param listener the listener used to track script progress
* @param timeoutMS the max time to wait; failing if exceeded * @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. * Replaces the given implementations of the provided service class with the given class.
* *
* @param tool the tool whose services to update (optional) * @param tool the tool whose services to update (optional)
* @param service the service to override * @param service the service to override
* @param replacement the new version of the service * @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.program.util.FieldNameFieldLocation;
import ghidra.test.AbstractProgramBasedTest; import ghidra.test.AbstractProgramBasedTest;
import ghidra.test.ClassicSampleX86ProgramBuilder; import ghidra.test.ClassicSampleX86ProgramBuilder;
import ghidra.util.Msg;
import ghidra.util.datastruct.Accumulator; import ghidra.util.datastruct.Accumulator;
import ghidra.util.datastruct.ListAccumulator; import ghidra.util.datastruct.ListAccumulator;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -51,7 +52,7 @@ import ghidra.util.table.GhidraTable;
import ghidra.util.task.TaskMonitor; 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}. * {@link LocationDescriptor}.
*/ */
public abstract class AbstractLocationReferencesTest extends AbstractProgramBasedTest { public abstract class AbstractLocationReferencesTest extends AbstractProgramBasedTest {
@ -98,7 +99,7 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
// //
// Arrays/Structures // Arrays/Structures
// //
DataType type = new IntegerDataType(); DataType type = new IntegerDataType();
DataType pointer = new PointerDataType(type); DataType pointer = new PointerDataType(type);
ArrayDataType array = new ArrayDataType(pointer, 4, pointer.getLength()); 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) { private void doGoToDataNameFieldAt(Address a, int[] path) {
openData(a); openData(a);
// note: the path here is // note: the path here is
FieldNameFieldLocation location = new FieldNameFieldLocation(program, a, path, "name", 0); FieldNameFieldLocation location = new FieldNameFieldLocation(program, a, path, "name", 0);
ProgramLocationPluginEvent event = ProgramLocationPluginEvent event =
new ProgramLocationPluginEvent("Test", location, program); new ProgramLocationPluginEvent("Test", location, program);
@ -318,7 +319,10 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
protected void assertResultCount(int expected) { protected void assertResultCount(int expected) {
List<Address> referenceAddresses = getResultAddresses(); 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) { protected void assertResultCount(String msg, int expected) {

View file

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

View file

@ -31,8 +31,7 @@ import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.decompile.actions.FindReferencesToSymbolAction; import ghidra.app.plugin.core.decompile.actions.FindReferencesToSymbolAction;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
import ghidra.app.services.DataTypeReference; import ghidra.app.services.*;
import ghidra.app.services.DataTypeReferenceFinder;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
@ -172,6 +171,19 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
compositeFieldReferencesCallCount.incrementAndGet(); compositeFieldReferencesCallCount.incrementAndGet();
} }
@Mock
public void findReferences(Program p, FieldMatcher fieldMatcher,
Consumer<DataTypeReference> callback, TaskMonitor monitor) {
if (fieldMatcher.isIgnored()) {
// an empty field matcher signals a data type search
dataTypeReferencesCallCount.incrementAndGet();
}
else {
compositeFieldReferencesCallCount.incrementAndGet();
}
}
public int getFindDataTypeReferencesCallCount() { public int getFindDataTypeReferencesCallCount() {
return dataTypeReferencesCallCount.get(); return dataTypeReferencesCallCount.get();
} }

View file

@ -31,9 +31,9 @@ public class DecompilerFindReferencesToActionTest
public void testActionEnablement() throws Exception { public void testActionEnablement() throws Exception {
/* /*
Decomp of 'init_string': Decomp of 'init_string':
1| 1|
2| void init_string(mystring *ptr) 2| void init_string(mystring *ptr)
3| 3|
@ -42,17 +42,17 @@ public class DecompilerFindReferencesToActionTest
6| return; 6| return;
7| } 7| }
8| 8|
Note: there are two places in this function we can search for data type references: Note: there are two places in this function we can search for data type references:
1) the parameter (line 2, cols 17-25, 26-30) 1) the parameter (line 2, cols 17-25, 26-30)
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field) 2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
*/ */
decompile(INIT_STRING_ADDR); decompile(INIT_STRING_ADDR);
// //
// Action should not enabled unless on the data type // Action should not enabled unless on the data type
// //
// Empty line // Empty line
int line = 1; int line = 1;
int charPosition = 0; int charPosition = 0;
@ -99,9 +99,9 @@ public class DecompilerFindReferencesToActionTest
@Test @Test
public void testFindDataTypeReferences_ToEntireDataType_FromParameter() throws Exception { public void testFindDataTypeReferences_ToEntireDataType_FromParameter() throws Exception {
/* /*
Decomp of 'init_string': Decomp of 'init_string':
1| 1|
2| void init_string(mystring *ptr) 2| void init_string(mystring *ptr)
3| 3|
@ -110,11 +110,11 @@ public class DecompilerFindReferencesToActionTest
6| return; 6| return;
7| } 7| }
8| 8|
Note: there are two places in this function we can search for data type references: Note: there are two places in this function we can search for data type references:
1) the parameter (line 2, cols 17-25, 26-30) 1) the parameter (line 2, cols 17-25, 26-30)
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field) 2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
*/ */
decompile(INIT_STRING_ADDR); decompile(INIT_STRING_ADDR);
@ -130,9 +130,9 @@ public class DecompilerFindReferencesToActionTest
@Test @Test
public void testFindDataTypeReferences_ToEntireDataType_FromVariable() throws Exception { public void testFindDataTypeReferences_ToEntireDataType_FromVariable() throws Exception {
/* /*
Decomp of 'init_string': Decomp of 'init_string':
1| 1|
2| void init_string(mystring *ptr) 2| void init_string(mystring *ptr)
3| 3|
@ -141,11 +141,11 @@ public class DecompilerFindReferencesToActionTest
6| return; 6| return;
7| } 7| }
8| 8|
Note: there are two places in this function we can search for data type references: Note: there are two places in this function we can search for data type references:
1) the parameter (line 2, cols 17-25, 26-30) 1) the parameter (line 2, cols 17-25, 26-30)
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field) 2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
*/ */
decompile(INIT_STRING_ADDR); decompile(INIT_STRING_ADDR);
@ -161,9 +161,9 @@ public class DecompilerFindReferencesToActionTest
@Test @Test
public void testFindDataTypeReferences_ToFieldOfDataType() throws Exception { public void testFindDataTypeReferences_ToFieldOfDataType() throws Exception {
/* /*
Decomp of 'init_string': Decomp of 'init_string':
1| 1|
2| void init_string(mystring *ptr) 2| void init_string(mystring *ptr)
3| 3|
@ -172,11 +172,11 @@ public class DecompilerFindReferencesToActionTest
6| return; 6| return;
7| } 7| }
8| 8|
Note: there are two places in this function we can search for data type references: Note: there are two places in this function we can search for data type references:
1) the parameter (line 2, cols 17-25, 26-30) 1) the parameter (line 2, cols 17-25, 26-30)
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field) 2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
*/ */
decompile(INIT_STRING_ADDR); decompile(INIT_STRING_ADDR);
@ -209,9 +209,9 @@ public class DecompilerFindReferencesToActionTest
public void testFindDataTypeReferences_ToCurrentAddress() throws Exception { public void testFindDataTypeReferences_ToCurrentAddress() throws Exception {
/* /*
Decomp of 'init_string': Decomp of 'init_string':
1| 1|
2| void init_string(mystring *ptr) 2| void init_string(mystring *ptr)
3| 3|
@ -247,6 +247,6 @@ public class DecompilerFindReferencesToActionTest
//================================================================================================== //==================================================================================================
// Private Methods // Private Methods
//================================================================================================== //==================================================================================================
} }

View file

@ -17,8 +17,7 @@ package ghidra.app.plugin.core.decompile;
import java.util.function.Consumer; import java.util.function.Consumer;
import ghidra.app.services.DataTypeReference; import ghidra.app.services.*;
import ghidra.app.services.DataTypeReferenceFinder;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -40,4 +39,10 @@ public class StubDataTypeReferenceFinder implements DataTypeReferenceFinder {
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException { Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
// stub // stub
} }
@Override
public void findReferences(Program program, FieldMatcher fieldMatcher,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
// stub
}
} }

View file

@ -20,30 +20,30 @@ import java.util.List;
import ghidra.app.decompiler.ClangFieldToken; import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangLine; import ghidra.app.decompiler.ClangLine;
import ghidra.app.services.DataTypeReference; import ghidra.app.services.DataTypeReference;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.data.Composite; import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
/** /**
* This class represents the use of a field of a {@link Composite} data type <b>where there is * This class represents the use of a field of a {@link Composite} data type <b>where there is no
* no variable in the Decompiler</b> for that data type. A normal variable access in the * variable in the Decompiler</b> for that data type. A normal variable access in the Decompiler
* Decompiler may look like so: * may look like so:
* <pre> * <pre>
* Foo f; * Foo f;
* ... * ...
* return f.some_field; * return f.some_field;
* </pre> * </pre>
* *
* Alternatively, an anonymous variable access would look like this: * Alternatively, an anonymous variable access would look like this:
* <pre> * <pre>
* Bar b; * Bar b;
* ... * ...
* return b-><b>foo_array[1].some_field</b>; * return b-><b>foo_array[1].some_field</b>;
* </pre> * </pre>
* *
* In this case, <code><b>foo_array[1]</b></code> is a <code>Foo</code>, whose * In this case, <code><b>foo_array[1]</b></code> is a <code>Foo</code>, whose
* <code><b>some_field</b></code> is * <code><b>some_field</b></code> is being accessed anonymously, since there is no variable of
* being accessed anonymously, since there is no variable of <code>Foo</code> declared * <code>Foo</code> declared in the current function.
* in the current function.
*/ */
public class AnonymousVariableAccessDR extends VariableAccessDR { public class AnonymousVariableAccessDR extends VariableAccessDR {
@ -52,42 +52,57 @@ public class AnonymousVariableAccessDR extends VariableAccessDR {
} }
@Override @Override
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) { public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
List<DataTypeReference> results) {
// //
// This class is backed by a ClangFieldToken. That class's data type is the composite // This class is backed by a ClangFieldToken. That class's data type is the composite that
// that contains the field being accessed. A variable being accessed has 2 types being // contains the field being accessed. A variable being accessed has 2 types being
// touched: the aforementioned composite and the type of the field itself. // touched: the aforementioned composite and the type of the field itself.
// //
// This can match in one of two cases: // This can match in one of two cases:
// 1) the client seeks to match a given field inside of the containing composite, or // 1) the passed in type must match the field type and not the parent type, or
// 2) the client seeks to match only the type, which means that the field type itself must match // 2) the passed in type must match the parent type, along with supplied field name/offset.
// //
ClangFieldToken field = (ClangFieldToken) sourceToken; ClangFieldToken field = (ClangFieldToken) sourceToken;
DataType compositeType = field.getDataType(); DataType compositeType = field.getDataType();
DataType fieldDt = DecompilerReference.getFieldDataType(field); DataType fieldDt = DecompilerReference.getFieldDataType(field);
boolean matchesComposite = isEqual(dt, compositeType); boolean matchesCompositeType = isEqual(dt, compositeType);
boolean matchesField = isEqual(dt, fieldDt); boolean matchesFieldType = isEqual(dt, fieldDt);
boolean noMatch = !(matchesComposite || matchesField); boolean noMatch = !(matchesCompositeType || matchesFieldType);
if (noMatch) { if (noMatch) {
return; return;
} }
if (fieldName == null) { //
// case 2; no field name to check // Case 1
if (matchesField) { //
// If the client did not specify a field to match, then we only want to match on the type
// of this reference's field type and NOT the composite type, since this reference is
// referring to the field and not the composite.
//
if (fieldMatcher.isIgnored()) {
if (matchesFieldType) {
// no field name and the search type matches this reference's field type
results.add(createReference(variable)); results.add(createReference(variable));
} }
// else there is no field and the search type does not match the reference's type
return; return;
} }
// case 1; check the field name and the composite type //
if (matchesComposite && field.getText().equals(fieldName)) { // Case 2
results.add( //
new DataTypeReference(compositeType, fieldName, getFunction(), getAddress(), // The client has requested a particular field of the parent composite. We only have a
getContext())); // match if the parent type matches and the field name/offset matches.
//
String text = field.getText();
int offset = field.getOffset();
if (matchesCompositeType && fieldMatcher.matches(text, offset)) {
results.add(new DataTypeReference(compositeType, fieldMatcher.getFieldName(),
getFunction(), getAddress(), getContext()));
} }
} }
} }

View file

@ -28,8 +28,7 @@ import ghidra.app.decompiler.parallel.*;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference; import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.services.DataTypeReference; import ghidra.app.services.*;
import ghidra.app.services.DataTypeReferenceFinder;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.BuiltInDataType; import ghidra.program.model.data.BuiltInDataType;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
@ -58,10 +57,28 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
@Override @Override
public void findReferences(Program program, DataType dataType, String fieldName, public void findReferences(Program program, DataType dataType, String fieldName,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException { Consumer<DataTypeReference> consumer, TaskMonitor monitor) throws CancelledException {
FieldMatcher fieldMatcher = new FieldMatcher(dataType, fieldName);
DecompilerDataTypeFinderQCallback qCallback = DecompilerDataTypeFinderQCallback qCallback =
new DecompilerDataTypeFinderQCallback(program, dataType, fieldName, callback); new DecompilerDataTypeFinderQCallback(program, dataType, fieldMatcher, consumer);
doFindReferences(program, dataType, qCallback, consumer, monitor);
}
@Override
public void findReferences(Program program, FieldMatcher fieldMatcher,
Consumer<DataTypeReference> consumer, TaskMonitor monitor) throws CancelledException {
DataType dataType = fieldMatcher.getDataType();
DecompilerDataTypeFinderQCallback qCallback =
new DecompilerDataTypeFinderQCallback(program, dataType, fieldMatcher, consumer);
doFindReferences(program, dataType, qCallback, consumer, monitor);
}
private void doFindReferences(Program program, DataType dataType,
DecompilerDataTypeFinderQCallback qCallback, Consumer<DataTypeReference> consumer,
TaskMonitor monitor) throws CancelledException {
Set<Function> functions = filterFunctions(program, dataType, monitor); Set<Function> functions = filterFunctions(program, dataType, monitor);
@ -87,7 +104,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
buildTypeLineage(dt, types); buildTypeLineage(dt, types);
Set<Function> results = new HashSet<>(); Set<Function> results = new HashSet<>();
accumulateFunctionCallsToDefinedData(program, types, results, monitor); accumulateFunctionCallsToDefinedData(program, dt, types, results, monitor);
Listing listing = program.getListing(); Listing listing = program.getListing();
FunctionIterator it = listing.getFunctions(true); FunctionIterator it = listing.getFunctions(true);
@ -115,21 +132,23 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
return results; return results;
} }
private void accumulateFunctionCallsToDefinedData(Program program, Set<DataType> potentialTypes, private void accumulateFunctionCallsToDefinedData(Program program, DataType dataType,
Set<Function> results, TaskMonitor monitor) throws CancelledException { Set<DataType> potentialTypes, Set<Function> results, TaskMonitor monitor)
throws CancelledException {
Listing listing = program.getListing(); Listing listing = program.getListing();
AtomicInteger counter = new AtomicInteger(); AtomicInteger counter = new AtomicInteger();
SetAccumulator<LocationReference> accumulator = new SetAccumulator<>(); SetAccumulator<LocationReference> accumulator = new SetAccumulator<>();
Predicate<Data> dataMatcher = data -> { Predicate<Data> dataMatcher = data -> {
counter.incrementAndGet(); counter.incrementAndGet();
DataType dataType = data.getDataType(); DataType dt = data.getDataType();
boolean matches = potentialTypes.contains(dataType); boolean matches = potentialTypes.contains(dt);
return matches; return matches;
}; };
ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher, null, FieldMatcher emptyMatcher = new FieldMatcher(dataType);
monitor); ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher,
emptyMatcher, monitor);
for (LocationReference ref : accumulator) { for (LocationReference ref : accumulator) {
Address address = ref.getLocationOfUse(); Address address = ref.getLocationOfUse();
@ -158,12 +177,10 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
// We have a different type, should we search for it? // We have a different type, should we search for it?
if (baseType instanceof BuiltInDataType) { if (baseType instanceof BuiltInDataType) {
// When given a wrapper type (e.g., typedef) , ignore // When given a wrapper type (e.g., typedef) , ignore built-ins (e.g., int, byte, etc),
// built-ins (e.g., int, byte, etc), as // as they will be of little value due to their volume in the program and the user
// they will be of little value due to their volume in the program and the // *probably* did not intend to search for them. (Below we do not do this check, which
// user *probably* did not intend to search for them. (Below we do not do // allows the user to search directly for a built-in type, if they wish.)
// this check, which allows the user to search directly for a
// built-in type, if they wish.)
return; return;
} }
@ -207,16 +224,16 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
private Consumer<DataTypeReference> callback; private Consumer<DataTypeReference> callback;
private DataType dataType; private DataType dataType;
private String fieldName; private FieldMatcher fieldMatcher;
/* Search for composite field access */ /* Search for composite field access */
DecompilerDataTypeFinderQCallback(Program program, DataType dataType, String fieldName, DecompilerDataTypeFinderQCallback(Program program, DataType dataType,
Consumer<DataTypeReference> callback) { FieldMatcher fieldMatcher, Consumer<DataTypeReference> callback) {
super(program, new DecompilerConfigurer()); super(program, new DecompilerConfigurer());
this.dataType = dataType; this.dataType = dataType;
this.fieldName = fieldName; this.fieldMatcher = fieldMatcher;
this.callback = callback; this.callback = callback;
} }
@ -230,7 +247,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
} }
DecompilerDataTypeFinder finder = DecompilerDataTypeFinder finder =
new DecompilerDataTypeFinder(results, function, dataType, fieldName); new DecompilerDataTypeFinder(results, function, dataType, fieldMatcher);
List<DataTypeReference> refs = finder.findUsage(); List<DataTypeReference> refs = finder.findUsage();
refs.forEach(r -> callback.accept(r)); refs.forEach(r -> callback.accept(r));
@ -263,14 +280,14 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
private DecompileResults decompilation; private DecompileResults decompilation;
private Function function; private Function function;
private DataType dataType; private DataType dataType;
private String fieldName; private FieldMatcher fieldMatcher;
DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType, DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType,
String fieldName) { FieldMatcher fieldMatcher) {
this.decompilation = results; this.decompilation = results;
this.function = function; this.function = function;
this.dataType = dataType; this.dataType = dataType;
this.fieldName = fieldName; this.fieldMatcher = fieldMatcher;
} }
List<DataTypeReference> findUsage() { List<DataTypeReference> findUsage() {
@ -300,7 +317,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
/** Finds any search input match in the given reference */ /** Finds any search input match in the given reference */
private void matchUsage(DecompilerReference reference, List<DataTypeReference> results) { private void matchUsage(DecompilerReference reference, List<DataTypeReference> results) {
reference.accumulateMatches(dataType, fieldName, results); reference.accumulateMatches(dataType, fieldMatcher, results);
} }
private List<DecompilerReference> findVariableReferences(ClangTokenGroup tokens) { private List<DecompilerReference> findVariableReferences(ClangTokenGroup tokens) {
@ -320,20 +337,20 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
* the case with Composite types, may have one of its fields accessed. Each result * the case with Composite types, may have one of its fields accessed. Each result
* found by this method will be at least a variable access and may also itself have * found by this method will be at least a variable access and may also itself have
* field accesses. * field accesses.
* *
* <p>Sometimes a line is structured such that there are anonymous variable accesses. This * <p>Sometimes a line is structured such that there are anonymous variable accesses. This
* is the case where a Composite is being accessed, but the Composite itself is * is the case where a Composite is being accessed, but the Composite itself is
* not a variable in the current function. See {@link AnonymousVariableAccessDR} for * not a variable in the current function. See {@link AnonymousVariableAccessDR} for
* more details. * more details.
* *
* @param line the current line being processed from the Decompiler * @param line the current line being processed from the Decompiler
* @param results the accumulator into which matches will be placed * @param results the accumulator into which matches will be placed
*/ */
private void findVariablesInLine(ClangLine line, List<DecompilerReference> results) { private void findVariablesInLine(ClangLine line, List<DecompilerReference> results) {
List<ClangToken> allTokens = line.getAllTokens(); List<ClangToken> allTokens = line.getAllTokens();
Iterable<ClangToken> filteredTokens = IterableUtils.filteredIterable(allTokens, Iterable<ClangToken> filteredTokens =
token -> { IterableUtils.filteredIterable(allTokens, token -> {
// Only include desirable tokens (this is really just for easier debugging). // Only include desirable tokens (this is really just for easier debugging).
// Update this filter if the loop below ever needs other types of tokens. // Update this filter if the loop below ever needs other types of tokens.
return (token instanceof ClangTypeToken) || return (token instanceof ClangTypeToken) ||

View file

@ -23,7 +23,7 @@ import ghidra.program.model.data.*;
import ghidra.util.Msg; import ghidra.util.Msg;
/** /**
* A class that represents access to a Decompiler {@link ClangFieldToken} object. This is the field * A class that represents access to a Decompiler {@link ClangFieldToken} object. This is the field
* of a variable, denoted by {@link ClangVariableToken}. * of a variable, denoted by {@link ClangVariableToken}.
*/ */
public class DecompilerFieldAccess extends DecompilerVariable { public class DecompilerFieldAccess extends DecompilerVariable {
@ -73,6 +73,12 @@ public class DecompilerFieldAccess extends DecompilerVariable {
return dt; return dt;
} }
@Override
public int getOffset() {
ClangFieldToken field = (ClangFieldToken) variable;
return field.getOffset();
}
protected DataType getBaseType(DataType dt) { protected DataType getBaseType(DataType dt) {
if (dt instanceof Array) { if (dt instanceof Array) {
return getBaseType(((Array) dt).getDataType()); return getBaseType(((Array) dt).getDataType());

View file

@ -21,6 +21,7 @@ import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContextBuilder; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContextBuilder;
import ghidra.app.services.DataTypeReference; import ghidra.app.services.DataTypeReference;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
@ -51,12 +52,12 @@ public abstract class DecompilerReference {
* The <tt>fieldName</tt> is optional. If not included, then only data type matches will * The <tt>fieldName</tt> is optional. If not included, then only data type matches will
* be sought. If it is included, then a match is only included when it is a reference * be sought. If it is included, then a match is only included when it is a reference
* to the given data type where that type is being accessed by the given field name. * to the given data type where that type is being accessed by the given field name.
* *
* @param dt the data type to find * @param dt the data type to find
* @param fieldName the optional field name used to restrict matches. * @param fieldMatcher the optional field matcher used to restrict matches.
* @param results the accumulator object into which will be placed any matches * @param results the accumulator object into which will be placed any matches
*/ */
public abstract void accumulateMatches(DataType dt, String fieldName, public abstract void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
List<DataTypeReference> results); List<DataTypeReference> results);
public DecompilerVariable getVariable() { public DecompilerVariable getVariable() {
@ -160,7 +161,7 @@ public abstract class DecompilerReference {
int offset = field.getOffset(); int offset = field.getOffset();
int n = parent.getLength(); int n = parent.getLength();
if (offset >= 0 && offset < n) { if (offset >= 0 && offset < n) {
DataTypeComponent dtc = parent.getComponentAt(field.getOffset()); DataTypeComponent dtc = parent.getComponentContaining(field.getOffset());
fieldDt = dtc.getDataType(); fieldDt = dtc.getDataType();
} }
} }

View file

@ -188,6 +188,10 @@ public abstract class DecompilerVariable {
return text; return text;
} }
public int getOffset() {
return Integer.MIN_VALUE; // subclasses can override
}
@Override @Override
public String toString() { public String toString() {
String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n"; String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n";

View file

@ -20,6 +20,7 @@ import java.util.List;
import ghidra.app.decompiler.ClangLine; import ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.ClangTypeToken; import ghidra.app.decompiler.ClangTypeToken;
import ghidra.app.services.DataTypeReference; import ghidra.app.services.DataTypeReference;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
public class ReturnTypeDR extends DecompilerReference { public class ReturnTypeDR extends DecompilerReference {
@ -29,11 +30,11 @@ public class ReturnTypeDR extends DecompilerReference {
} }
@Override @Override
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) { public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
List<DataTypeReference> results) {
if (fieldName != null) { if (!fieldMatcher.isIgnored()) {
// Return Types do not have any field usage return; // Return Types do not have any field usage
return;
} }
DataType myDt = getDataType(); DataType myDt = getDataType();

View file

@ -21,6 +21,7 @@ import java.util.List;
import ghidra.app.decompiler.*; import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
import ghidra.app.services.DataTypeReference; import ghidra.app.services.DataTypeReference;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
@ -58,10 +59,11 @@ public class VariableAccessDR extends DecompilerReference {
} }
@Override @Override
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) { public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
List<DataTypeReference> results) {
if (fields.isEmpty()) { if (fields.isEmpty()) {
DecompilerVariable var = getMatch(dt, fieldName, variable, null); DecompilerVariable var = getMatch(dt, fieldMatcher, variable, null);
if (var != null) { if (var != null) {
DataTypeReference ref = createReference(var); DataTypeReference ref = createReference(var);
results.add(ref); results.add(ref);
@ -77,7 +79,7 @@ public class VariableAccessDR extends DecompilerReference {
for (DecompilerVariable field : fields) { for (DecompilerVariable field : fields) {
DecompilerVariable next = field; DecompilerVariable next = field;
DecompilerVariable var = getMatch(dt, fieldName, start, next); DecompilerVariable var = getMatch(dt, fieldMatcher, start, next);
if (var != null) { if (var != null) {
DataTypeReference ref = createReference(var, next); DataTypeReference ref = createReference(var, next);
results.add(ref); results.add(ref);
@ -87,46 +89,46 @@ public class VariableAccessDR extends DecompilerReference {
} }
// //
// Handle the last variable by itself (for the case where we are matching just on the // Handle the last variable by itself (for the case where we are matching just on the type,
// type, with no field name) // with no field name)
// //
if (fieldName != null) { if (fieldMatcher.isIgnored()) {
return; return;
} }
DecompilerVariable var = getMatch(dt, null, start, null); DecompilerVariable var = getMatch(dt, fieldMatcher, start, null);
if (var != null) { if (var != null) {
DataTypeReference ref = createReference(var); DataTypeReference ref = createReference(var);
results.add(ref); results.add(ref);
} }
} }
private DecompilerVariable getMatch(DataType dt, String fieldName, DecompilerVariable var, private DecompilerVariable getMatch(DataType dt, FieldMatcher fieldMatcher,
DecompilerVariable potentialField) { DecompilerVariable var, DecompilerVariable potentialField) {
// Note: for now, I ignore the precedence of casting; if any cast type is a match, then // Note: for now, I ignore the precedence of casting; if any cast type is a match, then
// signal hooray // signal hooray
boolean searchForField = fieldName != null; boolean searchForField = !fieldMatcher.isIgnored();
DecompilerVariable fieldVar = searchForField ? potentialField : null; DecompilerVariable fieldVar = searchForField ? potentialField : null;
DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar); DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar);
if (match == null) { if (match == null) {
// wrong type, nothing to do return null; // wrong type, nothing to do
return null;
} }
// Matches on the type, does the field match? // Matches on the type, does the field match?
if (fieldName == null) { if (fieldMatcher.isIgnored()) {
return match; // no field to match return match; // no field to match
} }
if (potentialField == null) { if (potentialField == null) {
// check for the case where we have not been passed a 'potential field', but the given // check for the case where we have not been passed a 'potential field', but the given
// 'var' is itself the field we seek, such as in an if statement like this: // 'var' is itself may be the field we seek, such as in an if statement like this:
// if (color == RED) // if (color == RED)
// where 'RED' is the variable we are checking // where 'RED' is the variable we are checking
String name = var.getName(); String name = var.getName();
if (fieldName.equals(name)) { int offset = var.getOffset();
if (fieldMatcher.matches(name, offset)) {
return var; return var;
} }
@ -134,7 +136,8 @@ public class VariableAccessDR extends DecompilerReference {
} }
String name = potentialField.getName(); String name = potentialField.getName();
if (fieldName.equals(name)) { int offset = potentialField.getOffset();
if (fieldMatcher.matches(name, offset)) {
return match; return match;
} }
return null; return null;
@ -156,13 +159,13 @@ public class VariableAccessDR extends DecompilerReference {
// //
// Unusual Code Alert! // Unusual Code Alert!
// It is a bit odd to check the field when you are looking for the type that contains // It is a bit odd to check the field when you are looking for the type that contains the
// the field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the // field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the data type of
// data type of the thing that contains it. So, if you have: // the thing that contains it. So, if you have:
// foo.bar // foo.bar
// then the 'bar' field will have a data type of Foo. Unfortunately, this is not // then the 'bar' field will have a data type of Foo. Unfortunately, this is not always
// always the case. For now, when the variable is global, we need to check the field // the case. For now, when the variable is global, we need to check the field. Sad face
// Sad face emoji. // emoji.
// //
HighVariable highVariable = var.variable.getHighVariable(); HighVariable highVariable = var.variable.getHighVariable();
if (highVariable instanceof HighGlobal) { if (highVariable instanceof HighGlobal) {

View file

@ -20,6 +20,7 @@ import java.util.List;
import ghidra.app.decompiler.*; import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext; import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
import ghidra.app.services.DataTypeReference; import ghidra.app.services.DataTypeReference;
import ghidra.app.services.FieldMatcher;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
@ -52,7 +53,8 @@ public abstract class VariableDR extends DecompilerReference {
} }
@Override @Override
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) { public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
List<DataTypeReference> results) {
if (variable == null) { if (variable == null) {
// This implies our API was misused in that a variable was never set after creation // This implies our API was misused in that a variable was never set after creation
@ -61,21 +63,18 @@ public abstract class VariableDR extends DecompilerReference {
DataType dataType = getDataType(); DataType dataType = getDataType();
if (!isEqual(dataType, dt)) { if (!isEqual(dataType, dt)) {
// wrong type, nothing to do return; // wrong type, nothing to do
return;
}
LocationReferenceContext context = getContext();
Function function = getFunction();
Address address = getAddress();
if (fieldName == null) {
// no field to check, a match on the the type is good enough
results.add(new DataTypeReference(dataType, null, getFunction(), address, context));
return;
} }
String name = variable.getName(); String name = variable.getName();
if (name.equals(fieldName)) { int offset = variable.getOffset();
if (fieldMatcher.matches(name, offset)) {
// this will be null if the field matcher is empty
String fieldName = fieldMatcher.getFieldName();
Function function = getFunction();
Address address = getAddress();
LocationReferenceContext context = getContext();
results.add(new DataTypeReference(dataType, fieldName, function, address, context)); results.add(new DataTypeReference(dataType, fieldName, function, address, context));
} }
} }

View file

@ -41,10 +41,10 @@ import utilities.util.reflection.ReflectionUtilities;
* the entire application. * the entire application.
* <p> * <p>
* DockingActions can be invoked from the global menu, a popup menu, a toolbar, and/or a keybinding, * DockingActions can be invoked from the global menu, a popup menu, a toolbar, and/or a keybinding,
* depending on whether or not menuBarData, popupMenuData, toolBarData, and/or keyBindingData have * depending on whether or not menuBarData, popupMenuData, toolBarData, and/or keyBindingData have
* been set. * been set.
* <p> * <p>
* <b> * <b>
* Implementors of this class should override {@link #actionPerformed(ActionContext)}. * Implementors of this class should override {@link #actionPerformed(ActionContext)}.
* </b> * </b>
* <p> * <p>
@ -199,12 +199,12 @@ public abstract class DockingAction implements DockingActionIf {
* <P> * <P>
* If the client wants the action on all windows, then they can call {@link #shouldAddToAllWindows} * If the client wants the action on all windows, then they can call {@link #shouldAddToAllWindows}
* <P> * <P>
* If the client wants the action to be on a window only when the window can produce * If the client wants the action to be on a window only when the window can produce
* a certain context type, the the client should call * a certain context type, the the client should call
* {@link #addToWindowWhen(Class)} * {@link #addToWindowWhen(Class)}
* <P> * <P>
* Otherwise, by default, the action will only be on the main window. * Otherwise, by default, the action will only be on the main window.
* *
*/ */
@Override @Override
public final boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) { public final boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) {
@ -212,7 +212,7 @@ public abstract class DockingAction implements DockingActionIf {
if (menuBarData == null && toolBarData == null) { if (menuBarData == null && toolBarData == null) {
return false; return false;
} }
// clients can specify that the action should be on all windows. // clients can specify that the action should be on all windows.
if (shouldAddToAllWindows) { if (shouldAddToAllWindows) {
return true; return true;
@ -242,6 +242,14 @@ public abstract class DockingAction implements DockingActionIf {
DockingWindowManager.getHelpService().registerHelp(this, location); DockingWindowManager.getHelpService().registerHelp(this, location);
} }
/**
* Returns the help location for this action
* @return the help location for this action
*/
public HelpLocation getHelpLocation() {
return DockingWindowManager.getHelpService().getHelpLocation(this);
}
/** /**
* Signals the the help system that this action does not need a help entry. Some actions * Signals the the help system that this action does not need a help entry. Some actions
* are so obvious that they do not require help, such as an action that renames a file. * are so obvious that they do not require help, such as an action that renames a file.
@ -579,10 +587,10 @@ public abstract class DockingAction implements DockingActionIf {
/** /**
* Sets a predicate for dynamically determining the action's enabled state. If this * Sets a predicate for dynamically determining the action's enabled state. If this
* predicate is not set, the action's enable state must be controlled directly using the * predicate is not set, the action's enable state must be controlled directly using the
* {@link DockingAction#setEnabled(boolean)} method. See * {@link DockingAction#setEnabled(boolean)} method. See
* {@link DockingActionIf#isEnabledForContext(ActionContext)} * {@link DockingActionIf#isEnabledForContext(ActionContext)}
* *
* @param predicate the predicate that will be used to dynamically determine an action's * @param predicate the predicate that will be used to dynamically determine an action's
* enabled state. * enabled state.
*/ */
public void enabledWhen(Predicate<ActionContext> predicate) { public void enabledWhen(Predicate<ActionContext> predicate) {
@ -592,10 +600,10 @@ public abstract class DockingAction implements DockingActionIf {
/** /**
* Sets a predicate for dynamically determining if this action should be included in * Sets a predicate for dynamically determining if this action should be included in
* an impending pop-up menu. If this predicate is not set, the action's will be included * an impending pop-up menu. If this predicate is not set, the action's will be included
* in an impending pop-up, if it is enabled. See * in an impending pop-up, if it is enabled. See
* {@link DockingActionIf#isAddToPopup(ActionContext)} * {@link DockingActionIf#isAddToPopup(ActionContext)}
* *
* @param predicate the predicate that will be used to dynamically determine an action's * @param predicate the predicate that will be used to dynamically determine an action's
* enabled state. * enabled state.
*/ */
public void popupWhen(Predicate<ActionContext> predicate) { public void popupWhen(Predicate<ActionContext> predicate) {
@ -603,10 +611,10 @@ public abstract class DockingAction implements DockingActionIf {
} }
/** /**
* Sets a predicate for dynamically determining if this action is valid for the current * Sets a predicate for dynamically determining if this action is valid for the current
* {@link ActionContext}. See {@link DockingActionIf#isValidContext(ActionContext)} * {@link ActionContext}. See {@link DockingActionIf#isValidContext(ActionContext)}
* *
* @param predicate the predicate that will be used to dynamically determine an action's * @param predicate the predicate that will be used to dynamically determine an action's
* validity for a given {@link ActionContext} * validity for a given {@link ActionContext}
*/ */
public void validContextWhen(Predicate<ActionContext> predicate) { public void validContextWhen(Predicate<ActionContext> predicate) {
@ -620,14 +628,14 @@ public abstract class DockingAction implements DockingActionIf {
* that can produce an ActionContext that is appropriate for this action. * that can produce an ActionContext that is appropriate for this action.
* <P> * <P>
* @param contextClass the ActionContext class required to be producible by a * @param contextClass the ActionContext class required to be producible by a
* provider that is hosted in that window before this action is added to that * provider that is hosted in that window before this action is added to that
* window. * window.
* *
*/ */
public void addToWindowWhen(Class<? extends ActionContext> contextClass) { public void addToWindowWhen(Class<? extends ActionContext> contextClass) {
addToWindowWhenContextClass = contextClass; addToWindowWhenContextClass = contextClass;
} }
/** /**
* Tells this action to add itself to all windows * Tells this action to add itself to all windows
* <P> * <P>
@ -664,5 +672,5 @@ public abstract class DockingAction implements DockingActionIf {
String classInfo = trace[0].toString(); String classInfo = trace[0].toString();
return classInfo; return classInfo;
} }
} }

View file

@ -27,8 +27,8 @@ import docking.widgets.label.GDLabel;
import docking.widgets.label.GHtmlLabel; import docking.widgets.label.GHtmlLabel;
/** /**
* A dialog that has text fields to get user input. * A dialog that has text fields to get user input.
* *
*/ */
public class InputWithChoicesDialog extends DialogComponentProvider { public class InputWithChoicesDialog extends DialogComponentProvider {
@ -39,12 +39,12 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
/** /**
* Creates a provider for a generic input dialog with the specified title, * Creates a provider for a generic input dialog with the specified title,
* a label and a editable comboBox pre-populated with selectable values. The user * a label and a editable comboBox pre-populated with selectable values. The user
* can check the value of {@link #isCanceled()} to know whether or not * can check the value of {@link #isCanceled()} to know whether or not
* the user canceled the operation. To get the user selected value use the * the user canceled the operation. To get the user selected value use the
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then * {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
* null will be returned from <code>getValue()</code>. * null will be returned from <code>getValue()</code>.
* <P> * <P>
* *
* @param dialogTitle used as the name of the dialog's title bar * @param dialogTitle used as the name of the dialog's title bar
* @param label value to use for the label of the text field * @param label value to use for the label of the text field
* @param optionValues values to populate the combo box * @param optionValues values to populate the combo box
@ -69,12 +69,12 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
/** /**
* Creates a provider for a generic input dialog with the specified title, * Creates a provider for a generic input dialog with the specified title,
* a label and a editable comboBox pre-populated with selectable values. The user * a label and a editable comboBox pre-populated with selectable values. The user
* can check the value of {@link #isCanceled()} to know whether or not * can check the value of {@link #isCanceled()} to know whether or not
* the user canceled the operation. To get the user selected value use the * the user canceled the operation. To get the user selected value use the
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then * {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
* null will be returned from <code>getValue()</code>. * null will be returned from <code>getValue()</code>.
* <P> * <P>
* *
* @param dialogTitle used as the name of the dialog's title bar * @param dialogTitle used as the name of the dialog's title bar
* @param label value to use for the label of the text field * @param label value to use for the label of the text field
* @param optionValues values to populate the combo box * @param optionValues values to populate the combo box
@ -180,6 +180,11 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
if (isCanceled) { if (isCanceled) {
return null; return null;
} }
if (allowEdits) {
return combo.getText();
}
Object selectedItem = combo.getSelectedItem(); Object selectedItem = combo.getSelectedItem();
return selectedItem == null ? null : selectedItem.toString(); return selectedItem == null ? null : selectedItem.toString();
} }
@ -188,7 +193,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
* Set the current choice to value. * Set the current choice to value.
* @param value updated choice * @param value updated choice
* @throws NoSuchElementException if choice does not permit edits and value is * @throws NoSuchElementException if choice does not permit edits and value is
* not a valid choice. * not a valid choice.
*/ */
public void setValue(String value) { public void setValue(String value) {
combo.setSelectedItem(value); combo.setSelectedItem(value);

View file

@ -57,17 +57,24 @@ public final class NumericUtilities {
} }
/** /**
* parses the given string as a numeric value, detecting whether or not it begins with a Hex * Parses the given string as a numeric value, detecting whether or not it begins with a Hex
* prefix, and if not, parses as a long int value. * prefix, and if not, parses as a long int value.
* @param numStr the number string
* @return the long value or 0
*
*/ */
public static long parseNumber(String numStr) { public static long parseNumber(String numStr) {
long value = 0; return parseNumber(numStr, Long.valueOf(0));
}
public static Long parseNumber(String numStr, Long defaultValue) {
numStr = (numStr == null ? "" : numStr.trim()); numStr = (numStr == null ? "" : numStr.trim());
if (numStr.length() == 0) { if (numStr.length() == 0) {
return value; return defaultValue;
} }
long value = 0;
try { try {
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) { if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
value = Integer.parseInt(numStr.substring(2), 16); value = Integer.parseInt(numStr.substring(2), 16);
@ -78,6 +85,7 @@ public final class NumericUtilities {
} }
catch (NumberFormatException exc) { catch (NumberFormatException exc) {
// do nothing special; use default value // do nothing special; use default value
return defaultValue;
} }
return value; return value;
@ -192,7 +200,7 @@ public final class NumericUtilities {
/** /**
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x * returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
* string. * string.
* *
* @param value the long value to convert * @param value the long value to convert
*/ */
public final static String toHexString(long value) { public final static String toHexString(long value) {
@ -202,7 +210,7 @@ public final class NumericUtilities {
/** /**
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x * returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
* string. * string.
* *
* @param value the long value to convert * @param value the long value to convert
* @param size number of bytes to be represented * @param size number of bytes to be represented
*/ */
@ -216,7 +224,7 @@ public final class NumericUtilities {
/** /**
* returns the value of the specified long as signed hexadecimal, prefixing with the * returns the value of the specified long as signed hexadecimal, prefixing with the
* HEX_PREFIX_x string. * HEX_PREFIX_x string.
* *
* @param value the long value to convert * @param value the long value to convert
*/ */
public final static String toSignedHexString(long value) { public final static String toSignedHexString(long value) {
@ -230,14 +238,14 @@ public final class NumericUtilities {
} }
/** /**
* Converts a <strong>unsigned</strong> long value, which is currently stored in a * Converts a <strong>unsigned</strong> long value, which is currently stored in a
* java <strong>signed</strong> long, into a {@link BigInteger}. * java <strong>signed</strong> long, into a {@link BigInteger}.
* <p> * <p>
* In other words, the full 64 bits of the primitive java <strong>signed</strong> * In other words, the full 64 bits of the primitive java <strong>signed</strong>
* long is being used to store an <strong>unsigned</strong> value. This * long is being used to store an <strong>unsigned</strong> value. This
* method converts this into a positive BigInteger value. * method converts this into a positive BigInteger value.
* *
* @param value java <strong>unsigned</strong> long value stuffed into a * @param value java <strong>unsigned</strong> long value stuffed into a
* java <strong>signed</strong> long * java <strong>signed</strong> long
* @return new {@link BigInteger} with the positive value of the unsigned long value * @return new {@link BigInteger} with the positive value of the unsigned long value
*/ */
@ -258,7 +266,7 @@ public final class NumericUtilities {
/** /**
* Get an unsigned aligned value corresponding to the specified unsigned value which will be * Get an unsigned aligned value corresponding to the specified unsigned value which will be
* greater than or equal the specified value. * greater than or equal the specified value.
* *
* @param unsignedValue value to be aligned * @param unsignedValue value to be aligned
* @param alignment alignment * @param alignment alignment
* @return aligned value * @return aligned value
@ -384,7 +392,7 @@ public final class NumericUtilities {
* Philosophically, it is hexadecimal, but the only valid digits are 0 and F. Any * Philosophically, it is hexadecimal, but the only valid digits are 0 and F. Any
* partially-included nibble will be broken down into bracketed bits. Displaying masks in this * partially-included nibble will be broken down into bracketed bits. Displaying masks in this
* way is convenient when shown proximal to related masked values. * way is convenient when shown proximal to related masked values.
* *
* @param msk the mask * @param msk the mask
* @param n the number of nibbles, starting at the right * @param n the number of nibbles, starting at the right
* @param truncate true if leading Xs may be truncated * @param truncate true if leading Xs may be truncated
@ -441,7 +449,7 @@ public final class NumericUtilities {
/** /**
* The reverse of {@link #convertMaskedValueToHexString(long, long, int, boolean, int, String)} * The reverse of {@link #convertMaskedValueToHexString(long, long, int, boolean, int, String)}
* *
* @param msk an object to receive the resulting mask * @param msk an object to receive the resulting mask
* @param val an object to receive the resulting value * @param val an object to receive the resulting value
* @param hex the input string to parse * @param hex the input string to parse
@ -669,7 +677,7 @@ public final class NumericUtilities {
* <td>-64h</td> * <td>-64h</td>
* </tr> * </tr>
* </table> * </table>
* *
* @param number The number to represent * @param number The number to represent
* @param radix The base in which <code>number</code> is represented * @param radix The base in which <code>number</code> is represented
* @param mode Specifies how the number is formatted with respect to its signed-ness * @param mode Specifies how the number is formatted with respect to its signed-ness
@ -849,7 +857,7 @@ public final class NumericUtilities {
/** /**
* Determine if the provided Number is an integer type -- Byte, Short, Integer, or Long. * Determine if the provided Number is an integer type -- Byte, Short, Integer, or Long.
* *
* @param number the object to check for for integer-type * @param number the object to check for for integer-type
* @return true if the provided number is an integer-type, false otherwise * @return true if the provided number is an integer-type, false otherwise
*/ */
@ -860,7 +868,7 @@ public final class NumericUtilities {
/** /**
* Determine if the provided Number class is an integer type. * Determine if the provided Number class is an integer type.
* *
* @param numClass Class of an object * @param numClass Class of an object
* @return true if the class parameter is a integer type, false otherwise * @return true if the class parameter is a integer type, false otherwise
*/ */
@ -870,7 +878,7 @@ public final class NumericUtilities {
/** /**
* Determine if the provided Number is a floating-point type -- Float or Double. * Determine if the provided Number is a floating-point type -- Float or Double.
* *
* @param number the object to check for for floating-point-type * @param number the object to check for for floating-point-type
* @return true if the provided number is a floating-point-type, false otherwise * @return true if the provided number is a floating-point-type, false otherwise
*/ */
@ -881,7 +889,7 @@ public final class NumericUtilities {
/** /**
* Determine if the provided Number class is a floating-point type. * Determine if the provided Number class is a floating-point type.
* *
* @param numClass Class of an object * @param numClass Class of an object
* @return true if the class parameter is a floating-point type, false otherwise * @return true if the class parameter is a floating-point type, false otherwise
*/ */
@ -895,7 +903,7 @@ public final class NumericUtilities {
private static interface IntegerRadixRenderer { private static interface IntegerRadixRenderer {
/** /**
* Format the given number in the provided radix base. * Format the given number in the provided radix base.
* *
* @param number the number to render * @param number the number to render
* @param radix the base in which to render * @param radix the base in which to render
* @return a string representing the provided number in the given base * @return a string representing the provided number in the given base

View file

@ -18,9 +18,8 @@ package ghidra.util.datastruct;
import java.util.*; import java.util.*;
/** /**
* Provides a list of integer ranges that are maintained in sorted order. * Provides a list of integer ranges that are maintained in sorted order. When a range is added
* When a range is added any ranges that overlap or are adjacent to one another * any ranges that overlap or are adjacent to one another will coalesce into a single range.
* will coalesce into a single range.
*/ */
public class SortedRangeList implements Iterable<Range> { public class SortedRangeList implements Iterable<Range> {
TreeSet<Range> set; TreeSet<Range> set;
@ -33,8 +32,7 @@ public class SortedRangeList implements Iterable<Range> {
} }
/** /**
* Creates a new sorted range list with ranges equivalent to those in the * Creates a new sorted range list with ranges equivalent to those in the specified list.
* specified list.
* @param list the sorted range list to make an equivalent copy of. * @param list the sorted range list to make an equivalent copy of.
*/ */
public SortedRangeList(SortedRangeList list) { public SortedRangeList(SortedRangeList list) {
@ -47,9 +45,8 @@ public class SortedRangeList implements Iterable<Range> {
} }
/** /**
* Adds the range from min to max to this sorted range list. * Adds the range from min to max to this sorted range list. If the range is adjacent to or
* If the range is adjacent to or overlaps any other existing ranges, * overlaps any other existing ranges, then those ranges will coalesce.
* then those ranges will coalesce.
* @param min the range minimum * @param min the range minimum
* @param max the range maximum (inclusive) * @param max the range maximum (inclusive)
*/ */
@ -82,15 +79,18 @@ public class SortedRangeList implements Iterable<Range> {
/** /**
* Returns an iterator over all the ranges in this list. * Returns an iterator over all the ranges in this list.
* @return the iterator
*/ */
public Iterator<Range> getRanges() { public Iterator<Range> getRanges() {
return set.iterator(); return set.iterator();
} }
/** /**
* Returns an iterator over all the ranges in this list that iterates in the direction specified. * Returns an iterator over all the ranges in this list that iterates in the direction
* @param forward true indicates to iterate forward from minimum to maximum range. * specified.
* false indicates backward iteration form maximum to minimum. * @param forward true indicates to iterate forward from minimum to maximum range; false
* indicates backward iteration form maximum to minimum.
* @return the iterator
*/ */
public Iterator<Range> getRanges(boolean forward) { public Iterator<Range> getRanges(boolean forward) {
if (forward) { if (forward) {
@ -106,6 +106,7 @@ public class SortedRangeList implements Iterable<Range> {
/** /**
* Returns the minimum int value in this sorted range list. * Returns the minimum int value in this sorted range list.
* @return the min value
* @throws NoSuchElementException if the list is empty. * @throws NoSuchElementException if the list is empty.
*/ */
public int getMin() throws NoSuchElementException { public int getMin() throws NoSuchElementException {
@ -115,6 +116,7 @@ public class SortedRangeList implements Iterable<Range> {
/** /**
* Returns the maximum int value in this sorted range list. * Returns the maximum int value in this sorted range list.
* @return the max value
* @throws NoSuchElementException if the list is empty. * @throws NoSuchElementException if the list is empty.
*/ */
public int getMax() throws NoSuchElementException { public int getMax() throws NoSuchElementException {
@ -124,13 +126,14 @@ public class SortedRangeList implements Iterable<Range> {
/** /**
* Returns the number of ranges in the list. * Returns the number of ranges in the list.
* @return the number of ranges
*/ */
public int getNumRanges() { public int getNumRanges() {
return set.size(); return set.size();
} }
/** /**
* Removes the indicated range of values from the list. This will remove * Removes the indicated range of values from the list. This will remove
* any ranges or portion of ranges that overlap the indicated range. * any ranges or portion of ranges that overlap the indicated range.
* @param min the minimum value for the range to remove. * @param min the minimum value for the range to remove.
* @param max the maximum value for the range to remove. * @param max the maximum value for the range to remove.
@ -182,6 +185,7 @@ public class SortedRangeList implements Iterable<Range> {
/** /**
* Returns true if the value is contained in any ranges within this list. * Returns true if the value is contained in any ranges within this list.
* @param value the value to check for. * @param value the value to check for.
* @return true if the value is contained in any ranges within this list.
*/ */
public boolean contains(int value) { public boolean contains(int value) {
Range key = new Range(value, value); Range key = new Range(value, value);
@ -217,6 +221,7 @@ public class SortedRangeList implements Iterable<Range> {
* Returns true if a single range contains all the values from min to max. * Returns true if a single range contains all the values from min to max.
* @param min the minimum value * @param min the minimum value
* @param max the maximum value * @param max the maximum value
* @return true if a single range contains all the values from min to max.
*/ */
public boolean contains(int min, int max) { public boolean contains(int min, int max) {
Range range = getRangeContaining(min); Range range = getRangeContaining(min);
@ -284,9 +289,12 @@ public class SortedRangeList implements Iterable<Range> {
} }
/** /**
* Returns true if the range from min to max intersects (overlaps) any ranges in this sorted range list. * Returns true if the range from min to max intersects (overlaps) any ranges in this sorted
* range list.
* @param min the range minimum value. * @param min the range minimum value.
* @param max the range maximum value * @param max the range maximum value.
* @return true if the range from min to max intersects (overlaps) any ranges in this sorted
* range list.
*/ */
public boolean intersects(int min, int max) { public boolean intersects(int min, int max) {
Range key = new Range(min, min); Range key = new Range(min, min);
@ -309,6 +317,7 @@ public class SortedRangeList implements Iterable<Range> {
/** /**
* Returns true if the range list is empty. * Returns true if the range list is empty.
* @return true if the range list is empty.
*/ */
public boolean isEmpty() { public boolean isEmpty() {
return set.isEmpty(); return set.isEmpty();
@ -327,7 +336,8 @@ public class SortedRangeList implements Iterable<Range> {
} }
/** /**
* Creates a new SortedRangeList that is the intersection of this range list and the other range list specified. * Creates a new SortedRangeList that is the intersection of this range list and the other
* range list specified.
* @param other the other range list * @param other the other range list
* @return the new SortedRangeList representing the intersection. * @return the new SortedRangeList representing the intersection.
*/ */
@ -339,12 +349,9 @@ public class SortedRangeList implements Iterable<Range> {
return srl2; return srl2;
} }
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override @Override
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(); StringBuilder buf = new StringBuilder();
Iterator<Range> it = getRanges(); Iterator<Range> it = getRanges();
if (it.hasNext()) { if (it.hasNext()) {
Range r = it.next(); Range r = it.next();
@ -365,4 +372,28 @@ public class SortedRangeList implements Iterable<Range> {
public void clear() { public void clear() {
set.clear(); set.clear();
} }
@Override
public int hashCode() {
return Objects.hash(set);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
SortedRangeList other = (SortedRangeList) obj;
if (!Objects.equals(set, other.set)) {
return false;
}
return true;
}
} }

View file

@ -48,10 +48,10 @@ public class ReferenceTagProcessor extends TagProcessor {
this.anchorManager = anchorManager; this.anchorManager = anchorManager;
// //
// Note: currently all help being built has the required stylesheet living under // Note: currently all help being built has the required stylesheet living under
// <help dir>/shared/<stylesheet name> // <help dir>/shared/<stylesheet name>
// //
// If we ever need a more robust styling mechanism, then this code would need to be // If we ever need a more robust styling mechanism, then this code would need to be
// updated to know how to search for the referenced stylesheet // updated to know how to search for the referenced stylesheet
Path helpPath = help.getHelpLocation(); Path helpPath = help.getHelpLocation();
FileSystem fs = helpPath.getFileSystem(); FileSystem fs = helpPath.getFileSystem();
@ -81,8 +81,8 @@ public class ReferenceTagProcessor extends TagProcessor {
if ("a".equals(tagType)) { if ("a".equals(tagType)) {
if (tagAttributes.containsKey("href")) { if (tagAttributes.containsKey("href")) {
try { try {
anchorManager.addAnchorRef( anchorManager
new HREF(help, file, tagAttributes.get("href"), lineNum)); .addAnchorRef(new HREF(help, file, tagAttributes.get("href"), lineNum));
} }
catch (URISyntaxException e) { catch (URISyntaxException e) {
errorCount++; errorCount++;
@ -102,8 +102,8 @@ public class ReferenceTagProcessor extends TagProcessor {
else if ("img".equals(tagType)) { else if ("img".equals(tagType)) {
if (tagAttributes.containsKey("src")) { if (tagAttributes.containsKey("src")) {
try { try {
anchorManager.addImageRef( anchorManager
new IMG(help, file, tagAttributes.get("src"), lineNum)); .addImageRef(new IMG(help, file, tagAttributes.get("src"), lineNum));
} }
catch (URISyntaxException e) { catch (URISyntaxException e) {
errorCount++; errorCount++;
@ -119,7 +119,7 @@ public class ReferenceTagProcessor extends TagProcessor {
else if ("link".equals(tagType)) { else if ("link".equals(tagType)) {
String rel = tagAttributes.get("rel"); String rel = tagAttributes.get("rel");
if (rel != null && "stylesheet".equals(rel.toLowerCase())) { if (rel != null && "stylesheet".equals(rel.toLowerCase())) {
// TODO there is at least one help module that has multiple style sheets. I see no reason to // TODO there is at least one help module that has multiple style sheets. I see no reason to
// enforce this constraint: // enforce this constraint:
// if (hasStyleSheet) { // if (hasStyleSheet) {
// errorCount++; // errorCount++;
@ -191,7 +191,8 @@ public class ReferenceTagProcessor extends TagProcessor {
if (!hasDefaultStyleSheet) { if (!hasDefaultStyleSheet) {
errorCount++; errorCount++;
errors.append("Incorrect stylesheet defined - none match " + defaultStyleSheet + errors.append("Incorrect stylesheet defined - none match " + defaultStyleSheet +
" in file " + htmlFile + EOL); " in file " + htmlFile + EOL + "\tDiscovered stylesheets: " + styleSheets + EOL);
} }
} }

View file

@ -38,10 +38,10 @@ public interface Data extends CodeUnit, Settings {
/** /**
* Get the class used to express the value of this data. * Get the class used to express the value of this data.
* *
* <p>NOTE: This determination is made based upon data type and settings only and does not * <p>NOTE: This determination is made based upon data type and settings only and does not
* examine memory bytes which are used to construct the data value object. * examine memory bytes which are used to construct the data value object.
* *
* @return value class or null if a consistent class is not utilized. * @return value class or null if a consistent class is not utilized.
*/ */
public Class<?> getValueClass(); public Class<?> getValueClass();
@ -216,7 +216,7 @@ public interface Data extends CodeUnit, Settings {
* Return the first immediate child component that contains the byte at the given offset. It * Return the first immediate child component that contains the byte at the given offset. It
* is important to note that with certain datatypes there may be more than one component * is important to note that with certain datatypes there may be more than one component
* containing the specified offset (see {@link #getComponentsContaining(int)}). * containing the specified offset (see {@link #getComponentsContaining(int)}).
* *
* @param offset the amount to add to this data items address to get the address of the * @param offset the amount to add to this data items address to get the address of the
* requested data item. * requested data item.
* @return first data component containing offset or null * @return first data component containing offset or null
@ -227,10 +227,10 @@ public interface Data extends CodeUnit, Settings {
public Data getComponentAt(int offset); public Data getComponentAt(int offset);
/** /**
* RReturn the first immediate child component that contains the byte at the given offset. It * Return the first immediate child component that contains the byte at the given offset. It
* is important to note that with certain datatypes there may be more than one component * is important to note that with certain datatypes there may be more than one component
* containing the specified offset (see {@link #getComponentsContaining(int)}). * containing the specified offset (see {@link #getComponentsContaining(int)}).
* *
* @param offset the amount to add to this data items address to get the * @param offset the amount to add to this data items address to get the
* @return first data component containing offset or null address of the requested data item. * @return first data component containing offset or null address of the requested data item.
*/ */

View file

@ -17,13 +17,15 @@
package ghidra.program.util; package ghidra.program.util;
import java.util.Objects;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
/** /**
* The <CODE>FieldNameFieldLocation</CODE> class provides specific information * The <CODE>FieldNameFieldLocation</CODE> class provides specific information about the Function
* about the Function Name field within a program location. * Name field within a program location.
*/ */
public class FieldNameFieldLocation extends CodeUnitLocation { public class FieldNameFieldLocation extends CodeUnitLocation {
@ -31,11 +33,11 @@ public class FieldNameFieldLocation extends CodeUnitLocation {
/** /**
* Construct a new FieldNameFieldLocation. * Construct a new FieldNameFieldLocation.
* *
* @param program the program of the location * @param program the program of the location
* @param addr the address of the codeunit. * @param addr the address of the code unit
* @param componentPath if not null, it is the array of indexes that point * @param componentPath if not null, it is the array of indexes that point to a specific data
* to a specific data type inside of another data type * type inside of another data type
* @param fieldName the field name * @param fieldName the field name
* @param charOffset the character position within the field name for this location. * @param charOffset the character position within the field name for this location.
*/ */
@ -48,14 +50,14 @@ public class FieldNameFieldLocation extends CodeUnitLocation {
} }
/** /**
* Default constructor needed for restoring * Default constructor needed for restoring a field name location from XML
* a field name location from XML
*/ */
public FieldNameFieldLocation() { public FieldNameFieldLocation() {
} }
/** /**
* Returns the field name of this location. * Returns the field name of this location.
* @return the name.
*/ */
public String getFieldName() { public String getFieldName() {
return fieldName; return fieldName;
@ -71,19 +73,19 @@ public class FieldNameFieldLocation extends CodeUnitLocation {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) if (this == obj) {
return true; return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
FieldNameFieldLocation other = (FieldNameFieldLocation) obj;
if (fieldName == null) {
if (other.fieldName != null)
return false;
} }
else if (!fieldName.equals(other.fieldName)) if (!super.equals(obj)) {
return false; return false;
}
if (getClass() != obj.getClass()) {
return false;
}
FieldNameFieldLocation other = (FieldNameFieldLocation) obj;
if (!Objects.equals(fieldName, other.fieldName)) {
return false;
}
return true; return true;
} }

View file

@ -15,14 +15,16 @@
*/ */
package ghidra.program.util; package ghidra.program.util;
import java.util.Objects;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableOffset; import ghidra.program.model.listing.VariableOffset;
/** /**
* The <CODE>OperandFieldLocation</CODE> class contains specific location information * The <CODE>OperandFieldLocation</CODE> class contains specific location information within the
* within the OPERAND field of a CodeUnitLocation object. * OPERAND field of a CodeUnitLocation object.
*/ */
public class OperandFieldLocation extends CodeUnitLocation { public class OperandFieldLocation extends CodeUnitLocation {
@ -32,17 +34,18 @@ public class OperandFieldLocation extends CodeUnitLocation {
/** /**
* Construct a new OperandFieldLocation object. * Construct a new OperandFieldLocation object.
* *
* @param program the program of the location * @param program the program of the location.
* @param addr address of the location; should not be null * @param addr address of the location; should not be null.
* @param componentPath array of indexes for each nested data component; the * @param componentPath array of indexes for each nested data component; the index is the data
* index is the data component's index within its parent; may be null * component's index within its parent; may be null.
* @param refAddr the reference 'to' address.
* @param rep the String representation of the operand. * @param rep the String representation of the operand.
* @param opIndex the index of the operand at this location. * @param opIndex the index of the operand at this location.
* @param characterOffset the character position from the beginning of the operand. * @param characterOffset the character position from the beginning of the operand.
*/ */
public OperandFieldLocation(Program program, Address addr, int[] componentPath, public OperandFieldLocation(Program program, Address addr, int[] componentPath, Address refAddr,
Address refAddr, String rep, int opIndex, int characterOffset) { String rep, int opIndex, int characterOffset) {
super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset); super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset);
@ -52,68 +55,63 @@ public class OperandFieldLocation extends CodeUnitLocation {
/** /**
* Construct a new OperandFieldLocation object. * Construct a new OperandFieldLocation object.
* *
* @param program the program of the location * @param program the program of the location.
* @param addr address of the location; should not be null * @param addr address of the location; should not be null.
* @param componentPath array of indexes for each nested data component; the * @param componentPath array of indexes for each nested data component; the index is the data
* index is the data component's index within its parent; may be null * component's index within its parent; may be null .
* @param refAddr the "referred to" address if the location is * @param refAddr the "referred to" address if the location is over a reference; may be null.
* over a reference; may be null
* @param rep the String representation of the operand. * @param rep the String representation of the operand.
* @param opIndex the index indicating the operand the location is on. * @param opIndex the index indicating the operand the location is on.
* @param subOpIndex the index of the Object within the operand, this can * @param subOpIndex the index of the Object within the operand, this can be used to call an
* be used to call an instructions getOpObjects() method * instructions getOpObjects() method.
* @param characterOffset the character position from the beginning of the operand field * @param characterOffset the character position from the beginning of the operand field.
*/ */
public OperandFieldLocation(Program program, Address addr, int[] componentPath, public OperandFieldLocation(Program program, Address addr, int[] componentPath, Address refAddr,
Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) { String rep, int opIndex, int subOpIndex, int characterOffset) {
super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset); super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset);
this.rep = rep; this.rep = rep;
this.subOpIndex = subOpIndex; this.subOpIndex = subOpIndex;
} }
/** /**
* Construct a new OperandFieldLocation object for an instruction operand. * Construct a new OperandFieldLocation object for an instruction operand.
* *
* @param program the program of the location * @param program the program of the location.
* @param addr address of the location; should not be null * @param addr address of the location; should not be null.
* @param variableOffset associated variable offset or null * @param variableOffset associated variable offset or null.
* @param refAddr the "referred to" address if the location is * @param refAddr the "referred to" address if the location is over a reference; may be null.
* over a reference; may be null
* @param rep the String representation of the operand. * @param rep the String representation of the operand.
* @param opIndex the index indicating the operand the location is on. * @param opIndex the index indicating the operand the location is on.
* @param subOpIndex the index of the Object within the operand, this can * @param subOpIndex the index of the Object within the operand, this can be used to call an
* be used to call an instructions getOpObjects() method * instructions getOpObjects() method.
* @param characterOffset the character position from the beginning of the operand field * @param characterOffset the character position from the beginning of the operand field.
*/ */
public OperandFieldLocation(Program program, Address addr, VariableOffset variableOffset, public OperandFieldLocation(Program program, Address addr, VariableOffset variableOffset,
Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) { Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) {
super(program, addr, null, refAddr, 0, opIndex, characterOffset); super(program, addr, null, refAddr, 0, opIndex, characterOffset);
this.rep = rep; this.rep = rep;
this.subOpIndex = subOpIndex; this.subOpIndex = subOpIndex;
this.variableOffset = variableOffset; this.variableOffset = variableOffset;
} }
/** /**
* Default constructor needed for restoring * Default constructor needed for restoring an operand field location from XML.
* an operand field location from XML. */
*/
public OperandFieldLocation() { public OperandFieldLocation() {
} }
/** /**
* Returns VariableOffset object if applicable or null * Returns VariableOffset object if applicable or null.
* @return the variable offset.
*/ */
public VariableOffset getVariableOffset() { public VariableOffset getVariableOffset() {
return variableOffset; return variableOffset;
} }
/** /**
* Returns a string representation of the opernand at this location. * Returns a string representation of the operand at this location.
* @return the representation.
*/ */
public String getOperandRepresentation() { public String getOperandRepresentation() {
return rep; return rep;
@ -121,6 +119,7 @@ public class OperandFieldLocation extends CodeUnitLocation {
/** /**
* Returns the index of the operand at this location. * Returns the index of the operand at this location.
* @return the index
*/ */
public int getOperandIndex() { public int getOperandIndex() {
return getColumn(); return getColumn();
@ -128,18 +127,15 @@ public class OperandFieldLocation extends CodeUnitLocation {
/** /**
* Returns the sub operand index at this location. * Returns the sub operand index at this location.
* This index can be used on the instruction.getOpObjects() * <p>
* to find the actual object (Address, Register, Scalar) the * This index can be used on the instruction.getOpObjects() to find the actual object (Address,
* cursor is over. * Register, Scalar) the cursor is over.
* @return 0-n if over a valid OpObject, -1 otherwise * @return 0-n if over a valid OpObject, -1 otherwise
*/ */
public int getSubOperandIndex() { public int getSubOperandIndex() {
return subOpIndex; return subOpIndex;
} }
/**
* Returns a String representation of this location.
*/
@Override @Override
public String toString() { public String toString() {
return super.toString() + ", OpRep = " + rep + ", subOpIndex = " + subOpIndex + return super.toString() + ", OpRep = " + rep + ", subOpIndex = " + subOpIndex +
@ -158,27 +154,25 @@ public class OperandFieldLocation extends CodeUnitLocation {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) if (this == obj) {
return true; return true;
if (!super.equals(obj)) }
if (!super.equals(obj)) {
return false; return false;
if (getClass() != obj.getClass()) }
if (getClass() != obj.getClass()) {
return false; return false;
}
OperandFieldLocation other = (OperandFieldLocation) obj; OperandFieldLocation other = (OperandFieldLocation) obj;
if (rep == null) { if (!Objects.equals(rep, other.rep)) {
if (other.rep != null) return false;
return false;
} }
else if (!rep.equals(other.rep)) if (subOpIndex != other.subOpIndex) {
return false; return false;
if (subOpIndex != other.subOpIndex)
return false;
if (variableOffset == null) {
if (other.variableOffset != null)
return false;
} }
else if (!variableOffset.equals(other.variableOffset)) if (!Objects.equals(variableOffset, other.variableOffset)) {
return false; return false;
}
return true; return true;
} }

View file

@ -95,7 +95,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
* Construct a new ProgramLocation for the given address. The address will be adjusted to the * Construct a new ProgramLocation for the given address. The address will be adjusted to the
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The * beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
* original address can be retrieved using the {@link #getByteAddress()}" method. * original address can be retrieved using the {@link #getByteAddress()}" method.
* *
* @param program the program associated with this program location (also used to obtain a * @param program the program associated with this program location (also used to obtain a
* code-unit-aligned address) * code-unit-aligned address)
* @param addr address of the location; cannot be null * @param addr address of the location; cannot be null
@ -118,7 +118,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
* Construct a new ProgramLocation for the given address. The address will be adjusted to the * Construct a new ProgramLocation for the given address. The address will be adjusted to the
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The * beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
* original address can be retrieved using the {@link #getByteAddress()} method. * original address can be retrieved using the {@link #getByteAddress()} method.
* *
* @param program the program associated with this program location (also used to obtain a * @param program the program associated with this program location (also used to obtain a
* code-unit-aligned address) * code-unit-aligned address)
* @param addr address for the location * @param addr address for the location
@ -132,7 +132,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
* Construct a new ProgramLocation for the given address. The address will be adjusted to the * Construct a new ProgramLocation for the given address. The address will be adjusted to the
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The * beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
* original address can be retrieved using the {@link #getByteAddress()} method. * original address can be retrieved using the {@link #getByteAddress()} method.
* *
* @param program the program associated with this program location (also used to obtain a * @param program the program associated with this program location (also used to obtain a
* code-unit-aligned address) * code-unit-aligned address)
* @param addr address for the location * @param addr address for the location
@ -150,7 +150,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
* Construct a new ProgramLocation for the given address. The address will be adjusted to the * Construct a new ProgramLocation for the given address. The address will be adjusted to the
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The * beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
* original address can be retrieved using the {@link #getByteAddress()} method. * original address can be retrieved using the {@link #getByteAddress()} method.
* *
* @param program the program associated with this program location (also used to obtain a * @param program the program associated with this program location (also used to obtain a
* code-unit-aligned address) * code-unit-aligned address)
* @param addr address for the location * @param addr address for the location
@ -170,6 +170,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Returns the componentPath for the {@link CodeUnit code unit}. Null will be returned if the * Returns the componentPath for the {@link CodeUnit code unit}. Null will be returned if the
* object is an {@link Instruction} or a top-level {@link Data} object. * object is an {@link Instruction} or a top-level {@link Data} object.
* @return the path.
*/ */
public int[] getComponentPath() { public int[] getComponentPath() {
return componentPath; return componentPath;
@ -177,6 +178,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Returns the program associated with this location. * Returns the program associated with this location.
* @return the program.
*/ */
public Program getProgram() { public Program getProgram() {
return program; return program;
@ -184,11 +186,12 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Returns the address associated with this location. * Returns the address associated with this location.
* *
* <p> * <p>
* Note: this may not be the same as the byte address. For example, in a {@link CodeUnit code * Note: this may not be the same as the byte address. For example, in a {@link CodeUnit code
* unit} location this may be the minimum address of the code unit that contains the byte * unit} location this may be the minimum address of the code unit that contains the byte
* address. * address.
* @return the address.
*/ */
public Address getAddress() { public Address getAddress() {
return addr; return addr;
@ -196,6 +199,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Returns the byte level address associated with this location. * Returns the byte level address associated with this location.
* @return the byte address.
*/ */
public Address getByteAddress() { public Address getByteAddress() {
return byteAddr; return byteAddr;
@ -203,6 +207,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Returns the "referred to" address if the location is over an address in some field. * Returns the "referred to" address if the location is over an address in some field.
* @return the address.
*/ */
public Address getRefAddress() { public Address getRefAddress() {
return refAddr; return refAddr;
@ -210,7 +215,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Save this program location to the given save state object. * Save this program location to the given save state object.
* *
* @param obj the save state object for saving the location * @param obj the save state object for saving the location
*/ */
public void saveState(SaveState obj) { public void saveState(SaveState obj) {
@ -231,7 +236,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Restore this program location using the given program and save state object. * Restore this program location using the given program and save state object.
* *
* @param program1 program to restore from * @param program1 program to restore from
* @param obj the save state to restore from * @param obj the save state to restore from
*/ */
@ -254,7 +259,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Get the program location for the given program and save state object. * Get the program location for the given program and save state object.
* *
* @param program the program for the location * @param program the program for the location
* @param saveState the state to restore * @param saveState the state to restore
* @return the restored program location * @return the restored program location
@ -274,7 +279,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
} }
// no address, it must be in a removed block; we can't use it // no address, it must be in a removed block; we can't use it
} }
catch (RuntimeException e) { // restoreState may not parse the address if it is no longer valid. catch (RuntimeException e) { // state may not parse the address if it is no longer valid
} }
catch (ClassNotFoundException e) { catch (ClassNotFoundException e) {
// not sure why we are ignoring this--if you know, then please let everyone else know // not sure why we are ignoring this--if you know, then please let everyone else know
@ -419,7 +424,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
} }
CodeUnit cu = p.getListing().getCodeUnitContaining(addr); CodeUnit cu = p.getListing().getCodeUnitContaining(addr);
// if the codeunit is a data, try and dig down to the lowest subdata containing the address // if the code unit is data, get the lowest sub-data containing the address
if (cu instanceof Data) { if (cu instanceof Data) {
Data data = (Data) cu; Data data = (Data) cu;
cu = data.getPrimitiveAt((int) addr.subtract(data.getAddress())); cu = data.getPrimitiveAt((int) addr.subtract(data.getAddress()));
@ -437,10 +442,10 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
} }
/** /**
* Returns true if this location represents a valid location in the given program * Returns true if this location represents a valid location in the given program.
* *
* @param testProgram the program to test if this location is valid. * @param testProgram the program to test if this location is valid.
* @return true if this location represents a valid location in the given program * @return true if this location represents a valid location in the given program.
*/ */
public boolean isValid(Program testProgram) { public boolean isValid(Program testProgram) {
return addr == null || testProgram.getAddressFactory().isValidAddress(addr); return addr == null || testProgram.getAddressFactory().isValidAddress(addr);
@ -448,7 +453,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Returns the row within the program location. * Returns the row within the program location.
* *
* @return the row within the program location. * @return the row within the program location.
*/ */
public int getRow() { public int getRow() {
@ -456,9 +461,9 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
} }
/** /**
* Returns the character offset in the display item at the (row,col) * Returns the character offset in the display item at the (row,col).
* *
* @return the character offset in the display item at the (row,col) * @return the character offset in the display item at the (row,col).
*/ */
public int getCharOffset() { public int getCharOffset() {
return charOffset; return charOffset;
@ -467,6 +472,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
/** /**
* Returns the column index of the display piece represented by this location. For most * Returns the column index of the display piece represented by this location. For most
* locations, there is only one display item per row, in which case this value will be 0. * locations, there is only one display item per row, in which case this value will be 0.
* @return the column.
*/ */
public int getColumn() { public int getColumn() {
return col; return col;

View file

@ -0,0 +1,34 @@
/* ###
* 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 utility.function;
/**
* A generic functional interface that is more semantically sound than {@link Runnable}. Use
* anywhere you wish to have a generic callback function and you need to throw an exception.
*
* @param <T> the return type of your choice
* @param <E> the exception of your choice
*/
@FunctionalInterface
public interface ExceptionalSupplier<T, E extends Exception> {
/**
* The supplier method
* @return the item to return
* @throws E the declared exception
*/
public T get() throws E;
}