mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-28 15:11:44 +00:00
GP-1382 - Added Find actions to the Data Types provider to allow users
to find structures by size or by offset(s).
This commit is contained in:
parent
f9463e600d
commit
ec88c732d2
@ -228,6 +228,7 @@ src/main/help/help/topics/DataTypeManagerPlugin/images/DisassociateDialog.png||G
|
||||
src/main/help/help/topics/DataTypeManagerPlugin/images/EditPaths.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeManagerPlugin/images/FavoriteDts.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeManagerPlugin/images/FindDataTypes.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeManagerPlugin/images/FindDataTypesBySize.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeManagerPlugin/images/PreviewWindow.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeManagerPlugin/images/RevertDialog.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/DataTypeManagerPlugin/images/SearchResults.png||GHIDRA||||END|
|
||||
|
@ -650,7 +650,7 @@
|
||||
types to another program.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Find_Data_Types"></A> <A name="FindDataTypesDialog"></A>Find Data Types by
|
||||
<H3><A name="Find_Data_Types_By_Name"></A> <A name="FindDataTypesDialog"></A>Find Data Types by
|
||||
Name</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
@ -667,8 +667,8 @@
|
||||
<P align="center"><IMG src="images/FindDataTypes.png" alt="" border="0"><BR>
|
||||
</P>
|
||||
|
||||
<P>Enter a value in the "Please enter the search string: " field, or choose a
|
||||
previously entered value from the combo box. Either press the <Enter> key or
|
||||
<P>Enter a value in the "Please enter the search string: " field.
|
||||
Either press the <Enter> key or
|
||||
select the <B>OK</B> button. A new <I>Find Data Type</I> window will appear with
|
||||
the filter set to the entered search text.</P>
|
||||
|
||||
@ -681,9 +681,37 @@
|
||||
<H3><A name="Find_Data_Types_By_Size"></A>Find Data Types by Size</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>This is similar to the <B>Find Data Types by Name</B> action (show above) except
|
||||
that it lets you specify a size that will be used to show only those data types with
|
||||
the same length.</P>
|
||||
|
||||
<P align="center"><IMG src="images/FindDataTypesBySize.png" alt="" border="0"><BR>
|
||||
</P>
|
||||
|
||||
|
||||
<P>This is similar to the <A HREF="#Find_Data_Types_By_Name">
|
||||
<B>Find Data Types by Name</B></A> action (show above) except that it lets you specify
|
||||
one or more sizes that will be used to show only those data types with the same length.
|
||||
</P>
|
||||
<P>
|
||||
This dialog accepts a single integer value or multiple values separated by a ','.
|
||||
You may also enter ranges in this dialog using ':' as the range separator. Values
|
||||
with a leading '0x' will be treated as hex.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Find_Structues_By_Size"></A>Find Structures by Size</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<P>This is similar to the <B>Find Data Types by Size</B> action (show above) except
|
||||
that it filters results to show only Structures.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Find_Structures_By_Offset"></A>Find Structures by Offset</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<P>This is similar to the <B>Find Structures by Size</B> action (show above) except
|
||||
that it lets you specify one or more offset values that will be used to show only those
|
||||
structures that contain any of the specified offsets.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Export_Data_Types"></A>Export Data Type(s)</H3>
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
@ -177,9 +177,14 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
addLocalAction(new ExpandAllAction(plugin)); // Tree
|
||||
|
||||
// VeryLast group
|
||||
addLocalAction(getIncludeDataMembersInSearchAction()); // Common
|
||||
addLocalAction(new FindDataTypesAction(plugin)); // Common
|
||||
addLocalAction(new FindDataTypesBySizeAction(plugin)); // Common
|
||||
addLocalAction(new FindDataTypesByNameAction(plugin, "1"));
|
||||
addLocalAction(new FindDataTypesBySizeAction(plugin, "2"));
|
||||
addLocalAction(new FindStructuresByOffsetAction(plugin, "3"));
|
||||
addLocalAction(new FindStructuresBySizeAction(plugin, "4"));
|
||||
includeDataMembersInSearchAction =
|
||||
new IncludeDataTypesInFilterAction(plugin, this, "5");
|
||||
addLocalAction(includeDataMembersInSearchAction);
|
||||
|
||||
addLocalAction(new ApplyFunctionDataTypesAction(plugin)); // Tree
|
||||
addLocalAction(new CaptureFunctionDataTypesAction(plugin)); // Tree
|
||||
addLocalAction(new SetFavoriteDataTypeAction(plugin)); // Data Type
|
||||
@ -189,7 +194,6 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
// ZVeryLast group
|
||||
addLocalAction(new FindReferencesToDataTypeAction(plugin)); // DataType
|
||||
addLocalAction(new FindReferencesToFieldAction(plugin)); // DataType
|
||||
// addLocalAction( new FindDataTypesContainingAction(plugin) ); // DataType
|
||||
addLocalAction(new FindBaseDataTypeAction(plugin)); // DataType
|
||||
addLocalAction(new DisplayTypeAsGraphAction(plugin));
|
||||
|
||||
@ -322,13 +326,6 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
return previewWindowAction;
|
||||
}
|
||||
|
||||
private ToggleDockingAction getIncludeDataMembersInSearchAction() {
|
||||
if (includeDataMembersInSearchAction == null) {
|
||||
includeDataMembersInSearchAction = new IncludeDataTypesInFilterAction(plugin, this);
|
||||
}
|
||||
return includeDataMembersInSearchAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
GTreeNode clickedNode = null;
|
||||
@ -641,7 +638,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
getPreviewWindowAction().setSelected(previewWindowVisible);
|
||||
|
||||
boolean dataMembersInSearch = saveState.getBoolean(INCLUDE_DATA_MEMBERS_IN_SEARCH, false);
|
||||
getIncludeDataMembersInSearchAction().setSelected(dataMembersInSearch);
|
||||
includeDataMembersInSearchAction.setSelected(dataMembersInSearch);
|
||||
}
|
||||
|
||||
void save(SaveState saveState) {
|
||||
@ -651,7 +648,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
getConflictHandlerModesAction().getCurrentUserData().toString());
|
||||
saveState.putBoolean(PREVIEW_WINDOW_STATE, getPreviewWindowAction().isSelected());
|
||||
saveState.putBoolean(INCLUDE_DATA_MEMBERS_IN_SEARCH,
|
||||
getIncludeDataMembersInSearchAction().isSelected());
|
||||
includeDataMembersInSearchAction.isSelected());
|
||||
}
|
||||
|
||||
public DataTypeArchiveGTree getGTree() {
|
||||
@ -788,7 +785,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
archiveGTree.setIncludeDataTypeMembersInSearch(includeDataMembersInFilter);
|
||||
|
||||
// make sure the action is in sync
|
||||
ToggleDockingAction action = getIncludeDataMembersInSearchAction();
|
||||
ToggleDockingAction action = includeDataMembersInSearchAction;
|
||||
boolean selected = action.isSelected();
|
||||
if (selected != includeDataMembersInFilter) {
|
||||
action.setSelected(includeDataMembersInFilter);
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,30 +15,32 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingUtils;
|
||||
import docking.action.*;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
public class FindDataTypesAction extends DockingAction {
|
||||
public class FindDataTypesByNameAction extends DockingAction {
|
||||
|
||||
public static final String NAME = "Find Data Types by Name";
|
||||
|
||||
private final DataTypeManagerPlugin plugin;
|
||||
|
||||
public FindDataTypesAction(DataTypeManagerPlugin plugin) {
|
||||
super("Find Data Types", plugin.getName());
|
||||
public FindDataTypesByNameAction(DataTypeManagerPlugin plugin, String menuSubGroup) {
|
||||
super("Find Data Types by Name", plugin.getName());
|
||||
this.plugin = plugin;
|
||||
|
||||
setMenuBarData(new MenuData(new String[] { "Find Data Types by Name..." }, null,
|
||||
"VeryLast", -1, "1"));
|
||||
setKeyBindingData(new KeyBindingData(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
|
||||
|
||||
setEnabled(true);
|
||||
setMenuBarData(new MenuData(new String[] { NAME + "..." }, null,
|
||||
"VeryLast", -1, menuSubGroup));
|
||||
setKeyBindingData(
|
||||
new KeyBindingData(KeyEvent.VK_F, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
||||
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Find_Data_Types_By_Name"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,16 +50,14 @@ public class FindDataTypesAction extends DockingAction {
|
||||
new InputDialog("Find Data Types", "Please enter the search string: ");
|
||||
PluginTool tool = plugin.getTool();
|
||||
tool.showDialog(inputDialog);
|
||||
|
||||
if (inputDialog.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String searchString = inputDialog.getValue();
|
||||
String title = "Find Data Type";
|
||||
final DataTypesProvider newProvider = plugin.createProvider();
|
||||
String searchString = inputDialog.getValue();
|
||||
DataTypesProvider newProvider = plugin.createProvider();
|
||||
newProvider.setIncludeDataTypeMembersInFilter(plugin.includeDataMembersInSearch());
|
||||
newProvider.setTitle(title);
|
||||
newProvider.setTitle(NAME);
|
||||
newProvider.setFilterText(searchString);
|
||||
newProvider.setVisible(true);
|
||||
}
|
@ -18,7 +18,7 @@ package ghidra.app.plugin.core.datamgr.actions;
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.dialogs.NumberInputDialog;
|
||||
import docking.widgets.dialogs.NumberRangeInputDialog;
|
||||
import docking.widgets.tree.*;
|
||||
import docking.widgets.tree.support.CombinedGTreeFilter;
|
||||
import docking.widgets.tree.support.GTreeFilter;
|
||||
@ -28,40 +28,50 @@ import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Range;
|
||||
import ghidra.util.datastruct.SortedRangeList;
|
||||
|
||||
public class FindDataTypesBySizeAction extends DockingAction {
|
||||
|
||||
public static final String NAME = "Find Data Types by Size";
|
||||
|
||||
private DataTypeManagerPlugin plugin;
|
||||
|
||||
public FindDataTypesBySizeAction(DataTypeManagerPlugin plugin) {
|
||||
super("Find Data Types By Size", plugin.getName());
|
||||
public FindDataTypesBySizeAction(DataTypeManagerPlugin plugin, String menuSubGroup) {
|
||||
this(plugin, NAME, menuSubGroup);
|
||||
}
|
||||
|
||||
FindDataTypesBySizeAction(DataTypeManagerPlugin plugin, String name, String menuSubGroup) {
|
||||
super(name, plugin.getName());
|
||||
this.plugin = plugin;
|
||||
|
||||
setMenuBarData(
|
||||
new MenuData(new String[] { "Find Data Types by Size..." }, null, "VeryLast", -1, "2"));
|
||||
|
||||
setEnabled(true);
|
||||
new MenuData(new String[] { name + "..." }, null, "VeryLast", -1, menuSubGroup));
|
||||
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Find_Data_Types_By_Size"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
NumberInputDialog numberInputDialog = new NumberInputDialog("bytes", 1, 1);
|
||||
|
||||
if (!numberInputDialog.show()) {
|
||||
NumberRangeInputDialog inputDialog =
|
||||
new NumberRangeInputDialog(getName(), "Size(s)");
|
||||
if (!inputDialog.show()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int value = numberInputDialog.getValue();
|
||||
|
||||
String title = "Find Data Types With Size";
|
||||
final DataTypesProvider newProvider = plugin.createProvider();
|
||||
newProvider.setTitle(title);
|
||||
SortedRangeList values = inputDialog.getValue();
|
||||
DataTypesProvider newProvider = plugin.createProvider();
|
||||
newProvider.setTitle(getName());
|
||||
DataTypeArchiveGTree tree = newProvider.getGTree();
|
||||
tree.setFilterProvider(new MyTreeFilterProvider(tree, new SizeGTreeFilter(value)));
|
||||
GTreeFilter filter = createFilter(values);
|
||||
tree.setFilterProvider(new MyTreeFilterProvider(tree, filter));
|
||||
newProvider.setVisible(true);
|
||||
}
|
||||
|
||||
protected GTreeFilter createFilter(SortedRangeList values) {
|
||||
return new SizeGTreeFilter(values);
|
||||
}
|
||||
|
||||
private class MyTreeFilterProvider extends DefaultGTreeFilterProvider {
|
||||
private GTreeFilter secondaryFilter;
|
||||
|
||||
@ -82,10 +92,10 @@ public class FindDataTypesBySizeAction extends DockingAction {
|
||||
|
||||
private class SizeGTreeFilter implements GTreeFilter {
|
||||
|
||||
private final int size;
|
||||
private final SortedRangeList sizes;
|
||||
|
||||
SizeGTreeFilter(int size) {
|
||||
this.size = size;
|
||||
SizeGTreeFilter(SortedRangeList sizes) {
|
||||
this.sizes = sizes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,9 +109,15 @@ public class FindDataTypesBySizeAction extends DockingAction {
|
||||
return false;
|
||||
}
|
||||
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
int length = dataType.getLength();
|
||||
return length == size;
|
||||
DataType dt = dataTypeNode.getDataType();
|
||||
int length = dt.getLength();
|
||||
for (Range range : sizes) {
|
||||
if (range.contains(length)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,251 +0,0 @@
|
||||
/* ###
|
||||
* 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.plugin.core.datamgr.actions;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.tree.GTree;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.program.database.DatabaseObject;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class FindDataTypesContainingAction extends DockingAction {
|
||||
// private final DataTypeManagerPlugin plugin;
|
||||
|
||||
public FindDataTypesContainingAction(DataTypeManagerPlugin plugin) {
|
||||
super("Find Data Types Containing", plugin.getName());
|
||||
// this.plugin = plugin;
|
||||
|
||||
setPopupMenuData(
|
||||
new MenuData(new String[] { "Find Data Types Containing..." }, null, "ZVeryLast"));
|
||||
|
||||
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Data_Type_Manager_Plugin"));
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return (context instanceof DataTypesActionContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return;
|
||||
}
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gTree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
StringBuffer text = new StringBuffer();
|
||||
|
||||
if (selectionPaths.length != 1) {
|
||||
Msg.showInfo(getClass(), gTree, "Find Data Types Containing",
|
||||
"You must select a single data type in the tree.");
|
||||
return;
|
||||
}
|
||||
TreePath path = gTree.getSelectionPath();
|
||||
Object node = path.getLastPathComponent();
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
Msg.showInfo(getClass(), gTree, "Find Data Types Containing",
|
||||
"You must select a single data type in the tree.");
|
||||
return;
|
||||
}
|
||||
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
Iterator<DataType> parentDtIterator = getDataTypesContaining(dataType, gTree);
|
||||
if (!parentDtIterator.hasNext()) {
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
ArchiveType type = dataTypeManager.getType();
|
||||
String typeName = " archive";
|
||||
if (type.equals(ArchiveType.PROGRAM)) {
|
||||
typeName = " program";
|
||||
}
|
||||
else if (type.equals(ArchiveType.BUILT_IN)) {
|
||||
typeName = "";
|
||||
}
|
||||
Msg.showInfo(getClass(), gTree,
|
||||
"Find Data Types Containing \"" + dataType.getPathName() + "\"",
|
||||
"<HTML><B>" + HTMLUtilities.friendlyEncodeHTML(dataType.getPathName()) +
|
||||
"</B> isn't contained in any other data types in the <B>" +
|
||||
HTMLUtilities.friendlyEncodeHTML(dataTypeManager.getName()) + "</B>" +
|
||||
typeName + "!!!</HTML>");
|
||||
return;
|
||||
}
|
||||
|
||||
text.append("<HTML>");
|
||||
text.append("<B>" + HTMLUtilities.friendlyEncodeHTML(dataType.getPathName()) +
|
||||
"</B> is contained in:<BR>");
|
||||
int count = 0;
|
||||
while (parentDtIterator.hasNext()) {
|
||||
DataType parentDt = parentDtIterator.next();
|
||||
count++;
|
||||
if (count >= 50) {
|
||||
text.append("<BR><BR>Too many to display...");
|
||||
break;
|
||||
}
|
||||
text.append(
|
||||
"<BR> " + HTMLUtilities.friendlyEncodeHTML(parentDt.getPathName()));
|
||||
}
|
||||
text.append("</HTML>");
|
||||
|
||||
Msg.showInfo(getClass(), gTree,
|
||||
"Find Data Types Containing \"" + dataType.getPathName() + "\"", text.toString());
|
||||
}
|
||||
|
||||
protected Iterator<DataType> getDataTypesContaining(DataType dataType, GTree gTree) {
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
if (dataTypeManager == null) {
|
||||
Msg.showInfo(getClass(), gTree,
|
||||
"Find Data Types Containing \"" + dataType.getName() + "\"",
|
||||
"\"" + dataType.getName() + "\" doesn't have a data type manager.");
|
||||
return new DataTypeArrayIterator(new DataType[0]);
|
||||
}
|
||||
if (dataType instanceof DatabaseObject) {
|
||||
return new DataTypeArrayIterator(dataType.getParents());
|
||||
}
|
||||
return new DataTypeContainingIterator(dataType);
|
||||
}
|
||||
|
||||
private class DataTypeArrayIterator implements Iterator<DataType> {
|
||||
DataType[] dataTypes;
|
||||
int nextIndex = 0;
|
||||
|
||||
private DataTypeArrayIterator(DataType[] dataTypes) {
|
||||
this.dataTypes = dataTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextIndex < dataTypes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType next() {
|
||||
return dataTypes[nextIndex++];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
}
|
||||
}
|
||||
|
||||
private class DataTypeContainingIterator implements Iterator<DataType> {
|
||||
DataType dataType;
|
||||
Iterator<DataType> iterator;
|
||||
DataType nextDataType = null;
|
||||
|
||||
private DataTypeContainingIterator(DataType dataType) {
|
||||
this.dataType = dataType;
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
if (dataTypeManager == null) {
|
||||
iterator = new DataTypeArrayIterator(new DataType[0]);
|
||||
}
|
||||
else {
|
||||
iterator = dataTypeManager.getAllDataTypes();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (nextDataType != null) {
|
||||
return true;
|
||||
}
|
||||
while (iterator.hasNext()) {
|
||||
DataType dt = iterator.next();
|
||||
Collection<DataType> containedDataTypes = getDirectContainedDatatypes(dt);
|
||||
for (DataType containedDt : containedDataTypes) {
|
||||
if (containedDt == dataType) {
|
||||
nextDataType = dt;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType next() {
|
||||
if (nextDataType == null) {
|
||||
hasNext();
|
||||
}
|
||||
DataType tempDataType = nextDataType;
|
||||
nextDataType = null;
|
||||
return tempDataType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DataType> getDirectContainedDatatypes(DataType dt) {
|
||||
List<DataType> list = new ArrayList<DataType>();
|
||||
if (dt instanceof Array) {
|
||||
Array array = (Array) dt;
|
||||
list.add(array.getDataType());
|
||||
}
|
||||
else if (dt instanceof Pointer) {
|
||||
Pointer ptr = (Pointer) dt;
|
||||
DataType ptrDt = ptr.getDataType();
|
||||
if (ptrDt != null) {
|
||||
list.add(ptrDt);
|
||||
}
|
||||
}
|
||||
else if (dt instanceof Composite) {
|
||||
Composite composite = (Composite) dt;
|
||||
int n = composite.getNumComponents();
|
||||
for (int i = 0; i < n; i++) {
|
||||
DataTypeComponent component = composite.getComponent(i);
|
||||
list.add(component.getDataType());
|
||||
}
|
||||
}
|
||||
else if (dt instanceof TypeDef) {
|
||||
TypeDef typedef = (TypeDef) dt;
|
||||
list.add(typedef.getDataType());
|
||||
}
|
||||
else if (dt instanceof Enum) {
|
||||
}
|
||||
else if (dt instanceof FunctionDefinition) {
|
||||
FunctionDefinition funDef = (FunctionDefinition) dt;
|
||||
list.add(funDef.getReturnType());
|
||||
ParameterDefinition[] arguments = funDef.getArguments();
|
||||
for (ParameterDefinition parameter : arguments) {
|
||||
list.add(parameter.getDataType());
|
||||
}
|
||||
}
|
||||
else if (dt instanceof BuiltInDataType) {
|
||||
}
|
||||
else if (dt instanceof MissingBuiltInDataType) {
|
||||
}
|
||||
else if (dt.equals(DataType.DEFAULT)) {
|
||||
|
||||
}
|
||||
else {
|
||||
throw new AssertException("Unknown data Type:" + dt.getDisplayName());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/* ###
|
||||
* 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.plugin.core.datamgr.actions;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.dialogs.NumberRangeInputDialog;
|
||||
import docking.widgets.tree.*;
|
||||
import docking.widgets.tree.support.CombinedGTreeFilter;
|
||||
import docking.widgets.tree.support.GTreeFilter;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Range;
|
||||
import ghidra.util.datastruct.SortedRangeList;
|
||||
import util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Allows the user to supply one or more offsets that are used to search for structures that have
|
||||
* any of those offsets.
|
||||
*/
|
||||
public class FindStructuresByOffsetAction extends DockingAction {
|
||||
|
||||
public static final String NAME = "Find Structures by Offset";
|
||||
|
||||
private DataTypeManagerPlugin plugin;
|
||||
|
||||
public FindStructuresByOffsetAction(DataTypeManagerPlugin plugin, String menuSubGroup) {
|
||||
super(NAME, plugin.getName());
|
||||
this.plugin = plugin;
|
||||
|
||||
setMenuBarData(
|
||||
new MenuData(new String[] { NAME + "..." }, null, "VeryLast", -1, menuSubGroup));
|
||||
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Find_Data_Types_By_Offset"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
NumberRangeInputDialog inputDialog =
|
||||
new NumberRangeInputDialog(NAME, "Offset(s)");
|
||||
if (!inputDialog.show()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SortedRangeList values = inputDialog.getValue();
|
||||
DataTypesProvider newProvider = plugin.createProvider();
|
||||
newProvider.setTitle(NAME);
|
||||
DataTypeArchiveGTree tree = newProvider.getGTree();
|
||||
tree.setFilterProvider(new MyTreeFilterProvider(tree, new OffsetGTreeFilter(values)));
|
||||
newProvider.setVisible(true);
|
||||
}
|
||||
|
||||
private class MyTreeFilterProvider extends DefaultGTreeFilterProvider {
|
||||
private GTreeFilter secondaryFilter;
|
||||
|
||||
MyTreeFilterProvider(GTree tree, GTreeFilter secondaryFilter) {
|
||||
super(tree);
|
||||
this.secondaryFilter = secondaryFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GTreeFilter getFilter() {
|
||||
GTreeFilter filter = super.getFilter();
|
||||
if (filter == null) {
|
||||
return secondaryFilter;
|
||||
}
|
||||
return new CombinedGTreeFilter(filter, secondaryFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private class OffsetGTreeFilter implements GTreeFilter {
|
||||
|
||||
private final SortedRangeList offsets;
|
||||
|
||||
OffsetGTreeFilter(SortedRangeList offsets) {
|
||||
this.offsets = offsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showFilterMatches() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsNode(GTreeNode node) {
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
return false;
|
||||
}
|
||||
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
if (!(dataType instanceof Structure)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Structure structure = (Structure) dataType;
|
||||
OffsetIterator it = new OffsetIterator(structure);
|
||||
for (Integer structureOffset : CollectionUtils.asIterable(it)) {
|
||||
for (Range range : offsets) {
|
||||
if (range.contains(structureOffset)) {
|
||||
return true;
|
||||
}
|
||||
if (structureOffset > range.max) {
|
||||
// ranges are ascending sorted order; the structure offset is already
|
||||
// bigger than this range, so no more ranges can match
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private class OffsetIterator implements Iterator<Integer> {
|
||||
|
||||
private DataTypeComponent[] components;
|
||||
private int index = 0;
|
||||
private int length;
|
||||
|
||||
OffsetIterator(Structure s) {
|
||||
this.components = s.getComponents();
|
||||
this.length = components.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (index >= length) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer next() {
|
||||
DataTypeComponent dtc = components[index++];
|
||||
return dtc.getOffset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/* ###
|
||||
* 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.plugin.core.datamgr.actions;
|
||||
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.support.GTreeFilter;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.Structure;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.datastruct.Range;
|
||||
import ghidra.util.datastruct.SortedRangeList;
|
||||
|
||||
/**
|
||||
* Allows the user to supply one or more sizes that are used to search for structures that have
|
||||
* that size.
|
||||
*/
|
||||
public class FindStructuresBySizeAction extends FindDataTypesBySizeAction {
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
public static final String NAME = "Find Structures by Size";
|
||||
|
||||
public FindStructuresBySizeAction(DataTypeManagerPlugin plugin, String menuSubGroup) {
|
||||
super(plugin, NAME, menuSubGroup);
|
||||
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Find_Structures_By_Size"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GTreeFilter createFilter(SortedRangeList values) {
|
||||
return new StructureSizeGTreeFilter(values);
|
||||
}
|
||||
|
||||
private class StructureSizeGTreeFilter implements GTreeFilter {
|
||||
|
||||
private final SortedRangeList sizes;
|
||||
|
||||
StructureSizeGTreeFilter(SortedRangeList sizes) {
|
||||
this.sizes = sizes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showFilterMatches() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsNode(GTreeNode node) {
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
return false;
|
||||
}
|
||||
DataTypeNode dataTypeNode = (DataTypeNode) node;
|
||||
DataType dataType = dataTypeNode.getDataType();
|
||||
if (!(dataType instanceof Structure)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Structure structure = (Structure) dataType;
|
||||
int length = structure.getLength();
|
||||
for (Range range : sizes) {
|
||||
if (range.contains(length)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,23 +15,25 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.actions;
|
||||
|
||||
import docking.action.MenuData;
|
||||
import docking.action.ToggleDockingAction;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
|
||||
import ghidra.util.HelpLocation;
|
||||
import docking.action.MenuData;
|
||||
import docking.action.ToggleDockingAction;
|
||||
|
||||
public class IncludeDataTypesInFilterAction extends ToggleDockingAction {
|
||||
|
||||
private final DataTypesProvider provider;
|
||||
|
||||
public IncludeDataTypesInFilterAction(DataTypeManagerPlugin plugin, DataTypesProvider provider) {
|
||||
public IncludeDataTypesInFilterAction(DataTypeManagerPlugin plugin, DataTypesProvider provider,
|
||||
String menuSubGroup) {
|
||||
super("Include Data Members in Filter", plugin.getName());
|
||||
this.provider = provider;
|
||||
|
||||
setMenuBarData(new MenuData(new String[] { "Include Data Members in Filter" }, null,
|
||||
"VeryLast", MenuData.NO_MNEMONIC, "3"));
|
||||
setDescription("Selected indicates to include member names and data types in filter operations.");
|
||||
"VeryLast", MenuData.NO_MNEMONIC, menuSubGroup));
|
||||
setDescription(
|
||||
"Selected indicates to include member names and data types in filter operations.");
|
||||
|
||||
setEnabled(true);
|
||||
setSelected(false); // default to off!
|
||||
|
@ -0,0 +1,178 @@
|
||||
/* ###
|
||||
* 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 docking.widgets.dialogs;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.test.AbstractDockingTest;
|
||||
import ghidra.util.datastruct.Range;
|
||||
import ghidra.util.datastruct.SortedRangeList;
|
||||
|
||||
public class NumberRangeInputDialogTest extends AbstractDockingTest {
|
||||
|
||||
private NumberRangeInputDialog dialog;
|
||||
private JTextField textField;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
createAndShowDialog();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
close(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeInput_SingleValue_Hex() {
|
||||
|
||||
setText("0x4");
|
||||
|
||||
ok();
|
||||
|
||||
assertValue(0x4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeInput_MultiValue_Mixed() {
|
||||
|
||||
setText("0x4,0x12, 100");
|
||||
|
||||
ok();
|
||||
|
||||
assertValues(0x4, 0x12, 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeInput_Range_OneRange() {
|
||||
|
||||
setText("0x4:0x12");
|
||||
|
||||
ok();
|
||||
|
||||
assertValues(new Range(0x4, 0x12));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeInput_MixedValues_RangeAndSingleValues() {
|
||||
|
||||
setText("100, -20, 0x4:0x6, -100");
|
||||
|
||||
ok();
|
||||
|
||||
assertValues(-100, -20, 0x4, 0x5, 0x6, 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeInput_SingleValue_InvalidNumber() {
|
||||
|
||||
setText("0xBob");
|
||||
|
||||
ok();
|
||||
|
||||
assertStatusText("Unable to parse as a number: '0xBob'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeInput_MixedValues_InvalidNumber() {
|
||||
|
||||
setText("100, 0xBob");
|
||||
|
||||
ok();
|
||||
|
||||
assertStatusText("Unable to parse as a number: '0xBob'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeInput_Range_InvalidNumber() {
|
||||
|
||||
setText("100:0xBob");
|
||||
|
||||
ok();
|
||||
|
||||
assertStatusText("Unable to parse as a number: '100:0xBob'");
|
||||
}
|
||||
|
||||
private void assertStatusText(String expected) {
|
||||
String actual = runSwing(() -> dialog.getStatusText());
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private void assertValues(Range range) {
|
||||
SortedRangeList ranges = dialog.getValue();
|
||||
assertEquals(range.size(), ranges.getNumValues());
|
||||
assertEquals(range, ranges.getRange(0));
|
||||
}
|
||||
|
||||
private void assertValues(int... values) {
|
||||
SortedRangeList ranges = dialog.getValue();
|
||||
assertEquals(values.length, ranges.getNumValues());
|
||||
|
||||
Set<Integer> set = new HashSet<>();
|
||||
for (int value : values) {
|
||||
set.add(value);
|
||||
}
|
||||
|
||||
for (Range range : ranges) {
|
||||
for (int value : range) {
|
||||
assertTrue("Range value not expected: " + value, set.contains(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValue(int expected) {
|
||||
SortedRangeList ranges = dialog.getValue();
|
||||
assertEquals(1, ranges.getNumValues());
|
||||
assertEquals(expected, ranges.getRange(0).min);
|
||||
assertEquals(expected, ranges.getRange(0).max);
|
||||
}
|
||||
|
||||
private void createAndShowDialog() {
|
||||
dialog = new NumberRangeInputDialog("My Dialog Title", "Offset(s)");
|
||||
show(dialog);
|
||||
textField = getTextField(dialog);
|
||||
}
|
||||
|
||||
private void ok() {
|
||||
pressButtonByText(dialog, "OK");
|
||||
}
|
||||
|
||||
private void setText(String value) {
|
||||
setText(textField, value);
|
||||
}
|
||||
|
||||
private void show(DialogComponentProvider theDialog) {
|
||||
|
||||
runSwing(() -> {
|
||||
DockingWindowManager.showDialog(theDialog);
|
||||
}, false);
|
||||
|
||||
waitForDialogComponent(NumberRangeInputDialog.class);
|
||||
}
|
||||
|
||||
private JTextField getTextField(NumberRangeInputDialog theDialog) {
|
||||
return theDialog.getTextField();
|
||||
}
|
||||
}
|
@ -24,8 +24,7 @@ import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@ -35,6 +34,7 @@ import javax.swing.tree.TreePath;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.DockingUtils;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.ToggleDockingActionIf;
|
||||
@ -42,11 +42,12 @@ import docking.actions.KeyBindingUtils;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.tool.util.DockingToolConstants;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.dialogs.NumberRangeInputDialog;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.plugin.core.compositeeditor.ApplyAction;
|
||||
import ghidra.app.plugin.core.compositeeditor.CompositeEditorTableAction;
|
||||
import ghidra.app.plugin.core.datamgr.actions.CreateTypeDefDialog;
|
||||
import ghidra.app.plugin.core.datamgr.actions.*;
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
@ -62,9 +63,10 @@ import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.database.data.ProgramDataTypeManager;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.test.*;
|
||||
import ghidra.util.classfinder.ClassFilter;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.classfinder.ClassSearchTask;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import util.CollectionUtils;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
/**
|
||||
@ -74,10 +76,11 @@ import utilities.util.FileUtilities;
|
||||
|
||||
public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
private static final String BUILTIN_NAME = "BuiltInTypes";
|
||||
private static final String PROGRAM_FILENAME = "notepad";
|
||||
private static final String PROGRAM_FILENAME = "sample";
|
||||
|
||||
private TestEnv env;
|
||||
private PluginTool tool;
|
||||
private ProgramBuilder builder;
|
||||
private ProgramDB program;
|
||||
private DataTypeManagerPlugin plugin;
|
||||
private DataTypeArchiveGTree tree;
|
||||
@ -122,7 +125,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
}
|
||||
|
||||
private ProgramDB buildProgram() throws Exception {
|
||||
ProgramBuilder builder = new ProgramBuilder("notepad", ProgramBuilder._TOY, this);
|
||||
builder = new ProgramBuilder("sample", ProgramBuilder._TOY, this);
|
||||
|
||||
builder.createMemory(".text", "0x1001000", 0x100);
|
||||
CategoryPath miscPath = new CategoryPath("/MISC");
|
||||
@ -231,10 +234,10 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
// select "New Category" action
|
||||
DataTypeTestUtils.performAction(action, tree, false);
|
||||
|
||||
waitForDialogComponent("Cannot Edit Tree Node");
|
||||
DialogComponentProvider dialog = waitForDialogComponent("Cannot Edit Tree Node");
|
||||
close(dialog);
|
||||
|
||||
// verify that the tree opens a new node with the default
|
||||
// category name is "New Category"
|
||||
// verify that the tree opens a new node with the default category name is "New Category"
|
||||
assertEquals(childCount + 1, miscNode.getChildCount());
|
||||
GTreeNode node = miscNode.getChild("New Category");
|
||||
assertNotNull(node);
|
||||
@ -620,7 +623,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
|
||||
toggleDetailedSearch(true);
|
||||
assertSingleFilterMatch(
|
||||
new String[] { "Data Types", "notepad", "Category1", "Category2", "MyStruct" });
|
||||
new String[] { "Data Types", "sample", "Category1", "Category2", "MyStruct" });
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -663,48 +666,34 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
|
||||
@Test
|
||||
public void testRefreshBuiltins() throws Exception {
|
||||
|
||||
try {
|
||||
doTestRefreshBuiltins();
|
||||
}
|
||||
finally {
|
||||
cleanupTestBuiltin();
|
||||
}
|
||||
}
|
||||
|
||||
private void doTestRefreshBuiltins() throws Exception {
|
||||
GTreeNode treeRoot = tree.getModelRoot();
|
||||
GTreeNode builtInNode = treeRoot.getChild("BuiltInTypes");
|
||||
|
||||
assertNull("Test setup Error: ghidra.app.test.TestDataType was not removed!",
|
||||
builtInNode.getChild("TestDataType"));
|
||||
|
||||
compileJavaFile();
|
||||
compileJavaDataType();
|
||||
|
||||
DockingActionIf action = getAction(plugin, "Refresh BuiltInTypes");
|
||||
assertTrue(action.isEnabledForContext(treeContext));
|
||||
DataTypeTestUtils.performAction(action, tree, false);
|
||||
|
||||
waitForTasks();
|
||||
|
||||
waitForProgram();
|
||||
|
||||
waitForActionToBeEnabled(action);
|
||||
|
||||
builtInNode = treeRoot.getChild("BuiltInTypes");
|
||||
assertNotNull(builtInNode.getChild("TestDataType"));
|
||||
|
||||
ClassFilter filter = new BuiltInDataTypeClassExclusionFilter();
|
||||
ArrayList<DataType> listOne =
|
||||
new ArrayList<>(ClassSearcher.getInstances(BuiltInDataType.class, filter));
|
||||
|
||||
DataTypeManager bdtm = plugin.getBuiltInDataTypesManager();
|
||||
ArrayList<DataType> listTwo = new ArrayList<>();
|
||||
Iterator<DataType> iter = bdtm.getAllDataTypes();
|
||||
while (iter.hasNext()) {
|
||||
DataType dt = iter.next();
|
||||
listTwo.add(dt);
|
||||
}
|
||||
for (DataType dt : listOne) {
|
||||
boolean found = false;
|
||||
for (DataType dt2 : listTwo) {
|
||||
if (dt.isEquivalent(dt2)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -793,10 +782,163 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
assertNotNull(message, action);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAction_FindStructureByOffset() {
|
||||
|
||||
DockingActionIf action = getAction(plugin, FindStructuresByOffsetAction.NAME);
|
||||
performAction(action, false);
|
||||
|
||||
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
|
||||
setText(dialog, "0x1");
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
|
||||
FindStructuresByOffsetAction.NAME);
|
||||
assertMatchingStructures(resultsProvider, "ArrayStruct");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAction_FindStructureByOffset_NoMatches() {
|
||||
|
||||
DockingActionIf action = getAction(plugin, FindStructuresByOffsetAction.NAME);
|
||||
performAction(action, false);
|
||||
|
||||
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
|
||||
setText(dialog, "0x100");
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
|
||||
FindStructuresByOffsetAction.NAME);
|
||||
assertMatchingStructures(resultsProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAction_FindStructureByOffset_Range() {
|
||||
|
||||
DockingActionIf action = getAction(plugin, FindStructuresByOffsetAction.NAME);
|
||||
performAction(action, false);
|
||||
|
||||
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
|
||||
setText(dialog, "0x1:0x3,20");
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
|
||||
FindStructuresByOffsetAction.NAME);
|
||||
assertMatchingStructures(resultsProvider, "ArrayStruct");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAction_FindStructureByOffset_MixedInput() {
|
||||
|
||||
createStructureWithOffset_0x4(); // 0x4
|
||||
createStructureWithOffset_0x8(); // 0x4, 0x8
|
||||
createStructureWithOffset_0x10(); // 0x4, 0x8, 0x10
|
||||
createStructureWithOffset_0x20(); // 0x8, 0x16, 0x20
|
||||
|
||||
DockingActionIf action = getAction(plugin, FindStructuresByOffsetAction.NAME);
|
||||
performAction(action, false);
|
||||
|
||||
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
|
||||
setText(dialog, "0x8:0x10, 32");
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
|
||||
FindStructuresByOffsetAction.NAME);
|
||||
assertMatchingStructures(resultsProvider, "Structure_0x8", "Structure_0x10",
|
||||
"Structure_0x20");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAction_FindStructureBySize() {
|
||||
|
||||
createStructureWithOffset_0x4(); // 6
|
||||
createStructureWithOffset_0x8(); // 10
|
||||
createStructureWithOffset_0x10(); // 12
|
||||
createStructureWithOffset_0x20(); // 22
|
||||
|
||||
DockingActionIf action = getAction(plugin, FindStructuresBySizeAction.NAME);
|
||||
performAction(action, false);
|
||||
|
||||
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
|
||||
setText(dialog, "10");
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
|
||||
FindStructuresBySizeAction.NAME);
|
||||
assertMatchingStructures(resultsProvider, "Structure_0x8");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAction_FindStructureBySize_Ranage() {
|
||||
|
||||
createStructureWithOffset_0x4(); // 6
|
||||
createStructureWithOffset_0x8(); // 10
|
||||
createStructureWithOffset_0x10(); // 12
|
||||
createStructureWithOffset_0x20(); // 22
|
||||
|
||||
DockingActionIf action = getAction(plugin, FindStructuresBySizeAction.NAME);
|
||||
performAction(action, false);
|
||||
|
||||
NumberRangeInputDialog dialog = waitForDialogComponent(NumberRangeInputDialog.class);
|
||||
setText(dialog, "12:22");
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
DataTypesProvider resultsProvider = waitForComponentProvider(DataTypesProvider.class,
|
||||
FindStructuresBySizeAction.NAME);
|
||||
assertMatchingStructures(resultsProvider, "Structure_0x10", "Structure_0x20");
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private methods
|
||||
//==================================================================================================
|
||||
|
||||
private void createStructureWithOffset_0x4() {
|
||||
|
||||
StructureDataType stuct = new StructureDataType("Structure_0x4", 0);
|
||||
stuct.add(new DWordDataType());
|
||||
stuct.add(new WordDataType());
|
||||
builder.addDataType(stuct);
|
||||
}
|
||||
|
||||
private void createStructureWithOffset_0x8() {
|
||||
|
||||
StructureDataType stuct = new StructureDataType("Structure_0x8", 0);
|
||||
stuct.add(new DWordDataType());
|
||||
stuct.add(new DWordDataType());
|
||||
stuct.add(new WordDataType());
|
||||
builder.addDataType(stuct);
|
||||
}
|
||||
|
||||
private void createStructureWithOffset_0x10() {
|
||||
|
||||
StructureDataType stuct = new StructureDataType("Structure_0x10", 0);
|
||||
stuct.add(new DWordDataType());
|
||||
stuct.add(new DWordDataType());
|
||||
stuct.add(new WordDataType());
|
||||
stuct.add(new WordDataType());
|
||||
builder.addDataType(stuct);
|
||||
}
|
||||
|
||||
private void createStructureWithOffset_0x20() {
|
||||
|
||||
StructureDataType stuct = new StructureDataType("Structure_0x20", 0);
|
||||
stuct.add(new QWordDataType());
|
||||
stuct.add(new QWordDataType());
|
||||
stuct.add(new DWordDataType());
|
||||
stuct.add(new WordDataType());
|
||||
builder.addDataType(stuct);
|
||||
}
|
||||
|
||||
private void setText(NumberRangeInputDialog dialog, String text) {
|
||||
runSwing(() -> dialog.setValue(text));
|
||||
}
|
||||
|
||||
private void editSignature(String name, String newSignature) {
|
||||
expandNode(programNode);
|
||||
GTreeNode child = programNode.getChild(name);
|
||||
@ -916,6 +1058,41 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMatchingStructures(DataTypesProvider resultsProvider, String... names) {
|
||||
|
||||
DataTypeArchiveGTree gTree = resultsProvider.getGTree();
|
||||
waitForTree(gTree);
|
||||
Map<String, Structure> structures = getStructures(resultsProvider);
|
||||
assertEquals("Incorrect number of matches.\n\tExpected: " + Arrays.toString(names) +
|
||||
"\n\tFound: " + structures.keySet(), names.length, structures.size());
|
||||
for (String name : names) {
|
||||
if (!structures.containsKey(name)) {
|
||||
fail("Structure not found in results: '" + name + "'.\nFound: " +
|
||||
structures.keySet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Structure> getStructures(DataTypesProvider resultsProvider) {
|
||||
|
||||
Map<String, Structure> map = new HashMap<>();
|
||||
DataTypeArchiveGTree gTree = resultsProvider.getGTree();
|
||||
GTreeNode rootNode = gTree.getViewRoot();
|
||||
Iterator<GTreeNode> it = rootNode.iterator(true);
|
||||
for (GTreeNode node : CollectionUtils.asIterable(it)) {
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
continue;
|
||||
}
|
||||
DataTypeNode dtNode = (DataTypeNode) node;
|
||||
DataType dt = dtNode.getDataType();
|
||||
if (dt instanceof Structure) {
|
||||
map.put(dt.getName(), (Structure) dt);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private void assertEmptyTree() {
|
||||
final GTreeNode rootNode = tree.getViewRoot();
|
||||
final Integer[] box = new Integer[1];
|
||||
@ -1061,7 +1238,7 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
}
|
||||
}
|
||||
|
||||
private void compileJavaFile() throws Exception {
|
||||
private void compileJavaDataType() throws Exception {
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
@ -1072,8 +1249,8 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
Assert.fail("Could not create directory " + binDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
File javaFile = new File(binDir, "TestDataType.java");
|
||||
|
||||
File javaFile = new File(binDir, "TestDataType.java");
|
||||
FileUtilities.copyFile(file, javaFile, false, TaskMonitor.DUMMY);
|
||||
assertTrue(javaFile.exists());
|
||||
|
||||
@ -1088,6 +1265,31 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupTestBuiltin() throws Exception {
|
||||
File binDir = getClassesDirectory();
|
||||
File javaFile = new File(binDir, "TestDataType.java");
|
||||
File classFile = new File(binDir, "TestDataType.class");
|
||||
javaFile.delete();
|
||||
classFile.delete();
|
||||
|
||||
// force built-ins to get restored
|
||||
Task task = new ClassSearchTask();
|
||||
task.run(TaskMonitor.DUMMY);
|
||||
|
||||
DockingActionIf action = getAction(plugin, "Refresh BuiltInTypes");
|
||||
assertTrue(action.isEnabledForContext(treeContext));
|
||||
DataTypeTestUtils.performAction(action, tree, false);
|
||||
|
||||
waitForTasks();
|
||||
waitForProgram();
|
||||
waitForActionToBeEnabled(action);
|
||||
|
||||
GTreeNode treeRoot = tree.getModelRoot();
|
||||
GTreeNode builtInNode = treeRoot.getChild("BuiltInTypes");
|
||||
assertNull("Test setup Error: ghidra.app.test.TestDataType was not removed!",
|
||||
builtInNode.getChild("TestDataType"));
|
||||
}
|
||||
|
||||
private File getTestDataTypeFile() {
|
||||
URL url = getClass().getResource("TestDataType.txt");
|
||||
try {
|
||||
|
@ -754,6 +754,26 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to find a component provider <b>with the given title</b>. Most plugins will
|
||||
* only ever have a single provider. In those cases, use
|
||||
* {@link #waitForComponentProvider(Class)}. This version of that method is to allow you to
|
||||
* differentiate between multiple instances of a given provider that have different titles.
|
||||
*
|
||||
* @param clazz The class of the ComponentProvider to locate
|
||||
* @param title the title of the component provider
|
||||
* @return The component provider, or null if one cannot be found
|
||||
*/
|
||||
public static <T extends ComponentProvider> T waitForComponentProvider(Class<T> clazz,
|
||||
String title) {
|
||||
|
||||
DockingWindowManager dwm = findActiveDockingWindowManager();
|
||||
assertNotNull("Unable to find a DockingWindowManager - is there a tool showing?", dwm);
|
||||
|
||||
T provider = doWaitForComponentProvider(dwm, clazz, title);
|
||||
return provider;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static DockingWindowManager findActiveDockingWindowManager() {
|
||||
DockingWindowManager activeInstance = DockingWindowManager.getActiveInstance();
|
||||
@ -795,6 +815,25 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
||||
"Timed-out waiting for ComponentProvider of class: " + clazz);
|
||||
}
|
||||
|
||||
private static <T extends ComponentProvider> T doWaitForComponentProvider(
|
||||
DockingWindowManager windowManager, Class<T> clazz, String title) {
|
||||
|
||||
Objects.requireNonNull(windowManager, "DockingWindowManager cannot be null");
|
||||
|
||||
int totalTime = 0;
|
||||
while (totalTime <= DEFAULT_WAIT_TIMEOUT) {
|
||||
|
||||
T t = getComponentProvider(windowManager, clazz);
|
||||
if (Objects.deepEquals(title, t.getTitle())) {
|
||||
return t;
|
||||
}
|
||||
totalTime += sleep(DEFAULT_WAIT_DELAY);
|
||||
}
|
||||
|
||||
throw new AssertionFailedError(
|
||||
"Timed-out waiting for ComponentProvider of class: " + clazz);
|
||||
}
|
||||
|
||||
/** These providers are those that appear in dialogs outside of the main frame **/
|
||||
private static <T extends ComponentProvider> T getDetachedWindowProvider(
|
||||
final Class<T> providerClass, final DockingWindowManager windowManager) {
|
||||
|
@ -0,0 +1,252 @@
|
||||
/* ###
|
||||
* 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 docking.widgets.dialogs;
|
||||
|
||||
import java.awt.event.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.textfield.HintTextField;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.datastruct.SortedRangeList;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
/**
|
||||
* An input dialog that accepts number input as discrete values or a range of values using
|
||||
* ':' as the range separator.
|
||||
*/
|
||||
public class NumberRangeInputDialog extends DialogComponentProvider {
|
||||
|
||||
private static final String RANGE_DELIMITER = ":";
|
||||
private static final String DEFAULT_VALUE = "";
|
||||
private static final String HINT_TEXT = "e.g. 2,5 or 1,4:8";
|
||||
private static final int MAX_SIZE = 256;
|
||||
|
||||
private boolean wasCancelled;
|
||||
private String inputLabel;
|
||||
private String initialValue = DEFAULT_VALUE;
|
||||
private SortedRangeList rangeList = new SortedRangeList();
|
||||
private HintTextField textField;
|
||||
private KeyListener keyListener;
|
||||
|
||||
public NumberRangeInputDialog(String title, String label) {
|
||||
super(title, true, true/* status */, true /* buttons */,
|
||||
false /* no tasks */);
|
||||
|
||||
keyListener = new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
int keyCode = e.getKeyCode();
|
||||
if (keyCode == KeyEvent.VK_ENTER) {
|
||||
okCallback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.inputLabel = label;
|
||||
setTransient(true);
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
buildMainPanel();
|
||||
|
||||
DocumentListener docListener = new DocumentListener() {
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
clearStatusText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
clearStatusText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
clearStatusText();
|
||||
}
|
||||
};
|
||||
textField.getDocument().addDocumentListener(docListener);
|
||||
setFocusComponent(textField);
|
||||
}
|
||||
|
||||
private void buildMainPanel() {
|
||||
|
||||
JPanel panel = new JPanel(new PairLayout(5, 5, 120));
|
||||
textField = new MyHintTextField(HINT_TEXT);
|
||||
textField.setText(initialValue);
|
||||
textField.addKeyListener(keyListener);
|
||||
textField.setName("number.range.input.dialog.text.field");
|
||||
panel.add(new GLabel(inputLabel, SwingConstants.RIGHT));
|
||||
panel.add(textField);
|
||||
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
this.addWorkPanel(panel);
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>show</code> displays the dialog, gets the user input
|
||||
*
|
||||
* @return false if the user cancelled the operation
|
||||
*/
|
||||
public boolean show() {
|
||||
DockingWindowManager.showDialog(this);
|
||||
return !wasCancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
wasCancelled = false;
|
||||
|
||||
if (!parseRanges()) {
|
||||
return;
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
JTextField getTextField() {
|
||||
return textField;
|
||||
}
|
||||
|
||||
private boolean parseRanges() {
|
||||
|
||||
// format: 1
|
||||
// 1,4
|
||||
// 1-4
|
||||
// 1-4,8
|
||||
// -8, 0xc, 0x10:0x20
|
||||
// -0x20:-0x10
|
||||
|
||||
String value = textField.getText();
|
||||
String[] parts = value.split(",");
|
||||
for (String rangeText : parts) {
|
||||
if (!addRange(rangeText)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean addRange(String rangeText) {
|
||||
|
||||
String trimmed = rangeText.trim();
|
||||
if (!trimmed.contains(RANGE_DELIMITER)) {
|
||||
try {
|
||||
long parsedLong = NumericUtilities.parseLong(trimmed);
|
||||
int intValue = (int) parsedLong;
|
||||
rangeList.addRange(intValue, intValue);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
setStatusText("Unable to parse as a number: '" + trimmed + "'");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// this must be a range
|
||||
String[] startAndEnd = trimmed.split(RANGE_DELIMITER);
|
||||
try {
|
||||
long parsedLong = NumericUtilities.parseLong(startAndEnd[0]);
|
||||
int startInt = (int) parsedLong;
|
||||
|
||||
parsedLong = NumericUtilities.parseLong(startAndEnd[1]);
|
||||
int endInt = (int) parsedLong;
|
||||
|
||||
rangeList.addRange(startInt, endInt);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
setStatusText("Unable to parse as a number: '" + trimmed + "'");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
wasCancelled = true;
|
||||
rangeList.clear();
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this dialog is cancelled
|
||||
* @return true if cancelled
|
||||
*/
|
||||
public boolean wasCancelled() {
|
||||
return wasCancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the first (and maybe only) text field
|
||||
* @return the text field value
|
||||
*/
|
||||
public SortedRangeList getValue() {
|
||||
return rangeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text of the primary text field
|
||||
* @param text the text
|
||||
*/
|
||||
public void setValue(String text) {
|
||||
textField.setText(text);
|
||||
}
|
||||
|
||||
private class MyHintTextField extends HintTextField {
|
||||
|
||||
MyHintTextField(String hintText) {
|
||||
super(hintText);
|
||||
setColumns(20);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Document createDefaultModel() {
|
||||
return new MyDocument(this);
|
||||
}
|
||||
|
||||
private class MyDocument extends PlainDocument {
|
||||
private JTextField documentTf;
|
||||
|
||||
private MyDocument(JTextField textField) {
|
||||
super();
|
||||
this.documentTf = textField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertString(int offs, String str, AttributeSet a)
|
||||
throws BadLocationException {
|
||||
if (str == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String text = documentTf.getText();
|
||||
if (text.length() + str.length() > MAX_SIZE) {
|
||||
int nTooMany = text.length() + str.length() - MAX_SIZE;
|
||||
int len = str.length() - nTooMany;
|
||||
str = str.substring(0, len);
|
||||
}
|
||||
super.insertString(offs, str, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -29,10 +29,10 @@ public class ClassSearchTask extends Task {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final TaskMonitor taskMonitor) {
|
||||
public void run(final TaskMonitor monitor) {
|
||||
|
||||
try {
|
||||
ClassSearcher.search(true, taskMonitor);
|
||||
ClassSearcher.search(true, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// user cancelled
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -30,7 +29,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||
* Creates a new empty sorted range list.
|
||||
*/
|
||||
public SortedRangeList() {
|
||||
set = new TreeSet<Range>();
|
||||
set = new TreeSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,7 +38,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||
* @param list the sorted range list to make an equivalent copy of.
|
||||
*/
|
||||
public SortedRangeList(SortedRangeList list) {
|
||||
set = new TreeSet<Range>();
|
||||
set = new TreeSet<>();
|
||||
Iterator<Range> it = list.set.iterator();
|
||||
while (it.hasNext()) {
|
||||
Range r = it.next();
|
||||
@ -98,7 +97,7 @@ public class SortedRangeList implements Iterable<Range> {
|
||||
return set.iterator();
|
||||
}
|
||||
Iterator<Range> it = set.iterator();
|
||||
LinkedList<Range> ll = new LinkedList<Range>();
|
||||
LinkedList<Range> ll = new LinkedList<>();
|
||||
while (it.hasNext()) {
|
||||
ll.addFirst(it.next());
|
||||
}
|
||||
@ -362,4 +361,8 @@ public class SortedRangeList implements Iterable<Range> {
|
||||
public Iterator<Range> iterator() {
|
||||
return getRanges(true);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
set.clear();
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
|
||||
private ChangeListener classSearcherListener = e -> refresh();
|
||||
|
||||
/**
|
||||
* Returns shared instance of built-in data-type manager.
|
||||
* Returns shared instance of built-in data type manager.
|
||||
* @return the manager
|
||||
*/
|
||||
public static synchronized BuiltInDataTypeManager getDataTypeManager() {
|
||||
if (manager == null) {
|
||||
@ -58,9 +59,6 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private BuiltInDataTypeManager() {
|
||||
super(BUILT_IN_DATA_TYPES_NAME);
|
||||
initialize();
|
||||
@ -104,8 +102,6 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
|
||||
populateBuiltInTypes();
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
private void initialize() {
|
||||
try {
|
||||
populateBuiltInTypes();
|
||||
@ -123,7 +119,8 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
|
||||
protected void populateBuiltInTypes() {
|
||||
int id = super.startTransaction("Populate");
|
||||
try {
|
||||
ArrayList<DataType> list = new ArrayList<>();
|
||||
|
||||
List<DataType> list = new ArrayList<>();
|
||||
ClassFilter filter = new BuiltInDataTypeClassExclusionFilter();
|
||||
List<BuiltInDataType> datatypes =
|
||||
ClassSearcher.getInstances(BuiltInDataType.class, filter);
|
||||
@ -141,7 +138,7 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
|
||||
datatype.getName() + "'");
|
||||
}
|
||||
else if (list.size() != 1) {
|
||||
throw new AssertException("Should be no duplicate named nuilt-in types");
|
||||
throw new AssertException("Should be no duplicate named built-in types");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,7 +153,8 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
|
||||
return DataTypeManager.BUILT_IN_ARCHIVE_UNIVERSAL_ID;
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Only Built-In data types can be resolved by the BuiltInTypes manager.");
|
||||
"Only Built-in data types can be resolved by the " + getClass().getSimpleName() +
|
||||
" manager.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -197,7 +195,6 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// do nothing - cannot close a built-in data type manager
|
||||
// close performed automatically during shutdown
|
||||
// cannot close a built-in data type manager; close performed automatically during shutdown
|
||||
}
|
||||
}
|
||||
|
@ -15,14 +15,18 @@
|
||||
*/
|
||||
package ghidra.util.datastruct;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* A class for holding a minimum and maximum signed int values that define a range.
|
||||
*/
|
||||
public class Range implements Comparable<Range> {
|
||||
public class Range implements Comparable<Range>, Iterable<Integer> {
|
||||
/** The range's minimum extent. */
|
||||
public int min;
|
||||
/** The range's maximum extent (inclusive). */
|
||||
public int max;
|
||||
|
||||
/**
|
||||
* Creates a range whose extent is from min to max.
|
||||
* @param min the minimum extent.
|
||||
@ -31,8 +35,8 @@ public class Range implements Comparable<Range> {
|
||||
*/
|
||||
public Range(int min, int max) {
|
||||
if (max < min) {
|
||||
throw new IllegalArgumentException("Range max (" + max
|
||||
+ ") cannot be less than min (" + min + ").");
|
||||
throw new IllegalArgumentException(
|
||||
"Range max (" + max + ") cannot be less than min (" + min + ").");
|
||||
}
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
@ -50,11 +54,11 @@ public class Range implements Comparable<Range> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(Object obj) {
|
||||
if (obj.getClass() != Range.class) {
|
||||
return false;
|
||||
}
|
||||
Range other = (Range)obj;
|
||||
Range other = (Range) obj;
|
||||
return other.min == min && other.max == max;
|
||||
}
|
||||
|
||||
@ -64,20 +68,29 @@ public class Range implements Comparable<Range> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "("+min+","+max+")";
|
||||
public String toString() {
|
||||
return "(" + min + "," + max + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the value is within the ranges extent.
|
||||
* @param value the value to check.
|
||||
* @return true if the value is within the ranges extent.
|
||||
*/
|
||||
public boolean contains(int value) {
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the range's size.
|
||||
* @return the size
|
||||
*/
|
||||
public long size() {
|
||||
return (long)max - (long)min + 1;
|
||||
return (long) max - (long) min + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Integer> iterator() {
|
||||
return IntStream.range(min, max + 1).iterator();
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import docking.widgets.table.GTableCellRenderer;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.*;
|
||||
import ghidra.app.plugin.core.datamgr.actions.FindStructuresBySizeAction;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||
import ghidra.app.plugin.core.datamgr.archive.InvalidFileArchive;
|
||||
import ghidra.app.plugin.core.datamgr.util.ConflictDialog;
|
||||
@ -155,6 +156,14 @@ public class DataTypeManagerPluginScreenShots extends GhidraScreenShotGenerator
|
||||
pressButtonByText(d, "Cancel");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDataTypesBySize() {
|
||||
performAction(FindStructuresBySizeAction.NAME, "DataTypeManagerPlugin", false);
|
||||
JDialog d = waitForJDialog(FindStructuresBySizeAction.NAME);
|
||||
captureDialog();
|
||||
pressButtonByText(d, "Cancel");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreviewWindow() {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user