GP-1514 - Find References to - Added support for finding usage of enum

fields

Closes #1967
This commit is contained in:
dragonmacher 2021-11-26 14:36:20 -05:00
parent 2fb860bcd7
commit 33b2bbbd0b
15 changed files with 108 additions and 135 deletions

View file

@ -27,7 +27,6 @@ import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
import ghidra.util.*;
@ -56,7 +55,7 @@ public abstract class AbstractFindReferencesDataTypeAction extends DockingAction
protected abstract DataType getDataType(ActionContext context);
protected String getDataTypeField() {
protected String getDataTypeField(DataType baseDataType) {
// The base implementation only searches for references to the data type, not specific
// fields. Subclasses can change this behavior
return null;
@ -89,23 +88,8 @@ public abstract class AbstractFindReferencesDataTypeAction extends DockingAction
DataType dataType = getDataType(context);
DataType baseDataType = ReferenceUtils.getBaseDataType(dataType);
String field = getDataTypeField();
// sanity check - should not happen
if (field != null && !(baseDataType instanceof Composite)) {
Msg.error(this, "Somehow have a field without a Composite parent--searching " +
"only for the parent type '" + dataType + "'; field '" + field + "'");
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dataType));
return;
}
if (field == null) {
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dataType));
}
else {
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(
(Composite) baseDataType, field));
}
String field = getDataTypeField(baseDataType);
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(baseDataType, field));
}
}

View file

@ -18,7 +18,6 @@ package ghidra.app.plugin.core.datamgr.actions;
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;
import org.apache.commons.lang3.StringUtils;
@ -35,8 +34,8 @@ import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.program.model.data.Enum;
import ghidra.util.*;
public class FindReferencesToFieldAction extends DockingAction {
@ -72,7 +71,7 @@ public class FindReferencesToFieldAction extends DockingAction {
}
DataTypeNode dtNode = (DataTypeNode) node;
DataType dataType = dtNode.getDataType();
return dataType instanceof Composite;
return dataType instanceof Composite || dataType instanceof Enum;
}
@Override
@ -83,7 +82,6 @@ public class FindReferencesToFieldAction extends DockingAction {
PluginTool tool = plugin.getTool();
FindAppliedDataTypesService service = tool.getService(FindAppliedDataTypesService.class);
if (service == null) {
Msg.showError(this, null, "Missing Plugin",
"The FindAppliedDataTypesService is not installed.\n" +
@ -91,7 +89,27 @@ public class FindReferencesToFieldAction extends DockingAction {
return;
}
Composite composite = (Composite) dataTypeNode.getDataType();
DataType dt = dataTypeNode.getDataType();
String[] choices = null;
if (dt instanceof Composite) {
choices = getCompisiteFieldNames((Composite) dt);
}
else if (dt instanceof Enum) {
choices = ((Enum) dt).getNames();
}
String userChoice = OptionDialog.showInputChoiceDialog(null, "Choose Field",
"Find uses of '" + dt.getName() + "' field", choices, null,
OptionDialog.QUESTION_MESSAGE);
if (userChoice == null) {
return;
}
Swing.runLater(
() -> service.findAndDisplayAppliedDataTypeAddresses(dt, userChoice));
}
private String[] getCompisiteFieldNames(Composite composite) {
DataTypeComponent[] components = composite.getDefinedComponents();
List<String> names = new ArrayList<>();
for (DataTypeComponent dataTypeComponent : components) {
@ -105,17 +123,7 @@ public class FindReferencesToFieldAction extends DockingAction {
names.add(fieldName);
}
String[] array = names.toArray(new String[names.size()]);
String userChoice = OptionDialog.showInputChoiceDialog(null, "Choose Field",
"Find uses of '" + composite.getName() + "' field", array, null,
OptionDialog.QUESTION_MESSAGE);
if (userChoice == null) {
return;
}
SwingUtilities.invokeLater(
() -> service.findAndDisplayAppliedDataTypeAddresses(composite, userChoice));
return names.toArray(String[]::new);
}
}

View file

@ -15,7 +15,6 @@
*/
package ghidra.app.plugin.core.navigation;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
/**
@ -24,7 +23,7 @@ import ghidra.program.model.data.DataType;
public interface FindAppliedDataTypesService {
/**
* Tells this service to find all places where the given datatype is defined <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.
*
* @param dataType The datatype which to base the search upon.
@ -32,11 +31,11 @@ public interface FindAppliedDataTypesService {
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType);
/**
* Tells this service to find all places where the given datatype is defined <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.
*
* @param dataType The datatype which to base the search upon.
* @param fieldName the sub-field for which to search
*/
public void findAndDisplayAppliedDataTypeAddresses(Composite dataType, String fieldName);
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName);
}

View file

