mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
GP-1556 - Added support for searching for structure fields by offset
This commit is contained in:
parent
883f5a687a
commit
812ea4fe1e
45 changed files with 1461 additions and 840 deletions
|
@ -58,7 +58,7 @@
|
|||
references to the address 0040767d.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG alt="" src="../../shared/tip.png">You can also <A href="#Data_Types">show
|
||||
<P><IMG alt="" src="images/tip.png">You can also <A href="#Data_Types">show
|
||||
references to data types</A> from the Data Type Manager. In this case, all locations where
|
||||
the selected data type is applied will be highlighted.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
@ -73,11 +73,11 @@
|
|||
|
||||
<LI>Select whichever of the following is available from the popup menu:
|
||||
<UL>
|
||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Show References to</B>
|
||||
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Show References to</B>
|
||||
from the popup menu.</LI>
|
||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Find References to</B>
|
||||
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Find References to</B>
|
||||
from the popup menu.</LI>
|
||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Find Uses of</B>
|
||||
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Find Uses of</B>
|
||||
from the popup menu.</LI>
|
||||
</UL>
|
||||
</OL>
|
||||
|
@ -134,12 +134,12 @@
|
|||
</P>
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG alt="" src="../../shared/tip.png">
|
||||
<IMG alt="" src="images/tip.png">
|
||||
To instead make a Program Selection Highlight, use the select button mentioned above.
|
||||
Then, click from the menu bar <B>Select
|
||||
<img src="../../shared/arrow.gif" alt="->" border="0">
|
||||
<img src="images/arrow.gif" alt="->" border="0">
|
||||
Program Highlight
|
||||
<img src="../../shared/arrow.gif" alt="->" border="0">
|
||||
<img src="images/arrow.gif" alt="->" border="0">
|
||||
Entire Selection
|
||||
</B>
|
||||
</P>
|
||||
|
@ -167,7 +167,7 @@
|
|||
the decompiled function will be shown.
|
||||
</P>
|
||||
|
||||
<P><A name="Make_Selection"></A><IMG alt="" src="../../shared/tip.png"> You can make a
|
||||
<P><A name="Make_Selection"></A><IMG alt="" src="images/tip.png"> You can make a
|
||||
selection in the Code Browser from the entries in the table:</P>
|
||||
|
||||
<OL>
|
||||
|
@ -198,16 +198,26 @@
|
|||
the action will display all places that function is applied.</P>
|
||||
|
||||
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG border="0" src="images/tip.png" alt=""> When searching for references to a field of
|
||||
a composite data type via the popup menu in the Data Type Manager,
|
||||
you can search by name or by offset within the parent data type. This is useful when
|
||||
the field you seek does not have a name.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG border="0" src="../../shared/note.png" alt="">
|
||||
<IMG border="0" src="images/note.png" alt="">
|
||||
<A name="Data_Type_Discovery"></A>
|
||||
By default, finding uses of
|
||||
data types will search not only for applied data types, but also will perform
|
||||
dynamic discovery of data types using the <CODE>Data Type Reference Finder</CODE>
|
||||
service. This causes the search to be slower, but also reports many more type
|
||||
uses. To disable the dynamic searching, use the
|
||||
<B>Search</B><img src="../../shared/arrow.gif" alt="->" border="0">
|
||||
<B>Search</B><img src="images/arrow.gif" alt="->" border="0">
|
||||
<B>Dynamic Data Type Discovery</B>
|
||||
<A href="help/topics/Tool/ToolOptions_Dialog.htm">tool option</A>.
|
||||
</P>
|
||||
|
@ -221,7 +231,7 @@
|
|||
<OL start="1" type="1">
|
||||
<LI>Right-mouse anywhere on the code unit*</LI>
|
||||
|
||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Show References to
|
||||
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Show References to
|
||||
Address </B> from the popup menu.</LI>
|
||||
</OL>
|
||||
|
||||
|
@ -240,7 +250,7 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG border="0" src="../../shared/note.png" alt=""> This action will show only direct
|
||||
<IMG border="0" src="images/note.png" alt=""> This action will show only direct
|
||||
references to the current code unit. No other special reference finding will take place.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
@ -250,7 +260,7 @@
|
|||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG border="0" src="../../shared/tip.png" alt=""> <I>see <A href=
|
||||
<IMG border="0" src="images/tip.png" alt=""> <I>see <A href=
|
||||
"help/topics/DockingWindows/Docking_Windows.htm#Renaming_Windows">Docking Windows - Renaming
|
||||
Windows</A></I>
|
||||
</P>
|
||||
|
|
|
@ -25,9 +25,10 @@ import docking.DockingUtils;
|
|||
import docking.action.*;
|
||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.*;
|
||||
|
||||
public abstract class AbstractFindReferencesDataTypeAction extends DockingAction {
|
||||
|
@ -89,7 +90,40 @@ public abstract class AbstractFindReferencesDataTypeAction extends DockingAction
|
|||
DataType dataType = getDataType(context);
|
||||
DataType baseDataType = ReferenceUtils.getBaseDataType(dataType);
|
||||
String field = getDataTypeField(baseDataType);
|
||||
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(baseDataType, field));
|
||||
Swing.runLater(() -> doFindDataTypeUsage(service, baseDataType, field));
|
||||
}
|
||||
|
||||
private void doFindDataTypeUsage(FindAppliedDataTypesService service, DataType dt,
|
||||
String field) {
|
||||
|
||||
if (field == null) {
|
||||
// no field specified; search for all uses of the given type
|
||||
service.findAndDisplayAppliedDataTypeAddresses(dt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dt instanceof Structure) {
|
||||
Integer offset = getOffsetForDeafaultFieldName((Structure) dt, field);
|
||||
if (offset != null) {
|
||||
// The user has picked a field by it's default name. In this case we need to
|
||||
// search by offset to ensure we find code that does not use the default name.
|
||||
FieldMatcher fieldMatcher = new FieldMatcher(dt, offset);
|
||||
service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
service.findAndDisplayAppliedDataTypeAddresses(dt, field);
|
||||
}
|
||||
|
||||
private Integer getOffsetForDeafaultFieldName(Structure structure, String fieldName) {
|
||||
DataTypeComponent[] components = structure.getComponents();
|
||||
for (DataTypeComponent dtc : components) {
|
||||
String defaultName = dtc.getDefaultFieldName();
|
||||
if (fieldName.equals(defaultName)) {
|
||||
return dtc.getOffset();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,8 +53,8 @@ public class FindDataTypesBySizeAction extends DockingAction {
|
|||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
NumberRangeInputDialog inputDialog =
|
||||
new NumberRangeInputDialog(getName(), "Size(s)");
|
||||
NumberRangeInputDialog inputDialog = new NumberRangeInputDialog(getName(), "Size(s)");
|
||||
inputDialog.setHelpLocation(getHelpLocation());
|
||||
if (!inputDialog.show()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
|||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
|
@ -98,15 +99,27 @@ public class FindReferencesToFieldAction extends DockingAction {
|
|||
choices = ((Enum) dt).getNames();
|
||||
}
|
||||
|
||||
String userChoice = OptionDialog.showInputChoiceDialog(null, "Choose Field",
|
||||
"Find uses of '" + dt.getName() + "' field", choices, null,
|
||||
OptionDialog.QUESTION_MESSAGE);
|
||||
String message = "Find uses of '" + dt.getName() + "' field by name or offset";
|
||||
String userChoice = OptionDialog.showEditableInputChoiceDialog(null, "Choose Field",
|
||||
message, choices, null, OptionDialog.QUESTION_MESSAGE);
|
||||
if (userChoice == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Swing.runLater(
|
||||
() -> service.findAndDisplayAppliedDataTypeAddresses(dt, userChoice));
|
||||
FieldMatcher fieldMatcher;
|
||||
Long longChoice = parseInt(userChoice);
|
||||
if (longChoice != null) {
|
||||
fieldMatcher = new FieldMatcher(dt, longChoice.intValue());
|
||||
}
|
||||
else {
|
||||
fieldMatcher = new FieldMatcher(dt, userChoice);
|
||||
}
|
||||
|
||||
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher));
|
||||
}
|
||||
|
||||
private Long parseInt(String s) {
|
||||
return NumericUtilities.parseNumber(s, null);
|
||||
}
|
||||
|
||||
private String[] getCompisiteFieldNames(Composite composite) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.navigation;
|
||||
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
/**
|
||||
|
@ -38,4 +39,18 @@ public interface FindAppliedDataTypesService {
|
|||
* @param fieldName the sub-field for which to search
|
||||
*/
|
||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName);
|
||||
|
||||
/**
|
||||
* Tells this service to find all places where the given datatype is applied <b>and</b> will
|
||||
* display the results of the search.
|
||||
* <p>
|
||||
* The supplied field matcher will be used to restrict matches to the given field. The matcher
|
||||
* may be 'empty', supplying only the data type for which to search. In this case, all uses
|
||||
* of the type will be matched, regardless of field.
|
||||
*
|
||||
* @param dataType The datatype which to base the search upon.
|
||||
* @param fieldMatcher the field matcher.
|
||||
*/
|
||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType,
|
||||
FieldMatcher fieldMatcher);
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ import ghidra.util.exception.CancelledException;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A location descriptor that should be extended by location descriptor implementations that
|
||||
* are based upon data types.
|
||||
* A location descriptor that should be extended by location descriptor implementations that are
|
||||
* based upon data types.
|
||||
*/
|
||||
abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
||||
|
||||
|
@ -70,18 +70,28 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
|||
findDataTypeReferences(accumulator, monitor);
|
||||
}
|
||||
|
||||
/** The original data type that this location descriptor describes */
|
||||
/**
|
||||
* The original data type that this location descriptor describes
|
||||
* @return the type
|
||||
*/
|
||||
protected abstract DataType getSourceDataType();
|
||||
|
||||
/** Generates the label for the results window */
|
||||
/**
|
||||
* Generates the label for the results window
|
||||
* @return the label
|
||||
*/
|
||||
protected abstract String generateLabel();
|
||||
|
||||
/** Returns the name of the data type, for example, 'Foo' or 'Foo.bar.baz' */
|
||||
/**
|
||||
* Returns the name of the data type, for example, 'Foo' or 'Foo.bar.baz'
|
||||
* @return the name
|
||||
*/
|
||||
protected abstract String getDataTypeName();
|
||||
|
||||
/**
|
||||
* The base data type that this location descriptor describes (this may be the same as the
|
||||
* original data type.
|
||||
* @return the type.
|
||||
*/
|
||||
protected DataType getBaseDataType() {
|
||||
return getSourceDataType(); // by default these two values are the same
|
||||
|
@ -91,7 +101,7 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
|||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
DataType currentDataType = getDataType();
|
||||
ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, null, program,
|
||||
ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, program,
|
||||
useDynamicSearching, monitor);
|
||||
}
|
||||
|
||||
|
@ -177,8 +187,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
|||
List<Variable> allVariables = ReferenceUtils.getVariables(function, true);
|
||||
for (Variable variable : allVariables) {
|
||||
DataType variableDataType = variable.getDataType();
|
||||
if (ReferenceUtils.getBaseDataType(variableDataType).isEquivalent(
|
||||
currentDataType)) {
|
||||
if (ReferenceUtils.getBaseDataType(variableDataType)
|
||||
.isEquivalent(currentDataType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.awt.Color;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.fieldpanel.support.Highlight;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.app.util.viewer.field.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Data;
|
||||
|
@ -34,34 +35,39 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeLocationDescriptor {
|
||||
|
||||
private String typeAndFieldName;
|
||||
private String fieldName;
|
||||
// this is either "Foo.bar" or "Foo offset 1"
|
||||
private String typeDisplayString;
|
||||
private FieldMatcher fieldMatcher;
|
||||
|
||||
public GenericCompositeDataTypeLocationDescriptor(
|
||||
GenericCompositeDataTypeProgramLocation location, Program program) {
|
||||
super(location, program, location.getDataType());
|
||||
|
||||
this.fieldName = location.getFieldName();
|
||||
this.typeAndFieldName = getDataTypeName() + '.' + fieldName;
|
||||
this.fieldMatcher = location.getFieldMatcher();
|
||||
this.typeDisplayString = fieldMatcher.getDisplayText();
|
||||
label = generateLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
ReferenceUtils.findDataTypeReferences(accumulator, getDataType(), fieldName, program,
|
||||
ReferenceUtils.findDataTypeFieldReferences(accumulator, fieldMatcher, program,
|
||||
useDynamicSearching, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return super.getTypeName() + "." + fieldName;
|
||||
// not sure why this code was doing this and not using our variable; oversight?
|
||||
// return super.getTypeName() + "." + fieldName;
|
||||
return typeDisplayString;
|
||||
}
|
||||
|
||||
// implemented to ignore the location being provided, since this is a 'dummy' type class
|
||||
@Override
|
||||
protected String generateLabel() {
|
||||
return "\"" + originalDataType.getName() + "." + fieldName + "\" (DataType)";
|
||||
// changed this; keep old code around in case an edge case appears
|
||||
// return "\"" + originalDataType.getName() + "." + fieldName + "\" (DataType)";
|
||||
return "\"" + typeDisplayString + "\" (DataType)";
|
||||
}
|
||||
|
||||
// Overridden to perform a simple check against data types, since the program locations are
|
||||
|
@ -79,7 +85,7 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
|
|||
GenericCompositeDataTypeLocationDescriptor otherDescriptor =
|
||||
(GenericCompositeDataTypeLocationDescriptor) obj;
|
||||
return getDataType().equals(otherDescriptor.getDataType()) &&
|
||||
fieldName.equals(otherDescriptor.fieldName);
|
||||
fieldMatcher.equals(otherDescriptor.fieldMatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -113,14 +119,15 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
|
|||
else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
||||
|
||||
// Not sure how to get the correct part of the text. This is a hack for now.
|
||||
int offset = StringUtils.indexOfIgnoreCase(text, typeAndFieldName, 0);
|
||||
int offset = StringUtils.indexOfIgnoreCase(text, typeDisplayString, 0);
|
||||
if (offset != -1) {
|
||||
return new Highlight[] {
|
||||
new Highlight(offset, offset + typeAndFieldName.length() - 1, highlightColor) };
|
||||
return new Highlight[] { new Highlight(offset,
|
||||
offset + typeDisplayString.length() - 1, highlightColor) };
|
||||
}
|
||||
}
|
||||
else if (FieldNameFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
||||
|
||||
String fieldName = fieldMatcher.getFieldName();
|
||||
if (text.equalsIgnoreCase(fieldName)) {
|
||||
return new Highlight[] { new Highlight(0, text.length(), highlightColor) };
|
||||
}
|
||||
|
|
|
@ -17,9 +17,11 @@ package ghidra.app.plugin.core.navigation.locationreferences;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* A class to signal that the ProgramLocation is used for data types and is not really
|
||||
|
@ -30,15 +32,24 @@ import ghidra.program.model.listing.Program;
|
|||
*/
|
||||
public class GenericCompositeDataTypeProgramLocation extends GenericDataTypeProgramLocation {
|
||||
|
||||
private String fieldName;
|
||||
private FieldMatcher fieldMatcher;
|
||||
|
||||
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, String fieldName) {
|
||||
this(program, dataType, new FieldMatcher(dataType, fieldName));
|
||||
}
|
||||
|
||||
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType,
|
||||
FieldMatcher fieldMatcher) {
|
||||
super(program, dataType);
|
||||
this.fieldName = Objects.requireNonNull(fieldName);
|
||||
this.fieldMatcher = Objects.requireNonNull(fieldMatcher);
|
||||
|
||||
// sanity check
|
||||
if (!Objects.equals(dataType, fieldMatcher.getDataType())) {
|
||||
throw new AssertException("Data type does not match the FieldMatcher type");
|
||||
}
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
public FieldMatcher getFieldMatcher() {
|
||||
return fieldMatcher;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -384,7 +384,7 @@ public abstract class LocationDescriptor {
|
|||
* Sets a listener on this descriptor that will be notified when the references contained
|
||||
* in this descriptor may no longer be accurate. For example, the listener will be called
|
||||
* when an undo or redo is performed in Ghidra.
|
||||
* @param modelChangeListener The listener to add.
|
||||
* @param modelFreshnessListener The listener to add.
|
||||
*/
|
||||
void setModelFreshnessListener(ChangeListener modelFreshnessListener) {
|
||||
this.modelFreshnessListener = modelFreshnessListener;
|
||||
|
|
|
@ -169,12 +169,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
|||
if (!context.equals(other.context)) {
|
||||
return false;
|
||||
}
|
||||
if (locationOfUseAddress == null) {
|
||||
if (other.locationOfUseAddress != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!locationOfUseAddress.equals(other.locationOfUseAddress)) {
|
||||
if (!Objects.equals(locationOfUseAddress, other.locationOfUseAddress)) {
|
||||
return false;
|
||||
}
|
||||
return refType.equals(other.refType);
|
||||
|
@ -194,7 +189,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
|||
"\taddress: " + locationOfUseAddress + ",\n" +
|
||||
((refType.equals("")) ? "" : "\trefType: " + refType + ",\n") +
|
||||
"\tisOffcut: " + isOffcutReference + ",\n" +
|
||||
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + ",") +
|
||||
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + "\n") +
|
||||
"}";
|
||||
//@formatter:off
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ public class LocationReferenceContext {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Json.toString(this);
|
||||
return getPlainText();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,8 +26,7 @@ import ghidra.app.events.ProgramClosedPluginEvent;
|
|||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
import ghidra.app.util.query.TableService;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
|
@ -211,8 +210,7 @@ public class LocationReferencesPlugin extends Plugin
|
|||
}
|
||||
|
||||
private void disposeProviderList() {
|
||||
for (int i = 0; i < providerList.size(); i++) {
|
||||
LocationReferencesProvider provider = providerList.get(i);
|
||||
for (LocationReferencesProvider provider : providerList) {
|
||||
provider.dispose();
|
||||
}
|
||||
providerList.clear();
|
||||
|
@ -261,8 +259,8 @@ public class LocationReferencesPlugin extends Plugin
|
|||
}
|
||||
|
||||
protected void programClosed(Program program) {
|
||||
for (Iterator<LocationReferencesProvider> iterator =
|
||||
providerList.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<LocationReferencesProvider> iterator = providerList.iterator(); iterator
|
||||
.hasNext();) {
|
||||
LocationReferencesProvider provider = iterator.next();
|
||||
if (provider.getProgram() == program) {
|
||||
provider.dispose();
|
||||
|
@ -293,11 +291,19 @@ public class LocationReferencesPlugin extends Plugin
|
|||
|
||||
@Override
|
||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType) {
|
||||
findAndDisplayAppliedDataTypeAddresses(dataType, null);
|
||||
findAndDisplayAppliedDataTypeAddresses(dataType, (FieldMatcher) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName) {
|
||||
FieldMatcher matcher = new FieldMatcher(dataType, fieldName);
|
||||
findAndDisplayAppliedDataTypeAddresses(dataType, matcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType,
|
||||
FieldMatcher fieldMatcher) {
|
||||
|
||||
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||
GoToService goToService = tool.getService(GoToService.class);
|
||||
Program program = programManagerService.getCurrentProgram();
|
||||
|
@ -308,9 +314,9 @@ public class LocationReferencesPlugin extends Plugin
|
|||
}
|
||||
|
||||
ProgramLocation genericLocation;
|
||||
if (fieldName != null) {
|
||||
if (fieldMatcher != null) {
|
||||
genericLocation =
|
||||
new GenericCompositeDataTypeProgramLocation(program, dataType, fieldName);
|
||||
new GenericCompositeDataTypeProgramLocation(program, dataType, fieldMatcher);
|
||||
}
|
||||
else {
|
||||
genericLocation = new GenericDataTypeProgramLocation(program, dataType);
|
||||
|
|
|
@ -20,11 +20,11 @@ import java.util.Stack;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.DataTypeReferenceFinder;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Array;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.*;
|
||||
|
@ -162,20 +162,24 @@ public final class ReferenceUtils {
|
|||
|
||||
/**
|
||||
* Returns all references (locations) that use the given datatype.
|
||||
* <br>
|
||||
* <b>Note: </b> This method call may take a long time, as it must search all of the
|
||||
* data within the program and may also perform long running tasks, like decompiling every
|
||||
* function in the program.
|
||||
* <br>
|
||||
* @param accumulator the results storage
|
||||
* <p>
|
||||
* <b>Note: </b> This method call may take a long time, as it must search all of the data
|
||||
* within the program and may also perform long running tasks, like decompiling every function
|
||||
* in the program.
|
||||
*
|
||||
* @param accumulator the results storage.
|
||||
* @param dataType The datatype for which to find references.
|
||||
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be
|
||||
* a {@link Composite} to search for a field
|
||||
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be a
|
||||
* {@link Composite} to search for a field.
|
||||
* @param program The program from within which to find references.
|
||||
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
||||
* dummy monitor will be used.
|
||||
* @throws CancelledException if the monitor is cancelled
|
||||
* dummy monitor will be used.
|
||||
* @throws CancelledException if the monitor is cancelled.
|
||||
* @deprecated use {@link #findDataTypeFieldReferences(Accumulator, FieldMatcher, Program,
|
||||
* boolean, TaskMonitor)}.
|
||||
* @Deprecated(since = "10.2")
|
||||
*/
|
||||
@Deprecated
|
||||
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
|
||||
DataType dataType, String fieldName, Program program, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
@ -184,24 +188,26 @@ public final class ReferenceUtils {
|
|||
|
||||
/**
|
||||
* Returns all references (locations) that use the given datatype.
|
||||
* <br>
|
||||
* <b>Note: </b> This method call may take a long time, as it must search all of the
|
||||
* data within the program and may also perform long running tasks, like decompiling every
|
||||
* function in the program.
|
||||
* <br>
|
||||
* @param accumulator the results storage
|
||||
* <p>
|
||||
* <b>Note: </b> This method call may take a long time, as it must search all of the data
|
||||
* within the program and may also perform long running tasks, like decompiling every function
|
||||
* in the program.
|
||||
*
|
||||
* @param accumulator the results storage.
|
||||
* @param dataType The datatype for which to find references.
|
||||
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be
|
||||
* a {@link Composite} to search for a field
|
||||
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be a
|
||||
* {@link Composite} to search for a field.
|
||||
* @param program The program from within which to find references.
|
||||
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service
|
||||
* will be used to search for data types that are not applied in memory.
|
||||
* Using the service will be slower, but will recover type usage that could
|
||||
* not be found by examining the Listing.
|
||||
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
|
||||
* search for data types that are not applied in memory. Using the service will be slower, but
|
||||
* will recover type usage that could not be found by examining the Listing.
|
||||
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
||||
* dummy monitor will be used.
|
||||
* @throws CancelledException if the monitor is cancelled
|
||||
* dummy monitor will be used.
|
||||
* @throws CancelledException if the monitor is cancelled.
|
||||
* @deprecated use {@link #findDataTypeFieldReferences(Accumulator, FieldMatcher, Program,
|
||||
* boolean, TaskMonitor)}.
|
||||
*/
|
||||
@Deprecated(since = "10.2")
|
||||
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
|
||||
DataType dataType, String fieldName, Program program, boolean discoverTypes,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
@ -209,12 +215,72 @@ public final class ReferenceUtils {
|
|||
// Note: none of the params can be null, but this one gets used much later, so check now
|
||||
Objects.requireNonNull(dataType, () -> "Data Type cannot be null");
|
||||
|
||||
if (monitor == null) {
|
||||
monitor = TaskMonitor.DUMMY;
|
||||
}
|
||||
FieldMatcher fieldMatcher = new FieldMatcher(dataType, fieldName);
|
||||
doFindDataTypeReferences(accumulator, fieldMatcher, program, discoverTypes, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all references (locations) that use the given datatype.
|
||||
* <p>
|
||||
* <b>Note: </b> This method call may take a long time, as it must search all of the data
|
||||
* within the program and may also perform long running tasks, like decompiling every function
|
||||
* in the program.
|
||||
*
|
||||
* @param accumulator the results storage.
|
||||
* @param dataType The datatype for which to find references.
|
||||
* @param program The program from within which to find references.
|
||||
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
|
||||
* search for data types that are not applied in memory. Using the service will be slower, but
|
||||
* will recover type usage that could not be found by examining the Listing.
|
||||
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
||||
* dummy monitor will be used.
|
||||
* @throws CancelledException if the monitor is cancelled.
|
||||
*/
|
||||
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
|
||||
DataType dataType, Program program, boolean discoverTypes, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
doFindDataTypeReferences(accumulator, new FieldMatcher(dataType), program, discoverTypes,
|
||||
monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all references (locations) that use the given datatype.
|
||||
* <p>
|
||||
* <b>Note: </b> This method call may take a long time, as it must search all of the data
|
||||
* within the program and may also perform long running tasks, like decompiling every function
|
||||
* in the program.
|
||||
* <p>
|
||||
* The supplied field matcher will be used to restrict matches to the given field. The matcher
|
||||
* may be 'empty', supplying only the data type for which to search. In this case, all uses
|
||||
* of the type will be matched, regardless of field.
|
||||
*
|
||||
* @param accumulator the results storage.
|
||||
* @param fieldMatcher the field matcher.
|
||||
* @param program The program from within which to find references.
|
||||
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
|
||||
* search for data types that are not applied in memory. Using the service will be slower, but
|
||||
* will recover type usage that could not be found by examining the Listing.
|
||||
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
||||
* dummy monitor will be used.
|
||||
* @throws CancelledException if the monitor is cancelled.
|
||||
*/
|
||||
public static void findDataTypeFieldReferences(Accumulator<LocationReference> accumulator,
|
||||
FieldMatcher fieldMatcher, Program program, boolean discoverTypes, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
Objects.requireNonNull(fieldMatcher, "FieldMatcher cannot be null");
|
||||
doFindDataTypeReferences(accumulator, fieldMatcher, program, discoverTypes, monitor);
|
||||
}
|
||||
|
||||
private static void doFindDataTypeReferences(Accumulator<LocationReference> accumulator,
|
||||
FieldMatcher fieldMatcher, Program program, boolean discoverTypes, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
monitor = TaskMonitor.dummyIfNull(monitor);
|
||||
|
||||
DataType dataType = fieldMatcher.getDataType();
|
||||
Listing listing = program.getListing();
|
||||
|
||||
long dataCount = listing.getNumDefinedData();
|
||||
int functionCount = program.getFunctionManager().getFunctionCount();
|
||||
int totalCount = (int) dataCount + functionCount;
|
||||
|
@ -226,9 +292,10 @@ public final class ReferenceUtils {
|
|||
// the code.
|
||||
Accumulator<LocationReference> asSet = asSet(accumulator);
|
||||
|
||||
if (fieldName == null) {
|
||||
// It only makes sense to search here when we do not have a field
|
||||
|
||||
if (fieldMatcher.isIgnored()) {
|
||||
//
|
||||
// It only makes sense to search here when we do not have a field to match
|
||||
//
|
||||
boolean localsOnly = discoverTypes;
|
||||
FunctionIterator iterator = listing.getFunctions(false);
|
||||
findDataTypeMatchesInFunctionHeaders(asSet, iterator, dataType, localsOnly, monitor);
|
||||
|
@ -244,10 +311,11 @@ public final class ReferenceUtils {
|
|||
boolean matches = dataTypesMatch(dataType, baseType);
|
||||
return matches;
|
||||
};
|
||||
findDataTypeMatchesInDefinedData(asSet, program, dataMatcher, fieldName, monitor);
|
||||
|
||||
findDataTypeMatchesInDefinedData(asSet, program, dataMatcher, fieldMatcher, monitor);
|
||||
|
||||
if (discoverTypes) {
|
||||
findDataTypeMatchesOutsideOfListing(asSet, program, dataType, fieldName, monitor);
|
||||
findDataTypeMatchesOutsideOfListing(asSet, program, dataType, fieldMatcher, monitor);
|
||||
}
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
@ -265,7 +333,7 @@ public final class ReferenceUtils {
|
|||
|
||||
private static void findDataTypeMatchesOutsideOfListing(
|
||||
Accumulator<LocationReference> accumulator, Program program, DataType dataType,
|
||||
String fieldName, TaskMonitor monitor) throws CancelledException {
|
||||
FieldMatcher fieldMatcher, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
List<DataTypeReferenceFinder> finders =
|
||||
ClassSearcher.getInstances(DataTypeReferenceFinder.class);
|
||||
|
@ -284,12 +352,7 @@ public final class ReferenceUtils {
|
|||
}
|
||||
|
||||
for (DataTypeReferenceFinder finder : finders) {
|
||||
if (fieldName == null) {
|
||||
finder.findReferences(program, dataType, callback, monitor);
|
||||
}
|
||||
else {
|
||||
finder.findReferences(program, dataType, fieldName, callback, monitor);
|
||||
}
|
||||
finder.findReferences(program, fieldMatcher, callback, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -617,15 +680,15 @@ public final class ReferenceUtils {
|
|||
* some digging to see what is buried at the given address.
|
||||
*/
|
||||
private static LocationDescriptor createDataMemberLocationDescriptor(
|
||||
OperandFieldLocation location, Address refAddress) {
|
||||
OperandFieldLocation location, Address address) {
|
||||
|
||||
// TODO we don't support data types on external addresses; this could change in the future
|
||||
if (refAddress.isExternalAddress()) {
|
||||
// Note: we don't support data types on external addresses; this could change in the future
|
||||
if (address.isExternalAddress()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Program program = location.getProgram();
|
||||
Data outermostData = getDataContainingAddress(program, refAddress);
|
||||
Data outermostData = getDataContainingAddress(program, address);
|
||||
if (outermostData == null) {
|
||||
// no data
|
||||
return null;
|
||||
|
@ -634,12 +697,12 @@ public final class ReferenceUtils {
|
|||
String fieldPath = getFieldPath(location);
|
||||
if (!fieldPath.contains(".")) {
|
||||
|
||||
// no field reference, so don't create a structure member reference, but just
|
||||
// a generic data type reference
|
||||
// no field reference, so don't create a structure member reference, but just a generic
|
||||
// data type reference
|
||||
DataType type = outermostData.getDataType();
|
||||
if (type == DataType.DEFAULT || Undefined.isUndefined(type)) {
|
||||
// nobody wants to search for undefined usage; too many (this is the case
|
||||
// where the user is not on an actual data type)
|
||||
// nobody wants to search for undefined usage; too many (this is the case where the
|
||||
// user is not on an actual data type)
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -650,15 +713,15 @@ public final class ReferenceUtils {
|
|||
|
||||
String fieldName = getFieldName(location);
|
||||
Address parentAddress = outermostData.getMinAddress();
|
||||
int componentAddress = (int) refAddress.subtract(parentAddress);
|
||||
int componentAddress = (int) address.subtract(parentAddress);
|
||||
Data subData = outermostData.getComponentContaining(componentAddress);
|
||||
if (subData != null) {
|
||||
|
||||
int[] componentPath = subData.getComponentPath();
|
||||
FieldNameFieldLocation fieldLocation =
|
||||
new FieldNameFieldLocation(program, refAddress, componentPath, fieldName, 0);
|
||||
new FieldNameFieldLocation(program, address, componentPath, fieldName, 0);
|
||||
LocationDescriptor descriptor =
|
||||
createSubDataMemberLocationDescriptor(program, refAddress, fieldLocation, subData);
|
||||
createSubDataMemberLocationDescriptor(program, address, fieldLocation, subData);
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
|
@ -667,14 +730,37 @@ public final class ReferenceUtils {
|
|||
//
|
||||
DataType dt = outermostData.getDataType();
|
||||
if (dt instanceof Union) {
|
||||
AddressFieldLocation addressLocation = new AddressFieldLocation(program, refAddress,
|
||||
new int[] { 0 }, refAddress.toString(), 0);
|
||||
AddressFieldLocation addressLocation =
|
||||
new AddressFieldLocation(program, address, new int[] { 0 }, address.toString(), 0);
|
||||
return new UnionLocationDescriptor(addressLocation, program);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static GenericDataTypeLocationDescriptor createGenericDataTypeLocationDescriptor(
|
||||
OperandFieldLocation location) {
|
||||
|
||||
Data data = getDataAt(location);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DataType dt = data.getDataType();
|
||||
if (dt instanceof Enum) {
|
||||
|
||||
String enumText = location.getOperandRepresentation();
|
||||
GenericCompositeDataTypeProgramLocation genericLocation =
|
||||
new GenericCompositeDataTypeProgramLocation(location.getProgram(), dt, enumText);
|
||||
return new GenericCompositeDataTypeLocationDescriptor(genericLocation,
|
||||
location.getProgram());
|
||||
}
|
||||
|
||||
GenericDataTypeProgramLocation genericLocation =
|
||||
new GenericDataTypeProgramLocation(location.getProgram(), dt);
|
||||
return new GenericDataTypeLocationDescriptor(genericLocation, location.getProgram(), dt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a location descriptor using the String display markup and type information
|
||||
* found inside of the VariableOffset object.
|
||||
|
@ -813,9 +899,15 @@ public final class ReferenceUtils {
|
|||
private static LocationDescriptor createOperandLocationDescriptor(
|
||||
OperandFieldLocation location) {
|
||||
|
||||
// this is the 'to' address
|
||||
Address refAddress = getReferenceAddress(location);
|
||||
if (refAddress == null) {
|
||||
return null; // no reference and no variable-offset
|
||||
//
|
||||
// No reference address for this location. Try to create a generic data descriptor.
|
||||
//
|
||||
GenericDataTypeLocationDescriptor descriptor =
|
||||
createGenericDataTypeLocationDescriptor(location);
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
Address operandAddress = location.getAddress();
|
||||
|
@ -824,20 +916,18 @@ public final class ReferenceUtils {
|
|||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
Reference reference =
|
||||
referenceManager.getReference(operandAddress, refAddress, operandIndex);
|
||||
|
||||
if (reference == null) {
|
||||
|
||||
// Prefer using the reference, for consistency. Without that, the
|
||||
// VariableOffset object contains markup and type information we can use.
|
||||
// Having a VariableOffset without a reference occurs when a
|
||||
// register variable reference is inferred during instruction operand formatting.
|
||||
// Prefer using the reference, for consistency. Without that, the VariableOffset
|
||||
// object contains markup and type information we can use. Having a VariableOffset
|
||||
// without a reference occurs when a register variable reference is inferred during
|
||||
// instruction operand formatting.
|
||||
VariableOffset variableOffset = location.getVariableOffset();
|
||||
return createGenericDataTypeLocationDescriptor(program, variableOffset);
|
||||
}
|
||||
|
||||
// note: not sure why we are ignoring external references. It seems like that is
|
||||
// a thing you may want to find refs to. If you figure it out, update this
|
||||
// comment.
|
||||
// note: not sure why we are ignoring external references. It seems like that is a thing
|
||||
// you may want to find refs to. If you figure it out, update this comment.
|
||||
// if (reference.isExternalReference()) {
|
||||
// return null;
|
||||
// }
|
||||
|
@ -934,13 +1024,13 @@ public final class ReferenceUtils {
|
|||
* @param accumulator the results accumulator
|
||||
* @param program the program
|
||||
* @param dataMatcher the predicate that determines a successful match
|
||||
* @param fieldName the optional field name for which to search
|
||||
* @param fieldMatcher the field matcher; will be ignored if it contains null values
|
||||
* @param monitor the task monitor used to track progress and cancel the work
|
||||
* @throws CancelledException if the operation was cancelled
|
||||
*/
|
||||
public static void findDataTypeMatchesInDefinedData(Accumulator<LocationReference> accumulator,
|
||||
Program program, Predicate<Data> dataMatcher, String fieldName, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
Program program, Predicate<Data> dataMatcher, FieldMatcher fieldMatcher,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
Listing listing = program.getListing();
|
||||
DataIterator dataIter = listing.getDefinedData(true);
|
||||
|
@ -948,39 +1038,43 @@ public final class ReferenceUtils {
|
|||
monitor.checkCanceled();
|
||||
|
||||
Data data = dataIter.next();
|
||||
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, data, fieldName,
|
||||
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, data, fieldMatcher,
|
||||
dataMatcher, monitor);
|
||||
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static LocationReference createReferenceFromDefinedData(Data data, String fieldName) {
|
||||
private static LocationReference createReferenceFromDefinedData(Data data,
|
||||
FieldMatcher fieldMatcher) {
|
||||
Address dataAddress = data.getMinAddress();
|
||||
if (fieldName == null) {
|
||||
// no field--include the hit
|
||||
if (fieldMatcher.isIgnored()) {
|
||||
// no field to match; include the hit
|
||||
return new LocationReference(dataAddress, data.getPathName());
|
||||
}
|
||||
|
||||
DataTypeComponent component = getDataTypeComponent(data, fieldName);
|
||||
if (component == null) {
|
||||
// this implies the given data does not contain our field--do not include the hit
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: just check the current type; we may have to unroll it, looking for pointers
|
||||
// along the way if this is not sufficient
|
||||
DataType dt = data.getDataType();
|
||||
if (dt instanceof Pointer) {
|
||||
// For defined data, do not include pointer types when we have a field name. A
|
||||
// pointer to the base composite type is not a direct usage of the given field.
|
||||
// pointer to the base composite type is not a direct usage of the given field.find
|
||||
return null;
|
||||
}
|
||||
|
||||
DataType baseDt = getBaseDataType(data.getDataType());
|
||||
if (matchesEnumField(data, baseDt, fieldMatcher)) {
|
||||
return new LocationReference(dataAddress, fieldMatcher.getDisplayText());
|
||||
}
|
||||
|
||||
DataTypeComponent component = getDataTypeComponent(baseDt, fieldMatcher);
|
||||
if (component == null) {
|
||||
return null; // not in composite
|
||||
}
|
||||
|
||||
Address componentAddress;
|
||||
try {
|
||||
componentAddress = dataAddress.addNoWrap(component.getOffset());
|
||||
return new LocationReference(componentAddress, data.getPathName() + "." + fieldName);
|
||||
return new LocationReference(componentAddress,
|
||||
data.getPathName() + "." + fieldMatcher.getFieldName());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
// shouldn't happen
|
||||
|
@ -990,33 +1084,58 @@ public final class ReferenceUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static DataTypeComponent getDataTypeComponent(Data data, String fieldName) {
|
||||
DataType dt = getBaseDataType(data.getDataType());
|
||||
if (!(dt instanceof Composite)) {
|
||||
Msg.debug(ReferenceUtils.class,
|
||||
"Somehow searched for a field name on a Data Type that is not a Composite");
|
||||
return null;
|
||||
private static boolean matchesEnumField(Data data, DataType dt, FieldMatcher matcher) {
|
||||
if (!(dt instanceof Enum)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Composite c = (Composite) dt;
|
||||
DataTypeComponent[] components = c.getDefinedComponents();
|
||||
for (DataTypeComponent component : components) {
|
||||
if (SystemUtilities.isEqual(component.getFieldName(), fieldName)) {
|
||||
return component;
|
||||
Enum enumm = (Enum) dt;
|
||||
List<String> names = getEnumNames(data, enumm);
|
||||
for (String name : names) {
|
||||
long value = enumm.getValue(name);
|
||||
if (matcher.matches(name, (int) value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: sometimes this happens if the user searches on an array element field, which
|
||||
// exists only in the Listing markup
|
||||
private static List<String> getEnumNames(Data data, Enum enumm) {
|
||||
String enumEntryName = data.getDefaultValueRepresentation();
|
||||
List<String> enumNames = new ArrayList<>(List.of(enumm.getNames()));
|
||||
if (enumNames.contains(enumEntryName)) {
|
||||
return List.of(enumEntryName);
|
||||
}
|
||||
|
||||
if (!enumEntryName.contains(" | ")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// not a big fan of using this delimiter check, but will do so for now until there is
|
||||
// a better way, such as asking the enum to do this work for us
|
||||
String[] names = enumEntryName.split(" \\| ");
|
||||
return List.of(names);
|
||||
}
|
||||
|
||||
private static DataTypeComponent getDataTypeComponent(DataType dt, FieldMatcher matcher) {
|
||||
if (dt instanceof Composite) {
|
||||
Composite c = (Composite) dt;
|
||||
DataTypeComponent[] components = c.getDefinedComponents();
|
||||
for (DataTypeComponent component : components) {
|
||||
if (matcher.matches(component.getFieldName(), component.getOffset())) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void getMatchingDataTypesReferencesFromDataAndSubData(
|
||||
Accumulator<LocationReference> accumulator, Data data, String fieldName,
|
||||
Accumulator<LocationReference> accumulator, Data data, FieldMatcher fieldMatcher,
|
||||
Predicate<Data> dataMatcher, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
if (dataMatcher.test(data)) {
|
||||
getMatchingDataTypesReferencesFromData(accumulator, data, fieldName, monitor);
|
||||
getMatchingDataTypesReferencesFromData(accumulator, data, fieldMatcher, monitor);
|
||||
}
|
||||
|
||||
// We know that arrays are all the same element; we decided to just mark the beginning.
|
||||
|
@ -1031,16 +1150,16 @@ public final class ReferenceUtils {
|
|||
monitor.checkCanceled();
|
||||
|
||||
Data subData = data.getComponent(i);
|
||||
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, subData, fieldName,
|
||||
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, subData, fieldMatcher,
|
||||
dataMatcher, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
private static void getMatchingDataTypesReferencesFromData(
|
||||
Accumulator<LocationReference> accumulator, Data data, String fieldName,
|
||||
Accumulator<LocationReference> accumulator, Data data, FieldMatcher fieldMatcher,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
LocationReference ref = createReferenceFromDefinedData(data, fieldName);
|
||||
LocationReference ref = createReferenceFromDefinedData(data, fieldMatcher);
|
||||
if (ref == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -1058,12 +1177,13 @@ public final class ReferenceUtils {
|
|||
|
||||
Consumer<Reference> referenceConsumer = reference -> {
|
||||
Address toAddress = reference.getToAddress();
|
||||
if (fieldName == null) {
|
||||
if (fieldMatcher.isIgnored()) {
|
||||
// no field to match; use the data address
|
||||
accumulator.add(new LocationReference(reference, isOffcut(program, toAddress)));
|
||||
return;
|
||||
}
|
||||
|
||||
// only add the reference if it is directly to the field
|
||||
// have a field match; only add the reference if it is directly to the field
|
||||
if (toAddress.equals(dataAddress)) {
|
||||
accumulator.add(new LocationReference(reference, false));
|
||||
}
|
||||
|
@ -1113,7 +1233,6 @@ public final class ReferenceUtils {
|
|||
}
|
||||
|
||||
private static boolean dataTypesMatch(DataType searchType, DataType possibleType) {
|
||||
|
||||
if (isBuiltIn(searchType)) {
|
||||
Class<? extends DataType> clazz = searchType.getClass();
|
||||
return clazz.equals(possibleType.getClass());
|
||||
|
@ -1132,8 +1251,8 @@ public final class ReferenceUtils {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID.equals(
|
||||
sourceArchive.getSourceArchiveID())) {
|
||||
if (DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID
|
||||
.equals(sourceArchive.getSourceArchiveID())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1153,8 +1272,8 @@ public final class ReferenceUtils {
|
|||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
Reference[] variableRefsTo = referenceManager.getReferencesTo(variable);
|
||||
for (Reference ref : variableRefsTo) {
|
||||
accumulator.add(
|
||||
new LocationReference(ref, !ref.getToAddress().equals(variableAddress)));
|
||||
accumulator
|
||||
.add(new LocationReference(ref, !ref.getToAddress().equals(variableAddress)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,12 +40,10 @@ class UnionLocationDescriptor extends DataTypeLocationDescriptor {
|
|||
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
// Note: we pass null for the 'fieldName', as we have no way of disambiguating which
|
||||
// Note: we don't use 'fieldName' of the union, as we have no way of disambiguating which
|
||||
// field a reference will point to. So, grab all references.
|
||||
|
||||
String fieldName = null;
|
||||
ReferenceUtils.findDataTypeReferences(accumulator, union, fieldName, program,
|
||||
useDynamicSearching, monitor);
|
||||
ReferenceUtils.findDataTypeReferences(accumulator, union, program, useDynamicSearching,
|
||||
monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -46,8 +46,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
|
|||
* @throws CancelledException if the operation was cancelled
|
||||
*/
|
||||
public void findReferences(Program program, DataType dataType,
|
||||
Consumer<DataTypeReference> callback,
|
||||
TaskMonitor monitor) throws CancelledException;
|
||||
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
|
||||
|
||||
/**
|
||||
* Finds references in the current program to specific field of the given {@link Composite} type
|
||||
|
@ -58,11 +57,31 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
|
|||
*
|
||||
* @param program the program to search
|
||||
* @param dataType the type containing the field for which to search
|
||||
* @param fieldName the name of the composite's field for which to search
|
||||
* @param fieldName the name of the composite's field for which to search; may be null
|
||||
* @param callback the callback to be called when a reference is found
|
||||
* @param monitor the monitor that allows for progress and cancellation
|
||||
* @throws CancelledException if the operation was cancelled
|
||||
*/
|
||||
public void findReferences(Program program, DataType dataType, String fieldName,
|
||||
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
|
||||
|
||||
/**
|
||||
* Finds references in the current program to specific field of the given {@link Composite} type
|
||||
* in a manner appropriate with the given implementation.
|
||||
* <p>
|
||||
* The supplied field matcher will be used to restrict matches to the given field. The matcher
|
||||
* may be 'empty', supplying only the data type for which to search. In this case, all uses
|
||||
* of the type will be matched, regardless of field.
|
||||
* <p>
|
||||
* Note that this operation is multi-threaded and that results will be delivered as they
|
||||
* are found via the <code>callback</code>.
|
||||
*
|
||||
* @param program the program to search
|
||||
* @param fieldMatcher the field matcher to use for matching types
|
||||
* @param callback the callback to be called when a reference is found
|
||||
* @param monitor the monitor that allows for progress and cancellation
|
||||
* @throws CancelledException if the operation was cancelled
|
||||
*/
|
||||
public void findReferences(Program program, FieldMatcher fieldMatcher,
|
||||
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -79,8 +79,8 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
this.displayOptions = displayOptions;
|
||||
initDisplayOptions();
|
||||
|
||||
fieldOptions.getOptions(name).setOptionsHelpLocation(
|
||||
new HelpLocation("CodeBrowserPlugin", name));
|
||||
fieldOptions.getOptions(name)
|
||||
.setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", name));
|
||||
}
|
||||
|
||||
protected void initDisplayOptions() {
|
||||
|
@ -141,6 +141,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
* @param highlightProvider the HightLightProvider.
|
||||
* @param options the Options for display properties.
|
||||
* @param fieldOptions the Options for field specific properties.
|
||||
* @return the factory
|
||||
*/
|
||||
public abstract FieldFactory newInstance(FieldFormatModel formatModel,
|
||||
HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions);
|
||||
|
@ -185,6 +186,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Returns the Field name.
|
||||
* @return the name.
|
||||
*/
|
||||
public String getFieldName() {
|
||||
return name;
|
||||
|
@ -192,14 +194,15 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Returns the default field color.
|
||||
* @return the color.
|
||||
*/
|
||||
public Color getDefaultColor() {
|
||||
return Color.BLACK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the starting x position for the fields generated by this
|
||||
* factory.
|
||||
* Returns the starting x position for the fields generated by this factory.
|
||||
* @return the start x.
|
||||
*/
|
||||
public int getStartX() {
|
||||
return startX;
|
||||
|
@ -207,6 +210,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Sets the starting x position for the fields generated by this factory.
|
||||
* @param x the x position.
|
||||
*/
|
||||
public void setStartX(int x) {
|
||||
startX = x;
|
||||
|
@ -214,6 +218,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Returns the width of the fields generated by this factory.
|
||||
* @return the width.
|
||||
*/
|
||||
public int getWidth() {
|
||||
return width;
|
||||
|
@ -221,6 +226,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Sets the width of the fields generated by this factory.
|
||||
* @param w the width.
|
||||
*/
|
||||
public void setWidth(int w) {
|
||||
width = w;
|
||||
|
@ -228,6 +234,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Returns the FieldModel that this factory belongs to.
|
||||
* @return the model.
|
||||
*/
|
||||
public FieldFormatModel getFieldModel() {
|
||||
return model;
|
||||
|
@ -235,6 +242,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Returns true if this FieldFactory is currently enabled to generate Fields.
|
||||
* @return true if enabled.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
|
@ -261,12 +269,14 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
/**
|
||||
* Return a FieldLocation that corresponds to the given index, fieldNum, and ProgramLocation
|
||||
* IF and ONLY IF the given programLocation is the type generated by this class's
|
||||
* {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each FieldFactory
|
||||
* should generate and process a unique ProgramLocation class.
|
||||
* {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each
|
||||
* FieldFactory should generate and process a unique ProgramLocation class.
|
||||
*
|
||||
* @param bf the ListingField at the current cursor.
|
||||
* @param index the line index (corresponds to an address)
|
||||
* @param fieldNum the index of field within the layout to try and get a FieldLocation.
|
||||
* @param loc the ProgramLocation to be converted into a FieldLocation.
|
||||
* @return the location.
|
||||
*/
|
||||
public abstract FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
|
||||
ProgramLocation loc);
|
||||
|
@ -276,6 +286,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
* @param row the row within this field
|
||||
* @param col the col on the given row within this field.
|
||||
* @param bf the ListingField containing the cursor.
|
||||
* @return the location.
|
||||
*/
|
||||
public abstract ProgramLocation getProgramLocation(int row, int col, ListingField bf);
|
||||
|
||||
|
@ -313,6 +324,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Returns a description of the fields generated by this factory.
|
||||
* @return the text.
|
||||
*/
|
||||
public String getFieldText() {
|
||||
return name;
|
||||
|
@ -320,14 +332,12 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Returns the font metrics used by this field factory
|
||||
* @return the metrics.
|
||||
*/
|
||||
public FontMetrics getMetrics() {
|
||||
return getMetrics(style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the metrics.
|
||||
*/
|
||||
protected FontMetrics getMetrics(int fontStyle) {
|
||||
if (fontStyle == -1) {
|
||||
return defaultMetrics;
|
||||
|
@ -335,8 +345,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
|||
return fontMetrics[fontStyle];
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
// we know
|
||||
@SuppressWarnings("deprecation") // we know
|
||||
private void setMetrics(Font newFont) {
|
||||
defaultMetrics = Toolkit.getDefaultToolkit().getFontMetrics(newFont);
|
||||
for (int i = 0; i < fontMetrics.length; i++) {
|
||||
|
|
|
@ -57,7 +57,7 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
private final static String SPACE_AFTER_SEPARATOR_OPTION =
|
||||
GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Add Space After Separator";
|
||||
|
||||
public static enum UNDERLINE_CHOICE {
|
||||
public enum UNDERLINE_CHOICE {
|
||||
Hidden, All, None
|
||||
}
|
||||
|
||||
|
@ -183,12 +183,6 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FactoryField for the given object at index index.
|
||||
*
|
||||
* @param obj the object whose properties should be displayed.
|
||||
* @param varWidth the amount of variable width spacing for any fields before this one.
|
||||
*/
|
||||
ListingField getField(Object obj, ProxyObj<?> proxy, int varWidth) {
|
||||
if (!enabled) {
|
||||
return null;
|
||||
|
@ -222,10 +216,10 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
|
||||
ListingTextField btf = (ListingTextField) lf;
|
||||
FieldElement fieldElement = btf.getFieldElement(row, col);
|
||||
|
||||
if (!(fieldElement instanceof OperandFieldElement)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
OperandFieldElement element = (OperandFieldElement) fieldElement;
|
||||
int opIndex = element.getOperandIndex();
|
||||
int subOpIndex = element.getOperandSubIndex();
|
||||
|
@ -276,11 +270,10 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
else if (obj instanceof Data) {
|
||||
Data data = (Data) obj;
|
||||
Address refAddr = null;
|
||||
Reference primaryReference =
|
||||
data.getProgram()
|
||||
.getReferenceManager()
|
||||
.getPrimaryReferenceFrom(
|
||||
data.getMinAddress(), 0);
|
||||
Program program = data.getProgram();
|
||||
ReferenceManager referenceManager = program.getReferenceManager();
|
||||
Address minAddress = data.getMinAddress();
|
||||
Reference primaryReference = referenceManager.getPrimaryReferenceFrom(minAddress, 0);
|
||||
Object value = data.getValue();
|
||||
if (primaryReference != null) {
|
||||
refAddr = primaryReference.getToAddress();
|
||||
|
@ -291,20 +284,18 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
}
|
||||
}
|
||||
|
||||
Program program = data.getProgram();
|
||||
if (value instanceof Scalar) {
|
||||
Scalar scalar = (Scalar) value;
|
||||
Equate equate = program.getEquateTable()
|
||||
.getEquate(data.getMinAddress(), opIndex,
|
||||
scalar.getValue());
|
||||
EquateTable equateTable = program.getEquateTable();
|
||||
Equate equate = equateTable.getEquate(minAddress, opIndex, scalar.getValue());
|
||||
if (equate != null) {
|
||||
return new EquateOperandFieldLocation(program, data.getMinAddress(), refAddr,
|
||||
return new EquateOperandFieldLocation(program, minAddress, refAddr,
|
||||
equate.getDisplayName(), equate, opIndex, subOpIndex,
|
||||
translatedLocation.col());
|
||||
}
|
||||
}
|
||||
return new OperandFieldLocation(program, data.getMinAddress(), data.getComponentPath(),
|
||||
refAddr, codeUnitFormat.getDataValueRepresentationString(data), 0, col);
|
||||
return new OperandFieldLocation(program, minAddress, data.getComponentPath(), refAddr,
|
||||
codeUnitFormat.getDataValueRepresentationString(data), 0, col);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -477,9 +468,9 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
|
||||
private int addElements(Instruction inst, List<OperandFieldElement> elements, List<?> objList,
|
||||
int opIndex, int subOpIndex, boolean underline, int characterOffset) {
|
||||
for (int i = 0; i < objList.size(); i++) {
|
||||
characterOffset = addElement(inst, elements, objList.get(i), underline, opIndex,
|
||||
subOpIndex, characterOffset);
|
||||
for (Object element : objList) {
|
||||
characterOffset = addElement(inst, elements, element, underline, opIndex, subOpIndex,
|
||||
characterOffset);
|
||||
}
|
||||
return characterOffset;
|
||||
}
|
||||
|
|
|
@ -43,16 +43,10 @@ public class SubDataFieldFactory extends OperandFieldFactory {
|
|||
* @param path the component path for the data
|
||||
*/
|
||||
public SubDataFieldFactory(String name, int[] path) {
|
||||
super();
|
||||
this.componentPath = path;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param provider The FieldProvider object that serves as the SubDataFieldFactory factory.
|
||||
* @param model The Field model that will use this Address factory.
|
||||
*/
|
||||
private SubDataFieldFactory(String name, int[] componentPath, FieldFormatModel model,
|
||||
HighlightProvider hlProvider, ToolOptions displayOptions, ToolOptions fieldOptions) {
|
||||
super(model, hlProvider, displayOptions, fieldOptions);
|
||||
|
@ -60,12 +54,6 @@ public class SubDataFieldFactory extends OperandFieldFactory {
|
|||
this.componentPath = componentPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FactoryField for the given object at index index.
|
||||
* @param varWidth the amount of variable width spacing for any fields
|
||||
* before this one.
|
||||
* @param proxy the object whose properties should be displayed.
|
||||
*/
|
||||
@Override
|
||||
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
|
||||
Object obj = proxy.getObject();
|
||||
|
@ -138,8 +126,8 @@ public class SubDataFieldFactory extends OperandFieldFactory {
|
|||
|
||||
@Override
|
||||
public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider,
|
||||
ToolOptions displayOptions, ToolOptions fieldOptions) {
|
||||
return new SubDataFieldFactory(name, componentPath, formatModel, provider, displayOptions,
|
||||
ToolOptions options, ToolOptions fieldOptions) {
|
||||
return new SubDataFieldFactory(name, componentPath, formatModel, provider, options,
|
||||
fieldOptions);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,8 +48,7 @@ import ghidra.util.exception.AssertException;
|
|||
import ghidra.util.exception.RollbackException;
|
||||
import junit.framework.AssertionFailedError;
|
||||
import utility.application.ApplicationLayout;
|
||||
import utility.function.ExceptionalCallback;
|
||||
import utility.function.ExceptionalFunction;
|
||||
import utility.function.*;
|
||||
|
||||
public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDockingTest {
|
||||
|
||||
|
@ -68,8 +67,6 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
|||
private static Language Z80_LANGUAGE;
|
||||
|
||||
public AbstractGhidraHeadlessIntegrationTest() {
|
||||
super();
|
||||
|
||||
// Ensure that all error messages do NOT use a gui popup, and instead are routed to the
|
||||
// console.
|
||||
setErrorGUIEnabled(false);
|
||||
|
@ -222,6 +219,36 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a convenient method for modifying the current program, handling the transaction
|
||||
* logic and returning a result.
|
||||
* @param <T> the return type
|
||||
* @param <E> the exception type
|
||||
* @param p the program
|
||||
* @param s the code to execute
|
||||
* @return the supplier's return value
|
||||
* @see #modifyProgram(Program, ExceptionalCallback)
|
||||
* @see #modifyProgram(Program, ExceptionalFunction)
|
||||
*/
|
||||
public static <T, E extends Exception> T tx(Program p, ExceptionalSupplier<T, E> s) {
|
||||
int txId = p.startTransaction("Test - Function in Transaction");
|
||||
boolean commit = true;
|
||||
try {
|
||||
T t = s.get();
|
||||
p.flushEvents();
|
||||
waitForSwing();
|
||||
return t;
|
||||
}
|
||||
catch (Exception e) {
|
||||
commit = false;
|
||||
failWithException("Exception modifying program '" + p.getName() + "'", e);
|
||||
}
|
||||
finally {
|
||||
p.endTransaction(txId, commit);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a convenient method for modifying the current program, handling the transaction
|
||||
* logic. This method is calls {@link #tx(Program, ExceptionalCallback)}, but helps with
|
||||
|
|
|
@ -44,6 +44,7 @@ import ghidra.program.util.AddressFieldLocation;
|
|||
import ghidra.program.util.FieldNameFieldLocation;
|
||||
import ghidra.test.AbstractProgramBasedTest;
|
||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.datastruct.ListAccumulator;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -318,7 +319,10 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
|
|||
|
||||
protected void assertResultCount(int expected) {
|
||||
List<Address> referenceAddresses = getResultAddresses();
|
||||
assertEquals(expected, referenceAddresses.size());
|
||||
if (referenceAddresses.size() != expected) {
|
||||
Msg.debug(this, "Result addresses found: " + referenceAddresses);
|
||||
fail("Incorrect number of results; see console");
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertResultCount(String msg, int expected) {
|
||||
|
|
|
@ -15,13 +15,16 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.cmd.data.CreateDataCmd;
|
||||
import ghidra.app.util.viewer.field.FieldNameFieldFactory;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTest {
|
||||
|
@ -38,4 +41,61 @@ public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTes
|
|||
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
|
||||
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindStructureField_UnnamedDefaultField() {
|
||||
|
||||
// apply a structure with unnamed fields
|
||||
Structure struct = (Structure) getDt("/MyStruct");
|
||||
Address address = addr(0x01005560);
|
||||
assertTrue(applyCmd(program, new CreateDataCmd(address, struct)));
|
||||
|
||||
openData(0x01005560);
|
||||
|
||||
goTo(addr(0x01005560), FieldNameFieldFactory.FIELD_NAME, 1);
|
||||
|
||||
ProgramLocation location = codeBrowser.getCurrentLocation();
|
||||
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
|
||||
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindEnumByMember() {
|
||||
|
||||
//
|
||||
// This test searches for usage of an enum field. We will add two different enum field
|
||||
// uses to make sure we only find the one for which we are searching.
|
||||
//
|
||||
|
||||
Enum enoom = createEnum();
|
||||
Address otherAddress = addr(0x01008014); // 0x1 ONE; this also has references
|
||||
assertTrue(applyCmd(program, new CreateDataCmd(otherAddress, enoom)));
|
||||
|
||||
// this is the address will will use to search
|
||||
Address address = addr(0x01008019); // 0x0 ZERO
|
||||
assertTrue(applyCmd(program, new CreateDataCmd(address, enoom)));
|
||||
|
||||
goTo(address, "Operands", 1);
|
||||
|
||||
search();
|
||||
|
||||
assertResultCount(1);
|
||||
}
|
||||
|
||||
private DataType getDt(String path) {
|
||||
DataTypeManager dtm = program.getDataTypeManager();
|
||||
DataType dataType = dtm.getDataType(path);
|
||||
return dataType;
|
||||
}
|
||||
|
||||
private Enum createEnum() {
|
||||
return tx(program, () -> {
|
||||
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
|
||||
Enum dt = new EnumDataType("TestEnum", 1);
|
||||
dt.add("ZERO", 0);
|
||||
dt.add("ONE", 1);
|
||||
dt.add("TWO", 2);
|
||||
return (Enum) dtm.addDataType(dt, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -231,19 +230,6 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
|||
assertThat(descriptor, is(instanceOf(AddressLocationDescriptor.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldNameLocationDescriptor_StructureFieldName_ArrayInStructure()
|
||||
throws Exception {
|
||||
|
||||
openData(0x01005540);
|
||||
|
||||
goTo(addr(0x01005541), FieldNameFieldFactory.FIELD_NAME, 1);
|
||||
|
||||
ProgramLocation location = codeBrowser.getCurrentLocation();
|
||||
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
|
||||
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldNameLocationDescriptor_StructureInArray() throws Exception {
|
||||
|
||||
|
@ -379,8 +365,6 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
|||
assertContains(results, from1, from2, from3, stringAddr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
|
|
@ -162,8 +162,8 @@ public class RecoveredClassHelper {
|
|||
|
||||
extendedFlatAPI = new ExtendedFlatProgramAPI(program, monitor);
|
||||
|
||||
this.classDataTypesCategoryPath =
|
||||
extendedFlatAPI.createDataTypeCategoryPath(CategoryPath.ROOT, DTM_CLASS_DATA_FOLDER_NAME);
|
||||
this.classDataTypesCategoryPath = extendedFlatAPI
|
||||
.createDataTypeCategoryPath(CategoryPath.ROOT, DTM_CLASS_DATA_FOLDER_NAME);
|
||||
|
||||
this.createBookmarks = createBookmarks;
|
||||
this.useShortTemplates = useShortTemplates;
|
||||
|
@ -418,9 +418,8 @@ public class RecoveredClassHelper {
|
|||
// need to used the thunked function on the hashmap
|
||||
if (allConstructorsAndDestructors.contains(calledFunction)) {
|
||||
// get list of refs to this function from the calling function
|
||||
List<Address> referencesToFunctionBFromFunctionA =
|
||||
extendedFlatAPI.getReferencesToFunctionBFromFunctionA(function,
|
||||
referencedFunction);
|
||||
List<Address> referencesToFunctionBFromFunctionA = extendedFlatAPI
|
||||
.getReferencesToFunctionBFromFunctionA(function, referencedFunction);
|
||||
// add them to list of ref address pairs
|
||||
Iterator<Address> iterator = referencesToFunctionBFromFunctionA.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
|
@ -560,8 +559,8 @@ public class RecoveredClassHelper {
|
|||
* @return the reference to the class vftable in the given function or null if there isn't one
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public Address getFirstClassVftableReference(RecoveredClass recoveredClass,
|
||||
Function function) throws CancelledException {
|
||||
public Address getFirstClassVftableReference(RecoveredClass recoveredClass, Function function)
|
||||
throws CancelledException {
|
||||
|
||||
List<Address> vftableReferenceList = functionToVftableRefsMap.get(function);
|
||||
|
||||
|
@ -1100,9 +1099,10 @@ public class RecoveredClassHelper {
|
|||
if (!vftableClass.equals(recoveredClass)) {
|
||||
|
||||
if (DEBUG) {
|
||||
Msg.debug(this, "updating struct for " + recoveredClass.getName() +
|
||||
" but first vftable in function " + function.getEntryPoint().toString() +
|
||||
" is in class " + vftableClass.getName());
|
||||
Msg.debug(this,
|
||||
"updating struct for " + recoveredClass.getName() +
|
||||
" but first vftable in function " + function.getEntryPoint().toString() +
|
||||
" is in class " + vftableClass.getName());
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -1145,8 +1145,8 @@ public class RecoveredClassHelper {
|
|||
}
|
||||
|
||||
// add the other high variables that store vftable pointer
|
||||
highVariables.addAll(
|
||||
getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
|
||||
highVariables
|
||||
.addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
|
||||
Iterator<HighVariable> highVariableIterator = highVariables.iterator();
|
||||
|
||||
Address vftableAddress = null;
|
||||
|
@ -1262,7 +1262,8 @@ public class RecoveredClassHelper {
|
|||
continue;
|
||||
}
|
||||
|
||||
PointerDataType ptrUndefined = extendedFlatAPI.createPointerToUndefinedDataType(dataType);
|
||||
PointerDataType ptrUndefined =
|
||||
extendedFlatAPI.createPointerToUndefinedDataType(dataType);
|
||||
if (ptrUndefined != null) {
|
||||
function.getParameter(i).setDataType(ptrUndefined, SourceType.ANALYSIS);
|
||||
}
|
||||
|
@ -1364,12 +1365,7 @@ public class RecoveredClassHelper {
|
|||
|
||||
public Function getCalledFunctionFromCallPcodeOp(PcodeOp calledPcodeOp) {
|
||||
|
||||
Function calledFunction =
|
||||
api.getFunctionAt(calledPcodeOp.getInput(0).getAddress());
|
||||
if (calledFunction == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Function calledFunction = api.getFunctionAt(calledPcodeOp.getInput(0).getAddress());
|
||||
return calledFunction;
|
||||
}
|
||||
|
||||
|
@ -1770,7 +1766,8 @@ public class RecoveredClassHelper {
|
|||
ReferenceAddressPair refAddrPair = iterator.next();
|
||||
Address sourceAddr = refAddrPair.getSource();
|
||||
if (sourceAddr.compareTo(maxVftableReference) > 0) {
|
||||
Function calledFunction = extendedFlatAPI.getFunctionAt(refAddrPair.getDestination());
|
||||
Function calledFunction =
|
||||
extendedFlatAPI.getFunctionAt(refAddrPair.getDestination());
|
||||
if (calledFunction != null) {
|
||||
possibleParentDestructors.add(calledFunction);
|
||||
}
|
||||
|
@ -2080,7 +2077,8 @@ public class RecoveredClassHelper {
|
|||
Address constructorReference = constructorIterator.next();
|
||||
RecoveredClass recoveredClass = referenceToClassMap.get(constructorReference);
|
||||
|
||||
Function constructor = extendedFlatAPI.getReferencedFunction(constructorReference, true);
|
||||
Function constructor =
|
||||
extendedFlatAPI.getReferencedFunction(constructorReference, true);
|
||||
|
||||
if (recoveredClass.getIndeterminateList().contains(constructor)) {
|
||||
addConstructorToClass(recoveredClass, constructor);
|
||||
|
@ -2650,8 +2648,8 @@ public class RecoveredClassHelper {
|
|||
String className = namespace.getName();
|
||||
String classNameWithNamespace = namespace.getName(true);
|
||||
|
||||
CategoryPath classPath = extendedFlatAPI.createDataTypeCategoryPath(classDataTypesCategoryPath,
|
||||
classNameWithNamespace);
|
||||
CategoryPath classPath = extendedFlatAPI
|
||||
.createDataTypeCategoryPath(classDataTypesCategoryPath, classNameWithNamespace);
|
||||
|
||||
RecoveredClass newClass =
|
||||
new RecoveredClass(className, classPath, namespace, dataTypeManager);
|
||||
|
@ -3135,10 +3133,11 @@ public class RecoveredClassHelper {
|
|||
notInFunctionVftableRefs.add(addr);
|
||||
}
|
||||
else {
|
||||
boolean functionCreated = extendedFlatAPI.createFunction(program, addr);
|
||||
if (!functionCreated) {
|
||||
notInFunctionVftableRefs.add(addr);
|
||||
}
|
||||
boolean functionCreated = extendedFlatAPI.createFunction(prog, addr);
|
||||
if (!functionCreated) {
|
||||
notInFunctionVftableRefs.add(addr);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3215,9 +3214,8 @@ public class RecoveredClassHelper {
|
|||
List<RecoveredClass> parentClasses = recoveredClass.getClassHierarchy();
|
||||
|
||||
if (parentClasses.isEmpty()) {
|
||||
throw new Exception(
|
||||
recoveredClass.getClassNamespace().getName(true) +
|
||||
" should not have an empty class hierarchy");
|
||||
throw new Exception(recoveredClass.getClassNamespace().getName(true) +
|
||||
" should not have an empty class hierarchy");
|
||||
}
|
||||
|
||||
// if size one it only includes self
|
||||
|
@ -3303,8 +3301,9 @@ public class RecoveredClassHelper {
|
|||
|
||||
Msg.debug(this, msg1 + " at " + constructorFunction.getEntryPoint() + msg2);
|
||||
|
||||
program.getBookmarkManager().setBookmark(constructorFunction.getEntryPoint(),
|
||||
BookmarkType.ERROR, "Decompiler Error", msg1 + msg2);
|
||||
program.getBookmarkManager()
|
||||
.setBookmark(constructorFunction.getEntryPoint(), BookmarkType.ERROR,
|
||||
"Decompiler Error", msg1 + msg2);
|
||||
|
||||
// get the return type from the listing and in some cases it will
|
||||
// indicate the correct type to help determine the below type to add
|
||||
|
@ -3404,8 +3403,7 @@ public class RecoveredClassHelper {
|
|||
* @throws Exception when cancelled
|
||||
*/
|
||||
public void addVbaseDestructorsToClassNamespace(RecoveredClass recoveredClass,
|
||||
Structure classStruct)
|
||||
throws Exception {
|
||||
Structure classStruct) throws Exception {
|
||||
|
||||
Namespace classNamespace = recoveredClass.getClassNamespace();
|
||||
|
||||
|
@ -3627,8 +3625,8 @@ public class RecoveredClassHelper {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (program.getBookmarkManager().getBookmark(address, BookmarkType.ANALYSIS,
|
||||
"Function ID Analyzer") != null) {
|
||||
if (program.getBookmarkManager()
|
||||
.getBookmark(address, BookmarkType.ANALYSIS, "Function ID Analyzer") != null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -3654,8 +3652,8 @@ public class RecoveredClassHelper {
|
|||
|
||||
// first get the new class structure and verify it exists - don't remove others if
|
||||
// new one doesn't exist
|
||||
DataType classStructureDataType = dataTypeManager.getDataType(
|
||||
recoveredClass.getClassPath(), recoveredClass.getName());
|
||||
DataType classStructureDataType = dataTypeManager
|
||||
.getDataType(recoveredClass.getClassPath(), recoveredClass.getName());
|
||||
if (classStructureDataType == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -3798,6 +3796,7 @@ public class RecoveredClassHelper {
|
|||
|
||||
if (symbol != null && symbol.getSource() == SourceType.ANALYSIS &&
|
||||
!symbol.getName().equals(name) && !symbol.getParentNamespace().equals(namespace)) {
|
||||
|
||||
// add to list of bad namespaces to be cleaned up later
|
||||
Namespace parentNamespace = symbol.getParentNamespace();
|
||||
if (!parentNamespace.isGlobal()) {
|
||||
|
@ -4222,8 +4221,7 @@ public class RecoveredClassHelper {
|
|||
|
||||
RecoveredClass recoveredClass = recoveredClassIterator.next();
|
||||
|
||||
List<Function> allOtherConstructors =
|
||||
new ArrayList<Function>(getAllConstructors());
|
||||
List<Function> allOtherConstructors = new ArrayList<Function>(getAllConstructors());
|
||||
allOtherConstructors.removeAll(recoveredClass.getConstructorList());
|
||||
|
||||
// iterate through the vtable functions
|
||||
|
@ -4464,10 +4462,15 @@ public class RecoveredClassHelper {
|
|||
|
||||
monitor.checkCanceled();
|
||||
|
||||
Structure badStructure = badStructureIterator.next();
|
||||
// if not used by anything remove it
|
||||
Structure badStructure = badStructureIterator.next();
|
||||
ListAccumulator<LocationReference> accumulator = new ListAccumulator<>();
|
||||
ReferenceUtils.findDataTypeReferences(accumulator, badStructure, null, program, true,
|
||||
|
||||
// TODO Discovering types can be very slow for large binaries. Discovering types
|
||||
// should not be needed. Passing false here will still find all applied uses of the
|
||||
// given structure.
|
||||
boolean discoverTypes = true;
|
||||
ReferenceUtils.findDataTypeReferences(accumulator, badStructure, program, discoverTypes,
|
||||
monitor);
|
||||
|
||||
List<LocationReference> referenceList = accumulator.asList();
|
||||
|
@ -4631,7 +4634,8 @@ public class RecoveredClassHelper {
|
|||
* @throws Exception if other exception
|
||||
*/
|
||||
public void fillInAndApplyVftableStructAndNameVfunctions(RecoveredClass recoveredClass,
|
||||
Map<Address, DataType> vftableToStructureMap, Structure classStruct) throws CancelledException, Exception {
|
||||
Map<Address, DataType> vftableToStructureMap, Structure classStruct)
|
||||
throws CancelledException, Exception {
|
||||
|
||||
//create function definition for each virtual function and put in vftable structure and
|
||||
// data subfolder
|
||||
|
@ -4660,7 +4664,6 @@ public class RecoveredClassHelper {
|
|||
nameVfunctions(recoveredClass, vftableAddress, vftableStructureName);
|
||||
}
|
||||
|
||||
|
||||
List<Function> vFunctions = recoveredClass.getVirtualFunctions(vftableAddress);
|
||||
int vfunctionNumber = 1;
|
||||
Iterator<Function> vfIterator = vFunctions.iterator();
|
||||
|
@ -4683,7 +4686,6 @@ public class RecoveredClassHelper {
|
|||
replaceClassStructure(vfunction, recoveredClass.getName(), classStruct);
|
||||
}
|
||||
|
||||
|
||||
String forClassSuffix = getForClassSuffix(vftableStructureName);
|
||||
String functionDefName = vfunction.getName();
|
||||
int indexOfSuffix = functionDefName.indexOf(forClassSuffix);
|
||||
|
@ -4695,9 +4697,8 @@ public class RecoveredClassHelper {
|
|||
}
|
||||
|
||||
// get the classPath of highest level parent with vfAddress in their vftable
|
||||
classPath =
|
||||
getCategoryPathForFunctionSignature(vfunction, functionDefName, recoveredClass,
|
||||
vftableAddress);
|
||||
classPath = getCategoryPathForFunctionSignature(vfunction, functionDefName,
|
||||
recoveredClass, vftableAddress);
|
||||
|
||||
Symbol vfunctionSymbol = symbolTable.getPrimarySymbol(vfunction.getEntryPoint());
|
||||
Namespace parentNamespace = vfunctionSymbol.getParentNamespace();
|
||||
|
@ -5098,6 +5099,7 @@ public class RecoveredClassHelper {
|
|||
return functionPointerDataType;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to add precomment inside functions containing inlined constructors at approximate
|
||||
* address of start of inlined function
|
||||
|
@ -5154,7 +5156,8 @@ public class RecoveredClassHelper {
|
|||
while (inlinedDestructorIterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Function destructorFunction = inlinedDestructorIterator.next();
|
||||
Address classVftableRef = getFirstClassVftableReference(recoveredClass, destructorFunction);
|
||||
Address classVftableRef =
|
||||
getFirstClassVftableReference(recoveredClass, destructorFunction);
|
||||
|
||||
if (classVftableRef == null) {
|
||||
continue;
|
||||
|
@ -5320,8 +5323,7 @@ public class RecoveredClassHelper {
|
|||
extendedFlatAPI.getCalledFunctionByCallOrder(deletingDestructor, 1);
|
||||
if (firstCalledFunction == null ||
|
||||
!recoveredClass.getConstructorOrDestructorFunctions()
|
||||
.contains(
|
||||
firstCalledFunction)) {
|
||||
.contains(firstCalledFunction)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -5425,7 +5427,8 @@ public class RecoveredClassHelper {
|
|||
continue;
|
||||
}
|
||||
|
||||
Function referencedFunction = extendedFlatAPI.getReferencedFunction(codeUnitAddress, true);
|
||||
Function referencedFunction =
|
||||
extendedFlatAPI.getReferencedFunction(codeUnitAddress, true);
|
||||
if (referencedFunction == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -5456,12 +5459,10 @@ public class RecoveredClassHelper {
|
|||
if (parentDestructorClasses.size() == 1) {
|
||||
if (!parentDestructorClasses.get(0)
|
||||
.getDestructorList()
|
||||
.contains(
|
||||
parentDestructor)) {
|
||||
.contains(parentDestructor)) {
|
||||
addDestructorToClass(parentDestructorClasses.get(0), parentDestructor);
|
||||
parentDestructorClasses.get(0)
|
||||
.removeIndeterminateConstructorOrDestructor(
|
||||
parentDestructor);
|
||||
.removeIndeterminateConstructorOrDestructor(parentDestructor);
|
||||
}
|
||||
}
|
||||
// if more than one parent class for this function then let either inline or multi-class
|
||||
|
@ -5560,7 +5561,6 @@ public class RecoveredClassHelper {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to add alignment to the given length based on the default program address size
|
||||
* @param len the given length
|
||||
|
@ -5618,8 +5618,6 @@ public class RecoveredClassHelper {
|
|||
return NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DataTypeComponent[] definedComponents = structure.getDefinedComponents();
|
||||
|
||||
if (definedComponents.length == 0) {
|
||||
|
@ -5781,8 +5779,7 @@ public class RecoveredClassHelper {
|
|||
|
||||
if (recoveredClass.getClassOffsetToVftableMap()
|
||||
.values()
|
||||
.containsAll(
|
||||
recoveredClass.getVftableAddresses())) {
|
||||
.containsAll(recoveredClass.getVftableAddresses())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -5826,8 +5823,7 @@ public class RecoveredClassHelper {
|
|||
extendedFlatAPI.getCalledFunctionByCallOrder(vFunction, 2);
|
||||
if (firstCalledFunction != null && secondCalledFunction != null &&
|
||||
!recoveredClass.getConstructorOrDestructorFunctions()
|
||||
.contains(
|
||||
firstCalledFunction) &&
|
||||
.contains(firstCalledFunction) &&
|
||||
secondCalledFunction.equals(operator_delete) &&
|
||||
!getAllConstructorsAndDestructors().contains(vFunction)) {
|
||||
recoveredClass.addDeletingDestructor(vFunction);
|
||||
|
@ -6040,8 +6036,8 @@ public class RecoveredClassHelper {
|
|||
}
|
||||
|
||||
// add the other high variables that store vftable pointer
|
||||
highVariables.addAll(
|
||||
getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
|
||||
highVariables
|
||||
.addAll(getVariableThatStoresVftablePointer(highFunction, firstVftableReference));
|
||||
|
||||
Iterator<HighVariable> highVariableIterator = highVariables.iterator();
|
||||
|
||||
|
@ -6228,7 +6224,6 @@ public class RecoveredClassHelper {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to retrieve the offset of the class data in the given structure
|
||||
* @param recoveredClass the given class
|
||||
|
@ -6504,7 +6499,8 @@ public class RecoveredClassHelper {
|
|||
Address vftableAddress = vftableIterator.next();
|
||||
|
||||
// this gets the first function pointer in the vftable
|
||||
Function firstVirtualFunction = extendedFlatAPI.getReferencedFunction(vftableAddress);
|
||||
Function firstVirtualFunction =
|
||||
extendedFlatAPI.getReferencedFunction(vftableAddress);
|
||||
processDeletingDestructor(recoveredClass, firstVirtualFunction);
|
||||
}
|
||||
}
|
||||
|
@ -6550,7 +6546,8 @@ public class RecoveredClassHelper {
|
|||
monitor.checkCanceled();
|
||||
Address vftableAddress = vftableAddressIterator.next();
|
||||
|
||||
Function firstVirtualFunction = extendedFlatAPI.getReferencedFunction(vftableAddress);
|
||||
Function firstVirtualFunction =
|
||||
extendedFlatAPI.getReferencedFunction(vftableAddress);
|
||||
List<Function> virtualFunctions =
|
||||
recoveredClass.getVirtualFunctions(vftableAddress);
|
||||
|
||||
|
@ -7077,7 +7074,6 @@ public class RecoveredClassHelper {
|
|||
return classDataStructure;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to find the purecall function.
|
||||
* @param recoveredClasses List of RecoveredClass objects
|
||||
|
@ -7192,7 +7188,6 @@ public class RecoveredClassHelper {
|
|||
return listOfUniqueAddresses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to apply the function signature of the given function, if different, to the corresponding
|
||||
* function definition and call the method to update related structure fields and functions
|
||||
|
@ -7368,9 +7363,8 @@ public class RecoveredClassHelper {
|
|||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
private List<Object> updateCorrespondingFunctionDefinition(Function vfunction,
|
||||
DataTypeComponent structureComponent)
|
||||
throws DuplicateNameException, DataTypeDependencyException, InvalidInputException,
|
||||
CancelledException {
|
||||
DataTypeComponent structureComponent) throws DuplicateNameException,
|
||||
DataTypeDependencyException, InvalidInputException, CancelledException {
|
||||
|
||||
List<Object> changedItems = new ArrayList<Object>();
|
||||
|
||||
|
@ -7381,7 +7375,6 @@ public class RecoveredClassHelper {
|
|||
|
||||
FunctionSignature listingFunctionSignature = vfunction.getSignature(true);
|
||||
|
||||
|
||||
FunctionDefinition componentFunctionDefinition =
|
||||
getComponentFunctionDefinition(structureComponent);
|
||||
if (componentFunctionDefinition == null) {
|
||||
|
@ -7429,7 +7422,6 @@ public class RecoveredClassHelper {
|
|||
return componentFunctionDefinition;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to edit the function definition pointed to by the given structure component with any
|
||||
* differences in the given function definition
|
||||
|
@ -7440,7 +7432,6 @@ public class RecoveredClassHelper {
|
|||
private FunctionDefinition editFunctionDefinition(DataTypeComponent structureComponent,
|
||||
FunctionDefinition newFunctionDefinition) {
|
||||
|
||||
|
||||
DataType componentDataType = structureComponent.getDataType();
|
||||
if (!(componentDataType instanceof Pointer)) {
|
||||
throw new IllegalArgumentException("Structure component must be a pointer " +
|
||||
|
@ -7474,7 +7465,6 @@ public class RecoveredClassHelper {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to update the given function definition with the new function definition
|
||||
* @param functionDefinition the given function definition
|
||||
|
@ -7532,7 +7522,6 @@ public class RecoveredClassHelper {
|
|||
|
||||
}
|
||||
|
||||
|
||||
public List<Structure> getClassStructures() throws CancelledException {
|
||||
|
||||
Category category = program.getDataTypeManager().getCategory(classDataTypesCategoryPath);
|
||||
|
@ -7629,8 +7618,6 @@ public class RecoveredClassHelper {
|
|||
return functions;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Method to apply the given function definition to the corresponding function signatures that
|
||||
* do not match and also update the vftable structure fields to have the correct name if
|
||||
|
@ -7643,8 +7630,8 @@ public class RecoveredClassHelper {
|
|||
* @throws InvalidInputException if there are invalid inputs when performing changes
|
||||
*/
|
||||
public List<Object> applyNewFunctionDefinition(FunctionDefinition functionDefinition)
|
||||
throws CancelledException, DuplicateNameException,
|
||||
DataTypeDependencyException, InvalidInputException {
|
||||
throws CancelledException, DuplicateNameException, DataTypeDependencyException,
|
||||
InvalidInputException {
|
||||
|
||||
List<Object> changedItems = new ArrayList<Object>();
|
||||
|
||||
|
@ -7739,7 +7726,7 @@ public class RecoveredClassHelper {
|
|||
if (component.getDataType().equals(pointerToVfunction)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return vfunctionIndex;
|
||||
}
|
||||
|
||||
|
@ -7905,9 +7892,9 @@ public class RecoveredClassHelper {
|
|||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
private Object updateListingVfunctionSignature(Data listingVftable,
|
||||
DataTypeComponent structureComponent,
|
||||
Address vftableAddress) throws DuplicateNameException, DataTypeDependencyException,
|
||||
InvalidInputException, CancelledException {
|
||||
DataTypeComponent structureComponent, Address vftableAddress)
|
||||
throws DuplicateNameException, DataTypeDependencyException, InvalidInputException,
|
||||
CancelledException {
|
||||
|
||||
int numVfunctions = listingVftable.getNumComponents();
|
||||
|
||||
|
@ -8031,8 +8018,7 @@ public class RecoveredClassHelper {
|
|||
}
|
||||
|
||||
private List<Object> updateComponentFieldName(DataTypeComponent structureComponent,
|
||||
FunctionDefinition newFunctionSignature)
|
||||
throws DuplicateNameException {
|
||||
FunctionDefinition newFunctionSignature) throws DuplicateNameException {
|
||||
|
||||
List<Object> changedItems = new ArrayList<Object>();
|
||||
if (!structureComponent.getFieldName().equals(newFunctionSignature.getName())) {
|
||||
|
@ -8044,8 +8030,7 @@ public class RecoveredClassHelper {
|
|||
return changedItems;
|
||||
}
|
||||
|
||||
public List<Symbol> getClassVftableSymbols(Namespace classNamespace)
|
||||
throws CancelledException {
|
||||
public List<Symbol> getClassVftableSymbols(Namespace classNamespace) throws CancelledException {
|
||||
|
||||
List<Symbol> vftableSymbols = new ArrayList<Symbol>();
|
||||
|
||||
|
@ -8146,7 +8131,6 @@ public class RecoveredClassHelper {
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
// loop until the vftable pointer is added
|
||||
// if there is room and the component offset is not a structure, replace with vftablePtr
|
||||
// otherwise, within the range from top of containing component to the end of where the
|
||||
|
|
|
@ -31,8 +31,7 @@ import ghidra.app.nav.Navigatable;
|
|||
import ghidra.app.plugin.core.decompile.actions.FindReferencesToSymbolAction;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.DataTypeReferenceFinder;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
@ -172,6 +171,19 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
|
|||
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() {
|
||||
return dataTypeReferencesCallCount.get();
|
||||
}
|
||||
|
|
|
@ -17,8 +17,7 @@ package ghidra.app.plugin.core.decompile;
|
|||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.DataTypeReferenceFinder;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -40,4 +39,10 @@ public class StubDataTypeReferenceFinder implements DataTypeReferenceFinder {
|
|||
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findReferences(Program program, FieldMatcher fieldMatcher,
|
||||
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
|
||||
// stub
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,14 @@ import java.util.List;
|
|||
import ghidra.app.decompiler.ClangFieldToken;
|
||||
import ghidra.app.decompiler.ClangLine;
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
/**
|
||||
* This class represents the use of a field of a {@link Composite} data type <b>where there is
|
||||
* no variable in the Decompiler</b> for that data type. A normal variable access in the
|
||||
* Decompiler may look like so:
|
||||
* This class represents the use of a field of a {@link Composite} data type <b>where there is no
|
||||
* variable in the Decompiler</b> for that data type. A normal variable access in the Decompiler
|
||||
* may look like so:
|
||||
* <pre>
|
||||
* Foo f;
|
||||
* ...
|
||||
|
@ -41,9 +42,8 @@ import ghidra.program.model.data.DataType;
|
|||
* </pre>
|
||||
*
|
||||
* In this case, <code><b>foo_array[1]</b></code> is a <code>Foo</code>, whose
|
||||
* <code><b>some_field</b></code> is
|
||||
* being accessed anonymously, since there is no variable of <code>Foo</code> declared
|
||||
* in the current function.
|
||||
* <code><b>some_field</b></code> is being accessed anonymously, since there is no variable of
|
||||
* <code>Foo</code> declared in the current function.
|
||||
*/
|
||||
public class AnonymousVariableAccessDR extends VariableAccessDR {
|
||||
|
||||
|
@ -52,42 +52,57 @@ public class AnonymousVariableAccessDR extends VariableAccessDR {
|
|||
}
|
||||
|
||||
@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
|
||||
// that contains the field being accessed. A variable being accessed has 2 types being
|
||||
// This class is backed by a ClangFieldToken. That class's data type is the composite that
|
||||
// contains the field being accessed. A variable being accessed has 2 types being
|
||||
// touched: the aforementioned composite and the type of the field itself.
|
||||
//
|
||||
// This can match in one of two cases:
|
||||
// 1) the client seeks to match a given field inside of the containing composite, or
|
||||
// 2) the client seeks to match only the type, which means that the field type itself must match
|
||||
// 1) the passed in type must match the field type and not the parent type, or
|
||||
// 2) the passed in type must match the parent type, along with supplied field name/offset.
|
||||
//
|
||||
|
||||
ClangFieldToken field = (ClangFieldToken) sourceToken;
|
||||
DataType compositeType = field.getDataType();
|
||||
DataType fieldDt = DecompilerReference.getFieldDataType(field);
|
||||
|
||||
boolean matchesComposite = isEqual(dt, compositeType);
|
||||
boolean matchesField = isEqual(dt, fieldDt);
|
||||
boolean noMatch = !(matchesComposite || matchesField);
|
||||
boolean matchesCompositeType = isEqual(dt, compositeType);
|
||||
boolean matchesFieldType = isEqual(dt, fieldDt);
|
||||
boolean noMatch = !(matchesCompositeType || matchesFieldType);
|
||||
if (noMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fieldName == null) {
|
||||
// case 2; no field name to check
|
||||
if (matchesField) {
|
||||
//
|
||||
// Case 1
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
// else there is no field and the search type does not match the reference's type
|
||||
return;
|
||||
}
|
||||
|
||||
// case 1; check the field name and the composite type
|
||||
if (matchesComposite && field.getText().equals(fieldName)) {
|
||||
results.add(
|
||||
new DataTypeReference(compositeType, fieldName, getFunction(), getAddress(),
|
||||
getContext()));
|
||||
//
|
||||
// Case 2
|
||||
//
|
||||
// The client has requested a particular field of the parent composite. We only have a
|
||||
// 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,7 @@ import ghidra.app.decompiler.parallel.*;
|
|||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.DataTypeReferenceFinder;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.BuiltInDataType;
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
@ -58,10 +57,28 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
|
||||
@Override
|
||||
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 =
|
||||
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);
|
||||
|
||||
|
@ -87,7 +104,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
buildTypeLineage(dt, types);
|
||||
|
||||
Set<Function> results = new HashSet<>();
|
||||
accumulateFunctionCallsToDefinedData(program, types, results, monitor);
|
||||
accumulateFunctionCallsToDefinedData(program, dt, types, results, monitor);
|
||||
|
||||
Listing listing = program.getListing();
|
||||
FunctionIterator it = listing.getFunctions(true);
|
||||
|
@ -115,21 +132,23 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
return results;
|
||||
}
|
||||
|
||||
private void accumulateFunctionCallsToDefinedData(Program program, Set<DataType> potentialTypes,
|
||||
Set<Function> results, TaskMonitor monitor) throws CancelledException {
|
||||
private void accumulateFunctionCallsToDefinedData(Program program, DataType dataType,
|
||||
Set<DataType> potentialTypes, Set<Function> results, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
Listing listing = program.getListing();
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
SetAccumulator<LocationReference> accumulator = new SetAccumulator<>();
|
||||
Predicate<Data> dataMatcher = data -> {
|
||||
counter.incrementAndGet();
|
||||
DataType dataType = data.getDataType();
|
||||
boolean matches = potentialTypes.contains(dataType);
|
||||
DataType dt = data.getDataType();
|
||||
boolean matches = potentialTypes.contains(dt);
|
||||
return matches;
|
||||
};
|
||||
|
||||
ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher, null,
|
||||
monitor);
|
||||
FieldMatcher emptyMatcher = new FieldMatcher(dataType);
|
||||
ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher,
|
||||
emptyMatcher, monitor);
|
||||
|
||||
for (LocationReference ref : accumulator) {
|
||||
Address address = ref.getLocationOfUse();
|
||||
|
@ -158,12 +177,10 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
|
||||
// We have a different type, should we search for it?
|
||||
if (baseType instanceof BuiltInDataType) {
|
||||
// When given a wrapper type (e.g., typedef) , ignore
|
||||
// built-ins (e.g., int, byte, etc), as
|
||||
// they will be of little value due to their volume in the program and the
|
||||
// user *probably* did not intend to search for them. (Below we do not do
|
||||
// this check, which allows the user to search directly for a
|
||||
// built-in type, if they wish.)
|
||||
// When given a wrapper type (e.g., typedef) , ignore built-ins (e.g., int, byte, etc),
|
||||
// as they will be of little value due to their volume in the program and the user
|
||||
// *probably* did not intend to search for them. (Below we do not do this check, which
|
||||
// allows the user to search directly for a built-in type, if they wish.)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -207,16 +224,16 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
|
||||
private Consumer<DataTypeReference> callback;
|
||||
private DataType dataType;
|
||||
private String fieldName;
|
||||
private FieldMatcher fieldMatcher;
|
||||
|
||||
/* Search for composite field access */
|
||||
DecompilerDataTypeFinderQCallback(Program program, DataType dataType, String fieldName,
|
||||
Consumer<DataTypeReference> callback) {
|
||||
DecompilerDataTypeFinderQCallback(Program program, DataType dataType,
|
||||
FieldMatcher fieldMatcher, Consumer<DataTypeReference> callback) {
|
||||
|
||||
super(program, new DecompilerConfigurer());
|
||||
|
||||
this.dataType = dataType;
|
||||
this.fieldName = fieldName;
|
||||
this.fieldMatcher = fieldMatcher;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
|
@ -230,7 +247,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
}
|
||||
|
||||
DecompilerDataTypeFinder finder =
|
||||
new DecompilerDataTypeFinder(results, function, dataType, fieldName);
|
||||
new DecompilerDataTypeFinder(results, function, dataType, fieldMatcher);
|
||||
List<DataTypeReference> refs = finder.findUsage();
|
||||
|
||||
refs.forEach(r -> callback.accept(r));
|
||||
|
@ -263,14 +280,14 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
private DecompileResults decompilation;
|
||||
private Function function;
|
||||
private DataType dataType;
|
||||
private String fieldName;
|
||||
private FieldMatcher fieldMatcher;
|
||||
|
||||
DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType,
|
||||
String fieldName) {
|
||||
FieldMatcher fieldMatcher) {
|
||||
this.decompilation = results;
|
||||
this.function = function;
|
||||
this.dataType = dataType;
|
||||
this.fieldName = fieldName;
|
||||
this.fieldMatcher = fieldMatcher;
|
||||
}
|
||||
|
||||
List<DataTypeReference> findUsage() {
|
||||
|
@ -300,7 +317,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
|
||||
/** Finds any search input match in the given reference */
|
||||
private void matchUsage(DecompilerReference reference, List<DataTypeReference> results) {
|
||||
reference.accumulateMatches(dataType, fieldName, results);
|
||||
reference.accumulateMatches(dataType, fieldMatcher, results);
|
||||
}
|
||||
|
||||
private List<DecompilerReference> findVariableReferences(ClangTokenGroup tokens) {
|
||||
|
@ -332,8 +349,8 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
|||
private void findVariablesInLine(ClangLine line, List<DecompilerReference> results) {
|
||||
|
||||
List<ClangToken> allTokens = line.getAllTokens();
|
||||
Iterable<ClangToken> filteredTokens = IterableUtils.filteredIterable(allTokens,
|
||||
token -> {
|
||||
Iterable<ClangToken> filteredTokens =
|
||||
IterableUtils.filteredIterable(allTokens, token -> {
|
||||
// Only include desirable tokens (this is really just for easier debugging).
|
||||
// Update this filter if the loop below ever needs other types of tokens.
|
||||
return (token instanceof ClangTypeToken) ||
|
||||
|
|
|
@ -73,6 +73,12 @@ public class DecompilerFieldAccess extends DecompilerVariable {
|
|||
return dt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
ClangFieldToken field = (ClangFieldToken) variable;
|
||||
return field.getOffset();
|
||||
}
|
||||
|
||||
protected DataType getBaseType(DataType dt) {
|
||||
if (dt instanceof Array) {
|
||||
return getBaseType(((Array) dt).getDataType());
|
||||
|
|
|
@ -21,6 +21,7 @@ import ghidra.app.decompiler.*;
|
|||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContextBuilder;
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -53,10 +54,10 @@ public abstract class DecompilerReference {
|
|||
* to the given data type where that type is being accessed by the given field name.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public abstract void accumulateMatches(DataType dt, String fieldName,
|
||||
public abstract void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||
List<DataTypeReference> results);
|
||||
|
||||
public DecompilerVariable getVariable() {
|
||||
|
@ -160,7 +161,7 @@ public abstract class DecompilerReference {
|
|||
int offset = field.getOffset();
|
||||
int n = parent.getLength();
|
||||
if (offset >= 0 && offset < n) {
|
||||
DataTypeComponent dtc = parent.getComponentAt(field.getOffset());
|
||||
DataTypeComponent dtc = parent.getComponentContaining(field.getOffset());
|
||||
fieldDt = dtc.getDataType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -188,6 +188,10 @@ public abstract class DecompilerVariable {
|
|||
return text;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return Integer.MIN_VALUE; // subclasses can override
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n";
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
|||
import ghidra.app.decompiler.ClangLine;
|
||||
import ghidra.app.decompiler.ClangTypeToken;
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
public class ReturnTypeDR extends DecompilerReference {
|
||||
|
@ -29,11 +30,11 @@ public class ReturnTypeDR extends DecompilerReference {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
|
||||
public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||
List<DataTypeReference> results) {
|
||||
|
||||
if (fieldName != null) {
|
||||
// Return Types do not have any field usage
|
||||
return;
|
||||
if (!fieldMatcher.isIgnored()) {
|
||||
return; // Return Types do not have any field usage
|
||||
}
|
||||
|
||||
DataType myDt = getDataType();
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -58,10 +59,11 @@ public class VariableAccessDR extends DecompilerReference {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
|
||||
public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||
List<DataTypeReference> results) {
|
||||
|
||||
if (fields.isEmpty()) {
|
||||
DecompilerVariable var = getMatch(dt, fieldName, variable, null);
|
||||
DecompilerVariable var = getMatch(dt, fieldMatcher, variable, null);
|
||||
if (var != null) {
|
||||
DataTypeReference ref = createReference(var);
|
||||
results.add(ref);
|
||||
|
@ -77,7 +79,7 @@ public class VariableAccessDR extends DecompilerReference {
|
|||
for (DecompilerVariable field : fields) {
|
||||
|
||||
DecompilerVariable next = field;
|
||||
DecompilerVariable var = getMatch(dt, fieldName, start, next);
|
||||
DecompilerVariable var = getMatch(dt, fieldMatcher, start, next);
|
||||
if (var != null) {
|
||||
DataTypeReference ref = createReference(var, next);
|
||||
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
|
||||
// type, with no field name)
|
||||
// Handle the last variable by itself (for the case where we are matching just on the type,
|
||||
// with no field name)
|
||||
//
|
||||
if (fieldName != null) {
|
||||
if (fieldMatcher.isIgnored()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DecompilerVariable var = getMatch(dt, null, start, null);
|
||||
DecompilerVariable var = getMatch(dt, fieldMatcher, start, null);
|
||||
if (var != null) {
|
||||
DataTypeReference ref = createReference(var);
|
||||
results.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
private DecompilerVariable getMatch(DataType dt, String fieldName, DecompilerVariable var,
|
||||
DecompilerVariable potentialField) {
|
||||
private DecompilerVariable getMatch(DataType dt, FieldMatcher fieldMatcher,
|
||||
DecompilerVariable var, DecompilerVariable potentialField) {
|
||||
|
||||
// Note: for now, I ignore the precedence of casting; if any cast type is a match, then
|
||||
// signal hooray
|
||||
boolean searchForField = fieldName != null;
|
||||
boolean searchForField = !fieldMatcher.isIgnored();
|
||||
DecompilerVariable fieldVar = searchForField ? potentialField : null;
|
||||
DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar);
|
||||
if (match == null) {
|
||||
// wrong type, nothing to do
|
||||
return null;
|
||||
return null; // wrong type, nothing to do
|
||||
}
|
||||
|
||||
// Matches on the type, does the field match?
|
||||
if (fieldName == null) {
|
||||
if (fieldMatcher.isIgnored()) {
|
||||
return match; // no field to match
|
||||
}
|
||||
|
||||
if (potentialField == null) {
|
||||
|
||||
// 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)
|
||||
// where 'RED' is the variable we are checking
|
||||
String name = var.getName();
|
||||
if (fieldName.equals(name)) {
|
||||
int offset = var.getOffset();
|
||||
if (fieldMatcher.matches(name, offset)) {
|
||||
return var;
|
||||
}
|
||||
|
||||
|
@ -134,7 +136,8 @@ public class VariableAccessDR extends DecompilerReference {
|
|||
}
|
||||
|
||||
String name = potentialField.getName();
|
||||
if (fieldName.equals(name)) {
|
||||
int offset = potentialField.getOffset();
|
||||
if (fieldMatcher.matches(name, offset)) {
|
||||
return match;
|
||||
}
|
||||
return null;
|
||||
|
@ -156,13 +159,13 @@ public class VariableAccessDR extends DecompilerReference {
|
|||
|
||||
//
|
||||
// Unusual Code Alert!
|
||||
// It is a bit odd to check the field when you are looking for the type that contains
|
||||
// the field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the
|
||||
// data type of the thing that contains it. So, if you have:
|
||||
// It is a bit odd to check the field when you are looking for the type that contains the
|
||||
// field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the data type of
|
||||
// the thing that contains it. So, if you have:
|
||||
// foo.bar
|
||||
// then the 'bar' field will have a data type of Foo. Unfortunately, this is not
|
||||
// always the case. For now, when the variable is global, we need to check the field
|
||||
// Sad face emoji.
|
||||
// then the 'bar' field will have a data type of Foo. Unfortunately, this is not always
|
||||
// the case. For now, when the variable is global, we need to check the field. Sad face
|
||||
// emoji.
|
||||
//
|
||||
HighVariable highVariable = var.variable.getHighVariable();
|
||||
if (highVariable instanceof HighGlobal) {
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
|||
import ghidra.app.decompiler.*;
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
||||
import ghidra.app.services.DataTypeReference;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
@ -52,7 +53,8 @@ public abstract class VariableDR extends DecompilerReference {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
|
||||
public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||
List<DataTypeReference> results) {
|
||||
|
||||
if (variable == null) {
|
||||
// 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();
|
||||
if (!isEqual(dataType, dt)) {
|
||||
// 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;
|
||||
return; // wrong type, nothing to do
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,6 +242,14 @@ public abstract class DockingAction implements DockingActionIf {
|
|||
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
|
||||
* are so obvious that they do not require help, such as an action that renames a file.
|
||||
|
|
|
@ -180,6 +180,11 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
|||
if (isCanceled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (allowEdits) {
|
||||
return combo.getText();
|
||||
}
|
||||
|
||||
Object selectedItem = combo.getSelectedItem();
|
||||
return selectedItem == null ? null : selectedItem.toString();
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
* @param numStr the number string
|
||||
* @return the long value or 0
|
||||
*
|
||||
*/
|
||||
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());
|
||||
if (numStr.length() == 0) {
|
||||
return value;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
long value = 0;
|
||||
try {
|
||||
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
|
||||
value = Integer.parseInt(numStr.substring(2), 16);
|
||||
|
@ -78,6 +85,7 @@ public final class NumericUtilities {
|
|||
}
|
||||
catch (NumberFormatException exc) {
|
||||
// do nothing special; use default value
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
|
|
@ -18,9 +18,8 @@ package ghidra.util.datastruct;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Provides a list of integer ranges that are maintained in sorted order.
|
||||
* When a range is added any ranges that overlap or are adjacent to one another
|
||||
* will coalesce into a single range.
|
||||
* Provides a list of integer ranges that are maintained in sorted order. When a range is added
|
||||
* any ranges that overlap or are adjacent to one another will coalesce into a single range.
|
||||
*/
|
||||
public class SortedRangeList implements Iterable<Range> {
|
||||
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
|
||||
* specified list.
|
||||
* Creates a new sorted range list with ranges equivalent to those in the specified list.
|
||||
* @param list the sorted range list to make an equivalent copy of.
|
||||
*/
|
||||
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.
|
||||
* If the range is adjacent to or overlaps any other existing ranges,
|
||||
* then those ranges will coalesce.
|
||||
* Adds the range from min to max to this sorted range list. If the range is adjacent to or
|
||||
* overlaps any other existing ranges, then those ranges will coalesce.
|
||||
* @param min the range minimum
|
||||
* @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.
|
||||
* @return the iterator
|
||||
*/
|
||||
public Iterator<Range> getRanges() {
|
||||
return set.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over all the ranges in this list that iterates in the direction specified.
|
||||
* @param forward true indicates to iterate forward from minimum to maximum range.
|
||||
* false indicates backward iteration form maximum to minimum.
|
||||
* Returns an iterator over all the ranges in this list that iterates in the direction
|
||||
* specified.
|
||||
* @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) {
|
||||
if (forward) {
|
||||
|
@ -106,6 +106,7 @@ public class SortedRangeList implements Iterable<Range> {
|
|||
|
||||
/**
|
||||
* Returns the minimum int value in this sorted range list.
|
||||
* @return the min value
|
||||
* @throws NoSuchElementException if the list is empty.
|
||||
*/
|
||||
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.
|
||||
* @return the max value
|
||||
* @throws NoSuchElementException if the list is empty.
|
||||
*/
|
||||
public int getMax() throws NoSuchElementException {
|
||||
|
@ -124,6 +126,7 @@ public class SortedRangeList implements Iterable<Range> {
|
|||
|
||||
/**
|
||||
* Returns the number of ranges in the list.
|
||||
* @return the number of ranges
|
||||
*/
|
||||
public int getNumRanges() {
|
||||
return set.size();
|
||||
|
@ -182,6 +185,7 @@ public class SortedRangeList implements Iterable<Range> {
|
|||
/**
|
||||
* Returns true if the value is contained in any ranges within this list.
|
||||
* @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) {
|
||||
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.
|
||||
* @param min the minimum 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) {
|
||||
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 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) {
|
||||
Range key = new Range(min, min);
|
||||
|
@ -309,6 +317,7 @@ public class SortedRangeList implements Iterable<Range> {
|
|||
|
||||
/**
|
||||
* Returns true if the range list is empty.
|
||||
* @return true if the range list is empty.
|
||||
*/
|
||||
public boolean 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
|
||||
* @return the new SortedRangeList representing the intersection.
|
||||
*/
|
||||
|
@ -339,12 +349,9 @@ public class SortedRangeList implements Iterable<Range> {
|
|||
return srl2;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
Iterator<Range> it = getRanges();
|
||||
if (it.hasNext()) {
|
||||
Range r = it.next();
|
||||
|
@ -365,4 +372,28 @@ public class SortedRangeList implements Iterable<Range> {
|
|||
public void 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,8 +81,8 @@ public class ReferenceTagProcessor extends TagProcessor {
|
|||
if ("a".equals(tagType)) {
|
||||
if (tagAttributes.containsKey("href")) {
|
||||
try {
|
||||
anchorManager.addAnchorRef(
|
||||
new HREF(help, file, tagAttributes.get("href"), lineNum));
|
||||
anchorManager
|
||||
.addAnchorRef(new HREF(help, file, tagAttributes.get("href"), lineNum));
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
errorCount++;
|
||||
|
@ -102,8 +102,8 @@ public class ReferenceTagProcessor extends TagProcessor {
|
|||
else if ("img".equals(tagType)) {
|
||||
if (tagAttributes.containsKey("src")) {
|
||||
try {
|
||||
anchorManager.addImageRef(
|
||||
new IMG(help, file, tagAttributes.get("src"), lineNum));
|
||||
anchorManager
|
||||
.addImageRef(new IMG(help, file, tagAttributes.get("src"), lineNum));
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
errorCount++;
|
||||
|
@ -191,7 +191,8 @@ public class ReferenceTagProcessor extends TagProcessor {
|
|||
if (!hasDefaultStyleSheet) {
|
||||
errorCount++;
|
||||
errors.append("Incorrect stylesheet defined - none match " + defaultStyleSheet +
|
||||
" in file " + htmlFile + EOL);
|
||||
" in file " + htmlFile + EOL + "\tDiscovered stylesheets: " + styleSheets + EOL);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -227,7 +227,7 @@ public interface Data extends CodeUnit, Settings {
|
|||
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
|
||||
* containing the specified offset (see {@link #getComponentsContaining(int)}).
|
||||
*
|
||||
|
|
|
@ -17,13 +17,15 @@
|
|||
|
||||
package ghidra.program.util;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* The <CODE>FieldNameFieldLocation</CODE> class provides specific information
|
||||
* about the Function Name field within a program location.
|
||||
* The <CODE>FieldNameFieldLocation</CODE> class provides specific information about the Function
|
||||
* Name field within a program location.
|
||||
*/
|
||||
public class FieldNameFieldLocation extends CodeUnitLocation {
|
||||
|
||||
|
@ -33,9 +35,9 @@ public class FieldNameFieldLocation extends CodeUnitLocation {
|
|||
* Construct a new FieldNameFieldLocation.
|
||||
*
|
||||
* @param program the program of the location
|
||||
* @param addr the address of the codeunit.
|
||||
* @param componentPath if not null, it is the array of indexes that point
|
||||
* to a specific data type inside of another data type
|
||||
* @param addr the address of the code unit
|
||||
* @param componentPath if not null, it is the array of indexes that point to a specific data
|
||||
* type inside of another data type
|
||||
* @param fieldName the field name
|
||||
* @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
|
||||
* a field name location from XML
|
||||
* Default constructor needed for restoring a field name location from XML
|
||||
*/
|
||||
public FieldNameFieldLocation() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field name of this location.
|
||||
* @return the name.
|
||||
*/
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
|
@ -71,19 +73,19 @@ public class FieldNameFieldLocation extends CodeUnitLocation {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
if (this == obj) {
|
||||
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;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
FieldNameFieldLocation other = (FieldNameFieldLocation) obj;
|
||||
if (!Objects.equals(fieldName, other.fieldName)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,16 @@
|
|||
*/
|
||||
package ghidra.program.util;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.VariableOffset;
|
||||
|
||||
/**
|
||||
* The <CODE>OperandFieldLocation</CODE> class contains specific location information
|
||||
* within the OPERAND field of a CodeUnitLocation object.
|
||||
* The <CODE>OperandFieldLocation</CODE> class contains specific location information within the
|
||||
* OPERAND field of a CodeUnitLocation object.
|
||||
*/
|
||||
public class OperandFieldLocation extends CodeUnitLocation {
|
||||
|
||||
|
@ -33,16 +35,17 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
|||
/**
|
||||
* Construct a new OperandFieldLocation object.
|
||||
*
|
||||
* @param program the program of the location
|
||||
* @param addr address of the location; should not be null
|
||||
* @param componentPath array of indexes for each nested data component; the
|
||||
* index is the data component's index within its parent; may be null
|
||||
* @param program the program of the location.
|
||||
* @param addr address of the location; should not be null.
|
||||
* @param componentPath array of indexes for each nested data component; the index is the data
|
||||
* component's index within its parent; may be null.
|
||||
* @param refAddr the reference 'to' address.
|
||||
* @param rep the String representation of the operand.
|
||||
* @param opIndex the index of the operand at this location.
|
||||
* @param characterOffset the character position from the beginning of the operand.
|
||||
*/
|
||||
public OperandFieldLocation(Program program, Address addr, int[] componentPath,
|
||||
Address refAddr, String rep, int opIndex, int characterOffset) {
|
||||
public OperandFieldLocation(Program program, Address addr, int[] componentPath, Address refAddr,
|
||||
String rep, int opIndex, int characterOffset) {
|
||||
|
||||
super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset);
|
||||
|
||||
|
@ -53,23 +56,20 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
|||
/**
|
||||
* Construct a new OperandFieldLocation object.
|
||||
*
|
||||
* @param program the program of the location
|
||||
* @param addr address of the location; should not be null
|
||||
* @param componentPath array of indexes for each nested data component; the
|
||||
* index is the data component's index within its parent; may be null
|
||||
* @param refAddr the "referred to" address if the location is
|
||||
* over a reference; may be null
|
||||
* @param program the program of the location.
|
||||
* @param addr address of the location; should not be null.
|
||||
* @param componentPath array of indexes for each nested data component; the index is the data
|
||||
* component's index within its parent; may be null .
|
||||
* @param refAddr the "referred to" address if the location is over a reference; may be null.
|
||||
* @param rep the String representation of the operand.
|
||||
* @param opIndex the index indicating the operand the location is on.
|
||||
* @param subOpIndex the index of the Object within the operand, this can
|
||||
* be used to call an instructions getOpObjects() method
|
||||
* @param characterOffset the character position from the beginning of the operand field
|
||||
* @param subOpIndex the index of the Object within the operand, this can be used to call an
|
||||
* instructions getOpObjects() method.
|
||||
* @param characterOffset the character position from the beginning of the operand field.
|
||||
*/
|
||||
public OperandFieldLocation(Program program, Address addr, int[] componentPath,
|
||||
Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) {
|
||||
|
||||
public OperandFieldLocation(Program program, Address addr, int[] componentPath, Address refAddr,
|
||||
String rep, int opIndex, int subOpIndex, int characterOffset) {
|
||||
super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset);
|
||||
|
||||
this.rep = rep;
|
||||
this.subOpIndex = subOpIndex;
|
||||
}
|
||||
|
@ -77,43 +77,41 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
|||
/**
|
||||
* Construct a new OperandFieldLocation object for an instruction operand.
|
||||
*
|
||||
* @param program the program of the location
|
||||
* @param addr address of the location; should not be null
|
||||
* @param variableOffset associated variable offset or null
|
||||
* @param refAddr the "referred to" address if the location is
|
||||
* over a reference; may be null
|
||||
* @param program the program of the location.
|
||||
* @param addr address of the location; should not be null.
|
||||
* @param variableOffset associated variable offset or null.
|
||||
* @param refAddr the "referred to" address if the location is over a reference; may be null.
|
||||
* @param rep the String representation of the operand.
|
||||
* @param opIndex the index indicating the operand the location is on.
|
||||
* @param subOpIndex the index of the Object within the operand, this can
|
||||
* be used to call an instructions getOpObjects() method
|
||||
* @param characterOffset the character position from the beginning of the operand field
|
||||
* @param subOpIndex the index of the Object within the operand, this can be used to call an
|
||||
* instructions getOpObjects() method.
|
||||
* @param characterOffset the character position from the beginning of the operand field.
|
||||
*/
|
||||
public OperandFieldLocation(Program program, Address addr, VariableOffset variableOffset,
|
||||
Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) {
|
||||
|
||||
super(program, addr, null, refAddr, 0, opIndex, characterOffset);
|
||||
|
||||
this.rep = rep;
|
||||
this.subOpIndex = subOpIndex;
|
||||
this.variableOffset = variableOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor needed for restoring
|
||||
* an operand field location from XML.
|
||||
*/
|
||||
* Default constructor needed for restoring an operand field location from XML.
|
||||
*/
|
||||
public OperandFieldLocation() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns VariableOffset object if applicable or null
|
||||
* Returns VariableOffset object if applicable or null.
|
||||
* @return the variable offset.
|
||||
*/
|
||||
public VariableOffset getVariableOffset() {
|
||||
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() {
|
||||
return rep;
|
||||
|
@ -121,6 +119,7 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
|||
|
||||
/**
|
||||
* Returns the index of the operand at this location.
|
||||
* @return the index
|
||||
*/
|
||||
public int getOperandIndex() {
|
||||
return getColumn();
|
||||
|
@ -128,18 +127,15 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
|||
|
||||
/**
|
||||
* Returns the sub operand index at this location.
|
||||
* This index can be used on the instruction.getOpObjects()
|
||||
* to find the actual object (Address, Register, Scalar) the
|
||||
* cursor is over.
|
||||
* <p>
|
||||
* This index can be used on the instruction.getOpObjects() to find the actual object (Address,
|
||||
* Register, Scalar) the cursor is over.
|
||||
* @return 0-n if over a valid OpObject, -1 otherwise
|
||||
*/
|
||||
public int getSubOperandIndex() {
|
||||
return subOpIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String representation of this location.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + ", OpRep = " + rep + ", subOpIndex = " + subOpIndex +
|
||||
|
@ -158,27 +154,25 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
if (this == obj) {
|
||||
return true;
|
||||
if (!super.equals(obj))
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
OperandFieldLocation other = (OperandFieldLocation) obj;
|
||||
if (rep == null) {
|
||||
if (other.rep != null)
|
||||
return false;
|
||||
if (!Objects.equals(rep, other.rep)) {
|
||||
return false;
|
||||
}
|
||||
else if (!rep.equals(other.rep))
|
||||
if (subOpIndex != other.subOpIndex) {
|
||||
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 true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
* object is an {@link Instruction} or a top-level {@link Data} object.
|
||||
* @return the path.
|
||||
*/
|
||||
public int[] getComponentPath() {
|
||||
return componentPath;
|
||||
|
@ -177,6 +178,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
|||
|
||||
/**
|
||||
* Returns the program associated with this location.
|
||||
* @return the program.
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return program;
|
||||
|
@ -189,6 +191,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
|||
* 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
|
||||
* address.
|
||||
* @return the address.
|
||||
*/
|
||||
public Address getAddress() {
|
||||
return addr;
|
||||
|
@ -196,6 +199,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
|||
|
||||
/**
|
||||
* Returns the byte level address associated with this location.
|
||||
* @return the byte address.
|
||||
*/
|
||||
public Address getByteAddress() {
|
||||
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.
|
||||
* @return the address.
|
||||
*/
|
||||
public Address getRefAddress() {
|
||||
return refAddr;
|
||||
|
@ -274,7 +279,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
|||
}
|
||||
// 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) {
|
||||
// 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);
|
||||
|
||||
// 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) {
|
||||
Data data = (Data) cu;
|
||||
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.
|
||||
* @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) {
|
||||
return addr == null || testProgram.getAddressFactory().isValidAddress(addr);
|
||||
|
@ -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() {
|
||||
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
|
||||
* locations, there is only one display item per row, in which case this value will be 0.
|
||||
* @return the column.
|
||||
*/
|
||||
public int getColumn() {
|
||||
return col;
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue