mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +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>
|
references to the address 0040767d.</P>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P><IMG alt="" src="../../shared/tip.png">You can also <A href="#Data_Types">show
|
<P><IMG alt="" src="images/tip.png">You can also <A href="#Data_Types">show
|
||||||
references to data types</A> from the Data Type Manager. In this case, all locations where
|
references to data types</A> from the Data Type Manager. In this case, all locations where
|
||||||
the selected data type is applied will be highlighted.</P>
|
the selected data type is applied will be highlighted.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
@ -73,11 +73,11 @@
|
||||||
|
|
||||||
<LI>Select whichever of the following is available from the popup menu:
|
<LI>Select whichever of the following is available from the popup menu:
|
||||||
<UL>
|
<UL>
|
||||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Show References to</B>
|
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Show References to</B>
|
||||||
from the popup menu.</LI>
|
from the popup menu.</LI>
|
||||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Find References to</B>
|
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Find References to</B>
|
||||||
from the popup menu.</LI>
|
from the popup menu.</LI>
|
||||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Find Uses of</B>
|
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Find Uses of</B>
|
||||||
from the popup menu.</LI>
|
from the popup menu.</LI>
|
||||||
</UL>
|
</UL>
|
||||||
</OL>
|
</OL>
|
||||||
|
@ -134,12 +134,12 @@
|
||||||
</P>
|
</P>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>
|
<P>
|
||||||
<IMG alt="" src="../../shared/tip.png">
|
<IMG alt="" src="images/tip.png">
|
||||||
To instead make a Program Selection Highlight, use the select button mentioned above.
|
To instead make a Program Selection Highlight, use the select button mentioned above.
|
||||||
Then, click from the menu bar <B>Select
|
Then, click from the menu bar <B>Select
|
||||||
<img src="../../shared/arrow.gif" alt="->" border="0">
|
<img src="images/arrow.gif" alt="->" border="0">
|
||||||
Program Highlight
|
Program Highlight
|
||||||
<img src="../../shared/arrow.gif" alt="->" border="0">
|
<img src="images/arrow.gif" alt="->" border="0">
|
||||||
Entire Selection
|
Entire Selection
|
||||||
</B>
|
</B>
|
||||||
</P>
|
</P>
|
||||||
|
@ -167,7 +167,7 @@
|
||||||
the decompiled function will be shown.
|
the decompiled function will be shown.
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
<P><A name="Make_Selection"></A><IMG alt="" src="../../shared/tip.png"> You can make a
|
<P><A name="Make_Selection"></A><IMG alt="" src="images/tip.png"> You can make a
|
||||||
selection in the Code Browser from the entries in the table:</P>
|
selection in the Code Browser from the entries in the table:</P>
|
||||||
|
|
||||||
<OL>
|
<OL>
|
||||||
|
@ -198,16 +198,26 @@
|
||||||
the action will display all places that function is applied.</P>
|
the action will display all places that function is applied.</P>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
<IMG border="0" src="images/tip.png" alt=""> When searching for references to a field of
|
||||||
|
a composite data type via the popup menu in the Data Type Manager,
|
||||||
|
you can search by name or by offset within the parent data type. This is useful when
|
||||||
|
the field you seek does not have a name.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>
|
<P>
|
||||||
<IMG border="0" src="../../shared/note.png" alt="">
|
<IMG border="0" src="images/note.png" alt="">
|
||||||
<A name="Data_Type_Discovery"></A>
|
<A name="Data_Type_Discovery"></A>
|
||||||
By default, finding uses of
|
By default, finding uses of
|
||||||
data types will search not only for applied data types, but also will perform
|
data types will search not only for applied data types, but also will perform
|
||||||
dynamic discovery of data types using the <CODE>Data Type Reference Finder</CODE>
|
dynamic discovery of data types using the <CODE>Data Type Reference Finder</CODE>
|
||||||
service. This causes the search to be slower, but also reports many more type
|
service. This causes the search to be slower, but also reports many more type
|
||||||
uses. To disable the dynamic searching, use the
|
uses. To disable the dynamic searching, use the
|
||||||
<B>Search</B><img src="../../shared/arrow.gif" alt="->" border="0">
|
<B>Search</B><img src="images/arrow.gif" alt="->" border="0">
|
||||||
<B>Dynamic Data Type Discovery</B>
|
<B>Dynamic Data Type Discovery</B>
|
||||||
<A href="help/topics/Tool/ToolOptions_Dialog.htm">tool option</A>.
|
<A href="help/topics/Tool/ToolOptions_Dialog.htm">tool option</A>.
|
||||||
</P>
|
</P>
|
||||||
|
@ -221,7 +231,7 @@
|
||||||
<OL start="1" type="1">
|
<OL start="1" type="1">
|
||||||
<LI>Right-mouse anywhere on the code unit*</LI>
|
<LI>Right-mouse anywhere on the code unit*</LI>
|
||||||
|
|
||||||
<LI>Select <B>References <IMG alt="" src="../../shared/arrow.gif"> Show References to
|
<LI>Select <B>References <IMG alt="" src="images/arrow.gif"> Show References to
|
||||||
Address </B> from the popup menu.</LI>
|
Address </B> from the popup menu.</LI>
|
||||||
</OL>
|
</OL>
|
||||||
|
|
||||||
|
@ -240,7 +250,7 @@
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>
|
<P>
|
||||||
<IMG border="0" src="../../shared/note.png" alt=""> This action will show only direct
|
<IMG border="0" src="images/note.png" alt=""> This action will show only direct
|
||||||
references to the current code unit. No other special reference finding will take place.
|
references to the current code unit. No other special reference finding will take place.
|
||||||
</P>
|
</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
@ -250,7 +260,7 @@
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>
|
<P>
|
||||||
<IMG border="0" src="../../shared/tip.png" alt=""> <I>see <A href=
|
<IMG border="0" src="images/tip.png" alt=""> <I>see <A href=
|
||||||
"help/topics/DockingWindows/Docking_Windows.htm#Renaming_Windows">Docking Windows - Renaming
|
"help/topics/DockingWindows/Docking_Windows.htm#Renaming_Windows">Docking Windows - Renaming
|
||||||
Windows</A></I>
|
Windows</A></I>
|
||||||
</P>
|
</P>
|
||||||
|
|
|
@ -25,9 +25,10 @@ import docking.DockingUtils;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.app.util.HelpTopics;
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
|
||||||
public abstract class AbstractFindReferencesDataTypeAction extends DockingAction {
|
public abstract class AbstractFindReferencesDataTypeAction extends DockingAction {
|
||||||
|
@ -89,7 +90,40 @@ public abstract class AbstractFindReferencesDataTypeAction extends DockingAction
|
||||||
DataType dataType = getDataType(context);
|
DataType dataType = getDataType(context);
|
||||||
DataType baseDataType = ReferenceUtils.getBaseDataType(dataType);
|
DataType baseDataType = ReferenceUtils.getBaseDataType(dataType);
|
||||||
String field = getDataTypeField(baseDataType);
|
String field = getDataTypeField(baseDataType);
|
||||||
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(baseDataType, field));
|
Swing.runLater(() -> doFindDataTypeUsage(service, baseDataType, field));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void doFindDataTypeUsage(FindAppliedDataTypesService service, DataType dt,
|
||||||
|
String field) {
|
||||||
|
|
||||||
|
if (field == null) {
|
||||||
|
// no field specified; search for all uses of the given type
|
||||||
|
service.findAndDisplayAppliedDataTypeAddresses(dt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dt instanceof Structure) {
|
||||||
|
Integer offset = getOffsetForDeafaultFieldName((Structure) dt, field);
|
||||||
|
if (offset != null) {
|
||||||
|
// The user has picked a field by it's default name. In this case we need to
|
||||||
|
// search by offset to ensure we find code that does not use the default name.
|
||||||
|
FieldMatcher fieldMatcher = new FieldMatcher(dt, offset);
|
||||||
|
service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service.findAndDisplayAppliedDataTypeAddresses(dt, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getOffsetForDeafaultFieldName(Structure structure, String fieldName) {
|
||||||
|
DataTypeComponent[] components = structure.getComponents();
|
||||||
|
for (DataTypeComponent dtc : components) {
|
||||||
|
String defaultName = dtc.getDefaultFieldName();
|
||||||
|
if (fieldName.equals(defaultName)) {
|
||||||
|
return dtc.getOffset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,8 @@ public class FindDataTypesBySizeAction extends DockingAction {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
|
|
||||||
NumberRangeInputDialog inputDialog =
|
NumberRangeInputDialog inputDialog = new NumberRangeInputDialog(getName(), "Size(s)");
|
||||||
new NumberRangeInputDialog(getName(), "Size(s)");
|
inputDialog.setHelpLocation(getHelpLocation());
|
||||||
if (!inputDialog.show()) {
|
if (!inputDialog.show()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
import ghidra.program.model.data.Enum;
|
||||||
|
@ -98,15 +99,27 @@ public class FindReferencesToFieldAction extends DockingAction {
|
||||||
choices = ((Enum) dt).getNames();
|
choices = ((Enum) dt).getNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
String userChoice = OptionDialog.showInputChoiceDialog(null, "Choose Field",
|
String message = "Find uses of '" + dt.getName() + "' field by name or offset";
|
||||||
"Find uses of '" + dt.getName() + "' field", choices, null,
|
String userChoice = OptionDialog.showEditableInputChoiceDialog(null, "Choose Field",
|
||||||
OptionDialog.QUESTION_MESSAGE);
|
message, choices, null, OptionDialog.QUESTION_MESSAGE);
|
||||||
if (userChoice == null) {
|
if (userChoice == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Swing.runLater(
|
FieldMatcher fieldMatcher;
|
||||||
() -> service.findAndDisplayAppliedDataTypeAddresses(dt, userChoice));
|
Long longChoice = parseInt(userChoice);
|
||||||
|
if (longChoice != null) {
|
||||||
|
fieldMatcher = new FieldMatcher(dt, longChoice.intValue());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fieldMatcher = new FieldMatcher(dt, userChoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long parseInt(String s) {
|
||||||
|
return NumericUtilities.parseNumber(s, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] getCompisiteFieldNames(Composite composite) {
|
private String[] getCompisiteFieldNames(Composite composite) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.navigation;
|
package ghidra.app.plugin.core.navigation;
|
||||||
|
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,7 +26,7 @@ public interface FindAppliedDataTypesService {
|
||||||
/**
|
/**
|
||||||
* Tells this service to find all places where the given datatype is applied <b>and</b> will
|
* Tells this service to find all places where the given datatype is applied <b>and</b> will
|
||||||
* display the results of the search.
|
* display the results of the search.
|
||||||
*
|
*
|
||||||
* @param dataType The datatype which to base the search upon.
|
* @param dataType The datatype which to base the search upon.
|
||||||
*/
|
*/
|
||||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType);
|
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType);
|
||||||
|
@ -33,9 +34,23 @@ public interface FindAppliedDataTypesService {
|
||||||
/**
|
/**
|
||||||
* Tells this service to find all places where the given datatype is applied <b>and</b> will
|
* Tells this service to find all places where the given datatype is applied <b>and</b> will
|
||||||
* display the results of the search.
|
* display the results of the search.
|
||||||
*
|
*
|
||||||
* @param dataType The datatype which to base the search upon.
|
* @param dataType The datatype which to base the search upon.
|
||||||
* @param fieldName the sub-field for which to search
|
* @param fieldName the sub-field for which to search
|
||||||
*/
|
*/
|
||||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName);
|
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells this service to find all places where the given datatype is applied <b>and</b> will
|
||||||
|
* display the results of the search.
|
||||||
|
* <p>
|
||||||
|
* The supplied field matcher will be used to restrict matches to the given field. The matcher
|
||||||
|
* may be 'empty', supplying only the data type for which to search. In this case, all uses
|
||||||
|
* of the type will be matched, regardless of field.
|
||||||
|
*
|
||||||
|
* @param dataType The datatype which to base the search upon.
|
||||||
|
* @param fieldMatcher the field matcher.
|
||||||
|
*/
|
||||||
|
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType,
|
||||||
|
FieldMatcher fieldMatcher);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,8 @@ import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A location descriptor that should be extended by location descriptor implementations that
|
* A location descriptor that should be extended by location descriptor implementations that are
|
||||||
* are based upon data types.
|
* based upon data types.
|
||||||
*/
|
*/
|
||||||
abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
||||||
|
|
||||||
|
@ -70,18 +70,28 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
||||||
findDataTypeReferences(accumulator, monitor);
|
findDataTypeReferences(accumulator, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The original data type that this location descriptor describes */
|
/**
|
||||||
|
* The original data type that this location descriptor describes
|
||||||
|
* @return the type
|
||||||
|
*/
|
||||||
protected abstract DataType getSourceDataType();
|
protected abstract DataType getSourceDataType();
|
||||||
|
|
||||||
/** Generates the label for the results window */
|
/**
|
||||||
|
* Generates the label for the results window
|
||||||
|
* @return the label
|
||||||
|
*/
|
||||||
protected abstract String generateLabel();
|
protected abstract String generateLabel();
|
||||||
|
|
||||||
/** Returns the name of the data type, for example, 'Foo' or 'Foo.bar.baz' */
|
/**
|
||||||
|
* Returns the name of the data type, for example, 'Foo' or 'Foo.bar.baz'
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
protected abstract String getDataTypeName();
|
protected abstract String getDataTypeName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base data type that this location descriptor describes (this may be the same as the
|
* The base data type that this location descriptor describes (this may be the same as the
|
||||||
* original data type.
|
* original data type.
|
||||||
|
* @return the type.
|
||||||
*/
|
*/
|
||||||
protected DataType getBaseDataType() {
|
protected DataType getBaseDataType() {
|
||||||
return getSourceDataType(); // by default these two values are the same
|
return getSourceDataType(); // by default these two values are the same
|
||||||
|
@ -91,7 +101,7 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
||||||
TaskMonitor monitor) throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
DataType currentDataType = getDataType();
|
DataType currentDataType = getDataType();
|
||||||
ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, null, program,
|
ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, program,
|
||||||
useDynamicSearching, monitor);
|
useDynamicSearching, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,8 +187,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
||||||
List<Variable> allVariables = ReferenceUtils.getVariables(function, true);
|
List<Variable> allVariables = ReferenceUtils.getVariables(function, true);
|
||||||
for (Variable variable : allVariables) {
|
for (Variable variable : allVariables) {
|
||||||
DataType variableDataType = variable.getDataType();
|
DataType variableDataType = variable.getDataType();
|
||||||
if (ReferenceUtils.getBaseDataType(variableDataType).isEquivalent(
|
if (ReferenceUtils.getBaseDataType(variableDataType)
|
||||||
currentDataType)) {
|
.isEquivalent(currentDataType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,8 +276,8 @@ abstract class DataTypeLocationDescriptor extends LocationDescriptor {
|
||||||
}
|
}
|
||||||
// check for pointer names
|
// check for pointer names
|
||||||
else if (label.endsWith("*") && label.startsWith(paramName)) {
|
else if (label.endsWith("*") && label.startsWith(paramName)) {
|
||||||
// see if we need to chop off some '*'s, as we may have searched for a pointer to a
|
// see if we need to chop off some '*'s, as we may have searched for a pointer to a
|
||||||
// pointer and have found a match against a simple pointer and thus the display may
|
// pointer and have found a match against a simple pointer and thus the display may
|
||||||
// not match our label
|
// not match our label
|
||||||
if (paramParts.length == 1) {
|
if (paramParts.length == 1) {
|
||||||
return paramName; // not a full declaration, just the name
|
return paramName; // not a full declaration, just the name
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.awt.Color;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.support.Highlight;
|
import docking.widgets.fieldpanel.support.Highlight;
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.app.util.viewer.field.*;
|
import ghidra.app.util.viewer.field.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Data;
|
import ghidra.program.model.listing.Data;
|
||||||
|
@ -34,34 +35,39 @@ import ghidra.util.task.TaskMonitor;
|
||||||
*/
|
*/
|
||||||
public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeLocationDescriptor {
|
public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeLocationDescriptor {
|
||||||
|
|
||||||
private String typeAndFieldName;
|
// this is either "Foo.bar" or "Foo offset 1"
|
||||||
private String fieldName;
|
private String typeDisplayString;
|
||||||
|
private FieldMatcher fieldMatcher;
|
||||||
|
|
||||||
public GenericCompositeDataTypeLocationDescriptor(
|
public GenericCompositeDataTypeLocationDescriptor(
|
||||||
GenericCompositeDataTypeProgramLocation location, Program program) {
|
GenericCompositeDataTypeProgramLocation location, Program program) {
|
||||||
super(location, program, location.getDataType());
|
super(location, program, location.getDataType());
|
||||||
|
|
||||||
this.fieldName = location.getFieldName();
|
this.fieldMatcher = location.getFieldMatcher();
|
||||||
this.typeAndFieldName = getDataTypeName() + '.' + fieldName;
|
this.typeDisplayString = fieldMatcher.getDisplayText();
|
||||||
label = generateLabel();
|
label = generateLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
|
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
ReferenceUtils.findDataTypeReferences(accumulator, getDataType(), fieldName, program,
|
ReferenceUtils.findDataTypeFieldReferences(accumulator, fieldMatcher, program,
|
||||||
useDynamicSearching, monitor);
|
useDynamicSearching, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTypeName() {
|
public String getTypeName() {
|
||||||
return super.getTypeName() + "." + fieldName;
|
// not sure why this code was doing this and not using our variable; oversight?
|
||||||
|
// return super.getTypeName() + "." + fieldName;
|
||||||
|
return typeDisplayString;
|
||||||
}
|
}
|
||||||
|
|
||||||
// implemented to ignore the location being provided, since this is a 'dummy' type class
|
// implemented to ignore the location being provided, since this is a 'dummy' type class
|
||||||
@Override
|
@Override
|
||||||
protected String generateLabel() {
|
protected String generateLabel() {
|
||||||
return "\"" + originalDataType.getName() + "." + fieldName + "\" (DataType)";
|
// changed this; keep old code around in case an edge case appears
|
||||||
|
// return "\"" + originalDataType.getName() + "." + fieldName + "\" (DataType)";
|
||||||
|
return "\"" + typeDisplayString + "\" (DataType)";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overridden to perform a simple check against data types, since the program locations are
|
// Overridden to perform a simple check against data types, since the program locations are
|
||||||
|
@ -79,7 +85,7 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
|
||||||
GenericCompositeDataTypeLocationDescriptor otherDescriptor =
|
GenericCompositeDataTypeLocationDescriptor otherDescriptor =
|
||||||
(GenericCompositeDataTypeLocationDescriptor) obj;
|
(GenericCompositeDataTypeLocationDescriptor) obj;
|
||||||
return getDataType().equals(otherDescriptor.getDataType()) &&
|
return getDataType().equals(otherDescriptor.getDataType()) &&
|
||||||
fieldName.equals(otherDescriptor.fieldName);
|
fieldMatcher.equals(otherDescriptor.fieldMatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -113,14 +119,15 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
|
||||||
else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
else if (OperandFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
||||||
|
|
||||||
// Not sure how to get the correct part of the text. This is a hack for now.
|
// Not sure how to get the correct part of the text. This is a hack for now.
|
||||||
int offset = StringUtils.indexOfIgnoreCase(text, typeAndFieldName, 0);
|
int offset = StringUtils.indexOfIgnoreCase(text, typeDisplayString, 0);
|
||||||
if (offset != -1) {
|
if (offset != -1) {
|
||||||
return new Highlight[] {
|
return new Highlight[] { new Highlight(offset,
|
||||||
new Highlight(offset, offset + typeAndFieldName.length() - 1, highlightColor) };
|
offset + typeDisplayString.length() - 1, highlightColor) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (FieldNameFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
else if (FieldNameFieldFactory.class.isAssignableFrom(fieldFactoryClass)) {
|
||||||
|
|
||||||
|
String fieldName = fieldMatcher.getFieldName();
|
||||||
if (text.equalsIgnoreCase(fieldName)) {
|
if (text.equalsIgnoreCase(fieldName)) {
|
||||||
return new Highlight[] { new Highlight(0, text.length(), highlightColor) };
|
return new Highlight[] { new Highlight(0, text.length(), highlightColor) };
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,28 +17,39 @@ package ghidra.app.plugin.core.navigation.locationreferences;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.program.model.data.Composite;
|
import ghidra.program.model.data.Composite;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.exception.AssertException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to signal that the ProgramLocation is used for data types and is not really
|
* A class to signal that the ProgramLocation is used for data types and is not really
|
||||||
* connected to the listing. This is a subclass is designed for data types that have fields, such
|
* connected to the listing. This is a subclass is designed for data types that have fields, such
|
||||||
* as {@link Composite} types and {@link Enum} types.
|
* as {@link Composite} types and {@link Enum} types.
|
||||||
*
|
*
|
||||||
* @see GenericCompositeDataTypeLocationDescriptor
|
* @see GenericCompositeDataTypeLocationDescriptor
|
||||||
*/
|
*/
|
||||||
public class GenericCompositeDataTypeProgramLocation extends GenericDataTypeProgramLocation {
|
public class GenericCompositeDataTypeProgramLocation extends GenericDataTypeProgramLocation {
|
||||||
|
|
||||||
private String fieldName;
|
private FieldMatcher fieldMatcher;
|
||||||
|
|
||||||
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, String fieldName) {
|
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, String fieldName) {
|
||||||
|
this(program, dataType, new FieldMatcher(dataType, fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType,
|
||||||
|
FieldMatcher fieldMatcher) {
|
||||||
super(program, dataType);
|
super(program, dataType);
|
||||||
this.fieldName = Objects.requireNonNull(fieldName);
|
this.fieldMatcher = Objects.requireNonNull(fieldMatcher);
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (!Objects.equals(dataType, fieldMatcher.getDataType())) {
|
||||||
|
throw new AssertException("Data type does not match the FieldMatcher type");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFieldName() {
|
public FieldMatcher getFieldMatcher() {
|
||||||
return fieldName;
|
return fieldMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,10 +260,10 @@ public abstract class LocationDescriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a generic {@link ProgramLocation} based upon the <tt>program</tt> and
|
* Returns a generic {@link ProgramLocation} based upon the <tt>program</tt> and
|
||||||
* <tt>homeAddress</tt> of this <tt>LocationDescriptor</tt>. Subclasses should override this
|
* <tt>homeAddress</tt> of this <tt>LocationDescriptor</tt>. Subclasses should override this
|
||||||
* method to return more specific addresses.
|
* method to return more specific addresses.
|
||||||
*
|
*
|
||||||
* @return a generic ProgramLocation.
|
* @return a generic ProgramLocation.
|
||||||
*/
|
*/
|
||||||
ProgramLocation getHomeLocation() {
|
ProgramLocation getHomeLocation() {
|
||||||
|
@ -329,9 +329,9 @@ public abstract class LocationDescriptor {
|
||||||
TaskMonitor monitor) throws CancelledException;
|
TaskMonitor monitor) throws CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a descriptive category name for this location descriptor. This is used for
|
* Returns a descriptive category name for this location descriptor. This is used for
|
||||||
* display in a popup menu.
|
* display in a popup menu.
|
||||||
*
|
*
|
||||||
* @return a descriptive category name for this location descriptor
|
* @return a descriptive category name for this location descriptor
|
||||||
*/
|
*/
|
||||||
public String getTypeName() {
|
public String getTypeName() {
|
||||||
|
@ -355,8 +355,8 @@ public abstract class LocationDescriptor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all location references for the given descriptor, loading them if not already loaded.
|
* Gets all location references for the given descriptor, loading them if not already loaded.
|
||||||
*
|
*
|
||||||
* @param accumulator the datastructure into which will be placed a collection of
|
* @param accumulator the datastructure into which will be placed a collection of
|
||||||
* location references that reference the location this descriptor is representing.
|
* location references that reference the location this descriptor is representing.
|
||||||
* @param monitor A monitor to report progress or cancel the gathering of addresses.
|
* @param monitor A monitor to report progress or cancel the gathering of addresses.
|
||||||
* @param reload True signals to perform a new search for reference addresses; false will
|
* @param reload True signals to perform a new search for reference addresses; false will
|
||||||
|
@ -370,10 +370,10 @@ public abstract class LocationDescriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When true, the search algorithm will use dynamic searching when possible, which is to
|
* When true, the search algorithm will use dynamic searching when possible, which is to
|
||||||
* not only find references that are already created, but to also use external tools to
|
* not only find references that are already created, but to also use external tools to
|
||||||
* locate potential references.
|
* locate potential references.
|
||||||
*
|
*
|
||||||
* @param useDynamicSearching true to perform dynamic searching
|
* @param useDynamicSearching true to perform dynamic searching
|
||||||
*/
|
*/
|
||||||
void setUseDynamicSearching(boolean useDynamicSearching) {
|
void setUseDynamicSearching(boolean useDynamicSearching) {
|
||||||
|
@ -384,7 +384,7 @@ public abstract class LocationDescriptor {
|
||||||
* Sets a listener on this descriptor that will be notified when the references contained
|
* Sets a listener on this descriptor that will be notified when the references contained
|
||||||
* in this descriptor may no longer be accurate. For example, the listener will be called
|
* in this descriptor may no longer be accurate. For example, the listener will be called
|
||||||
* when an undo or redo is performed in Ghidra.
|
* when an undo or redo is performed in Ghidra.
|
||||||
* @param modelChangeListener The listener to add.
|
* @param modelFreshnessListener The listener to add.
|
||||||
*/
|
*/
|
||||||
void setModelFreshnessListener(ChangeListener modelFreshnessListener) {
|
void setModelFreshnessListener(ChangeListener modelFreshnessListener) {
|
||||||
this.modelFreshnessListener = modelFreshnessListener;
|
this.modelFreshnessListener = modelFreshnessListener;
|
||||||
|
|
|
@ -99,7 +99,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
||||||
* Returns the address where the item described by this object is used. For example, for
|
* Returns the address where the item described by this object is used. For example, for
|
||||||
* data types, the address is where a data type is applied; for references, this value is the
|
* data types, the address is where a data type is applied; for references, this value is the
|
||||||
* <tt>from</tt> address.
|
* <tt>from</tt> address.
|
||||||
*
|
*
|
||||||
* @return the address where the item described by this object is used.
|
* @return the address where the item described by this object is used.
|
||||||
*/
|
*/
|
||||||
public Address getLocationOfUse() {
|
public Address getLocationOfUse() {
|
||||||
|
@ -110,7 +110,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
||||||
* Returns the context associated with this location. This could be a String that highlights
|
* Returns the context associated with this location. This could be a String that highlights
|
||||||
* what part of a function signature the location matches or a line from the Decompiler
|
* what part of a function signature the location matches or a line from the Decompiler
|
||||||
* that matches.
|
* that matches.
|
||||||
*
|
*
|
||||||
* @return the context
|
* @return the context
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
||||||
* Returns the context associated with this location. The context may be a simple plain string
|
* Returns the context associated with this location. The context may be a simple plain string
|
||||||
* or may be String that highlights part of a function signature the location matches or
|
* or may be String that highlights part of a function signature the location matches or
|
||||||
* a line from the Decompiler that matches.
|
* a line from the Decompiler that matches.
|
||||||
*
|
*
|
||||||
* @return the context
|
* @return the context
|
||||||
*/
|
*/
|
||||||
public LocationReferenceContext getContext() {
|
public LocationReferenceContext getContext() {
|
||||||
|
@ -169,12 +169,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
||||||
if (!context.equals(other.context)) {
|
if (!context.equals(other.context)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (locationOfUseAddress == null) {
|
if (!Objects.equals(locationOfUseAddress, other.locationOfUseAddress)) {
|
||||||
if (other.locationOfUseAddress != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!locationOfUseAddress.equals(other.locationOfUseAddress)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return refType.equals(other.refType);
|
return refType.equals(other.refType);
|
||||||
|
@ -194,7 +189,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
||||||
"\taddress: " + locationOfUseAddress + ",\n" +
|
"\taddress: " + locationOfUseAddress + ",\n" +
|
||||||
((refType.equals("")) ? "" : "\trefType: " + refType + ",\n") +
|
((refType.equals("")) ? "" : "\trefType: " + refType + ",\n") +
|
||||||
"\tisOffcut: " + isOffcutReference + ",\n" +
|
"\tisOffcut: " + isOffcutReference + ",\n" +
|
||||||
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + ",") +
|
((context == EMPTY_CONTEXT) ? "" : "\tcontext: " + context + "\n") +
|
||||||
"}";
|
"}";
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import ghidra.util.HTMLUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to hold context representation for {@link LocationReference}s.
|
* A class to hold context representation for {@link LocationReference}s.
|
||||||
*
|
*
|
||||||
* @see LocationReferenceContextBuilder
|
* @see LocationReferenceContextBuilder
|
||||||
*/
|
*/
|
||||||
public class LocationReferenceContext {
|
public class LocationReferenceContext {
|
||||||
|
@ -107,7 +107,7 @@ public class LocationReferenceContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns any sub-strings of this context's overall text that match client-defined input
|
* Returns any sub-strings of this context's overall text that match client-defined input
|
||||||
*
|
*
|
||||||
* See the {@link LocationReferenceContextBuilder} for how to define matching text pieces
|
* See the {@link LocationReferenceContextBuilder} for how to define matching text pieces
|
||||||
* @return the matching strings
|
* @return the matching strings
|
||||||
*/
|
*/
|
||||||
|
@ -123,7 +123,7 @@ public class LocationReferenceContext {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return Json.toString(this);
|
return getPlainText();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -26,8 +26,7 @@ import ghidra.app.events.ProgramClosedPluginEvent;
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||||
import ghidra.app.services.GoToService;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.ProgramManager;
|
|
||||||
import ghidra.app.util.XReferenceUtils;
|
import ghidra.app.util.XReferenceUtils;
|
||||||
import ghidra.app.util.query.TableService;
|
import ghidra.app.util.query.TableService;
|
||||||
import ghidra.framework.options.ToolOptions;
|
import ghidra.framework.options.ToolOptions;
|
||||||
|
@ -211,8 +210,7 @@ public class LocationReferencesPlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disposeProviderList() {
|
private void disposeProviderList() {
|
||||||
for (int i = 0; i < providerList.size(); i++) {
|
for (LocationReferencesProvider provider : providerList) {
|
||||||
LocationReferencesProvider provider = providerList.get(i);
|
|
||||||
provider.dispose();
|
provider.dispose();
|
||||||
}
|
}
|
||||||
providerList.clear();
|
providerList.clear();
|
||||||
|
@ -261,8 +259,8 @@ public class LocationReferencesPlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void programClosed(Program program) {
|
protected void programClosed(Program program) {
|
||||||
for (Iterator<LocationReferencesProvider> iterator =
|
for (Iterator<LocationReferencesProvider> iterator = providerList.iterator(); iterator
|
||||||
providerList.iterator(); iterator.hasNext();) {
|
.hasNext();) {
|
||||||
LocationReferencesProvider provider = iterator.next();
|
LocationReferencesProvider provider = iterator.next();
|
||||||
if (provider.getProgram() == program) {
|
if (provider.getProgram() == program) {
|
||||||
provider.dispose();
|
provider.dispose();
|
||||||
|
@ -293,11 +291,19 @@ public class LocationReferencesPlugin extends Plugin
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType) {
|
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType) {
|
||||||
findAndDisplayAppliedDataTypeAddresses(dataType, null);
|
findAndDisplayAppliedDataTypeAddresses(dataType, (FieldMatcher) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName) {
|
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName) {
|
||||||
|
FieldMatcher matcher = new FieldMatcher(dataType, fieldName);
|
||||||
|
findAndDisplayAppliedDataTypeAddresses(dataType, matcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType,
|
||||||
|
FieldMatcher fieldMatcher) {
|
||||||
|
|
||||||
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||||
GoToService goToService = tool.getService(GoToService.class);
|
GoToService goToService = tool.getService(GoToService.class);
|
||||||
Program program = programManagerService.getCurrentProgram();
|
Program program = programManagerService.getCurrentProgram();
|
||||||
|
@ -308,9 +314,9 @@ public class LocationReferencesPlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgramLocation genericLocation;
|
ProgramLocation genericLocation;
|
||||||
if (fieldName != null) {
|
if (fieldMatcher != null) {
|
||||||
genericLocation =
|
genericLocation =
|
||||||
new GenericCompositeDataTypeProgramLocation(program, dataType, fieldName);
|
new GenericCompositeDataTypeProgramLocation(program, dataType, fieldMatcher);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
genericLocation = new GenericDataTypeProgramLocation(program, dataType);
|
genericLocation = new GenericDataTypeProgramLocation(program, dataType);
|
||||||
|
|
|
@ -20,11 +20,11 @@ import java.util.Stack;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DataTypeReferenceFinder;
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Array;
|
import ghidra.program.model.data.Array;
|
||||||
|
import ghidra.program.model.data.Enum;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
|
@ -162,20 +162,24 @@ public final class ReferenceUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all references (locations) that use the given datatype.
|
* Returns all references (locations) that use the given datatype.
|
||||||
* <br>
|
* <p>
|
||||||
* <b>Note: </b> This method call may take a long time, as it must search all of the
|
* <b>Note: </b> This method call may take a long time, as it must search all of the data
|
||||||
* data within the program and may also perform long running tasks, like decompiling every
|
* within the program and may also perform long running tasks, like decompiling every function
|
||||||
* function in the program.
|
* in the program.
|
||||||
* <br>
|
*
|
||||||
* @param accumulator the results storage
|
* @param accumulator the results storage.
|
||||||
* @param dataType The datatype for which to find references.
|
* @param dataType The datatype for which to find references.
|
||||||
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be
|
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be a
|
||||||
* a {@link Composite} to search for a field
|
* {@link Composite} to search for a field.
|
||||||
* @param program The program from within which to find references.
|
* @param program The program from within which to find references.
|
||||||
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
||||||
* dummy monitor will be used.
|
* dummy monitor will be used.
|
||||||
* @throws CancelledException if the monitor is cancelled
|
* @throws CancelledException if the monitor is cancelled.
|
||||||
|
* @deprecated use {@link #findDataTypeFieldReferences(Accumulator, FieldMatcher, Program,
|
||||||
|
* boolean, TaskMonitor)}.
|
||||||
|
* @Deprecated(since = "10.2")
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
|
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
|
||||||
DataType dataType, String fieldName, Program program, TaskMonitor monitor)
|
DataType dataType, String fieldName, Program program, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
@ -184,24 +188,26 @@ public final class ReferenceUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all references (locations) that use the given datatype.
|
* Returns all references (locations) that use the given datatype.
|
||||||
* <br>
|
* <p>
|
||||||
* <b>Note: </b> This method call may take a long time, as it must search all of the
|
* <b>Note: </b> This method call may take a long time, as it must search all of the data
|
||||||
* data within the program and may also perform long running tasks, like decompiling every
|
* within the program and may also perform long running tasks, like decompiling every function
|
||||||
* function in the program.
|
* in the program.
|
||||||
* <br>
|
*
|
||||||
* @param accumulator the results storage
|
* @param accumulator the results storage.
|
||||||
* @param dataType The datatype for which to find references.
|
* @param dataType The datatype for which to find references.
|
||||||
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be
|
* @param fieldName optional field name for which to search; the <tt>dataType</tt> must be a
|
||||||
* a {@link Composite} to search for a field
|
* {@link Composite} to search for a field.
|
||||||
* @param program The program from within which to find references.
|
* @param program The program from within which to find references.
|
||||||
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service
|
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
|
||||||
* will be used to search for data types that are not applied in memory.
|
* search for data types that are not applied in memory. Using the service will be slower, but
|
||||||
* Using the service will be slower, but will recover type usage that could
|
* will recover type usage that could not be found by examining the Listing.
|
||||||
* not be found by examining the Listing.
|
|
||||||
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
||||||
* dummy monitor will be used.
|
* dummy monitor will be used.
|
||||||
* @throws CancelledException if the monitor is cancelled
|
* @throws CancelledException if the monitor is cancelled.
|
||||||
|
* @deprecated use {@link #findDataTypeFieldReferences(Accumulator, FieldMatcher, Program,
|
||||||
|
* boolean, TaskMonitor)}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "10.2")
|
||||||
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
|
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
|
||||||
DataType dataType, String fieldName, Program program, boolean discoverTypes,
|
DataType dataType, String fieldName, Program program, boolean discoverTypes,
|
||||||
TaskMonitor monitor) throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
@ -209,12 +215,72 @@ public final class ReferenceUtils {
|
||||||
// Note: none of the params can be null, but this one gets used much later, so check now
|
// Note: none of the params can be null, but this one gets used much later, so check now
|
||||||
Objects.requireNonNull(dataType, () -> "Data Type cannot be null");
|
Objects.requireNonNull(dataType, () -> "Data Type cannot be null");
|
||||||
|
|
||||||
if (monitor == null) {
|
FieldMatcher fieldMatcher = new FieldMatcher(dataType, fieldName);
|
||||||
monitor = TaskMonitor.DUMMY;
|
doFindDataTypeReferences(accumulator, fieldMatcher, program, discoverTypes, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all references (locations) that use the given datatype.
|
||||||
|
* <p>
|
||||||
|
* <b>Note: </b> This method call may take a long time, as it must search all of the data
|
||||||
|
* within the program and may also perform long running tasks, like decompiling every function
|
||||||
|
* in the program.
|
||||||
|
*
|
||||||
|
* @param accumulator the results storage.
|
||||||
|
* @param dataType The datatype for which to find references.
|
||||||
|
* @param program The program from within which to find references.
|
||||||
|
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
|
||||||
|
* search for data types that are not applied in memory. Using the service will be slower, but
|
||||||
|
* will recover type usage that could not be found by examining the Listing.
|
||||||
|
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
||||||
|
* dummy monitor will be used.
|
||||||
|
* @throws CancelledException if the monitor is cancelled.
|
||||||
|
*/
|
||||||
|
public static void findDataTypeReferences(Accumulator<LocationReference> accumulator,
|
||||||
|
DataType dataType, Program program, boolean discoverTypes, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
|
doFindDataTypeReferences(accumulator, new FieldMatcher(dataType), program, discoverTypes,
|
||||||
|
monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all references (locations) that use the given datatype.
|
||||||
|
* <p>
|
||||||
|
* <b>Note: </b> This method call may take a long time, as it must search all of the data
|
||||||
|
* within the program and may also perform long running tasks, like decompiling every function
|
||||||
|
* in the program.
|
||||||
|
* <p>
|
||||||
|
* The supplied field matcher will be used to restrict matches to the given field. The matcher
|
||||||
|
* may be 'empty', supplying only the data type for which to search. In this case, all uses
|
||||||
|
* of the type will be matched, regardless of field.
|
||||||
|
*
|
||||||
|
* @param accumulator the results storage.
|
||||||
|
* @param fieldMatcher the field matcher.
|
||||||
|
* @param program The program from within which to find references.
|
||||||
|
* @param discoverTypes if true, the {@link DataTypeReferenceFinder} service will be used to
|
||||||
|
* search for data types that are not applied in memory. Using the service will be slower, but
|
||||||
|
* will recover type usage that could not be found by examining the Listing.
|
||||||
|
* @param monitor A task monitor to be updated as data is searched; if this is null, then a
|
||||||
|
* dummy monitor will be used.
|
||||||
|
* @throws CancelledException if the monitor is cancelled.
|
||||||
|
*/
|
||||||
|
public static void findDataTypeFieldReferences(Accumulator<LocationReference> accumulator,
|
||||||
|
FieldMatcher fieldMatcher, Program program, boolean discoverTypes, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
|
Objects.requireNonNull(fieldMatcher, "FieldMatcher cannot be null");
|
||||||
|
doFindDataTypeReferences(accumulator, fieldMatcher, program, discoverTypes, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doFindDataTypeReferences(Accumulator<LocationReference> accumulator,
|
||||||
|
FieldMatcher fieldMatcher, Program program, boolean discoverTypes, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
|
monitor = TaskMonitor.dummyIfNull(monitor);
|
||||||
|
|
||||||
|
DataType dataType = fieldMatcher.getDataType();
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
|
|
||||||
long dataCount = listing.getNumDefinedData();
|
long dataCount = listing.getNumDefinedData();
|
||||||
int functionCount = program.getFunctionManager().getFunctionCount();
|
int functionCount = program.getFunctionManager().getFunctionCount();
|
||||||
int totalCount = (int) dataCount + functionCount;
|
int totalCount = (int) dataCount + functionCount;
|
||||||
|
@ -226,9 +292,10 @@ public final class ReferenceUtils {
|
||||||
// the code.
|
// the code.
|
||||||
Accumulator<LocationReference> asSet = asSet(accumulator);
|
Accumulator<LocationReference> asSet = asSet(accumulator);
|
||||||
|
|
||||||
if (fieldName == null) {
|
if (fieldMatcher.isIgnored()) {
|
||||||
// It only makes sense to search here when we do not have a field
|
//
|
||||||
|
// It only makes sense to search here when we do not have a field to match
|
||||||
|
//
|
||||||
boolean localsOnly = discoverTypes;
|
boolean localsOnly = discoverTypes;
|
||||||
FunctionIterator iterator = listing.getFunctions(false);
|
FunctionIterator iterator = listing.getFunctions(false);
|
||||||
findDataTypeMatchesInFunctionHeaders(asSet, iterator, dataType, localsOnly, monitor);
|
findDataTypeMatchesInFunctionHeaders(asSet, iterator, dataType, localsOnly, monitor);
|
||||||
|
@ -244,10 +311,11 @@ public final class ReferenceUtils {
|
||||||
boolean matches = dataTypesMatch(dataType, baseType);
|
boolean matches = dataTypesMatch(dataType, baseType);
|
||||||
return matches;
|
return matches;
|
||||||
};
|
};
|
||||||
findDataTypeMatchesInDefinedData(asSet, program, dataMatcher, fieldName, monitor);
|
|
||||||
|
findDataTypeMatchesInDefinedData(asSet, program, dataMatcher, fieldMatcher, monitor);
|
||||||
|
|
||||||
if (discoverTypes) {
|
if (discoverTypes) {
|
||||||
findDataTypeMatchesOutsideOfListing(asSet, program, dataType, fieldName, monitor);
|
findDataTypeMatchesOutsideOfListing(asSet, program, dataType, fieldMatcher, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
@ -265,7 +333,7 @@ public final class ReferenceUtils {
|
||||||
|
|
||||||
private static void findDataTypeMatchesOutsideOfListing(
|
private static void findDataTypeMatchesOutsideOfListing(
|
||||||
Accumulator<LocationReference> accumulator, Program program, DataType dataType,
|
Accumulator<LocationReference> accumulator, Program program, DataType dataType,
|
||||||
String fieldName, TaskMonitor monitor) throws CancelledException {
|
FieldMatcher fieldMatcher, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
List<DataTypeReferenceFinder> finders =
|
List<DataTypeReferenceFinder> finders =
|
||||||
ClassSearcher.getInstances(DataTypeReferenceFinder.class);
|
ClassSearcher.getInstances(DataTypeReferenceFinder.class);
|
||||||
|
@ -284,12 +352,7 @@ public final class ReferenceUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (DataTypeReferenceFinder finder : finders) {
|
for (DataTypeReferenceFinder finder : finders) {
|
||||||
if (fieldName == null) {
|
finder.findReferences(program, fieldMatcher, callback, monitor);
|
||||||
finder.findReferences(program, dataType, callback, monitor);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
finder.findReferences(program, dataType, fieldName, callback, monitor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,15 +680,15 @@ public final class ReferenceUtils {
|
||||||
* some digging to see what is buried at the given address.
|
* some digging to see what is buried at the given address.
|
||||||
*/
|
*/
|
||||||
private static LocationDescriptor createDataMemberLocationDescriptor(
|
private static LocationDescriptor createDataMemberLocationDescriptor(
|
||||||
OperandFieldLocation location, Address refAddress) {
|
OperandFieldLocation location, Address address) {
|
||||||
|
|
||||||
// TODO we don't support data types on external addresses; this could change in the future
|
// Note: we don't support data types on external addresses; this could change in the future
|
||||||
if (refAddress.isExternalAddress()) {
|
if (address.isExternalAddress()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Program program = location.getProgram();
|
Program program = location.getProgram();
|
||||||
Data outermostData = getDataContainingAddress(program, refAddress);
|
Data outermostData = getDataContainingAddress(program, address);
|
||||||
if (outermostData == null) {
|
if (outermostData == null) {
|
||||||
// no data
|
// no data
|
||||||
return null;
|
return null;
|
||||||
|
@ -634,12 +697,12 @@ public final class ReferenceUtils {
|
||||||
String fieldPath = getFieldPath(location);
|
String fieldPath = getFieldPath(location);
|
||||||
if (!fieldPath.contains(".")) {
|
if (!fieldPath.contains(".")) {
|
||||||
|
|
||||||
// no field reference, so don't create a structure member reference, but just
|
// no field reference, so don't create a structure member reference, but just a generic
|
||||||
// a generic data type reference
|
// data type reference
|
||||||
DataType type = outermostData.getDataType();
|
DataType type = outermostData.getDataType();
|
||||||
if (type == DataType.DEFAULT || Undefined.isUndefined(type)) {
|
if (type == DataType.DEFAULT || Undefined.isUndefined(type)) {
|
||||||
// nobody wants to search for undefined usage; too many (this is the case
|
// nobody wants to search for undefined usage; too many (this is the case where the
|
||||||
// where the user is not on an actual data type)
|
// user is not on an actual data type)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,15 +713,15 @@ public final class ReferenceUtils {
|
||||||
|
|
||||||
String fieldName = getFieldName(location);
|
String fieldName = getFieldName(location);
|
||||||
Address parentAddress = outermostData.getMinAddress();
|
Address parentAddress = outermostData.getMinAddress();
|
||||||
int componentAddress = (int) refAddress.subtract(parentAddress);
|
int componentAddress = (int) address.subtract(parentAddress);
|
||||||
Data subData = outermostData.getComponentContaining(componentAddress);
|
Data subData = outermostData.getComponentContaining(componentAddress);
|
||||||
if (subData != null) {
|
if (subData != null) {
|
||||||
|
|
||||||
int[] componentPath = subData.getComponentPath();
|
int[] componentPath = subData.getComponentPath();
|
||||||
FieldNameFieldLocation fieldLocation =
|
FieldNameFieldLocation fieldLocation =
|
||||||
new FieldNameFieldLocation(program, refAddress, componentPath, fieldName, 0);
|
new FieldNameFieldLocation(program, address, componentPath, fieldName, 0);
|
||||||
LocationDescriptor descriptor =
|
LocationDescriptor descriptor =
|
||||||
createSubDataMemberLocationDescriptor(program, refAddress, fieldLocation, subData);
|
createSubDataMemberLocationDescriptor(program, address, fieldLocation, subData);
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,18 +730,41 @@ public final class ReferenceUtils {
|
||||||
//
|
//
|
||||||
DataType dt = outermostData.getDataType();
|
DataType dt = outermostData.getDataType();
|
||||||
if (dt instanceof Union) {
|
if (dt instanceof Union) {
|
||||||
AddressFieldLocation addressLocation = new AddressFieldLocation(program, refAddress,
|
AddressFieldLocation addressLocation =
|
||||||
new int[] { 0 }, refAddress.toString(), 0);
|
new AddressFieldLocation(program, address, new int[] { 0 }, address.toString(), 0);
|
||||||
return new UnionLocationDescriptor(addressLocation, program);
|
return new UnionLocationDescriptor(addressLocation, program);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static GenericDataTypeLocationDescriptor createGenericDataTypeLocationDescriptor(
|
||||||
|
OperandFieldLocation location) {
|
||||||
|
|
||||||
|
Data data = getDataAt(location);
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType dt = data.getDataType();
|
||||||
|
if (dt instanceof Enum) {
|
||||||
|
|
||||||
|
String enumText = location.getOperandRepresentation();
|
||||||
|
GenericCompositeDataTypeProgramLocation genericLocation =
|
||||||
|
new GenericCompositeDataTypeProgramLocation(location.getProgram(), dt, enumText);
|
||||||
|
return new GenericCompositeDataTypeLocationDescriptor(genericLocation,
|
||||||
|
location.getProgram());
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericDataTypeProgramLocation genericLocation =
|
||||||
|
new GenericDataTypeProgramLocation(location.getProgram(), dt);
|
||||||
|
return new GenericDataTypeLocationDescriptor(genericLocation, location.getProgram(), dt);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Creates a location descriptor using the String display markup and type information
|
* Creates a location descriptor using the String display markup and type information
|
||||||
* found inside of the VariableOffset object.
|
* found inside of the VariableOffset object.
|
||||||
*
|
*
|
||||||
* This method differs from createDataMemberLocationDescriptor() in that this method
|
* This method differs from createDataMemberLocationDescriptor() in that this method
|
||||||
* will create locations that represent DataTypes that are not applied in memory.
|
* will create locations that represent DataTypes that are not applied in memory.
|
||||||
*/
|
*/
|
||||||
|
@ -813,9 +899,15 @@ public final class ReferenceUtils {
|
||||||
private static LocationDescriptor createOperandLocationDescriptor(
|
private static LocationDescriptor createOperandLocationDescriptor(
|
||||||
OperandFieldLocation location) {
|
OperandFieldLocation location) {
|
||||||
|
|
||||||
|
// this is the 'to' address
|
||||||
Address refAddress = getReferenceAddress(location);
|
Address refAddress = getReferenceAddress(location);
|
||||||
if (refAddress == null) {
|
if (refAddress == null) {
|
||||||
return null; // no reference and no variable-offset
|
//
|
||||||
|
// No reference address for this location. Try to create a generic data descriptor.
|
||||||
|
//
|
||||||
|
GenericDataTypeLocationDescriptor descriptor =
|
||||||
|
createGenericDataTypeLocationDescriptor(location);
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
Address operandAddress = location.getAddress();
|
Address operandAddress = location.getAddress();
|
||||||
|
@ -824,20 +916,18 @@ public final class ReferenceUtils {
|
||||||
ReferenceManager referenceManager = program.getReferenceManager();
|
ReferenceManager referenceManager = program.getReferenceManager();
|
||||||
Reference reference =
|
Reference reference =
|
||||||
referenceManager.getReference(operandAddress, refAddress, operandIndex);
|
referenceManager.getReference(operandAddress, refAddress, operandIndex);
|
||||||
|
|
||||||
if (reference == null) {
|
if (reference == null) {
|
||||||
|
|
||||||
// Prefer using the reference, for consistency. Without that, the
|
// Prefer using the reference, for consistency. Without that, the VariableOffset
|
||||||
// VariableOffset object contains markup and type information we can use.
|
// object contains markup and type information we can use. Having a VariableOffset
|
||||||
// Having a VariableOffset without a reference occurs when a
|
// without a reference occurs when a register variable reference is inferred during
|
||||||
// register variable reference is inferred during instruction operand formatting.
|
// instruction operand formatting.
|
||||||
VariableOffset variableOffset = location.getVariableOffset();
|
VariableOffset variableOffset = location.getVariableOffset();
|
||||||
return createGenericDataTypeLocationDescriptor(program, variableOffset);
|
return createGenericDataTypeLocationDescriptor(program, variableOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: not sure why we are ignoring external references. It seems like that is
|
// note: not sure why we are ignoring external references. It seems like that is a thing
|
||||||
// a thing you may want to find refs to. If you figure it out, update this
|
// you may want to find refs to. If you figure it out, update this comment.
|
||||||
// comment.
|
|
||||||
// if (reference.isExternalReference()) {
|
// if (reference.isExternalReference()) {
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
|
@ -930,17 +1020,17 @@ public final class ReferenceUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches defined data for types that match, according to the given predicate.
|
* Searches defined data for types that match, according to the given predicate.
|
||||||
*
|
*
|
||||||
* @param accumulator the results accumulator
|
* @param accumulator the results accumulator
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @param dataMatcher the predicate that determines a successful match
|
* @param dataMatcher the predicate that determines a successful match
|
||||||
* @param fieldName the optional field name for which to search
|
* @param fieldMatcher the field matcher; will be ignored if it contains null values
|
||||||
* @param monitor the task monitor used to track progress and cancel the work
|
* @param monitor the task monitor used to track progress and cancel the work
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
*/
|
*/
|
||||||
public static void findDataTypeMatchesInDefinedData(Accumulator<LocationReference> accumulator,
|
public static void findDataTypeMatchesInDefinedData(Accumulator<LocationReference> accumulator,
|
||||||
Program program, Predicate<Data> dataMatcher, String fieldName, TaskMonitor monitor)
|
Program program, Predicate<Data> dataMatcher, FieldMatcher fieldMatcher,
|
||||||
throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
DataIterator dataIter = listing.getDefinedData(true);
|
DataIterator dataIter = listing.getDefinedData(true);
|
||||||
|
@ -948,39 +1038,43 @@ public final class ReferenceUtils {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
Data data = dataIter.next();
|
Data data = dataIter.next();
|
||||||
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, data, fieldName,
|
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, data, fieldMatcher,
|
||||||
dataMatcher, monitor);
|
dataMatcher, monitor);
|
||||||
|
|
||||||
monitor.incrementProgress(1);
|
monitor.incrementProgress(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LocationReference createReferenceFromDefinedData(Data data, String fieldName) {
|
private static LocationReference createReferenceFromDefinedData(Data data,
|
||||||
|
FieldMatcher fieldMatcher) {
|
||||||
Address dataAddress = data.getMinAddress();
|
Address dataAddress = data.getMinAddress();
|
||||||
if (fieldName == null) {
|
if (fieldMatcher.isIgnored()) {
|
||||||
// no field--include the hit
|
// no field to match; include the hit
|
||||||
return new LocationReference(dataAddress, data.getPathName());
|
return new LocationReference(dataAddress, data.getPathName());
|
||||||
}
|
}
|
||||||
|
|
||||||
DataTypeComponent component = getDataTypeComponent(data, fieldName);
|
|
||||||
if (component == null) {
|
|
||||||
// this implies the given data does not contain our field--do not include the hit
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: just check the current type; we may have to unroll it, looking for pointers
|
|
||||||
// along the way if this is not sufficient
|
|
||||||
DataType dt = data.getDataType();
|
DataType dt = data.getDataType();
|
||||||
if (dt instanceof Pointer) {
|
if (dt instanceof Pointer) {
|
||||||
// For defined data, do not include pointer types when we have a field name. A
|
// For defined data, do not include pointer types when we have a field name. A
|
||||||
// pointer to the base composite type is not a direct usage of the given field.
|
// pointer to the base composite type is not a direct usage of the given field.find
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataType baseDt = getBaseDataType(data.getDataType());
|
||||||
|
if (matchesEnumField(data, baseDt, fieldMatcher)) {
|
||||||
|
return new LocationReference(dataAddress, fieldMatcher.getDisplayText());
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent component = getDataTypeComponent(baseDt, fieldMatcher);
|
||||||
|
if (component == null) {
|
||||||
|
return null; // not in composite
|
||||||
|
}
|
||||||
|
|
||||||
Address componentAddress;
|
Address componentAddress;
|
||||||
try {
|
try {
|
||||||
componentAddress = dataAddress.addNoWrap(component.getOffset());
|
componentAddress = dataAddress.addNoWrap(component.getOffset());
|
||||||
return new LocationReference(componentAddress, data.getPathName() + "." + fieldName);
|
return new LocationReference(componentAddress,
|
||||||
|
data.getPathName() + "." + fieldMatcher.getFieldName());
|
||||||
}
|
}
|
||||||
catch (AddressOverflowException e) {
|
catch (AddressOverflowException e) {
|
||||||
// shouldn't happen
|
// shouldn't happen
|
||||||
|
@ -990,33 +1084,58 @@ public final class ReferenceUtils {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataTypeComponent getDataTypeComponent(Data data, String fieldName) {
|
private static boolean matchesEnumField(Data data, DataType dt, FieldMatcher matcher) {
|
||||||
DataType dt = getBaseDataType(data.getDataType());
|
if (!(dt instanceof Enum)) {
|
||||||
if (!(dt instanceof Composite)) {
|
return false;
|
||||||
Msg.debug(ReferenceUtils.class,
|
|
||||||
"Somehow searched for a field name on a Data Type that is not a Composite");
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Composite c = (Composite) dt;
|
Enum enumm = (Enum) dt;
|
||||||
DataTypeComponent[] components = c.getDefinedComponents();
|
List<String> names = getEnumNames(data, enumm);
|
||||||
for (DataTypeComponent component : components) {
|
for (String name : names) {
|
||||||
if (SystemUtilities.isEqual(component.getFieldName(), fieldName)) {
|
long value = enumm.getValue(name);
|
||||||
return component;
|
if (matcher.matches(name, (int) value)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Note: sometimes this happens if the user searches on an array element field, which
|
private static List<String> getEnumNames(Data data, Enum enumm) {
|
||||||
// exists only in the Listing markup
|
String enumEntryName = data.getDefaultValueRepresentation();
|
||||||
|
List<String> enumNames = new ArrayList<>(List.of(enumm.getNames()));
|
||||||
|
if (enumNames.contains(enumEntryName)) {
|
||||||
|
return List.of(enumEntryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enumEntryName.contains(" | ")) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// not a big fan of using this delimiter check, but will do so for now until there is
|
||||||
|
// a better way, such as asking the enum to do this work for us
|
||||||
|
String[] names = enumEntryName.split(" \\| ");
|
||||||
|
return List.of(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataTypeComponent getDataTypeComponent(DataType dt, FieldMatcher matcher) {
|
||||||
|
if (dt instanceof Composite) {
|
||||||
|
Composite c = (Composite) dt;
|
||||||
|
DataTypeComponent[] components = c.getDefinedComponents();
|
||||||
|
for (DataTypeComponent component : components) {
|
||||||
|
if (matcher.matches(component.getFieldName(), component.getOffset())) {
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void getMatchingDataTypesReferencesFromDataAndSubData(
|
private static void getMatchingDataTypesReferencesFromDataAndSubData(
|
||||||
Accumulator<LocationReference> accumulator, Data data, String fieldName,
|
Accumulator<LocationReference> accumulator, Data data, FieldMatcher fieldMatcher,
|
||||||
Predicate<Data> dataMatcher, TaskMonitor monitor) throws CancelledException {
|
Predicate<Data> dataMatcher, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
if (dataMatcher.test(data)) {
|
if (dataMatcher.test(data)) {
|
||||||
getMatchingDataTypesReferencesFromData(accumulator, data, fieldName, monitor);
|
getMatchingDataTypesReferencesFromData(accumulator, data, fieldMatcher, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We know that arrays are all the same element; we decided to just mark the beginning.
|
// We know that arrays are all the same element; we decided to just mark the beginning.
|
||||||
|
@ -1031,16 +1150,16 @@ public final class ReferenceUtils {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
Data subData = data.getComponent(i);
|
Data subData = data.getComponent(i);
|
||||||
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, subData, fieldName,
|
getMatchingDataTypesReferencesFromDataAndSubData(accumulator, subData, fieldMatcher,
|
||||||
dataMatcher, monitor);
|
dataMatcher, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void getMatchingDataTypesReferencesFromData(
|
private static void getMatchingDataTypesReferencesFromData(
|
||||||
Accumulator<LocationReference> accumulator, Data data, String fieldName,
|
Accumulator<LocationReference> accumulator, Data data, FieldMatcher fieldMatcher,
|
||||||
TaskMonitor monitor) throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
LocationReference ref = createReferenceFromDefinedData(data, fieldName);
|
LocationReference ref = createReferenceFromDefinedData(data, fieldMatcher);
|
||||||
if (ref == null) {
|
if (ref == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1058,12 +1177,13 @@ public final class ReferenceUtils {
|
||||||
|
|
||||||
Consumer<Reference> referenceConsumer = reference -> {
|
Consumer<Reference> referenceConsumer = reference -> {
|
||||||
Address toAddress = reference.getToAddress();
|
Address toAddress = reference.getToAddress();
|
||||||
if (fieldName == null) {
|
if (fieldMatcher.isIgnored()) {
|
||||||
|
// no field to match; use the data address
|
||||||
accumulator.add(new LocationReference(reference, isOffcut(program, toAddress)));
|
accumulator.add(new LocationReference(reference, isOffcut(program, toAddress)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only add the reference if it is directly to the field
|
// have a field match; only add the reference if it is directly to the field
|
||||||
if (toAddress.equals(dataAddress)) {
|
if (toAddress.equals(dataAddress)) {
|
||||||
accumulator.add(new LocationReference(reference, false));
|
accumulator.add(new LocationReference(reference, false));
|
||||||
}
|
}
|
||||||
|
@ -1113,7 +1233,6 @@ public final class ReferenceUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean dataTypesMatch(DataType searchType, DataType possibleType) {
|
private static boolean dataTypesMatch(DataType searchType, DataType possibleType) {
|
||||||
|
|
||||||
if (isBuiltIn(searchType)) {
|
if (isBuiltIn(searchType)) {
|
||||||
Class<? extends DataType> clazz = searchType.getClass();
|
Class<? extends DataType> clazz = searchType.getClass();
|
||||||
return clazz.equals(possibleType.getClass());
|
return clazz.equals(possibleType.getClass());
|
||||||
|
@ -1132,8 +1251,8 @@ public final class ReferenceUtils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID.equals(
|
if (DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID
|
||||||
sourceArchive.getSourceArchiveID())) {
|
.equals(sourceArchive.getSourceArchiveID())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -1141,7 +1260,7 @@ public final class ReferenceUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all references to the given variable
|
* Returns all references to the given variable
|
||||||
*
|
*
|
||||||
* @param accumulator the results accumulator
|
* @param accumulator the results accumulator
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @param variable the variable
|
* @param variable the variable
|
||||||
|
@ -1153,8 +1272,8 @@ public final class ReferenceUtils {
|
||||||
ReferenceManager referenceManager = program.getReferenceManager();
|
ReferenceManager referenceManager = program.getReferenceManager();
|
||||||
Reference[] variableRefsTo = referenceManager.getReferencesTo(variable);
|
Reference[] variableRefsTo = referenceManager.getReferencesTo(variable);
|
||||||
for (Reference ref : variableRefsTo) {
|
for (Reference ref : variableRefsTo) {
|
||||||
accumulator.add(
|
accumulator
|
||||||
new LocationReference(ref, !ref.getToAddress().equals(variableAddress)));
|
.add(new LocationReference(ref, !ref.getToAddress().equals(variableAddress)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,12 +40,10 @@ class UnionLocationDescriptor extends DataTypeLocationDescriptor {
|
||||||
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
|
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
// Note: we pass null for the 'fieldName', as we have no way of disambiguating which
|
// Note: we don't use 'fieldName' of the union, as we have no way of disambiguating which
|
||||||
// field a reference will point to. So, grab all references.
|
// field a reference will point to. So, grab all references.
|
||||||
|
ReferenceUtils.findDataTypeReferences(accumulator, union, program, useDynamicSearching,
|
||||||
String fieldName = null;
|
monitor);
|
||||||
ReferenceUtils.findDataTypeReferences(accumulator, union, fieldName, program,
|
|
||||||
useDynamicSearching, monitor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -38,7 +38,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that this operation is multi-threaded and that results will be delivered as they
|
* Note that this operation is multi-threaded and that results will be delivered as they
|
||||||
* are found via the <code>callback</code>.
|
* are found via the <code>callback</code>.
|
||||||
*
|
*
|
||||||
* @param program the program to search
|
* @param program the program to search
|
||||||
* @param dataType the type for which to search
|
* @param dataType the type for which to search
|
||||||
* @param callback the callback to be called when a reference is found
|
* @param callback the callback to be called when a reference is found
|
||||||
|
@ -46,8 +46,7 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
*/
|
*/
|
||||||
public void findReferences(Program program, DataType dataType,
|
public void findReferences(Program program, DataType dataType,
|
||||||
Consumer<DataTypeReference> callback,
|
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
|
||||||
TaskMonitor monitor) throws CancelledException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds references in the current program to specific field of the given {@link Composite} type
|
* Finds references in the current program to specific field of the given {@link Composite} type
|
||||||
|
@ -55,14 +54,34 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that this operation is multi-threaded and that results will be delivered as they
|
* Note that this operation is multi-threaded and that results will be delivered as they
|
||||||
* are found via the <code>callback</code>.
|
* are found via the <code>callback</code>.
|
||||||
*
|
*
|
||||||
* @param program the program to search
|
* @param program the program to search
|
||||||
* @param dataType the type containing the field for which to search
|
* @param dataType the type containing the field for which to search
|
||||||
* @param fieldName the name of the composite's field for which to search
|
* @param fieldName the name of the composite's field for which to search; may be null
|
||||||
* @param callback the callback to be called when a reference is found
|
* @param callback the callback to be called when a reference is found
|
||||||
* @param monitor the monitor that allows for progress and cancellation
|
* @param monitor the monitor that allows for progress and cancellation
|
||||||
* @throws CancelledException if the operation was cancelled
|
* @throws CancelledException if the operation was cancelled
|
||||||
*/
|
*/
|
||||||
public void findReferences(Program program, DataType dataType, String fieldName,
|
public void findReferences(Program program, DataType dataType, String fieldName,
|
||||||
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
|
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds references in the current program to specific field of the given {@link Composite} type
|
||||||
|
* in a manner appropriate with the given implementation.
|
||||||
|
* <p>
|
||||||
|
* The supplied field matcher will be used to restrict matches to the given field. The matcher
|
||||||
|
* may be 'empty', supplying only the data type for which to search. In this case, all uses
|
||||||
|
* of the type will be matched, regardless of field.
|
||||||
|
* <p>
|
||||||
|
* Note that this operation is multi-threaded and that results will be delivered as they
|
||||||
|
* are found via the <code>callback</code>.
|
||||||
|
*
|
||||||
|
* @param program the program to search
|
||||||
|
* @param fieldMatcher the field matcher to use for matching types
|
||||||
|
* @param callback the callback to be called when a reference is found
|
||||||
|
* @param monitor the monitor that allows for progress and cancellation
|
||||||
|
* @throws CancelledException if the operation was cancelled
|
||||||
|
*/
|
||||||
|
public void findReferences(Program program, FieldMatcher fieldMatcher,
|
||||||
|
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
this.displayOptions = displayOptions;
|
||||||
initDisplayOptions();
|
initDisplayOptions();
|
||||||
|
|
||||||
fieldOptions.getOptions(name).setOptionsHelpLocation(
|
fieldOptions.getOptions(name)
|
||||||
new HelpLocation("CodeBrowserPlugin", name));
|
.setOptionsHelpLocation(new HelpLocation("CodeBrowserPlugin", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initDisplayOptions() {
|
protected void initDisplayOptions() {
|
||||||
|
@ -141,6 +141,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
* @param highlightProvider the HightLightProvider.
|
* @param highlightProvider the HightLightProvider.
|
||||||
* @param options the Options for display properties.
|
* @param options the Options for display properties.
|
||||||
* @param fieldOptions the Options for field specific properties.
|
* @param fieldOptions the Options for field specific properties.
|
||||||
|
* @return the factory
|
||||||
*/
|
*/
|
||||||
public abstract FieldFactory newInstance(FieldFormatModel formatModel,
|
public abstract FieldFactory newInstance(FieldFormatModel formatModel,
|
||||||
HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions);
|
HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions);
|
||||||
|
@ -185,6 +186,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Field name.
|
* Returns the Field name.
|
||||||
|
* @return the name.
|
||||||
*/
|
*/
|
||||||
public String getFieldName() {
|
public String getFieldName() {
|
||||||
return name;
|
return name;
|
||||||
|
@ -192,14 +194,15 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default field color.
|
* Returns the default field color.
|
||||||
|
* @return the color.
|
||||||
*/
|
*/
|
||||||
public Color getDefaultColor() {
|
public Color getDefaultColor() {
|
||||||
return Color.BLACK;
|
return Color.BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the starting x position for the fields generated by this
|
* Returns the starting x position for the fields generated by this factory.
|
||||||
* factory.
|
* @return the start x.
|
||||||
*/
|
*/
|
||||||
public int getStartX() {
|
public int getStartX() {
|
||||||
return startX;
|
return startX;
|
||||||
|
@ -207,6 +210,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the starting x position for the fields generated by this factory.
|
* Sets the starting x position for the fields generated by this factory.
|
||||||
|
* @param x the x position.
|
||||||
*/
|
*/
|
||||||
public void setStartX(int x) {
|
public void setStartX(int x) {
|
||||||
startX = x;
|
startX = x;
|
||||||
|
@ -214,6 +218,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the width of the fields generated by this factory.
|
* Returns the width of the fields generated by this factory.
|
||||||
|
* @return the width.
|
||||||
*/
|
*/
|
||||||
public int getWidth() {
|
public int getWidth() {
|
||||||
return width;
|
return width;
|
||||||
|
@ -221,6 +226,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the width of the fields generated by this factory.
|
* Sets the width of the fields generated by this factory.
|
||||||
|
* @param w the width.
|
||||||
*/
|
*/
|
||||||
public void setWidth(int w) {
|
public void setWidth(int w) {
|
||||||
width = w;
|
width = w;
|
||||||
|
@ -228,6 +234,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the FieldModel that this factory belongs to.
|
* Returns the FieldModel that this factory belongs to.
|
||||||
|
* @return the model.
|
||||||
*/
|
*/
|
||||||
public FieldFormatModel getFieldModel() {
|
public FieldFormatModel getFieldModel() {
|
||||||
return model;
|
return model;
|
||||||
|
@ -235,6 +242,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this FieldFactory is currently enabled to generate Fields.
|
* Returns true if this FieldFactory is currently enabled to generate Fields.
|
||||||
|
* @return true if enabled.
|
||||||
*/
|
*/
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
|
@ -261,12 +269,14 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
/**
|
/**
|
||||||
* Return a FieldLocation that corresponds to the given index, fieldNum, and ProgramLocation
|
* Return a FieldLocation that corresponds to the given index, fieldNum, and ProgramLocation
|
||||||
* IF and ONLY IF the given programLocation is the type generated by this class's
|
* IF and ONLY IF the given programLocation is the type generated by this class's
|
||||||
* {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each FieldFactory
|
* {@link #getFieldLocation(ListingField, BigInteger, int, ProgramLocation)}. Each
|
||||||
* should generate and process a unique ProgramLocation class.
|
* FieldFactory should generate and process a unique ProgramLocation class.
|
||||||
|
*
|
||||||
* @param bf the ListingField at the current cursor.
|
* @param bf the ListingField at the current cursor.
|
||||||
* @param index the line index (corresponds to an address)
|
* @param index the line index (corresponds to an address)
|
||||||
* @param fieldNum the index of field within the layout to try and get a FieldLocation.
|
* @param fieldNum the index of field within the layout to try and get a FieldLocation.
|
||||||
* @param loc the ProgramLocation to be converted into a FieldLocation.
|
* @param loc the ProgramLocation to be converted into a FieldLocation.
|
||||||
|
* @return the location.
|
||||||
*/
|
*/
|
||||||
public abstract FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
|
public abstract FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
|
||||||
ProgramLocation loc);
|
ProgramLocation loc);
|
||||||
|
@ -276,6 +286,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
* @param row the row within this field
|
* @param row the row within this field
|
||||||
* @param col the col on the given row within this field.
|
* @param col the col on the given row within this field.
|
||||||
* @param bf the ListingField containing the cursor.
|
* @param bf the ListingField containing the cursor.
|
||||||
|
* @return the location.
|
||||||
*/
|
*/
|
||||||
public abstract ProgramLocation getProgramLocation(int row, int col, ListingField bf);
|
public abstract ProgramLocation getProgramLocation(int row, int col, ListingField bf);
|
||||||
|
|
||||||
|
@ -313,6 +324,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a description of the fields generated by this factory.
|
* Returns a description of the fields generated by this factory.
|
||||||
|
* @return the text.
|
||||||
*/
|
*/
|
||||||
public String getFieldText() {
|
public String getFieldText() {
|
||||||
return name;
|
return name;
|
||||||
|
@ -320,14 +332,12 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the font metrics used by this field factory
|
* Returns the font metrics used by this field factory
|
||||||
|
* @return the metrics.
|
||||||
*/
|
*/
|
||||||
public FontMetrics getMetrics() {
|
public FontMetrics getMetrics() {
|
||||||
return getMetrics(style);
|
return getMetrics(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Returns the metrics.
|
|
||||||
*/
|
|
||||||
protected FontMetrics getMetrics(int fontStyle) {
|
protected FontMetrics getMetrics(int fontStyle) {
|
||||||
if (fontStyle == -1) {
|
if (fontStyle == -1) {
|
||||||
return defaultMetrics;
|
return defaultMetrics;
|
||||||
|
@ -335,8 +345,7 @@ public abstract class FieldFactory implements ExtensionPoint {
|
||||||
return fontMetrics[fontStyle];
|
return fontMetrics[fontStyle];
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation") // we know
|
||||||
// we know
|
|
||||||
private void setMetrics(Font newFont) {
|
private void setMetrics(Font newFont) {
|
||||||
defaultMetrics = Toolkit.getDefaultToolkit().getFontMetrics(newFont);
|
defaultMetrics = Toolkit.getDefaultToolkit().getFontMetrics(newFont);
|
||||||
for (int i = 0; i < fontMetrics.length; i++) {
|
for (int i = 0; i < fontMetrics.length; i++) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ abstract class OperandFieldHelper extends FieldFactory {
|
||||||
private final static String SPACE_AFTER_SEPARATOR_OPTION =
|
private final static String SPACE_AFTER_SEPARATOR_OPTION =
|
||||||
GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Add Space After Separator";
|
GhidraOptions.OPERAND_GROUP_TITLE + Options.DELIMITER + "Add Space After Separator";
|
||||||
|
|
||||||
public static enum UNDERLINE_CHOICE {
|
public enum UNDERLINE_CHOICE {
|
||||||
Hidden, All, None
|
Hidden, All, None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,12 +183,6 @@ abstract class OperandFieldHelper extends FieldFactory {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the FactoryField for the given object at index index.
|
|
||||||
*
|
|
||||||
* @param obj the object whose properties should be displayed.
|
|
||||||
* @param varWidth the amount of variable width spacing for any fields before this one.
|
|
||||||
*/
|
|
||||||
ListingField getField(Object obj, ProxyObj<?> proxy, int varWidth) {
|
ListingField getField(Object obj, ProxyObj<?> proxy, int varWidth) {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -222,10 +216,10 @@ abstract class OperandFieldHelper extends FieldFactory {
|
||||||
|
|
||||||
ListingTextField btf = (ListingTextField) lf;
|
ListingTextField btf = (ListingTextField) lf;
|
||||||
FieldElement fieldElement = btf.getFieldElement(row, col);
|
FieldElement fieldElement = btf.getFieldElement(row, col);
|
||||||
|
|
||||||
if (!(fieldElement instanceof OperandFieldElement)) {
|
if (!(fieldElement instanceof OperandFieldElement)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
OperandFieldElement element = (OperandFieldElement) fieldElement;
|
OperandFieldElement element = (OperandFieldElement) fieldElement;
|
||||||
int opIndex = element.getOperandIndex();
|
int opIndex = element.getOperandIndex();
|
||||||
int subOpIndex = element.getOperandSubIndex();
|
int subOpIndex = element.getOperandSubIndex();
|
||||||
|
@ -276,11 +270,10 @@ abstract class OperandFieldHelper extends FieldFactory {
|
||||||
else if (obj instanceof Data) {
|
else if (obj instanceof Data) {
|
||||||
Data data = (Data) obj;
|
Data data = (Data) obj;
|
||||||
Address refAddr = null;
|
Address refAddr = null;
|
||||||
Reference primaryReference =
|
Program program = data.getProgram();
|
||||||
data.getProgram()
|
ReferenceManager referenceManager = program.getReferenceManager();
|
||||||
.getReferenceManager()
|
Address minAddress = data.getMinAddress();
|
||||||
.getPrimaryReferenceFrom(
|
Reference primaryReference = referenceManager.getPrimaryReferenceFrom(minAddress, 0);
|
||||||
data.getMinAddress(), 0);
|
|
||||||
Object value = data.getValue();
|
Object value = data.getValue();
|
||||||
if (primaryReference != null) {
|
if (primaryReference != null) {
|
||||||
refAddr = primaryReference.getToAddress();
|
refAddr = primaryReference.getToAddress();
|
||||||
|
@ -291,20 +284,18 @@ abstract class OperandFieldHelper extends FieldFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Program program = data.getProgram();
|
|
||||||
if (value instanceof Scalar) {
|
if (value instanceof Scalar) {
|
||||||
Scalar scalar = (Scalar) value;
|
Scalar scalar = (Scalar) value;
|
||||||
Equate equate = program.getEquateTable()
|
EquateTable equateTable = program.getEquateTable();
|
||||||
.getEquate(data.getMinAddress(), opIndex,
|
Equate equate = equateTable.getEquate(minAddress, opIndex, scalar.getValue());
|
||||||
scalar.getValue());
|
|
||||||
if (equate != null) {
|
if (equate != null) {
|
||||||
return new EquateOperandFieldLocation(program, data.getMinAddress(), refAddr,
|
return new EquateOperandFieldLocation(program, minAddress, refAddr,
|
||||||
equate.getDisplayName(), equate, opIndex, subOpIndex,
|
equate.getDisplayName(), equate, opIndex, subOpIndex,
|
||||||
translatedLocation.col());
|
translatedLocation.col());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new OperandFieldLocation(program, data.getMinAddress(), data.getComponentPath(),
|
return new OperandFieldLocation(program, minAddress, data.getComponentPath(), refAddr,
|
||||||
refAddr, codeUnitFormat.getDataValueRepresentationString(data), 0, col);
|
codeUnitFormat.getDataValueRepresentationString(data), 0, col);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -477,9 +468,9 @@ abstract class OperandFieldHelper extends FieldFactory {
|
||||||
|
|
||||||
private int addElements(Instruction inst, List<OperandFieldElement> elements, List<?> objList,
|
private int addElements(Instruction inst, List<OperandFieldElement> elements, List<?> objList,
|
||||||
int opIndex, int subOpIndex, boolean underline, int characterOffset) {
|
int opIndex, int subOpIndex, boolean underline, int characterOffset) {
|
||||||
for (int i = 0; i < objList.size(); i++) {
|
for (Object element : objList) {
|
||||||
characterOffset = addElement(inst, elements, objList.get(i), underline, opIndex,
|
characterOffset = addElement(inst, elements, element, underline, opIndex, subOpIndex,
|
||||||
subOpIndex, characterOffset);
|
characterOffset);
|
||||||
}
|
}
|
||||||
return characterOffset;
|
return characterOffset;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ import ghidra.util.classfinder.ClassSearcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates data value Fields for data subcomponents.
|
* Generates data value Fields for data subcomponents.
|
||||||
* <P>
|
* <P>
|
||||||
* This field is not meant to be loaded by the {@link ClassSearcher}, hence the X in the name.
|
* This field is not meant to be loaded by the {@link ClassSearcher}, hence the X in the name.
|
||||||
*/
|
*/
|
||||||
public class SubDataFieldFactory extends OperandFieldFactory {
|
public class SubDataFieldFactory extends OperandFieldFactory {
|
||||||
|
@ -43,16 +43,10 @@ public class SubDataFieldFactory extends OperandFieldFactory {
|
||||||
* @param path the component path for the data
|
* @param path the component path for the data
|
||||||
*/
|
*/
|
||||||
public SubDataFieldFactory(String name, int[] path) {
|
public SubDataFieldFactory(String name, int[] path) {
|
||||||
super();
|
|
||||||
this.componentPath = path;
|
this.componentPath = path;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param provider The FieldProvider object that serves as the SubDataFieldFactory factory.
|
|
||||||
* @param model The Field model that will use this Address factory.
|
|
||||||
*/
|
|
||||||
private SubDataFieldFactory(String name, int[] componentPath, FieldFormatModel model,
|
private SubDataFieldFactory(String name, int[] componentPath, FieldFormatModel model,
|
||||||
HighlightProvider hlProvider, ToolOptions displayOptions, ToolOptions fieldOptions) {
|
HighlightProvider hlProvider, ToolOptions displayOptions, ToolOptions fieldOptions) {
|
||||||
super(model, hlProvider, displayOptions, fieldOptions);
|
super(model, hlProvider, displayOptions, fieldOptions);
|
||||||
|
@ -60,12 +54,6 @@ public class SubDataFieldFactory extends OperandFieldFactory {
|
||||||
this.componentPath = componentPath;
|
this.componentPath = componentPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the FactoryField for the given object at index index.
|
|
||||||
* @param varWidth the amount of variable width spacing for any fields
|
|
||||||
* before this one.
|
|
||||||
* @param proxy the object whose properties should be displayed.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
|
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
|
||||||
Object obj = proxy.getObject();
|
Object obj = proxy.getObject();
|
||||||
|
@ -138,8 +126,8 @@ public class SubDataFieldFactory extends OperandFieldFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider,
|
public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider,
|
||||||
ToolOptions displayOptions, ToolOptions fieldOptions) {
|
ToolOptions options, ToolOptions fieldOptions) {
|
||||||
return new SubDataFieldFactory(name, componentPath, formatModel, provider, displayOptions,
|
return new SubDataFieldFactory(name, componentPath, formatModel, provider, options,
|
||||||
fieldOptions);
|
fieldOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,7 @@ import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.exception.RollbackException;
|
import ghidra.util.exception.RollbackException;
|
||||||
import junit.framework.AssertionFailedError;
|
import junit.framework.AssertionFailedError;
|
||||||
import utility.application.ApplicationLayout;
|
import utility.application.ApplicationLayout;
|
||||||
import utility.function.ExceptionalCallback;
|
import utility.function.*;
|
||||||
import utility.function.ExceptionalFunction;
|
|
||||||
|
|
||||||
public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDockingTest {
|
public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDockingTest {
|
||||||
|
|
||||||
|
@ -68,8 +67,6 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
private static Language Z80_LANGUAGE;
|
private static Language Z80_LANGUAGE;
|
||||||
|
|
||||||
public AbstractGhidraHeadlessIntegrationTest() {
|
public AbstractGhidraHeadlessIntegrationTest() {
|
||||||
super();
|
|
||||||
|
|
||||||
// Ensure that all error messages do NOT use a gui popup, and instead are routed to the
|
// Ensure that all error messages do NOT use a gui popup, and instead are routed to the
|
||||||
// console.
|
// console.
|
||||||
setErrorGUIEnabled(false);
|
setErrorGUIEnabled(false);
|
||||||
|
@ -167,7 +164,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
/**
|
/**
|
||||||
* Run a command against the specified program within a transaction.
|
* Run a command against the specified program within a transaction.
|
||||||
* The transaction will be committed unless the command throws a RollbackException.
|
* The transaction will be committed unless the command throws a RollbackException.
|
||||||
*
|
*
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @param cmd the command to apply
|
* @param cmd the command to apply
|
||||||
* @return result of command applyTo method
|
* @return result of command applyTo method
|
||||||
|
@ -199,7 +196,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
/**
|
/**
|
||||||
* Provides a convenient method for modifying the current program, handling the transaction
|
* Provides a convenient method for modifying the current program, handling the transaction
|
||||||
* logic.
|
* logic.
|
||||||
*
|
*
|
||||||
* @param p the program
|
* @param p the program
|
||||||
* @param c the code to execute
|
* @param c the code to execute
|
||||||
* @see #modifyProgram(Program, ExceptionalCallback)
|
* @see #modifyProgram(Program, ExceptionalCallback)
|
||||||
|
@ -222,11 +219,41 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a convenient method for modifying the current program, handling the transaction
|
||||||
|
* logic and returning a result.
|
||||||
|
* @param <T> the return type
|
||||||
|
* @param <E> the exception type
|
||||||
|
* @param p the program
|
||||||
|
* @param s the code to execute
|
||||||
|
* @return the supplier's return value
|
||||||
|
* @see #modifyProgram(Program, ExceptionalCallback)
|
||||||
|
* @see #modifyProgram(Program, ExceptionalFunction)
|
||||||
|
*/
|
||||||
|
public static <T, E extends Exception> T tx(Program p, ExceptionalSupplier<T, E> s) {
|
||||||
|
int txId = p.startTransaction("Test - Function in Transaction");
|
||||||
|
boolean commit = true;
|
||||||
|
try {
|
||||||
|
T t = s.get();
|
||||||
|
p.flushEvents();
|
||||||
|
waitForSwing();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
commit = false;
|
||||||
|
failWithException("Exception modifying program '" + p.getName() + "'", e);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
p.endTransaction(txId, commit);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a convenient method for modifying the current program, handling the transaction
|
* Provides a convenient method for modifying the current program, handling the transaction
|
||||||
* logic. This method is calls {@link #tx(Program, ExceptionalCallback)}, but helps with
|
* logic. This method is calls {@link #tx(Program, ExceptionalCallback)}, but helps with
|
||||||
* semantics.
|
* semantics.
|
||||||
*
|
*
|
||||||
* @param p the program
|
* @param p the program
|
||||||
* @param c the code to execute
|
* @param c the code to execute
|
||||||
* @see #modifyProgram(Program, ExceptionalFunction)
|
* @see #modifyProgram(Program, ExceptionalFunction)
|
||||||
|
@ -238,7 +265,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
/**
|
/**
|
||||||
* Provides a convenient method for modifying the current program, handling the transaction
|
* Provides a convenient method for modifying the current program, handling the transaction
|
||||||
* logic and returning a new item as a result
|
* logic and returning a new item as a result
|
||||||
*
|
*
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @param f the function for modifying the program and creating the desired result
|
* @param f the function for modifying the program and creating the desired result
|
||||||
* @return the result
|
* @return the result
|
||||||
|
@ -439,7 +466,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
/**
|
/**
|
||||||
* Returns the global symbol with the given name if and only if it is the only
|
* Returns the global symbol with the given name if and only if it is the only
|
||||||
* global symbol with that name.
|
* global symbol with that name.
|
||||||
*
|
*
|
||||||
* @param program the program to search.
|
* @param program the program to search.
|
||||||
* @param name the name of the global symbol to find.
|
* @param name the name of the global symbol to find.
|
||||||
* @return the global symbol with the given name if and only if it is the only one.
|
* @return the global symbol with the given name if and only if it is the only one.
|
||||||
|
@ -451,7 +478,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
/**
|
/**
|
||||||
* Returns the symbol in the given namespace with the given name if and only if it is the only
|
* Returns the symbol in the given namespace with the given name if and only if it is the only
|
||||||
* symbol in that namespace with that name.
|
* symbol in that namespace with that name.
|
||||||
*
|
*
|
||||||
* @param program the program to search.
|
* @param program the program to search.
|
||||||
* @param name the name of the symbol to find.
|
* @param name the name of the symbol to find.
|
||||||
* @param namespace the parent namespace; may be null
|
* @param namespace the parent namespace; may be null
|
||||||
|
@ -468,14 +495,14 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
/**
|
/**
|
||||||
* A convenience method that allows you to open the given program in a default tool,
|
* A convenience method that allows you to open the given program in a default tool,
|
||||||
* navigating to the given address.
|
* navigating to the given address.
|
||||||
*
|
*
|
||||||
* <P>Note: this is a blocking operation. Your test will not proceed while this method is
|
* <P>Note: this is a blocking operation. Your test will not proceed while this method is
|
||||||
* sleeping.
|
* sleeping.
|
||||||
*
|
*
|
||||||
* <P><B>Do not leave this call in your test when committing changes.</B>
|
* <P><B>Do not leave this call in your test when committing changes.</B>
|
||||||
* @param p the program
|
* @param p the program
|
||||||
* @param address the address
|
* @param address the address
|
||||||
*
|
*
|
||||||
* @throws Exception if there is an issue create a {@link TestEnv}
|
* @throws Exception if there is an issue create a {@link TestEnv}
|
||||||
*/
|
*/
|
||||||
public void debugProgramInTool(Program p, String address) throws Exception {
|
public void debugProgramInTool(Program p, String address) throws Exception {
|
||||||
|
@ -512,7 +539,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for a launched script to complete by using the given listener.
|
* Waits for a launched script to complete by using the given listener.
|
||||||
*
|
*
|
||||||
* @param listener the listener used to track script progress
|
* @param listener the listener used to track script progress
|
||||||
* @param timeoutMS the max time to wait; failing if exceeded
|
* @param timeoutMS the max time to wait; failing if exceeded
|
||||||
*/
|
*/
|
||||||
|
@ -534,7 +561,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the given implementations of the provided service class with the given class.
|
* Replaces the given implementations of the provided service class with the given class.
|
||||||
*
|
*
|
||||||
* @param tool the tool whose services to update (optional)
|
* @param tool the tool whose services to update (optional)
|
||||||
* @param service the service to override
|
* @param service the service to override
|
||||||
* @param replacement the new version of the service
|
* @param replacement the new version of the service
|
||||||
|
|
|
@ -44,6 +44,7 @@ import ghidra.program.util.AddressFieldLocation;
|
||||||
import ghidra.program.util.FieldNameFieldLocation;
|
import ghidra.program.util.FieldNameFieldLocation;
|
||||||
import ghidra.test.AbstractProgramBasedTest;
|
import ghidra.test.AbstractProgramBasedTest;
|
||||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||||
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.datastruct.Accumulator;
|
import ghidra.util.datastruct.Accumulator;
|
||||||
import ghidra.util.datastruct.ListAccumulator;
|
import ghidra.util.datastruct.ListAccumulator;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -51,7 +52,7 @@ import ghidra.util.table.GhidraTable;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for use by tests that exercise various types of
|
* A base class for use by tests that exercise various types of
|
||||||
* {@link LocationDescriptor}.
|
* {@link LocationDescriptor}.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractLocationReferencesTest extends AbstractProgramBasedTest {
|
public abstract class AbstractLocationReferencesTest extends AbstractProgramBasedTest {
|
||||||
|
@ -98,7 +99,7 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
|
||||||
|
|
||||||
//
|
//
|
||||||
// Arrays/Structures
|
// Arrays/Structures
|
||||||
//
|
//
|
||||||
DataType type = new IntegerDataType();
|
DataType type = new IntegerDataType();
|
||||||
DataType pointer = new PointerDataType(type);
|
DataType pointer = new PointerDataType(type);
|
||||||
ArrayDataType array = new ArrayDataType(pointer, 4, pointer.getLength());
|
ArrayDataType array = new ArrayDataType(pointer, 4, pointer.getLength());
|
||||||
|
@ -150,7 +151,7 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
|
||||||
private void doGoToDataNameFieldAt(Address a, int[] path) {
|
private void doGoToDataNameFieldAt(Address a, int[] path) {
|
||||||
openData(a);
|
openData(a);
|
||||||
|
|
||||||
// note: the path here is
|
// note: the path here is
|
||||||
FieldNameFieldLocation location = new FieldNameFieldLocation(program, a, path, "name", 0);
|
FieldNameFieldLocation location = new FieldNameFieldLocation(program, a, path, "name", 0);
|
||||||
ProgramLocationPluginEvent event =
|
ProgramLocationPluginEvent event =
|
||||||
new ProgramLocationPluginEvent("Test", location, program);
|
new ProgramLocationPluginEvent("Test", location, program);
|
||||||
|
@ -318,7 +319,10 @@ public abstract class AbstractLocationReferencesTest extends AbstractProgramBase
|
||||||
|
|
||||||
protected void assertResultCount(int expected) {
|
protected void assertResultCount(int expected) {
|
||||||
List<Address> referenceAddresses = getResultAddresses();
|
List<Address> referenceAddresses = getResultAddresses();
|
||||||
assertEquals(expected, referenceAddresses.size());
|
if (referenceAddresses.size() != expected) {
|
||||||
|
Msg.debug(this, "Result addresses found: " + referenceAddresses);
|
||||||
|
fail("Incorrect number of results; see console");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertResultCount(String msg, int expected) {
|
protected void assertResultCount(String msg, int expected) {
|
||||||
|
|
|
@ -15,13 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.navigation.locationreferences;
|
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.app.cmd.data.CreateDataCmd;
|
||||||
import ghidra.app.util.viewer.field.FieldNameFieldFactory;
|
import ghidra.app.util.viewer.field.FieldNameFieldFactory;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.data.Enum;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|
||||||
public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTest {
|
public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTest {
|
||||||
|
@ -38,4 +41,61 @@ public class LocationReferencesPlugin1Test extends AbstractLocationReferencesTes
|
||||||
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
|
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
|
||||||
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
|
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindStructureField_UnnamedDefaultField() {
|
||||||
|
|
||||||
|
// apply a structure with unnamed fields
|
||||||
|
Structure struct = (Structure) getDt("/MyStruct");
|
||||||
|
Address address = addr(0x01005560);
|
||||||
|
assertTrue(applyCmd(program, new CreateDataCmd(address, struct)));
|
||||||
|
|
||||||
|
openData(0x01005560);
|
||||||
|
|
||||||
|
goTo(addr(0x01005560), FieldNameFieldFactory.FIELD_NAME, 1);
|
||||||
|
|
||||||
|
ProgramLocation location = codeBrowser.getCurrentLocation();
|
||||||
|
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
|
||||||
|
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindEnumByMember() {
|
||||||
|
|
||||||
|
//
|
||||||
|
// This test searches for usage of an enum field. We will add two different enum field
|
||||||
|
// uses to make sure we only find the one for which we are searching.
|
||||||
|
//
|
||||||
|
|
||||||
|
Enum enoom = createEnum();
|
||||||
|
Address otherAddress = addr(0x01008014); // 0x1 ONE; this also has references
|
||||||
|
assertTrue(applyCmd(program, new CreateDataCmd(otherAddress, enoom)));
|
||||||
|
|
||||||
|
// this is the address will will use to search
|
||||||
|
Address address = addr(0x01008019); // 0x0 ZERO
|
||||||
|
assertTrue(applyCmd(program, new CreateDataCmd(address, enoom)));
|
||||||
|
|
||||||
|
goTo(address, "Operands", 1);
|
||||||
|
|
||||||
|
search();
|
||||||
|
|
||||||
|
assertResultCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataType getDt(String path) {
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
DataType dataType = dtm.getDataType(path);
|
||||||
|
return dataType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Enum createEnum() {
|
||||||
|
return tx(program, () -> {
|
||||||
|
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
Enum dt = new EnumDataType("TestEnum", 1);
|
||||||
|
dt.add("ZERO", 0);
|
||||||
|
dt.add("ONE", 1);
|
||||||
|
dt.add("TWO", 2);
|
||||||
|
return (Enum) dtm.addDataType(dt, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.navigation.locationreferences;
|
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -44,7 +43,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
||||||
int parameterColumn = 1;
|
int parameterColumn = 1;
|
||||||
goTo(address, "Function Signature", parameterColumn);
|
goTo(address, "Function Signature", parameterColumn);
|
||||||
|
|
||||||
// change the return type
|
// change the return type
|
||||||
DataType dataType = setReturnTypeToByte(address);
|
DataType dataType = setReturnTypeToByte(address);
|
||||||
|
|
||||||
search();
|
search();
|
||||||
|
@ -176,7 +175,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
||||||
@Test
|
@Test
|
||||||
public void testLabelLocationDescriptor() throws Exception {
|
public void testLabelLocationDescriptor() throws Exception {
|
||||||
|
|
||||||
// 010039fe - LAB_010039fe
|
// 010039fe - LAB_010039fe
|
||||||
Address address = addr(0x010039fe);
|
Address address = addr(0x010039fe);
|
||||||
int column = 3;
|
int column = 3;
|
||||||
goTo(address, "Label", column);
|
goTo(address, "Label", column);
|
||||||
|
@ -231,19 +230,6 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
||||||
assertThat(descriptor, is(instanceOf(AddressLocationDescriptor.class)));
|
assertThat(descriptor, is(instanceOf(AddressLocationDescriptor.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFieldNameLocationDescriptor_StructureFieldName_ArrayInStructure()
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
openData(0x01005540);
|
|
||||||
|
|
||||||
goTo(addr(0x01005541), FieldNameFieldFactory.FIELD_NAME, 1);
|
|
||||||
|
|
||||||
ProgramLocation location = codeBrowser.getCurrentLocation();
|
|
||||||
LocationDescriptor descriptor = ReferenceUtils.getLocationDescriptor(location);
|
|
||||||
assertThat(descriptor, is(instanceOf(StructureMemberLocationDescriptor.class)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFieldNameLocationDescriptor_StructureInArray() throws Exception {
|
public void testFieldNameLocationDescriptor_StructureInArray() throws Exception {
|
||||||
|
|
||||||
|
@ -261,7 +247,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFindReferencesToFunctionDefinitionDataTypeFromService() throws Exception {
|
public void testFindReferencesToFunctionDefinitionDataTypeFromService() throws Exception {
|
||||||
//
|
//
|
||||||
// For this test we will have to create a FunctionDefinitionData type that matches
|
// For this test we will have to create a FunctionDefinitionData type that matches
|
||||||
// that of an existing function
|
// that of an existing function
|
||||||
//
|
//
|
||||||
|
@ -312,7 +298,7 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
||||||
|
|
||||||
//
|
//
|
||||||
// Dynamic data types should show all references to the the outermost data, including
|
// Dynamic data types should show all references to the the outermost data, including
|
||||||
// offcut.
|
// offcut.
|
||||||
//
|
//
|
||||||
|
|
||||||
// go to an unused address
|
// go to an unused address
|
||||||
|
@ -379,11 +365,9 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
||||||
assertContains(results, from1, from2, from3, stringAddr);
|
assertContains(results, from1, from2, from3, stringAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
private void createString_CallStructure(String addressString) throws Exception {
|
private void createString_CallStructure(String addressString) throws Exception {
|
||||||
// String
|
// String
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -31,8 +31,7 @@ import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.app.plugin.core.decompile.actions.FindReferencesToSymbolAction;
|
import ghidra.app.plugin.core.decompile.actions.FindReferencesToSymbolAction;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider;
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DataTypeReferenceFinder;
|
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
@ -172,6 +171,19 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
|
||||||
compositeFieldReferencesCallCount.incrementAndGet();
|
compositeFieldReferencesCallCount.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
public void findReferences(Program p, FieldMatcher fieldMatcher,
|
||||||
|
Consumer<DataTypeReference> callback, TaskMonitor monitor) {
|
||||||
|
|
||||||
|
if (fieldMatcher.isIgnored()) {
|
||||||
|
// an empty field matcher signals a data type search
|
||||||
|
dataTypeReferencesCallCount.incrementAndGet();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
compositeFieldReferencesCallCount.incrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getFindDataTypeReferencesCallCount() {
|
public int getFindDataTypeReferencesCallCount() {
|
||||||
return dataTypeReferencesCallCount.get();
|
return dataTypeReferencesCallCount.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,9 @@ public class DecompilerFindReferencesToActionTest
|
||||||
public void testActionEnablement() throws Exception {
|
public void testActionEnablement() throws Exception {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Decomp of 'init_string':
|
Decomp of 'init_string':
|
||||||
|
|
||||||
1|
|
1|
|
||||||
2| void init_string(mystring *ptr)
|
2| void init_string(mystring *ptr)
|
||||||
3|
|
3|
|
||||||
|
@ -42,17 +42,17 @@ public class DecompilerFindReferencesToActionTest
|
||||||
6| return;
|
6| return;
|
||||||
7| }
|
7| }
|
||||||
8|
|
8|
|
||||||
|
|
||||||
Note: there are two places in this function we can search for data type references:
|
Note: there are two places in this function we can search for data type references:
|
||||||
1) the parameter (line 2, cols 17-25, 26-30)
|
1) the parameter (line 2, cols 17-25, 26-30)
|
||||||
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
|
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
decompile(INIT_STRING_ADDR);
|
decompile(INIT_STRING_ADDR);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Action should not enabled unless on the data type
|
// Action should not enabled unless on the data type
|
||||||
//
|
//
|
||||||
// Empty line
|
// Empty line
|
||||||
int line = 1;
|
int line = 1;
|
||||||
int charPosition = 0;
|
int charPosition = 0;
|
||||||
|
@ -99,9 +99,9 @@ public class DecompilerFindReferencesToActionTest
|
||||||
@Test
|
@Test
|
||||||
public void testFindDataTypeReferences_ToEntireDataType_FromParameter() throws Exception {
|
public void testFindDataTypeReferences_ToEntireDataType_FromParameter() throws Exception {
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Decomp of 'init_string':
|
Decomp of 'init_string':
|
||||||
|
|
||||||
1|
|
1|
|
||||||
2| void init_string(mystring *ptr)
|
2| void init_string(mystring *ptr)
|
||||||
3|
|
3|
|
||||||
|
@ -110,11 +110,11 @@ public class DecompilerFindReferencesToActionTest
|
||||||
6| return;
|
6| return;
|
||||||
7| }
|
7| }
|
||||||
8|
|
8|
|
||||||
|
|
||||||
Note: there are two places in this function we can search for data type references:
|
Note: there are two places in this function we can search for data type references:
|
||||||
1) the parameter (line 2, cols 17-25, 26-30)
|
1) the parameter (line 2, cols 17-25, 26-30)
|
||||||
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
|
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
decompile(INIT_STRING_ADDR);
|
decompile(INIT_STRING_ADDR);
|
||||||
|
@ -130,9 +130,9 @@ public class DecompilerFindReferencesToActionTest
|
||||||
@Test
|
@Test
|
||||||
public void testFindDataTypeReferences_ToEntireDataType_FromVariable() throws Exception {
|
public void testFindDataTypeReferences_ToEntireDataType_FromVariable() throws Exception {
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Decomp of 'init_string':
|
Decomp of 'init_string':
|
||||||
|
|
||||||
1|
|
1|
|
||||||
2| void init_string(mystring *ptr)
|
2| void init_string(mystring *ptr)
|
||||||
3|
|
3|
|
||||||
|
@ -141,11 +141,11 @@ public class DecompilerFindReferencesToActionTest
|
||||||
6| return;
|
6| return;
|
||||||
7| }
|
7| }
|
||||||
8|
|
8|
|
||||||
|
|
||||||
Note: there are two places in this function we can search for data type references:
|
Note: there are two places in this function we can search for data type references:
|
||||||
1) the parameter (line 2, cols 17-25, 26-30)
|
1) the parameter (line 2, cols 17-25, 26-30)
|
||||||
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
|
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
decompile(INIT_STRING_ADDR);
|
decompile(INIT_STRING_ADDR);
|
||||||
|
@ -161,9 +161,9 @@ public class DecompilerFindReferencesToActionTest
|
||||||
@Test
|
@Test
|
||||||
public void testFindDataTypeReferences_ToFieldOfDataType() throws Exception {
|
public void testFindDataTypeReferences_ToFieldOfDataType() throws Exception {
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Decomp of 'init_string':
|
Decomp of 'init_string':
|
||||||
|
|
||||||
1|
|
1|
|
||||||
2| void init_string(mystring *ptr)
|
2| void init_string(mystring *ptr)
|
||||||
3|
|
3|
|
||||||
|
@ -172,11 +172,11 @@ public class DecompilerFindReferencesToActionTest
|
||||||
6| return;
|
6| return;
|
||||||
7| }
|
7| }
|
||||||
8|
|
8|
|
||||||
|
|
||||||
Note: there are two places in this function we can search for data type references:
|
Note: there are two places in this function we can search for data type references:
|
||||||
1) the parameter (line 2, cols 17-25, 26-30)
|
1) the parameter (line 2, cols 17-25, 26-30)
|
||||||
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
|
2) the usage of the parameter: (line 5 at cols 2-5 for the type and cols 7-12 for the field)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
decompile(INIT_STRING_ADDR);
|
decompile(INIT_STRING_ADDR);
|
||||||
|
@ -209,9 +209,9 @@ public class DecompilerFindReferencesToActionTest
|
||||||
public void testFindDataTypeReferences_ToCurrentAddress() throws Exception {
|
public void testFindDataTypeReferences_ToCurrentAddress() throws Exception {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Decomp of 'init_string':
|
Decomp of 'init_string':
|
||||||
|
|
||||||
1|
|
1|
|
||||||
2| void init_string(mystring *ptr)
|
2| void init_string(mystring *ptr)
|
||||||
3|
|
3|
|
||||||
|
@ -247,6 +247,6 @@ public class DecompilerFindReferencesToActionTest
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Private Methods
|
// Private Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@ package ghidra.app.plugin.core.decompile;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DataTypeReferenceFinder;
|
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -40,4 +39,10 @@ public class StubDataTypeReferenceFinder implements DataTypeReferenceFinder {
|
||||||
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
|
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findReferences(Program program, FieldMatcher fieldMatcher,
|
||||||
|
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,30 +20,30 @@ import java.util.List;
|
||||||
import ghidra.app.decompiler.ClangFieldToken;
|
import ghidra.app.decompiler.ClangFieldToken;
|
||||||
import ghidra.app.decompiler.ClangLine;
|
import ghidra.app.decompiler.ClangLine;
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.DataTypeReference;
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.program.model.data.Composite;
|
import ghidra.program.model.data.Composite;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents the use of a field of a {@link Composite} data type <b>where there is
|
* This class represents the use of a field of a {@link Composite} data type <b>where there is no
|
||||||
* no variable in the Decompiler</b> for that data type. A normal variable access in the
|
* variable in the Decompiler</b> for that data type. A normal variable access in the Decompiler
|
||||||
* Decompiler may look like so:
|
* may look like so:
|
||||||
* <pre>
|
* <pre>
|
||||||
* Foo f;
|
* Foo f;
|
||||||
* ...
|
* ...
|
||||||
* return f.some_field;
|
* return f.some_field;
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Alternatively, an anonymous variable access would look like this:
|
* Alternatively, an anonymous variable access would look like this:
|
||||||
* <pre>
|
* <pre>
|
||||||
* Bar b;
|
* Bar b;
|
||||||
* ...
|
* ...
|
||||||
* return b-><b>foo_array[1].some_field</b>;
|
* return b-><b>foo_array[1].some_field</b>;
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* In this case, <code><b>foo_array[1]</b></code> is a <code>Foo</code>, whose
|
* In this case, <code><b>foo_array[1]</b></code> is a <code>Foo</code>, whose
|
||||||
* <code><b>some_field</b></code> is
|
* <code><b>some_field</b></code> is being accessed anonymously, since there is no variable of
|
||||||
* being accessed anonymously, since there is no variable of <code>Foo</code> declared
|
* <code>Foo</code> declared in the current function.
|
||||||
* in the current function.
|
|
||||||
*/
|
*/
|
||||||
public class AnonymousVariableAccessDR extends VariableAccessDR {
|
public class AnonymousVariableAccessDR extends VariableAccessDR {
|
||||||
|
|
||||||
|
@ -52,42 +52,57 @@ public class AnonymousVariableAccessDR extends VariableAccessDR {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
|
public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||||
|
List<DataTypeReference> results) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// This class is backed by a ClangFieldToken. That class's data type is the composite
|
// This class is backed by a ClangFieldToken. That class's data type is the composite that
|
||||||
// that contains the field being accessed. A variable being accessed has 2 types being
|
// contains the field being accessed. A variable being accessed has 2 types being
|
||||||
// touched: the aforementioned composite and the type of the field itself.
|
// touched: the aforementioned composite and the type of the field itself.
|
||||||
//
|
//
|
||||||
// This can match in one of two cases:
|
// This can match in one of two cases:
|
||||||
// 1) the client seeks to match a given field inside of the containing composite, or
|
// 1) the passed in type must match the field type and not the parent type, or
|
||||||
// 2) the client seeks to match only the type, which means that the field type itself must match
|
// 2) the passed in type must match the parent type, along with supplied field name/offset.
|
||||||
//
|
//
|
||||||
|
|
||||||
ClangFieldToken field = (ClangFieldToken) sourceToken;
|
ClangFieldToken field = (ClangFieldToken) sourceToken;
|
||||||
DataType compositeType = field.getDataType();
|
DataType compositeType = field.getDataType();
|
||||||
DataType fieldDt = DecompilerReference.getFieldDataType(field);
|
DataType fieldDt = DecompilerReference.getFieldDataType(field);
|
||||||
|
|
||||||
boolean matchesComposite = isEqual(dt, compositeType);
|
boolean matchesCompositeType = isEqual(dt, compositeType);
|
||||||
boolean matchesField = isEqual(dt, fieldDt);
|
boolean matchesFieldType = isEqual(dt, fieldDt);
|
||||||
boolean noMatch = !(matchesComposite || matchesField);
|
boolean noMatch = !(matchesCompositeType || matchesFieldType);
|
||||||
if (noMatch) {
|
if (noMatch) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldName == null) {
|
//
|
||||||
// case 2; no field name to check
|
// Case 1
|
||||||
if (matchesField) {
|
//
|
||||||
|
// If the client did not specify a field to match, then we only want to match on the type
|
||||||
|
// of this reference's field type and NOT the composite type, since this reference is
|
||||||
|
// referring to the field and not the composite.
|
||||||
|
//
|
||||||
|
if (fieldMatcher.isIgnored()) {
|
||||||
|
if (matchesFieldType) {
|
||||||
|
// no field name and the search type matches this reference's field type
|
||||||
results.add(createReference(variable));
|
results.add(createReference(variable));
|
||||||
}
|
}
|
||||||
|
// else there is no field and the search type does not match the reference's type
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// case 1; check the field name and the composite type
|
//
|
||||||
if (matchesComposite && field.getText().equals(fieldName)) {
|
// Case 2
|
||||||
results.add(
|
//
|
||||||
new DataTypeReference(compositeType, fieldName, getFunction(), getAddress(),
|
// The client has requested a particular field of the parent composite. We only have a
|
||||||
getContext()));
|
// match if the parent type matches and the field name/offset matches.
|
||||||
|
//
|
||||||
|
String text = field.getText();
|
||||||
|
int offset = field.getOffset();
|
||||||
|
if (matchesCompositeType && fieldMatcher.matches(text, offset)) {
|
||||||
|
results.add(new DataTypeReference(compositeType, fieldMatcher.getFieldName(),
|
||||||
|
getFunction(), getAddress(), getContext()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,7 @@ import ghidra.app.decompiler.parallel.*;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReference;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DataTypeReferenceFinder;
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.BuiltInDataType;
|
import ghidra.program.model.data.BuiltInDataType;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
|
@ -58,10 +57,28 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void findReferences(Program program, DataType dataType, String fieldName,
|
public void findReferences(Program program, DataType dataType, String fieldName,
|
||||||
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
|
Consumer<DataTypeReference> consumer, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
|
FieldMatcher fieldMatcher = new FieldMatcher(dataType, fieldName);
|
||||||
DecompilerDataTypeFinderQCallback qCallback =
|
DecompilerDataTypeFinderQCallback qCallback =
|
||||||
new DecompilerDataTypeFinderQCallback(program, dataType, fieldName, callback);
|
new DecompilerDataTypeFinderQCallback(program, dataType, fieldMatcher, consumer);
|
||||||
|
doFindReferences(program, dataType, qCallback, consumer, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findReferences(Program program, FieldMatcher fieldMatcher,
|
||||||
|
Consumer<DataTypeReference> consumer, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
|
DataType dataType = fieldMatcher.getDataType();
|
||||||
|
DecompilerDataTypeFinderQCallback qCallback =
|
||||||
|
new DecompilerDataTypeFinderQCallback(program, dataType, fieldMatcher, consumer);
|
||||||
|
|
||||||
|
doFindReferences(program, dataType, qCallback, consumer, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doFindReferences(Program program, DataType dataType,
|
||||||
|
DecompilerDataTypeFinderQCallback qCallback, Consumer<DataTypeReference> consumer,
|
||||||
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
Set<Function> functions = filterFunctions(program, dataType, monitor);
|
Set<Function> functions = filterFunctions(program, dataType, monitor);
|
||||||
|
|
||||||
|
@ -87,7 +104,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
buildTypeLineage(dt, types);
|
buildTypeLineage(dt, types);
|
||||||
|
|
||||||
Set<Function> results = new HashSet<>();
|
Set<Function> results = new HashSet<>();
|
||||||
accumulateFunctionCallsToDefinedData(program, types, results, monitor);
|
accumulateFunctionCallsToDefinedData(program, dt, types, results, monitor);
|
||||||
|
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
FunctionIterator it = listing.getFunctions(true);
|
FunctionIterator it = listing.getFunctions(true);
|
||||||
|
@ -115,21 +132,23 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void accumulateFunctionCallsToDefinedData(Program program, Set<DataType> potentialTypes,
|
private void accumulateFunctionCallsToDefinedData(Program program, DataType dataType,
|
||||||
Set<Function> results, TaskMonitor monitor) throws CancelledException {
|
Set<DataType> potentialTypes, Set<Function> results, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
AtomicInteger counter = new AtomicInteger();
|
AtomicInteger counter = new AtomicInteger();
|
||||||
SetAccumulator<LocationReference> accumulator = new SetAccumulator<>();
|
SetAccumulator<LocationReference> accumulator = new SetAccumulator<>();
|
||||||
Predicate<Data> dataMatcher = data -> {
|
Predicate<Data> dataMatcher = data -> {
|
||||||
counter.incrementAndGet();
|
counter.incrementAndGet();
|
||||||
DataType dataType = data.getDataType();
|
DataType dt = data.getDataType();
|
||||||
boolean matches = potentialTypes.contains(dataType);
|
boolean matches = potentialTypes.contains(dt);
|
||||||
return matches;
|
return matches;
|
||||||
};
|
};
|
||||||
|
|
||||||
ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher, null,
|
FieldMatcher emptyMatcher = new FieldMatcher(dataType);
|
||||||
monitor);
|
ReferenceUtils.findDataTypeMatchesInDefinedData(accumulator, program, dataMatcher,
|
||||||
|
emptyMatcher, monitor);
|
||||||
|
|
||||||
for (LocationReference ref : accumulator) {
|
for (LocationReference ref : accumulator) {
|
||||||
Address address = ref.getLocationOfUse();
|
Address address = ref.getLocationOfUse();
|
||||||
|
@ -158,12 +177,10 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
|
|
||||||
// We have a different type, should we search for it?
|
// We have a different type, should we search for it?
|
||||||
if (baseType instanceof BuiltInDataType) {
|
if (baseType instanceof BuiltInDataType) {
|
||||||
// When given a wrapper type (e.g., typedef) , ignore
|
// When given a wrapper type (e.g., typedef) , ignore built-ins (e.g., int, byte, etc),
|
||||||
// built-ins (e.g., int, byte, etc), as
|
// as they will be of little value due to their volume in the program and the user
|
||||||
// they will be of little value due to their volume in the program and the
|
// *probably* did not intend to search for them. (Below we do not do this check, which
|
||||||
// user *probably* did not intend to search for them. (Below we do not do
|
// allows the user to search directly for a built-in type, if they wish.)
|
||||||
// this check, which allows the user to search directly for a
|
|
||||||
// built-in type, if they wish.)
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,16 +224,16 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
|
|
||||||
private Consumer<DataTypeReference> callback;
|
private Consumer<DataTypeReference> callback;
|
||||||
private DataType dataType;
|
private DataType dataType;
|
||||||
private String fieldName;
|
private FieldMatcher fieldMatcher;
|
||||||
|
|
||||||
/* Search for composite field access */
|
/* Search for composite field access */
|
||||||
DecompilerDataTypeFinderQCallback(Program program, DataType dataType, String fieldName,
|
DecompilerDataTypeFinderQCallback(Program program, DataType dataType,
|
||||||
Consumer<DataTypeReference> callback) {
|
FieldMatcher fieldMatcher, Consumer<DataTypeReference> callback) {
|
||||||
|
|
||||||
super(program, new DecompilerConfigurer());
|
super(program, new DecompilerConfigurer());
|
||||||
|
|
||||||
this.dataType = dataType;
|
this.dataType = dataType;
|
||||||
this.fieldName = fieldName;
|
this.fieldMatcher = fieldMatcher;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +247,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
}
|
}
|
||||||
|
|
||||||
DecompilerDataTypeFinder finder =
|
DecompilerDataTypeFinder finder =
|
||||||
new DecompilerDataTypeFinder(results, function, dataType, fieldName);
|
new DecompilerDataTypeFinder(results, function, dataType, fieldMatcher);
|
||||||
List<DataTypeReference> refs = finder.findUsage();
|
List<DataTypeReference> refs = finder.findUsage();
|
||||||
|
|
||||||
refs.forEach(r -> callback.accept(r));
|
refs.forEach(r -> callback.accept(r));
|
||||||
|
@ -263,14 +280,14 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
private DecompileResults decompilation;
|
private DecompileResults decompilation;
|
||||||
private Function function;
|
private Function function;
|
||||||
private DataType dataType;
|
private DataType dataType;
|
||||||
private String fieldName;
|
private FieldMatcher fieldMatcher;
|
||||||
|
|
||||||
DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType,
|
DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType,
|
||||||
String fieldName) {
|
FieldMatcher fieldMatcher) {
|
||||||
this.decompilation = results;
|
this.decompilation = results;
|
||||||
this.function = function;
|
this.function = function;
|
||||||
this.dataType = dataType;
|
this.dataType = dataType;
|
||||||
this.fieldName = fieldName;
|
this.fieldMatcher = fieldMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DataTypeReference> findUsage() {
|
List<DataTypeReference> findUsage() {
|
||||||
|
@ -300,7 +317,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
|
|
||||||
/** Finds any search input match in the given reference */
|
/** Finds any search input match in the given reference */
|
||||||
private void matchUsage(DecompilerReference reference, List<DataTypeReference> results) {
|
private void matchUsage(DecompilerReference reference, List<DataTypeReference> results) {
|
||||||
reference.accumulateMatches(dataType, fieldName, results);
|
reference.accumulateMatches(dataType, fieldMatcher, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DecompilerReference> findVariableReferences(ClangTokenGroup tokens) {
|
private List<DecompilerReference> findVariableReferences(ClangTokenGroup tokens) {
|
||||||
|
@ -320,20 +337,20 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
|
||||||
* the case with Composite types, may have one of its fields accessed. Each result
|
* the case with Composite types, may have one of its fields accessed. Each result
|
||||||
* found by this method will be at least a variable access and may also itself have
|
* found by this method will be at least a variable access and may also itself have
|
||||||
* field accesses.
|
* field accesses.
|
||||||
*
|
*
|
||||||
* <p>Sometimes a line is structured such that there are anonymous variable accesses. This
|
* <p>Sometimes a line is structured such that there are anonymous variable accesses. This
|
||||||
* is the case where a Composite is being accessed, but the Composite itself is
|
* is the case where a Composite is being accessed, but the Composite itself is
|
||||||
* not a variable in the current function. See {@link AnonymousVariableAccessDR} for
|
* not a variable in the current function. See {@link AnonymousVariableAccessDR} for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* @param line the current line being processed from the Decompiler
|
* @param line the current line being processed from the Decompiler
|
||||||
* @param results the accumulator into which matches will be placed
|
* @param results the accumulator into which matches will be placed
|
||||||
*/
|
*/
|
||||||
private void findVariablesInLine(ClangLine line, List<DecompilerReference> results) {
|
private void findVariablesInLine(ClangLine line, List<DecompilerReference> results) {
|
||||||
|
|
||||||
List<ClangToken> allTokens = line.getAllTokens();
|
List<ClangToken> allTokens = line.getAllTokens();
|
||||||
Iterable<ClangToken> filteredTokens = IterableUtils.filteredIterable(allTokens,
|
Iterable<ClangToken> filteredTokens =
|
||||||
token -> {
|
IterableUtils.filteredIterable(allTokens, token -> {
|
||||||
// Only include desirable tokens (this is really just for easier debugging).
|
// Only include desirable tokens (this is really just for easier debugging).
|
||||||
// Update this filter if the loop below ever needs other types of tokens.
|
// Update this filter if the loop below ever needs other types of tokens.
|
||||||
return (token instanceof ClangTypeToken) ||
|
return (token instanceof ClangTypeToken) ||
|
||||||
|
|
|
@ -23,7 +23,7 @@ import ghidra.program.model.data.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that represents access to a Decompiler {@link ClangFieldToken} object. This is the field
|
* A class that represents access to a Decompiler {@link ClangFieldToken} object. This is the field
|
||||||
* of a variable, denoted by {@link ClangVariableToken}.
|
* of a variable, denoted by {@link ClangVariableToken}.
|
||||||
*/
|
*/
|
||||||
public class DecompilerFieldAccess extends DecompilerVariable {
|
public class DecompilerFieldAccess extends DecompilerVariable {
|
||||||
|
@ -73,6 +73,12 @@ public class DecompilerFieldAccess extends DecompilerVariable {
|
||||||
return dt;
|
return dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOffset() {
|
||||||
|
ClangFieldToken field = (ClangFieldToken) variable;
|
||||||
|
return field.getOffset();
|
||||||
|
}
|
||||||
|
|
||||||
protected DataType getBaseType(DataType dt) {
|
protected DataType getBaseType(DataType dt) {
|
||||||
if (dt instanceof Array) {
|
if (dt instanceof Array) {
|
||||||
return getBaseType(((Array) dt).getDataType());
|
return getBaseType(((Array) dt).getDataType());
|
||||||
|
|
|
@ -21,6 +21,7 @@ import ghidra.app.decompiler.*;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContextBuilder;
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContextBuilder;
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.DataTypeReference;
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
|
@ -51,12 +52,12 @@ public abstract class DecompilerReference {
|
||||||
* The <tt>fieldName</tt> is optional. If not included, then only data type matches will
|
* The <tt>fieldName</tt> is optional. If not included, then only data type matches will
|
||||||
* be sought. If it is included, then a match is only included when it is a reference
|
* be sought. If it is included, then a match is only included when it is a reference
|
||||||
* to the given data type where that type is being accessed by the given field name.
|
* to the given data type where that type is being accessed by the given field name.
|
||||||
*
|
*
|
||||||
* @param dt the data type to find
|
* @param dt the data type to find
|
||||||
* @param fieldName the optional field name used to restrict matches.
|
* @param fieldMatcher the optional field matcher used to restrict matches.
|
||||||
* @param results the accumulator object into which will be placed any matches
|
* @param results the accumulator object into which will be placed any matches
|
||||||
*/
|
*/
|
||||||
public abstract void accumulateMatches(DataType dt, String fieldName,
|
public abstract void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||||
List<DataTypeReference> results);
|
List<DataTypeReference> results);
|
||||||
|
|
||||||
public DecompilerVariable getVariable() {
|
public DecompilerVariable getVariable() {
|
||||||
|
@ -160,7 +161,7 @@ public abstract class DecompilerReference {
|
||||||
int offset = field.getOffset();
|
int offset = field.getOffset();
|
||||||
int n = parent.getLength();
|
int n = parent.getLength();
|
||||||
if (offset >= 0 && offset < n) {
|
if (offset >= 0 && offset < n) {
|
||||||
DataTypeComponent dtc = parent.getComponentAt(field.getOffset());
|
DataTypeComponent dtc = parent.getComponentContaining(field.getOffset());
|
||||||
fieldDt = dtc.getDataType();
|
fieldDt = dtc.getDataType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,6 +188,10 @@ public abstract class DecompilerVariable {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getOffset() {
|
||||||
|
return Integer.MIN_VALUE; // subclasses can override
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n";
|
String castString = casts.isEmpty() ? "" : "\tcasts: " + casts + ",\n";
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
||||||
import ghidra.app.decompiler.ClangLine;
|
import ghidra.app.decompiler.ClangLine;
|
||||||
import ghidra.app.decompiler.ClangTypeToken;
|
import ghidra.app.decompiler.ClangTypeToken;
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.DataTypeReference;
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
|
|
||||||
public class ReturnTypeDR extends DecompilerReference {
|
public class ReturnTypeDR extends DecompilerReference {
|
||||||
|
@ -29,11 +30,11 @@ public class ReturnTypeDR extends DecompilerReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
|
public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||||
|
List<DataTypeReference> results) {
|
||||||
|
|
||||||
if (fieldName != null) {
|
if (!fieldMatcher.isIgnored()) {
|
||||||
// Return Types do not have any field usage
|
return; // Return Types do not have any field usage
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataType myDt = getDataType();
|
DataType myDt = getDataType();
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
||||||
import ghidra.app.decompiler.*;
|
import ghidra.app.decompiler.*;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.DataTypeReference;
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
|
@ -58,10 +59,11 @@ public class VariableAccessDR extends DecompilerReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
|
public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||||
|
List<DataTypeReference> results) {
|
||||||
|
|
||||||
if (fields.isEmpty()) {
|
if (fields.isEmpty()) {
|
||||||
DecompilerVariable var = getMatch(dt, fieldName, variable, null);
|
DecompilerVariable var = getMatch(dt, fieldMatcher, variable, null);
|
||||||
if (var != null) {
|
if (var != null) {
|
||||||
DataTypeReference ref = createReference(var);
|
DataTypeReference ref = createReference(var);
|
||||||
results.add(ref);
|
results.add(ref);
|
||||||
|
@ -77,7 +79,7 @@ public class VariableAccessDR extends DecompilerReference {
|
||||||
for (DecompilerVariable field : fields) {
|
for (DecompilerVariable field : fields) {
|
||||||
|
|
||||||
DecompilerVariable next = field;
|
DecompilerVariable next = field;
|
||||||
DecompilerVariable var = getMatch(dt, fieldName, start, next);
|
DecompilerVariable var = getMatch(dt, fieldMatcher, start, next);
|
||||||
if (var != null) {
|
if (var != null) {
|
||||||
DataTypeReference ref = createReference(var, next);
|
DataTypeReference ref = createReference(var, next);
|
||||||
results.add(ref);
|
results.add(ref);
|
||||||
|
@ -87,46 +89,46 @@ public class VariableAccessDR extends DecompilerReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle the last variable by itself (for the case where we are matching just on the
|
// Handle the last variable by itself (for the case where we are matching just on the type,
|
||||||
// type, with no field name)
|
// with no field name)
|
||||||
//
|
//
|
||||||
if (fieldName != null) {
|
if (fieldMatcher.isIgnored()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecompilerVariable var = getMatch(dt, null, start, null);
|
DecompilerVariable var = getMatch(dt, fieldMatcher, start, null);
|
||||||
if (var != null) {
|
if (var != null) {
|
||||||
DataTypeReference ref = createReference(var);
|
DataTypeReference ref = createReference(var);
|
||||||
results.add(ref);
|
results.add(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DecompilerVariable getMatch(DataType dt, String fieldName, DecompilerVariable var,
|
private DecompilerVariable getMatch(DataType dt, FieldMatcher fieldMatcher,
|
||||||
DecompilerVariable potentialField) {
|
DecompilerVariable var, DecompilerVariable potentialField) {
|
||||||
|
|
||||||
// Note: for now, I ignore the precedence of casting; if any cast type is a match, then
|
// Note: for now, I ignore the precedence of casting; if any cast type is a match, then
|
||||||
// signal hooray
|
// signal hooray
|
||||||
boolean searchForField = fieldName != null;
|
boolean searchForField = !fieldMatcher.isIgnored();
|
||||||
DecompilerVariable fieldVar = searchForField ? potentialField : null;
|
DecompilerVariable fieldVar = searchForField ? potentialField : null;
|
||||||
DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar);
|
DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar);
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
// wrong type, nothing to do
|
return null; // wrong type, nothing to do
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matches on the type, does the field match?
|
// Matches on the type, does the field match?
|
||||||
if (fieldName == null) {
|
if (fieldMatcher.isIgnored()) {
|
||||||
return match; // no field to match
|
return match; // no field to match
|
||||||
}
|
}
|
||||||
|
|
||||||
if (potentialField == null) {
|
if (potentialField == null) {
|
||||||
|
|
||||||
// check for the case where we have not been passed a 'potential field', but the given
|
// check for the case where we have not been passed a 'potential field', but the given
|
||||||
// 'var' is itself the field we seek, such as in an if statement like this:
|
// 'var' is itself may be the field we seek, such as in an if statement like this:
|
||||||
// if (color == RED)
|
// if (color == RED)
|
||||||
// where 'RED' is the variable we are checking
|
// where 'RED' is the variable we are checking
|
||||||
String name = var.getName();
|
String name = var.getName();
|
||||||
if (fieldName.equals(name)) {
|
int offset = var.getOffset();
|
||||||
|
if (fieldMatcher.matches(name, offset)) {
|
||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +136,8 @@ public class VariableAccessDR extends DecompilerReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = potentialField.getName();
|
String name = potentialField.getName();
|
||||||
if (fieldName.equals(name)) {
|
int offset = potentialField.getOffset();
|
||||||
|
if (fieldMatcher.matches(name, offset)) {
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -156,13 +159,13 @@ public class VariableAccessDR extends DecompilerReference {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Unusual Code Alert!
|
// Unusual Code Alert!
|
||||||
// It is a bit odd to check the field when you are looking for the type that contains
|
// It is a bit odd to check the field when you are looking for the type that contains the
|
||||||
// the field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the
|
// field. BUT, in the Decompiler, SOMETIMES the 'field' happens to have the data type of
|
||||||
// data type of the thing that contains it. So, if you have:
|
// the thing that contains it. So, if you have:
|
||||||
// foo.bar
|
// foo.bar
|
||||||
// then the 'bar' field will have a data type of Foo. Unfortunately, this is not
|
// then the 'bar' field will have a data type of Foo. Unfortunately, this is not always
|
||||||
// always the case. For now, when the variable is global, we need to check the field
|
// the case. For now, when the variable is global, we need to check the field. Sad face
|
||||||
// Sad face emoji.
|
// emoji.
|
||||||
//
|
//
|
||||||
HighVariable highVariable = var.variable.getHighVariable();
|
HighVariable highVariable = var.variable.getHighVariable();
|
||||||
if (highVariable instanceof HighGlobal) {
|
if (highVariable instanceof HighGlobal) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
||||||
import ghidra.app.decompiler.*;
|
import ghidra.app.decompiler.*;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferenceContext;
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.DataTypeReference;
|
||||||
|
import ghidra.app.services.FieldMatcher;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
|
@ -52,7 +53,8 @@ public abstract class VariableDR extends DecompilerReference {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accumulateMatches(DataType dt, String fieldName, List<DataTypeReference> results) {
|
public void accumulateMatches(DataType dt, FieldMatcher fieldMatcher,
|
||||||
|
List<DataTypeReference> results) {
|
||||||
|
|
||||||
if (variable == null) {
|
if (variable == null) {
|
||||||
// This implies our API was misused in that a variable was never set after creation
|
// This implies our API was misused in that a variable was never set after creation
|
||||||
|
@ -61,21 +63,18 @@ public abstract class VariableDR extends DecompilerReference {
|
||||||
|
|
||||||
DataType dataType = getDataType();
|
DataType dataType = getDataType();
|
||||||
if (!isEqual(dataType, dt)) {
|
if (!isEqual(dataType, dt)) {
|
||||||
// wrong type, nothing to do
|
return; // wrong type, nothing to do
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationReferenceContext context = getContext();
|
|
||||||
Function function = getFunction();
|
|
||||||
Address address = getAddress();
|
|
||||||
if (fieldName == null) {
|
|
||||||
// no field to check, a match on the the type is good enough
|
|
||||||
results.add(new DataTypeReference(dataType, null, getFunction(), address, context));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = variable.getName();
|
String name = variable.getName();
|
||||||
if (name.equals(fieldName)) {
|
int offset = variable.getOffset();
|
||||||
|
if (fieldMatcher.matches(name, offset)) {
|
||||||
|
|
||||||
|
// this will be null if the field matcher is empty
|
||||||
|
String fieldName = fieldMatcher.getFieldName();
|
||||||
|
Function function = getFunction();
|
||||||
|
Address address = getAddress();
|
||||||
|
LocationReferenceContext context = getContext();
|
||||||
results.add(new DataTypeReference(dataType, fieldName, function, address, context));
|
results.add(new DataTypeReference(dataType, fieldName, function, address, context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,10 +41,10 @@ import utilities.util.reflection.ReflectionUtilities;
|
||||||
* the entire application.
|
* the entire application.
|
||||||
* <p>
|
* <p>
|
||||||
* DockingActions can be invoked from the global menu, a popup menu, a toolbar, and/or a keybinding,
|
* DockingActions can be invoked from the global menu, a popup menu, a toolbar, and/or a keybinding,
|
||||||
* depending on whether or not menuBarData, popupMenuData, toolBarData, and/or keyBindingData have
|
* depending on whether or not menuBarData, popupMenuData, toolBarData, and/or keyBindingData have
|
||||||
* been set.
|
* been set.
|
||||||
* <p>
|
* <p>
|
||||||
* <b>
|
* <b>
|
||||||
* Implementors of this class should override {@link #actionPerformed(ActionContext)}.
|
* Implementors of this class should override {@link #actionPerformed(ActionContext)}.
|
||||||
* </b>
|
* </b>
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -199,12 +199,12 @@ public abstract class DockingAction implements DockingActionIf {
|
||||||
* <P>
|
* <P>
|
||||||
* If the client wants the action on all windows, then they can call {@link #shouldAddToAllWindows}
|
* If the client wants the action on all windows, then they can call {@link #shouldAddToAllWindows}
|
||||||
* <P>
|
* <P>
|
||||||
* If the client wants the action to be on a window only when the window can produce
|
* If the client wants the action to be on a window only when the window can produce
|
||||||
* a certain context type, the the client should call
|
* a certain context type, the the client should call
|
||||||
* {@link #addToWindowWhen(Class)}
|
* {@link #addToWindowWhen(Class)}
|
||||||
* <P>
|
* <P>
|
||||||
* Otherwise, by default, the action will only be on the main window.
|
* Otherwise, by default, the action will only be on the main window.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) {
|
public final boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) {
|
||||||
|
@ -212,7 +212,7 @@ public abstract class DockingAction implements DockingActionIf {
|
||||||
if (menuBarData == null && toolBarData == null) {
|
if (menuBarData == null && toolBarData == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clients can specify that the action should be on all windows.
|
// clients can specify that the action should be on all windows.
|
||||||
if (shouldAddToAllWindows) {
|
if (shouldAddToAllWindows) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -242,6 +242,14 @@ public abstract class DockingAction implements DockingActionIf {
|
||||||
DockingWindowManager.getHelpService().registerHelp(this, location);
|
DockingWindowManager.getHelpService().registerHelp(this, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the help location for this action
|
||||||
|
* @return the help location for this action
|
||||||
|
*/
|
||||||
|
public HelpLocation getHelpLocation() {
|
||||||
|
return DockingWindowManager.getHelpService().getHelpLocation(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals the the help system that this action does not need a help entry. Some actions
|
* Signals the the help system that this action does not need a help entry. Some actions
|
||||||
* are so obvious that they do not require help, such as an action that renames a file.
|
* are so obvious that they do not require help, such as an action that renames a file.
|
||||||
|
@ -579,10 +587,10 @@ public abstract class DockingAction implements DockingActionIf {
|
||||||
/**
|
/**
|
||||||
* Sets a predicate for dynamically determining the action's enabled state. If this
|
* Sets a predicate for dynamically determining the action's enabled state. If this
|
||||||
* predicate is not set, the action's enable state must be controlled directly using the
|
* predicate is not set, the action's enable state must be controlled directly using the
|
||||||
* {@link DockingAction#setEnabled(boolean)} method. See
|
* {@link DockingAction#setEnabled(boolean)} method. See
|
||||||
* {@link DockingActionIf#isEnabledForContext(ActionContext)}
|
* {@link DockingActionIf#isEnabledForContext(ActionContext)}
|
||||||
*
|
*
|
||||||
* @param predicate the predicate that will be used to dynamically determine an action's
|
* @param predicate the predicate that will be used to dynamically determine an action's
|
||||||
* enabled state.
|
* enabled state.
|
||||||
*/
|
*/
|
||||||
public void enabledWhen(Predicate<ActionContext> predicate) {
|
public void enabledWhen(Predicate<ActionContext> predicate) {
|
||||||
|
@ -592,10 +600,10 @@ public abstract class DockingAction implements DockingActionIf {
|
||||||
/**
|
/**
|
||||||
* Sets a predicate for dynamically determining if this action should be included in
|
* Sets a predicate for dynamically determining if this action should be included in
|
||||||
* an impending pop-up menu. If this predicate is not set, the action's will be included
|
* an impending pop-up menu. If this predicate is not set, the action's will be included
|
||||||
* in an impending pop-up, if it is enabled. See
|
* in an impending pop-up, if it is enabled. See
|
||||||
* {@link DockingActionIf#isAddToPopup(ActionContext)}
|
* {@link DockingActionIf#isAddToPopup(ActionContext)}
|
||||||
*
|
*
|
||||||
* @param predicate the predicate that will be used to dynamically determine an action's
|
* @param predicate the predicate that will be used to dynamically determine an action's
|
||||||
* enabled state.
|
* enabled state.
|
||||||
*/
|
*/
|
||||||
public void popupWhen(Predicate<ActionContext> predicate) {
|
public void popupWhen(Predicate<ActionContext> predicate) {
|
||||||
|
@ -603,10 +611,10 @@ public abstract class DockingAction implements DockingActionIf {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a predicate for dynamically determining if this action is valid for the current
|
* Sets a predicate for dynamically determining if this action is valid for the current
|
||||||
* {@link ActionContext}. See {@link DockingActionIf#isValidContext(ActionContext)}
|
* {@link ActionContext}. See {@link DockingActionIf#isValidContext(ActionContext)}
|
||||||
*
|
*
|
||||||
* @param predicate the predicate that will be used to dynamically determine an action's
|
* @param predicate the predicate that will be used to dynamically determine an action's
|
||||||
* validity for a given {@link ActionContext}
|
* validity for a given {@link ActionContext}
|
||||||
*/
|
*/
|
||||||
public void validContextWhen(Predicate<ActionContext> predicate) {
|
public void validContextWhen(Predicate<ActionContext> predicate) {
|
||||||
|
@ -620,14 +628,14 @@ public abstract class DockingAction implements DockingActionIf {
|
||||||
* that can produce an ActionContext that is appropriate for this action.
|
* that can produce an ActionContext that is appropriate for this action.
|
||||||
* <P>
|
* <P>
|
||||||
* @param contextClass the ActionContext class required to be producible by a
|
* @param contextClass the ActionContext class required to be producible by a
|
||||||
* provider that is hosted in that window before this action is added to that
|
* provider that is hosted in that window before this action is added to that
|
||||||
* window.
|
* window.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void addToWindowWhen(Class<? extends ActionContext> contextClass) {
|
public void addToWindowWhen(Class<? extends ActionContext> contextClass) {
|
||||||
addToWindowWhenContextClass = contextClass;
|
addToWindowWhenContextClass = contextClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells this action to add itself to all windows
|
* Tells this action to add itself to all windows
|
||||||
* <P>
|
* <P>
|
||||||
|
@ -664,5 +672,5 @@ public abstract class DockingAction implements DockingActionIf {
|
||||||
String classInfo = trace[0].toString();
|
String classInfo = trace[0].toString();
|
||||||
return classInfo;
|
return classInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ import docking.widgets.label.GDLabel;
|
||||||
import docking.widgets.label.GHtmlLabel;
|
import docking.widgets.label.GHtmlLabel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dialog that has text fields to get user input.
|
* A dialog that has text fields to get user input.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class InputWithChoicesDialog extends DialogComponentProvider {
|
public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
|
|
||||||
|
@ -39,12 +39,12 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
/**
|
/**
|
||||||
* Creates a provider for a generic input dialog with the specified title,
|
* Creates a provider for a generic input dialog with the specified title,
|
||||||
* a label and a editable comboBox pre-populated with selectable values. The user
|
* a label and a editable comboBox pre-populated with selectable values. The user
|
||||||
* can check the value of {@link #isCanceled()} to know whether or not
|
* can check the value of {@link #isCanceled()} to know whether or not
|
||||||
* the user canceled the operation. To get the user selected value use the
|
* the user canceled the operation. To get the user selected value use the
|
||||||
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
|
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
|
||||||
* null will be returned from <code>getValue()</code>.
|
* null will be returned from <code>getValue()</code>.
|
||||||
* <P>
|
* <P>
|
||||||
*
|
*
|
||||||
* @param dialogTitle used as the name of the dialog's title bar
|
* @param dialogTitle used as the name of the dialog's title bar
|
||||||
* @param label value to use for the label of the text field
|
* @param label value to use for the label of the text field
|
||||||
* @param optionValues values to populate the combo box
|
* @param optionValues values to populate the combo box
|
||||||
|
@ -69,12 +69,12 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
/**
|
/**
|
||||||
* Creates a provider for a generic input dialog with the specified title,
|
* Creates a provider for a generic input dialog with the specified title,
|
||||||
* a label and a editable comboBox pre-populated with selectable values. The user
|
* a label and a editable comboBox pre-populated with selectable values. The user
|
||||||
* can check the value of {@link #isCanceled()} to know whether or not
|
* can check the value of {@link #isCanceled()} to know whether or not
|
||||||
* the user canceled the operation. To get the user selected value use the
|
* the user canceled the operation. To get the user selected value use the
|
||||||
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
|
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then
|
||||||
* null will be returned from <code>getValue()</code>.
|
* null will be returned from <code>getValue()</code>.
|
||||||
* <P>
|
* <P>
|
||||||
*
|
*
|
||||||
* @param dialogTitle used as the name of the dialog's title bar
|
* @param dialogTitle used as the name of the dialog's title bar
|
||||||
* @param label value to use for the label of the text field
|
* @param label value to use for the label of the text field
|
||||||
* @param optionValues values to populate the combo box
|
* @param optionValues values to populate the combo box
|
||||||
|
@ -180,6 +180,11 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
if (isCanceled) {
|
if (isCanceled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allowEdits) {
|
||||||
|
return combo.getText();
|
||||||
|
}
|
||||||
|
|
||||||
Object selectedItem = combo.getSelectedItem();
|
Object selectedItem = combo.getSelectedItem();
|
||||||
return selectedItem == null ? null : selectedItem.toString();
|
return selectedItem == null ? null : selectedItem.toString();
|
||||||
}
|
}
|
||||||
|
@ -188,7 +193,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
|
||||||
* Set the current choice to value.
|
* Set the current choice to value.
|
||||||
* @param value updated choice
|
* @param value updated choice
|
||||||
* @throws NoSuchElementException if choice does not permit edits and value is
|
* @throws NoSuchElementException if choice does not permit edits and value is
|
||||||
* not a valid choice.
|
* not a valid choice.
|
||||||
*/
|
*/
|
||||||
public void setValue(String value) {
|
public void setValue(String value) {
|
||||||
combo.setSelectedItem(value);
|
combo.setSelectedItem(value);
|
||||||
|
|
|
@ -57,17 +57,24 @@ public final class NumericUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* parses the given string as a numeric value, detecting whether or not it begins with a Hex
|
* Parses the given string as a numeric value, detecting whether or not it begins with a Hex
|
||||||
* prefix, and if not, parses as a long int value.
|
* prefix, and if not, parses as a long int value.
|
||||||
|
* @param numStr the number string
|
||||||
|
* @return the long value or 0
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public static long parseNumber(String numStr) {
|
public static long parseNumber(String numStr) {
|
||||||
long value = 0;
|
return parseNumber(numStr, Long.valueOf(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long parseNumber(String numStr, Long defaultValue) {
|
||||||
|
|
||||||
numStr = (numStr == null ? "" : numStr.trim());
|
numStr = (numStr == null ? "" : numStr.trim());
|
||||||
if (numStr.length() == 0) {
|
if (numStr.length() == 0) {
|
||||||
return value;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long value = 0;
|
||||||
try {
|
try {
|
||||||
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
|
if (numStr.startsWith(HEX_PREFIX_x) || numStr.startsWith(HEX_PREFIX_X)) {
|
||||||
value = Integer.parseInt(numStr.substring(2), 16);
|
value = Integer.parseInt(numStr.substring(2), 16);
|
||||||
|
@ -78,6 +85,7 @@ public final class NumericUtilities {
|
||||||
}
|
}
|
||||||
catch (NumberFormatException exc) {
|
catch (NumberFormatException exc) {
|
||||||
// do nothing special; use default value
|
// do nothing special; use default value
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
@ -192,7 +200,7 @@ public final class NumericUtilities {
|
||||||
/**
|
/**
|
||||||
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
||||||
* string.
|
* string.
|
||||||
*
|
*
|
||||||
* @param value the long value to convert
|
* @param value the long value to convert
|
||||||
*/
|
*/
|
||||||
public final static String toHexString(long value) {
|
public final static String toHexString(long value) {
|
||||||
|
@ -202,7 +210,7 @@ public final class NumericUtilities {
|
||||||
/**
|
/**
|
||||||
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
* returns the value of the specified long as hexadecimal, prefixing with the HEX_PREFIX_x
|
||||||
* string.
|
* string.
|
||||||
*
|
*
|
||||||
* @param value the long value to convert
|
* @param value the long value to convert
|
||||||
* @param size number of bytes to be represented
|
* @param size number of bytes to be represented
|
||||||
*/
|
*/
|
||||||
|
@ -216,7 +224,7 @@ public final class NumericUtilities {
|
||||||
/**
|
/**
|
||||||
* returns the value of the specified long as signed hexadecimal, prefixing with the
|
* returns the value of the specified long as signed hexadecimal, prefixing with the
|
||||||
* HEX_PREFIX_x string.
|
* HEX_PREFIX_x string.
|
||||||
*
|
*
|
||||||
* @param value the long value to convert
|
* @param value the long value to convert
|
||||||
*/
|
*/
|
||||||
public final static String toSignedHexString(long value) {
|
public final static String toSignedHexString(long value) {
|
||||||
|
@ -230,14 +238,14 @@ public final class NumericUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a <strong>unsigned</strong> long value, which is currently stored in a
|
* Converts a <strong>unsigned</strong> long value, which is currently stored in a
|
||||||
* java <strong>signed</strong> long, into a {@link BigInteger}.
|
* java <strong>signed</strong> long, into a {@link BigInteger}.
|
||||||
* <p>
|
* <p>
|
||||||
* In other words, the full 64 bits of the primitive java <strong>signed</strong>
|
* In other words, the full 64 bits of the primitive java <strong>signed</strong>
|
||||||
* long is being used to store an <strong>unsigned</strong> value. This
|
* long is being used to store an <strong>unsigned</strong> value. This
|
||||||
* method converts this into a positive BigInteger value.
|
* method converts this into a positive BigInteger value.
|
||||||
*
|
*
|
||||||
* @param value java <strong>unsigned</strong> long value stuffed into a
|
* @param value java <strong>unsigned</strong> long value stuffed into a
|
||||||
* java <strong>signed</strong> long
|
* java <strong>signed</strong> long
|
||||||
* @return new {@link BigInteger} with the positive value of the unsigned long value
|
* @return new {@link BigInteger} with the positive value of the unsigned long value
|
||||||
*/
|
*/
|
||||||
|
@ -258,7 +266,7 @@ public final class NumericUtilities {
|
||||||
/**
|
/**
|
||||||
* Get an unsigned aligned value corresponding to the specified unsigned value which will be
|
* Get an unsigned aligned value corresponding to the specified unsigned value which will be
|
||||||
* greater than or equal the specified value.
|
* greater than or equal the specified value.
|
||||||
*
|
*
|
||||||
* @param unsignedValue value to be aligned
|
* @param unsignedValue value to be aligned
|
||||||
* @param alignment alignment
|
* @param alignment alignment
|
||||||
* @return aligned value
|
* @return aligned value
|
||||||
|
@ -384,7 +392,7 @@ public final class NumericUtilities {
|
||||||
* Philosophically, it is hexadecimal, but the only valid digits are 0 and F. Any
|
* Philosophically, it is hexadecimal, but the only valid digits are 0 and F. Any
|
||||||
* partially-included nibble will be broken down into bracketed bits. Displaying masks in this
|
* partially-included nibble will be broken down into bracketed bits. Displaying masks in this
|
||||||
* way is convenient when shown proximal to related masked values.
|
* way is convenient when shown proximal to related masked values.
|
||||||
*
|
*
|
||||||
* @param msk the mask
|
* @param msk the mask
|
||||||
* @param n the number of nibbles, starting at the right
|
* @param n the number of nibbles, starting at the right
|
||||||
* @param truncate true if leading Xs may be truncated
|
* @param truncate true if leading Xs may be truncated
|
||||||
|
@ -441,7 +449,7 @@ public final class NumericUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The reverse of {@link #convertMaskedValueToHexString(long, long, int, boolean, int, String)}
|
* The reverse of {@link #convertMaskedValueToHexString(long, long, int, boolean, int, String)}
|
||||||
*
|
*
|
||||||
* @param msk an object to receive the resulting mask
|
* @param msk an object to receive the resulting mask
|
||||||
* @param val an object to receive the resulting value
|
* @param val an object to receive the resulting value
|
||||||
* @param hex the input string to parse
|
* @param hex the input string to parse
|
||||||
|
@ -669,7 +677,7 @@ public final class NumericUtilities {
|
||||||
* <td>-64h</td>
|
* <td>-64h</td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* </table>
|
* </table>
|
||||||
*
|
*
|
||||||
* @param number The number to represent
|
* @param number The number to represent
|
||||||
* @param radix The base in which <code>number</code> is represented
|
* @param radix The base in which <code>number</code> is represented
|
||||||
* @param mode Specifies how the number is formatted with respect to its signed-ness
|
* @param mode Specifies how the number is formatted with respect to its signed-ness
|
||||||
|
@ -849,7 +857,7 @@ public final class NumericUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the provided Number is an integer type -- Byte, Short, Integer, or Long.
|
* Determine if the provided Number is an integer type -- Byte, Short, Integer, or Long.
|
||||||
*
|
*
|
||||||
* @param number the object to check for for integer-type
|
* @param number the object to check for for integer-type
|
||||||
* @return true if the provided number is an integer-type, false otherwise
|
* @return true if the provided number is an integer-type, false otherwise
|
||||||
*/
|
*/
|
||||||
|
@ -860,7 +868,7 @@ public final class NumericUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the provided Number class is an integer type.
|
* Determine if the provided Number class is an integer type.
|
||||||
*
|
*
|
||||||
* @param numClass Class of an object
|
* @param numClass Class of an object
|
||||||
* @return true if the class parameter is a integer type, false otherwise
|
* @return true if the class parameter is a integer type, false otherwise
|
||||||
*/
|
*/
|
||||||
|
@ -870,7 +878,7 @@ public final class NumericUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the provided Number is a floating-point type -- Float or Double.
|
* Determine if the provided Number is a floating-point type -- Float or Double.
|
||||||
*
|
*
|
||||||
* @param number the object to check for for floating-point-type
|
* @param number the object to check for for floating-point-type
|
||||||
* @return true if the provided number is a floating-point-type, false otherwise
|
* @return true if the provided number is a floating-point-type, false otherwise
|
||||||
*/
|
*/
|
||||||
|
@ -881,7 +889,7 @@ public final class NumericUtilities {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the provided Number class is a floating-point type.
|
* Determine if the provided Number class is a floating-point type.
|
||||||
*
|
*
|
||||||
* @param numClass Class of an object
|
* @param numClass Class of an object
|
||||||
* @return true if the class parameter is a floating-point type, false otherwise
|
* @return true if the class parameter is a floating-point type, false otherwise
|
||||||
*/
|
*/
|
||||||
|
@ -895,7 +903,7 @@ public final class NumericUtilities {
|
||||||
private static interface IntegerRadixRenderer {
|
private static interface IntegerRadixRenderer {
|
||||||
/**
|
/**
|
||||||
* Format the given number in the provided radix base.
|
* Format the given number in the provided radix base.
|
||||||
*
|
*
|
||||||
* @param number the number to render
|
* @param number the number to render
|
||||||
* @param radix the base in which to render
|
* @param radix the base in which to render
|
||||||
* @return a string representing the provided number in the given base
|
* @return a string representing the provided number in the given base
|
||||||
|
|
|
@ -18,9 +18,8 @@ package ghidra.util.datastruct;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a list of integer ranges that are maintained in sorted order.
|
* Provides a list of integer ranges that are maintained in sorted order. When a range is added
|
||||||
* When a range is added any ranges that overlap or are adjacent to one another
|
* any ranges that overlap or are adjacent to one another will coalesce into a single range.
|
||||||
* will coalesce into a single range.
|
|
||||||
*/
|
*/
|
||||||
public class SortedRangeList implements Iterable<Range> {
|
public class SortedRangeList implements Iterable<Range> {
|
||||||
TreeSet<Range> set;
|
TreeSet<Range> set;
|
||||||
|
@ -33,8 +32,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new sorted range list with ranges equivalent to those in the
|
* Creates a new sorted range list with ranges equivalent to those in the specified list.
|
||||||
* specified list.
|
|
||||||
* @param list the sorted range list to make an equivalent copy of.
|
* @param list the sorted range list to make an equivalent copy of.
|
||||||
*/
|
*/
|
||||||
public SortedRangeList(SortedRangeList list) {
|
public SortedRangeList(SortedRangeList list) {
|
||||||
|
@ -47,9 +45,8 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the range from min to max to this sorted range list.
|
* Adds the range from min to max to this sorted range list. If the range is adjacent to or
|
||||||
* If the range is adjacent to or overlaps any other existing ranges,
|
* overlaps any other existing ranges, then those ranges will coalesce.
|
||||||
* then those ranges will coalesce.
|
|
||||||
* @param min the range minimum
|
* @param min the range minimum
|
||||||
* @param max the range maximum (inclusive)
|
* @param max the range maximum (inclusive)
|
||||||
*/
|
*/
|
||||||
|
@ -82,15 +79,18 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an iterator over all the ranges in this list.
|
* Returns an iterator over all the ranges in this list.
|
||||||
|
* @return the iterator
|
||||||
*/
|
*/
|
||||||
public Iterator<Range> getRanges() {
|
public Iterator<Range> getRanges() {
|
||||||
return set.iterator();
|
return set.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an iterator over all the ranges in this list that iterates in the direction specified.
|
* Returns an iterator over all the ranges in this list that iterates in the direction
|
||||||
* @param forward true indicates to iterate forward from minimum to maximum range.
|
* specified.
|
||||||
* false indicates backward iteration form maximum to minimum.
|
* @param forward true indicates to iterate forward from minimum to maximum range; false
|
||||||
|
* indicates backward iteration form maximum to minimum.
|
||||||
|
* @return the iterator
|
||||||
*/
|
*/
|
||||||
public Iterator<Range> getRanges(boolean forward) {
|
public Iterator<Range> getRanges(boolean forward) {
|
||||||
if (forward) {
|
if (forward) {
|
||||||
|
@ -106,6 +106,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the minimum int value in this sorted range list.
|
* Returns the minimum int value in this sorted range list.
|
||||||
|
* @return the min value
|
||||||
* @throws NoSuchElementException if the list is empty.
|
* @throws NoSuchElementException if the list is empty.
|
||||||
*/
|
*/
|
||||||
public int getMin() throws NoSuchElementException {
|
public int getMin() throws NoSuchElementException {
|
||||||
|
@ -115,6 +116,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the maximum int value in this sorted range list.
|
* Returns the maximum int value in this sorted range list.
|
||||||
|
* @return the max value
|
||||||
* @throws NoSuchElementException if the list is empty.
|
* @throws NoSuchElementException if the list is empty.
|
||||||
*/
|
*/
|
||||||
public int getMax() throws NoSuchElementException {
|
public int getMax() throws NoSuchElementException {
|
||||||
|
@ -124,13 +126,14 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of ranges in the list.
|
* Returns the number of ranges in the list.
|
||||||
|
* @return the number of ranges
|
||||||
*/
|
*/
|
||||||
public int getNumRanges() {
|
public int getNumRanges() {
|
||||||
return set.size();
|
return set.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the indicated range of values from the list. This will remove
|
* Removes the indicated range of values from the list. This will remove
|
||||||
* any ranges or portion of ranges that overlap the indicated range.
|
* any ranges or portion of ranges that overlap the indicated range.
|
||||||
* @param min the minimum value for the range to remove.
|
* @param min the minimum value for the range to remove.
|
||||||
* @param max the maximum value for the range to remove.
|
* @param max the maximum value for the range to remove.
|
||||||
|
@ -182,6 +185,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
/**
|
/**
|
||||||
* Returns true if the value is contained in any ranges within this list.
|
* Returns true if the value is contained in any ranges within this list.
|
||||||
* @param value the value to check for.
|
* @param value the value to check for.
|
||||||
|
* @return true if the value is contained in any ranges within this list.
|
||||||
*/
|
*/
|
||||||
public boolean contains(int value) {
|
public boolean contains(int value) {
|
||||||
Range key = new Range(value, value);
|
Range key = new Range(value, value);
|
||||||
|
@ -217,6 +221,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
* Returns true if a single range contains all the values from min to max.
|
* Returns true if a single range contains all the values from min to max.
|
||||||
* @param min the minimum value
|
* @param min the minimum value
|
||||||
* @param max the maximum value
|
* @param max the maximum value
|
||||||
|
* @return true if a single range contains all the values from min to max.
|
||||||
*/
|
*/
|
||||||
public boolean contains(int min, int max) {
|
public boolean contains(int min, int max) {
|
||||||
Range range = getRangeContaining(min);
|
Range range = getRangeContaining(min);
|
||||||
|
@ -284,9 +289,12 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the range from min to max intersects (overlaps) any ranges in this sorted range list.
|
* Returns true if the range from min to max intersects (overlaps) any ranges in this sorted
|
||||||
|
* range list.
|
||||||
* @param min the range minimum value.
|
* @param min the range minimum value.
|
||||||
* @param max the range maximum value
|
* @param max the range maximum value.
|
||||||
|
* @return true if the range from min to max intersects (overlaps) any ranges in this sorted
|
||||||
|
* range list.
|
||||||
*/
|
*/
|
||||||
public boolean intersects(int min, int max) {
|
public boolean intersects(int min, int max) {
|
||||||
Range key = new Range(min, min);
|
Range key = new Range(min, min);
|
||||||
|
@ -309,6 +317,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the range list is empty.
|
* Returns true if the range list is empty.
|
||||||
|
* @return true if the range list is empty.
|
||||||
*/
|
*/
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return set.isEmpty();
|
return set.isEmpty();
|
||||||
|
@ -327,7 +336,8 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new SortedRangeList that is the intersection of this range list and the other range list specified.
|
* Creates a new SortedRangeList that is the intersection of this range list and the other
|
||||||
|
* range list specified.
|
||||||
* @param other the other range list
|
* @param other the other range list
|
||||||
* @return the new SortedRangeList representing the intersection.
|
* @return the new SortedRangeList representing the intersection.
|
||||||
*/
|
*/
|
||||||
|
@ -339,12 +349,9 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
return srl2;
|
return srl2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuilder buf = new StringBuilder();
|
||||||
Iterator<Range> it = getRanges();
|
Iterator<Range> it = getRanges();
|
||||||
if (it.hasNext()) {
|
if (it.hasNext()) {
|
||||||
Range r = it.next();
|
Range r = it.next();
|
||||||
|
@ -365,4 +372,28 @@ public class SortedRangeList implements Iterable<Range> {
|
||||||
public void clear() {
|
public void clear() {
|
||||||
set.clear();
|
set.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SortedRangeList other = (SortedRangeList) obj;
|
||||||
|
if (!Objects.equals(set, other.set)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,10 +48,10 @@ public class ReferenceTagProcessor extends TagProcessor {
|
||||||
this.anchorManager = anchorManager;
|
this.anchorManager = anchorManager;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Note: currently all help being built has the required stylesheet living under
|
// Note: currently all help being built has the required stylesheet living under
|
||||||
// <help dir>/shared/<stylesheet name>
|
// <help dir>/shared/<stylesheet name>
|
||||||
//
|
//
|
||||||
// If we ever need a more robust styling mechanism, then this code would need to be
|
// If we ever need a more robust styling mechanism, then this code would need to be
|
||||||
// updated to know how to search for the referenced stylesheet
|
// updated to know how to search for the referenced stylesheet
|
||||||
Path helpPath = help.getHelpLocation();
|
Path helpPath = help.getHelpLocation();
|
||||||
FileSystem fs = helpPath.getFileSystem();
|
FileSystem fs = helpPath.getFileSystem();
|
||||||
|
@ -81,8 +81,8 @@ public class ReferenceTagProcessor extends TagProcessor {
|
||||||
if ("a".equals(tagType)) {
|
if ("a".equals(tagType)) {
|
||||||
if (tagAttributes.containsKey("href")) {
|
if (tagAttributes.containsKey("href")) {
|
||||||
try {
|
try {
|
||||||
anchorManager.addAnchorRef(
|
anchorManager
|
||||||
new HREF(help, file, tagAttributes.get("href"), lineNum));
|
.addAnchorRef(new HREF(help, file, tagAttributes.get("href"), lineNum));
|
||||||
}
|
}
|
||||||
catch (URISyntaxException e) {
|
catch (URISyntaxException e) {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
|
@ -102,8 +102,8 @@ public class ReferenceTagProcessor extends TagProcessor {
|
||||||
else if ("img".equals(tagType)) {
|
else if ("img".equals(tagType)) {
|
||||||
if (tagAttributes.containsKey("src")) {
|
if (tagAttributes.containsKey("src")) {
|
||||||
try {
|
try {
|
||||||
anchorManager.addImageRef(
|
anchorManager
|
||||||
new IMG(help, file, tagAttributes.get("src"), lineNum));
|
.addImageRef(new IMG(help, file, tagAttributes.get("src"), lineNum));
|
||||||
}
|
}
|
||||||
catch (URISyntaxException e) {
|
catch (URISyntaxException e) {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
|
@ -119,7 +119,7 @@ public class ReferenceTagProcessor extends TagProcessor {
|
||||||
else if ("link".equals(tagType)) {
|
else if ("link".equals(tagType)) {
|
||||||
String rel = tagAttributes.get("rel");
|
String rel = tagAttributes.get("rel");
|
||||||
if (rel != null && "stylesheet".equals(rel.toLowerCase())) {
|
if (rel != null && "stylesheet".equals(rel.toLowerCase())) {
|
||||||
// TODO there is at least one help module that has multiple style sheets. I see no reason to
|
// TODO there is at least one help module that has multiple style sheets. I see no reason to
|
||||||
// enforce this constraint:
|
// enforce this constraint:
|
||||||
// if (hasStyleSheet) {
|
// if (hasStyleSheet) {
|
||||||
// errorCount++;
|
// errorCount++;
|
||||||
|
@ -191,7 +191,8 @@ public class ReferenceTagProcessor extends TagProcessor {
|
||||||
if (!hasDefaultStyleSheet) {
|
if (!hasDefaultStyleSheet) {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
errors.append("Incorrect stylesheet defined - none match " + defaultStyleSheet +
|
errors.append("Incorrect stylesheet defined - none match " + defaultStyleSheet +
|
||||||
" in file " + htmlFile + EOL);
|
" in file " + htmlFile + EOL + "\tDiscovered stylesheets: " + styleSheets + EOL);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,10 +38,10 @@ public interface Data extends CodeUnit, Settings {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the class used to express the value of this data.
|
* Get the class used to express the value of this data.
|
||||||
*
|
*
|
||||||
* <p>NOTE: This determination is made based upon data type and settings only and does not
|
* <p>NOTE: This determination is made based upon data type and settings only and does not
|
||||||
* examine memory bytes which are used to construct the data value object.
|
* examine memory bytes which are used to construct the data value object.
|
||||||
*
|
*
|
||||||
* @return value class or null if a consistent class is not utilized.
|
* @return value class or null if a consistent class is not utilized.
|
||||||
*/
|
*/
|
||||||
public Class<?> getValueClass();
|
public Class<?> getValueClass();
|
||||||
|
@ -216,7 +216,7 @@ public interface Data extends CodeUnit, Settings {
|
||||||
* Return the first immediate child component that contains the byte at the given offset. It
|
* Return the first immediate child component that contains the byte at the given offset. It
|
||||||
* is important to note that with certain datatypes there may be more than one component
|
* is important to note that with certain datatypes there may be more than one component
|
||||||
* containing the specified offset (see {@link #getComponentsContaining(int)}).
|
* containing the specified offset (see {@link #getComponentsContaining(int)}).
|
||||||
*
|
*
|
||||||
* @param offset the amount to add to this data items address to get the address of the
|
* @param offset the amount to add to this data items address to get the address of the
|
||||||
* requested data item.
|
* requested data item.
|
||||||
* @return first data component containing offset or null
|
* @return first data component containing offset or null
|
||||||
|
@ -227,10 +227,10 @@ public interface Data extends CodeUnit, Settings {
|
||||||
public Data getComponentAt(int offset);
|
public Data getComponentAt(int offset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RReturn the first immediate child component that contains the byte at the given offset. It
|
* Return the first immediate child component that contains the byte at the given offset. It
|
||||||
* is important to note that with certain datatypes there may be more than one component
|
* is important to note that with certain datatypes there may be more than one component
|
||||||
* containing the specified offset (see {@link #getComponentsContaining(int)}).
|
* containing the specified offset (see {@link #getComponentsContaining(int)}).
|
||||||
*
|
*
|
||||||
* @param offset the amount to add to this data items address to get the
|
* @param offset the amount to add to this data items address to get the
|
||||||
* @return first data component containing offset or null address of the requested data item.
|
* @return first data component containing offset or null address of the requested data item.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,13 +17,15 @@
|
||||||
|
|
||||||
package ghidra.program.util;
|
package ghidra.program.util;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <CODE>FieldNameFieldLocation</CODE> class provides specific information
|
* The <CODE>FieldNameFieldLocation</CODE> class provides specific information about the Function
|
||||||
* about the Function Name field within a program location.
|
* Name field within a program location.
|
||||||
*/
|
*/
|
||||||
public class FieldNameFieldLocation extends CodeUnitLocation {
|
public class FieldNameFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
|
@ -31,11 +33,11 @@ public class FieldNameFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new FieldNameFieldLocation.
|
* Construct a new FieldNameFieldLocation.
|
||||||
*
|
*
|
||||||
* @param program the program of the location
|
* @param program the program of the location
|
||||||
* @param addr the address of the codeunit.
|
* @param addr the address of the code unit
|
||||||
* @param componentPath if not null, it is the array of indexes that point
|
* @param componentPath if not null, it is the array of indexes that point to a specific data
|
||||||
* to a specific data type inside of another data type
|
* type inside of another data type
|
||||||
* @param fieldName the field name
|
* @param fieldName the field name
|
||||||
* @param charOffset the character position within the field name for this location.
|
* @param charOffset the character position within the field name for this location.
|
||||||
*/
|
*/
|
||||||
|
@ -48,14 +50,14 @@ public class FieldNameFieldLocation extends CodeUnitLocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor needed for restoring
|
* Default constructor needed for restoring a field name location from XML
|
||||||
* a field name location from XML
|
|
||||||
*/
|
*/
|
||||||
public FieldNameFieldLocation() {
|
public FieldNameFieldLocation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the field name of this location.
|
* Returns the field name of this location.
|
||||||
|
* @return the name.
|
||||||
*/
|
*/
|
||||||
public String getFieldName() {
|
public String getFieldName() {
|
||||||
return fieldName;
|
return fieldName;
|
||||||
|
@ -71,19 +73,19 @@ public class FieldNameFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
if (!super.equals(obj))
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
FieldNameFieldLocation other = (FieldNameFieldLocation) obj;
|
|
||||||
if (fieldName == null) {
|
|
||||||
if (other.fieldName != null)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
else if (!fieldName.equals(other.fieldName))
|
if (!super.equals(obj)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FieldNameFieldLocation other = (FieldNameFieldLocation) obj;
|
||||||
|
if (!Objects.equals(fieldName, other.fieldName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.util;
|
package ghidra.program.util;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.listing.VariableOffset;
|
import ghidra.program.model.listing.VariableOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <CODE>OperandFieldLocation</CODE> class contains specific location information
|
* The <CODE>OperandFieldLocation</CODE> class contains specific location information within the
|
||||||
* within the OPERAND field of a CodeUnitLocation object.
|
* OPERAND field of a CodeUnitLocation object.
|
||||||
*/
|
*/
|
||||||
public class OperandFieldLocation extends CodeUnitLocation {
|
public class OperandFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
|
@ -32,17 +34,18 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new OperandFieldLocation object.
|
* Construct a new OperandFieldLocation object.
|
||||||
*
|
*
|
||||||
* @param program the program of the location
|
* @param program the program of the location.
|
||||||
* @param addr address of the location; should not be null
|
* @param addr address of the location; should not be null.
|
||||||
* @param componentPath array of indexes for each nested data component; the
|
* @param componentPath array of indexes for each nested data component; the index is the data
|
||||||
* index is the data component's index within its parent; may be null
|
* component's index within its parent; may be null.
|
||||||
|
* @param refAddr the reference 'to' address.
|
||||||
* @param rep the String representation of the operand.
|
* @param rep the String representation of the operand.
|
||||||
* @param opIndex the index of the operand at this location.
|
* @param opIndex the index of the operand at this location.
|
||||||
* @param characterOffset the character position from the beginning of the operand.
|
* @param characterOffset the character position from the beginning of the operand.
|
||||||
*/
|
*/
|
||||||
public OperandFieldLocation(Program program, Address addr, int[] componentPath,
|
public OperandFieldLocation(Program program, Address addr, int[] componentPath, Address refAddr,
|
||||||
Address refAddr, String rep, int opIndex, int characterOffset) {
|
String rep, int opIndex, int characterOffset) {
|
||||||
|
|
||||||
super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset);
|
super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset);
|
||||||
|
|
||||||
|
@ -52,68 +55,63 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new OperandFieldLocation object.
|
* Construct a new OperandFieldLocation object.
|
||||||
*
|
*
|
||||||
* @param program the program of the location
|
* @param program the program of the location.
|
||||||
* @param addr address of the location; should not be null
|
* @param addr address of the location; should not be null.
|
||||||
* @param componentPath array of indexes for each nested data component; the
|
* @param componentPath array of indexes for each nested data component; the index is the data
|
||||||
* index is the data component's index within its parent; may be null
|
* component's index within its parent; may be null .
|
||||||
* @param refAddr the "referred to" address if the location is
|
* @param refAddr the "referred to" address if the location is over a reference; may be null.
|
||||||
* over a reference; may be null
|
|
||||||
* @param rep the String representation of the operand.
|
* @param rep the String representation of the operand.
|
||||||
* @param opIndex the index indicating the operand the location is on.
|
* @param opIndex the index indicating the operand the location is on.
|
||||||
* @param subOpIndex the index of the Object within the operand, this can
|
* @param subOpIndex the index of the Object within the operand, this can be used to call an
|
||||||
* be used to call an instructions getOpObjects() method
|
* instructions getOpObjects() method.
|
||||||
* @param characterOffset the character position from the beginning of the operand field
|
* @param characterOffset the character position from the beginning of the operand field.
|
||||||
*/
|
*/
|
||||||
public OperandFieldLocation(Program program, Address addr, int[] componentPath,
|
public OperandFieldLocation(Program program, Address addr, int[] componentPath, Address refAddr,
|
||||||
Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) {
|
String rep, int opIndex, int subOpIndex, int characterOffset) {
|
||||||
|
|
||||||
super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset);
|
super(program, addr, componentPath, refAddr, 0, opIndex, characterOffset);
|
||||||
|
|
||||||
this.rep = rep;
|
this.rep = rep;
|
||||||
this.subOpIndex = subOpIndex;
|
this.subOpIndex = subOpIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new OperandFieldLocation object for an instruction operand.
|
* Construct a new OperandFieldLocation object for an instruction operand.
|
||||||
*
|
*
|
||||||
* @param program the program of the location
|
* @param program the program of the location.
|
||||||
* @param addr address of the location; should not be null
|
* @param addr address of the location; should not be null.
|
||||||
* @param variableOffset associated variable offset or null
|
* @param variableOffset associated variable offset or null.
|
||||||
* @param refAddr the "referred to" address if the location is
|
* @param refAddr the "referred to" address if the location is over a reference; may be null.
|
||||||
* over a reference; may be null
|
|
||||||
* @param rep the String representation of the operand.
|
* @param rep the String representation of the operand.
|
||||||
* @param opIndex the index indicating the operand the location is on.
|
* @param opIndex the index indicating the operand the location is on.
|
||||||
* @param subOpIndex the index of the Object within the operand, this can
|
* @param subOpIndex the index of the Object within the operand, this can be used to call an
|
||||||
* be used to call an instructions getOpObjects() method
|
* instructions getOpObjects() method.
|
||||||
* @param characterOffset the character position from the beginning of the operand field
|
* @param characterOffset the character position from the beginning of the operand field.
|
||||||
*/
|
*/
|
||||||
public OperandFieldLocation(Program program, Address addr, VariableOffset variableOffset,
|
public OperandFieldLocation(Program program, Address addr, VariableOffset variableOffset,
|
||||||
Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) {
|
Address refAddr, String rep, int opIndex, int subOpIndex, int characterOffset) {
|
||||||
|
|
||||||
super(program, addr, null, refAddr, 0, opIndex, characterOffset);
|
super(program, addr, null, refAddr, 0, opIndex, characterOffset);
|
||||||
|
|
||||||
this.rep = rep;
|
this.rep = rep;
|
||||||
this.subOpIndex = subOpIndex;
|
this.subOpIndex = subOpIndex;
|
||||||
this.variableOffset = variableOffset;
|
this.variableOffset = variableOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor needed for restoring
|
* Default constructor needed for restoring an operand field location from XML.
|
||||||
* an operand field location from XML.
|
*/
|
||||||
*/
|
|
||||||
public OperandFieldLocation() {
|
public OperandFieldLocation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns VariableOffset object if applicable or null
|
* Returns VariableOffset object if applicable or null.
|
||||||
|
* @return the variable offset.
|
||||||
*/
|
*/
|
||||||
public VariableOffset getVariableOffset() {
|
public VariableOffset getVariableOffset() {
|
||||||
return variableOffset;
|
return variableOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string representation of the opernand at this location.
|
* Returns a string representation of the operand at this location.
|
||||||
|
* @return the representation.
|
||||||
*/
|
*/
|
||||||
public String getOperandRepresentation() {
|
public String getOperandRepresentation() {
|
||||||
return rep;
|
return rep;
|
||||||
|
@ -121,6 +119,7 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of the operand at this location.
|
* Returns the index of the operand at this location.
|
||||||
|
* @return the index
|
||||||
*/
|
*/
|
||||||
public int getOperandIndex() {
|
public int getOperandIndex() {
|
||||||
return getColumn();
|
return getColumn();
|
||||||
|
@ -128,18 +127,15 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the sub operand index at this location.
|
* Returns the sub operand index at this location.
|
||||||
* This index can be used on the instruction.getOpObjects()
|
* <p>
|
||||||
* to find the actual object (Address, Register, Scalar) the
|
* This index can be used on the instruction.getOpObjects() to find the actual object (Address,
|
||||||
* cursor is over.
|
* Register, Scalar) the cursor is over.
|
||||||
* @return 0-n if over a valid OpObject, -1 otherwise
|
* @return 0-n if over a valid OpObject, -1 otherwise
|
||||||
*/
|
*/
|
||||||
public int getSubOperandIndex() {
|
public int getSubOperandIndex() {
|
||||||
return subOpIndex;
|
return subOpIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a String representation of this location.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + ", OpRep = " + rep + ", subOpIndex = " + subOpIndex +
|
return super.toString() + ", OpRep = " + rep + ", subOpIndex = " + subOpIndex +
|
||||||
|
@ -158,27 +154,25 @@ public class OperandFieldLocation extends CodeUnitLocation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj) {
|
||||||
return true;
|
return true;
|
||||||
if (!super.equals(obj))
|
}
|
||||||
|
if (!super.equals(obj)) {
|
||||||
return false;
|
return false;
|
||||||
if (getClass() != obj.getClass())
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
OperandFieldLocation other = (OperandFieldLocation) obj;
|
OperandFieldLocation other = (OperandFieldLocation) obj;
|
||||||
if (rep == null) {
|
if (!Objects.equals(rep, other.rep)) {
|
||||||
if (other.rep != null)
|
return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
else if (!rep.equals(other.rep))
|
if (subOpIndex != other.subOpIndex) {
|
||||||
return false;
|
return false;
|
||||||
if (subOpIndex != other.subOpIndex)
|
|
||||||
return false;
|
|
||||||
if (variableOffset == null) {
|
|
||||||
if (other.variableOffset != null)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
else if (!variableOffset.equals(other.variableOffset))
|
if (!Objects.equals(variableOffset, other.variableOffset)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
||||||
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
||||||
* original address can be retrieved using the {@link #getByteAddress()}" method.
|
* original address can be retrieved using the {@link #getByteAddress()}" method.
|
||||||
*
|
*
|
||||||
* @param program the program associated with this program location (also used to obtain a
|
* @param program the program associated with this program location (also used to obtain a
|
||||||
* code-unit-aligned address)
|
* code-unit-aligned address)
|
||||||
* @param addr address of the location; cannot be null
|
* @param addr address of the location; cannot be null
|
||||||
|
@ -118,7 +118,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
||||||
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
||||||
* original address can be retrieved using the {@link #getByteAddress()} method.
|
* original address can be retrieved using the {@link #getByteAddress()} method.
|
||||||
*
|
*
|
||||||
* @param program the program associated with this program location (also used to obtain a
|
* @param program the program associated with this program location (also used to obtain a
|
||||||
* code-unit-aligned address)
|
* code-unit-aligned address)
|
||||||
* @param addr address for the location
|
* @param addr address for the location
|
||||||
|
@ -132,7 +132,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
||||||
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
||||||
* original address can be retrieved using the {@link #getByteAddress()} method.
|
* original address can be retrieved using the {@link #getByteAddress()} method.
|
||||||
*
|
*
|
||||||
* @param program the program associated with this program location (also used to obtain a
|
* @param program the program associated with this program location (also used to obtain a
|
||||||
* code-unit-aligned address)
|
* code-unit-aligned address)
|
||||||
* @param addr address for the location
|
* @param addr address for the location
|
||||||
|
@ -150,7 +150,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
* Construct a new ProgramLocation for the given address. The address will be adjusted to the
|
||||||
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
* beginning of the {@link CodeUnit code unit} containing that address (if it exists). The
|
||||||
* original address can be retrieved using the {@link #getByteAddress()} method.
|
* original address can be retrieved using the {@link #getByteAddress()} method.
|
||||||
*
|
*
|
||||||
* @param program the program associated with this program location (also used to obtain a
|
* @param program the program associated with this program location (also used to obtain a
|
||||||
* code-unit-aligned address)
|
* code-unit-aligned address)
|
||||||
* @param addr address for the location
|
* @param addr address for the location
|
||||||
|
@ -170,6 +170,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
/**
|
/**
|
||||||
* Returns the componentPath for the {@link CodeUnit code unit}. Null will be returned if the
|
* Returns the componentPath for the {@link CodeUnit code unit}. Null will be returned if the
|
||||||
* object is an {@link Instruction} or a top-level {@link Data} object.
|
* object is an {@link Instruction} or a top-level {@link Data} object.
|
||||||
|
* @return the path.
|
||||||
*/
|
*/
|
||||||
public int[] getComponentPath() {
|
public int[] getComponentPath() {
|
||||||
return componentPath;
|
return componentPath;
|
||||||
|
@ -177,6 +178,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the program associated with this location.
|
* Returns the program associated with this location.
|
||||||
|
* @return the program.
|
||||||
*/
|
*/
|
||||||
public Program getProgram() {
|
public Program getProgram() {
|
||||||
return program;
|
return program;
|
||||||
|
@ -184,11 +186,12 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the address associated with this location.
|
* Returns the address associated with this location.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Note: this may not be the same as the byte address. For example, in a {@link CodeUnit code
|
* Note: this may not be the same as the byte address. For example, in a {@link CodeUnit code
|
||||||
* unit} location this may be the minimum address of the code unit that contains the byte
|
* unit} location this may be the minimum address of the code unit that contains the byte
|
||||||
* address.
|
* address.
|
||||||
|
* @return the address.
|
||||||
*/
|
*/
|
||||||
public Address getAddress() {
|
public Address getAddress() {
|
||||||
return addr;
|
return addr;
|
||||||
|
@ -196,6 +199,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the byte level address associated with this location.
|
* Returns the byte level address associated with this location.
|
||||||
|
* @return the byte address.
|
||||||
*/
|
*/
|
||||||
public Address getByteAddress() {
|
public Address getByteAddress() {
|
||||||
return byteAddr;
|
return byteAddr;
|
||||||
|
@ -203,6 +207,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the "referred to" address if the location is over an address in some field.
|
* Returns the "referred to" address if the location is over an address in some field.
|
||||||
|
* @return the address.
|
||||||
*/
|
*/
|
||||||
public Address getRefAddress() {
|
public Address getRefAddress() {
|
||||||
return refAddr;
|
return refAddr;
|
||||||
|
@ -210,7 +215,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save this program location to the given save state object.
|
* Save this program location to the given save state object.
|
||||||
*
|
*
|
||||||
* @param obj the save state object for saving the location
|
* @param obj the save state object for saving the location
|
||||||
*/
|
*/
|
||||||
public void saveState(SaveState obj) {
|
public void saveState(SaveState obj) {
|
||||||
|
@ -231,7 +236,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore this program location using the given program and save state object.
|
* Restore this program location using the given program and save state object.
|
||||||
*
|
*
|
||||||
* @param program1 program to restore from
|
* @param program1 program to restore from
|
||||||
* @param obj the save state to restore from
|
* @param obj the save state to restore from
|
||||||
*/
|
*/
|
||||||
|
@ -254,7 +259,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the program location for the given program and save state object.
|
* Get the program location for the given program and save state object.
|
||||||
*
|
*
|
||||||
* @param program the program for the location
|
* @param program the program for the location
|
||||||
* @param saveState the state to restore
|
* @param saveState the state to restore
|
||||||
* @return the restored program location
|
* @return the restored program location
|
||||||
|
@ -274,7 +279,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
}
|
}
|
||||||
// no address, it must be in a removed block; we can't use it
|
// no address, it must be in a removed block; we can't use it
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) { // restoreState may not parse the address if it is no longer valid.
|
catch (RuntimeException e) { // state may not parse the address if it is no longer valid
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e) {
|
catch (ClassNotFoundException e) {
|
||||||
// not sure why we are ignoring this--if you know, then please let everyone else know
|
// not sure why we are ignoring this--if you know, then please let everyone else know
|
||||||
|
@ -419,7 +424,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
}
|
}
|
||||||
CodeUnit cu = p.getListing().getCodeUnitContaining(addr);
|
CodeUnit cu = p.getListing().getCodeUnitContaining(addr);
|
||||||
|
|
||||||
// if the codeunit is a data, try and dig down to the lowest subdata containing the address
|
// if the code unit is data, get the lowest sub-data containing the address
|
||||||
if (cu instanceof Data) {
|
if (cu instanceof Data) {
|
||||||
Data data = (Data) cu;
|
Data data = (Data) cu;
|
||||||
cu = data.getPrimitiveAt((int) addr.subtract(data.getAddress()));
|
cu = data.getPrimitiveAt((int) addr.subtract(data.getAddress()));
|
||||||
|
@ -437,10 +442,10 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this location represents a valid location in the given program
|
* Returns true if this location represents a valid location in the given program.
|
||||||
*
|
*
|
||||||
* @param testProgram the program to test if this location is valid.
|
* @param testProgram the program to test if this location is valid.
|
||||||
* @return true if this location represents a valid location in the given program
|
* @return true if this location represents a valid location in the given program.
|
||||||
*/
|
*/
|
||||||
public boolean isValid(Program testProgram) {
|
public boolean isValid(Program testProgram) {
|
||||||
return addr == null || testProgram.getAddressFactory().isValidAddress(addr);
|
return addr == null || testProgram.getAddressFactory().isValidAddress(addr);
|
||||||
|
@ -448,7 +453,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the row within the program location.
|
* Returns the row within the program location.
|
||||||
*
|
*
|
||||||
* @return the row within the program location.
|
* @return the row within the program location.
|
||||||
*/
|
*/
|
||||||
public int getRow() {
|
public int getRow() {
|
||||||
|
@ -456,9 +461,9 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the character offset in the display item at the (row,col)
|
* Returns the character offset in the display item at the (row,col).
|
||||||
*
|
*
|
||||||
* @return the character offset in the display item at the (row,col)
|
* @return the character offset in the display item at the (row,col).
|
||||||
*/
|
*/
|
||||||
public int getCharOffset() {
|
public int getCharOffset() {
|
||||||
return charOffset;
|
return charOffset;
|
||||||
|
@ -467,6 +472,7 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
|
||||||
/**
|
/**
|
||||||
* Returns the column index of the display piece represented by this location. For most
|
* Returns the column index of the display piece represented by this location. For most
|
||||||
* locations, there is only one display item per row, in which case this value will be 0.
|
* locations, there is only one display item per row, in which case this value will be 0.
|
||||||
|
* @return the column.
|
||||||
*/
|
*/
|
||||||
public int getColumn() {
|
public int getColumn() {
|
||||||
return col;
|
return col;
|
||||||
|
|
|
@ -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