@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils;
import docking.widgets.fieldpanel.support.Highlight;
import ghidra.app.util.viewer.field.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.Composite;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.util.datastruct.Accumulator;
@ -30,8 +29,8 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* A data type location descriptor that allows you to represent a location for a member field of
* a composite.
* A data type location descriptor that allows you to represent a location for a member field of a
* data type, such as a composite or an enum
*/
public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeLocationDescriptor {
@ -50,9 +49,7 @@ public class GenericCompositeDataTypeLocationDescriptor extends GenericDataTypeL
@Override
protected void doGetReferences(Accumulator<LocationReference> accumulator, TaskMonitor monitor)
throws CancelledException {
Composite currentDataType = (Composite) getDataType();
ReferenceUtils.findDataTypeReferences(accumulator, currentDataType, fieldName, program,
ReferenceUtils.findDataTypeReferences(accumulator, getDataType(), fieldName, program,
useDynamicSearching, monitor);
}

View file

@ -18,12 +18,13 @@ package ghidra.app.plugin.core.navigation.locationreferences;
import java.util.Objects;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
/**
* A class to signal that the ProgramLocation is used for data types and is not really
* connected to the listing. This is a subclass specifically for {@link Composite} types and a
* particular field name of the given composite.
* connected to the listing. This is a subclass is designed for data types that have fields, such
* as {@link Composite} types and {@link Enum} types.
*
* @see GenericCompositeDataTypeLocationDescriptor
*/
@ -31,7 +32,7 @@ public class GenericCompositeDataTypeProgramLocation extends GenericDataTypeProg
private String fieldName;
GenericCompositeDataTypeProgramLocation(Program program, Composite dataType, String fieldName) {
GenericCompositeDataTypeProgramLocation(Program program, DataType dataType, String fieldName) {
super(program, dataType);
this.fieldName = Objects.requireNonNull(fieldName);
}

View file

@ -33,7 +33,6 @@ import ghidra.app.util.query.TableService;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Reference;
@ -311,7 +310,7 @@ public class LocationReferencesPlugin extends Plugin
}
@Override
public void findAndDisplayAppliedDataTypeAddresses(Composite dataType, String fieldName) {
public void findAndDisplayAppliedDataTypeAddresses(DataType dataType, String fieldName) {
ProgramManager programManagerService = tool.getService(ProgramManager.class);
GoToService goToService = tool.getService(GoToService.class);
Program program = programManagerService.getCurrentProgram();

View file

@ -31,7 +31,6 @@ import ghidra.program.util.*;
import ghidra.util.*;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.datastruct.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -210,11 +209,6 @@ public final class ReferenceUtils {
// Note: none of the params can be null, but this one gets used much later, so check now
Objects.requireNonNull(dataType, () -> "Data Type cannot be null");
// sanity check
if (fieldName != null && !(dataType instanceof Composite)) {
throw new IllegalArgumentException("Can only search for a field with a Composite type");
}
if (monitor == null) {
monitor = TaskMonitor.DUMMY;
}
@ -283,12 +277,6 @@ public final class ReferenceUtils {
accumulator.add(locationReference);
};
if (fieldName != null && !(dataType instanceof Composite)) {
throw new AssertException(
"Must have a Composite data type to perform a field search. Found " + dataType +
"; field '" + fieldName + "'");
}
if (finders.isEmpty()) {
Msg.debug(ReferenceUtils.class, "Unable to find any implementations of " +
DataTypeReferenceFinder.class.getSimpleName());
@ -300,7 +288,7 @@ public final class ReferenceUtils {
finder.findReferences(program, dataType, callback, monitor);
}
else {
finder.findReferences(program, (Composite) dataType, fieldName, callback, monitor);
finder.findReferences(program, dataType, fieldName, callback, monitor);
}
}
}

View file

@ -68,7 +68,7 @@ public class DataTypeReference {
fieldNameText +
"\tfunction: " + function.getName() + "\n" +
"\taddress: " + address + "\n" +
"\tcontext: " + context + "\n" +
"\tcontext: " + context.getPlainText() + "\n" +
"}";
//@formatter:on
}

View file

@ -57,12 +57,12 @@ public interface DataTypeReferenceFinder extends ExtensionPoint {
* are found via the <code>callback</code>.
*
* @param program the program to search
* @param composite 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 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, Composite composite, String fieldName,
public void findReferences(Program program, DataType dataType, String fieldName,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException;
}

View file

@ -18,14 +18,14 @@ package ghidra.app.plugin.core.decompile.actions;
import docking.ActionContext;
import docking.action.MenuData;
import ghidra.app.actions.AbstractFindReferencesDataTypeAction;
import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.*;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Enum;
import ghidra.util.HelpLocation;
public class FindReferencesToDataTypeAction extends AbstractFindReferencesDataTypeAction {
@ -44,12 +44,11 @@ public class FindReferencesToDataTypeAction extends AbstractFindReferencesDataTy
@Override
public DataType getDataType(ActionContext context) {
return DecompilerUtils.getDataType((DecompilerActionContext) context);
}
@Override
protected String getDataTypeField() {
protected String getDataTypeField(DataType dataType) {
DecompilerPanel decompilerPanel = controller.getDecompilerPanel();
ClangToken tokenAtCursor = decompilerPanel.getTokenAtCursor();
@ -57,6 +56,20 @@ public class FindReferencesToDataTypeAction extends AbstractFindReferencesDataTy
return tokenAtCursor.getText();
}
if (dataType instanceof Enum) {
// check for enum field
ClangVariableToken vt = (ClangVariableToken) tokenAtCursor;
String text = vt.getText();
Enum e = (Enum) dataType;
String[] names = e.getNames();
for (String name : names) {
if (name.equals(text)) {
return name;
}
}
}
return null;
}
@ -93,8 +106,7 @@ public class FindReferencesToDataTypeAction extends AbstractFindReferencesDataTy
String typeName = type.getName();
String menuName = "Find Uses of " + typeName;
String fieldName = getDataTypeField();
String fieldName = getDataTypeField(type);
if (fieldName != null) {
menuName += '.' + fieldName;
}

View file

@ -33,7 +33,6 @@ import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesPr
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesService;
import ghidra.app.services.DataTypeReference;
import ghidra.app.services.DataTypeReferenceFinder;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
@ -167,7 +166,7 @@ public abstract class AbstractDecompilerFindReferencesActionTest extends Abstrac
}
@Mock
public void findReferences(Program p, Composite composite, String fieldName,
public void findReferences(Program p, DataType dt, String fieldName,
Consumer<DataTypeReference> callback, TaskMonitor monitor) {
compositeFieldReferencesCallCount.incrementAndGet();

View file

@ -19,7 +19,6 @@ import java.util.function.Consumer;
import ghidra.app.services.DataTypeReference;
import ghidra.app.services.DataTypeReferenceFinder;
import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException;
@ -37,7 +36,7 @@ public class StubDataTypeReferenceFinder implements DataTypeReferenceFinder {
}
@Override
public void findReferences(Program program, Composite composite, String fieldName,
public void findReferences(Program program, DataType dataType, String fieldName,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
// stub
}

View file

@ -31,7 +31,8 @@ import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.app.services.DataTypeReference;
import ghidra.app.services.DataTypeReferenceFinder;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.data.BuiltInDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.util.Msg;
import ghidra.util.StringUtilities;
@ -52,29 +53,11 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
@Override
public void findReferences(Program program, DataType dataType,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
DecompilerDataTypeFinderQCallback qCallback =
new DecompilerDataTypeFinderQCallback(program, dataType, callback);
Set<Function> functions = filterFunctions(program, dataType, monitor);
try {
ParallelDecompiler.decompileFunctions(qCallback, functions, monitor);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // reset the flag
Msg.trace(this, "Interrupted while decompiling functions");
}
catch (Exception e) {
Msg.error(this, "Encountered an exception decompiling functions", e);
}
finally {
qCallback.dispose();
}
findReferences(program, dataType, null, callback, monitor);
}
@Override
public void findReferences(Program program, Composite dataType, String fieldName,
public void findReferences(Program program, DataType dataType, String fieldName,
Consumer<DataTypeReference> callback, TaskMonitor monitor) throws CancelledException {
DecompilerDataTypeFinderQCallback qCallback =
@ -227,12 +210,6 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
private DataType dataType;
private String fieldName;
/* Search for Data Type access only--no field usage */
DecompilerDataTypeFinderQCallback(Program program, DataType dataType,
Consumer<DataTypeReference> callback) {
this(program, dataType, null, callback);
}
/* Search for composite field access */
DecompilerDataTypeFinderQCallback(Program program, DataType dataType, String fieldName,
Consumer<DataTypeReference> callback) {

View file

@ -174,7 +174,7 @@ public abstract class DecompilerReference {
return "{\n" +
"\tvariable: " + StringUtilities.toStringWithIndent(variable) + ",\n" +
"\tdata type: " + getDataType() + ",\n"+
"\tline " + getContext() + ",\n" +
"\tline " + StringUtilities.toStringWithIndent(getContext().getPlainText()) + ",\n" +
"\tfunction: " + getFunction() + "\n" +
"}";
//@formatter:on

View file

@ -120,6 +120,16 @@ public class VariableAccessDR extends DecompilerReference {
}
if (potentialField == null) {
// check for the case where we have not been passed a 'potential field', but the given
// 'var' is itself the field we seek, such as in an if statement like this:
// if (color == RED)
// where 'RED' is the variable we are checking
String name = var.getName();
if (fieldName.equals(name)) {
return var;
}
return null; // we seek a field, but there is none
}
@ -268,7 +278,7 @@ public class VariableAccessDR extends DecompilerReference {
//@formatter:off
return "{\n" +
"\tline " + getContext() + ",\n" +
"\tline " + getContext().getPlainText() + ",\n" +
"\tfunction: " + getFunction() + "\n" +
"\tvariable: " + StringUtilities.toStringWithIndent(variable) + ",\n" +
"\tdata type: " + getDataType() + ",\n"+