mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-3222 - Fixed bugs in Data Type navigation that caused failure after
opening and archive for edit
This commit is contained in:
parent
591ed4de31
commit
0b532431cd
9 changed files with 172 additions and 176 deletions
|
@ -205,9 +205,9 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add project pathname to recently opened list.
|
* Add project archive name to recently opened list
|
||||||
* @param projectName
|
* @param projectName the project name
|
||||||
* @param pathname
|
* @param pathname the pathname
|
||||||
*/
|
*/
|
||||||
public void addRecentlyOpenedProjectArchive(String projectName, String pathname) {
|
public void addRecentlyOpenedProjectArchive(String projectName, String pathname) {
|
||||||
String projectPathname = DataTypeManagerHandler.getProjectPathname(projectName, pathname);
|
String projectPathname = DataTypeManagerHandler.getProjectPathname(projectName, pathname);
|
||||||
|
@ -239,8 +239,8 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a project archive file by project name and pathname
|
* Get a project archive file by project name and pathname
|
||||||
* @param projectName
|
* @param projectName the project name
|
||||||
* @param pathname
|
* @param pathname the project pathname
|
||||||
* @return project archive domain file or null if it does not exist
|
* @return project archive domain file or null if it does not exist
|
||||||
* or can not be found (e.g., projectName is not the active project)
|
* or can not be found (e.g., projectName is not the active project)
|
||||||
*/
|
*/
|
||||||
|
@ -272,10 +272,6 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||||
dataTypeManagerHandler.dispose();
|
dataTypeManagerHandler.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the Plugin to read its data-independant (preferences)
|
|
||||||
* properties from the input stream.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void readConfigState(SaveState saveState) {
|
public void readConfigState(SaveState saveState) {
|
||||||
dataTypeManagerHandler.restore(saveState);
|
dataTypeManagerHandler.restore(saveState);
|
||||||
|
@ -311,10 +307,6 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||||
dataTypePropertyManager.programClosed(program);
|
dataTypePropertyManager.programClosed(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Program was opened.
|
|
||||||
* @param program
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void programActivated(Program program) {
|
protected void programActivated(Program program) {
|
||||||
program.addListener(this);
|
program.addListener(this);
|
||||||
|
@ -574,7 +566,6 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
||||||
openDialog.addOkActionListener(listener);
|
openDialog.addOkActionListener(listener);
|
||||||
}
|
}
|
||||||
tool.showDialog(openDialog);
|
tool.showDialog(openDialog);
|
||||||
// updateActions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -83,7 +83,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
private HelpLocation helpLocation;
|
private HelpLocation helpLocation;
|
||||||
private DataTypeManagerPlugin plugin;
|
private DataTypeManagerPlugin plugin;
|
||||||
|
|
||||||
private HistoryList<DataType> navigationHistory = new HistoryList<>(15, dt -> {
|
private HistoryList<DataTypeIdUrl> navigationHistory = new HistoryList<>(15, url -> {
|
||||||
|
DataType dt = url.getDataType(plugin);
|
||||||
setDataTypeSelected(dt);
|
setDataTypeSelected(dt);
|
||||||
});
|
});
|
||||||
private MultiActionDockingAction nextAction;
|
private MultiActionDockingAction nextAction;
|
||||||
|
@ -465,35 +466,12 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
try {
|
try {
|
||||||
url = new DataTypeIdUrl(href);
|
url = new DataTypeIdUrl(href);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (IllegalArgumentException e) {
|
||||||
Msg.debug(this, "Could not parse Data Type ID URL '" + href + "'");
|
Msg.debug(this, "Could not parse Data Type ID URL '" + href + "'", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataTypeManager manager = getManager(url);
|
return url.getDataType(plugin);
|
||||||
if (manager == null) {
|
|
||||||
// this shouldn't be possible, unless the url is old and the manager has been closed
|
|
||||||
Msg.debug(this, "Could not find data type for " + event.getDescription());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataType dt = manager.findDataTypeForID(url.getDataTypeId());
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DataTypeManager getManager(DataTypeIdUrl url) {
|
|
||||||
UniversalID id = url.getDataTypeManagerId();
|
|
||||||
return getManager(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DataTypeManager getManager(UniversalID id) {
|
|
||||||
DataTypeManager[] mgs = plugin.getDataTypeManagers();
|
|
||||||
for (DataTypeManager dtm : mgs) {
|
|
||||||
if (dtm.getUniversalID().equals(id)) {
|
|
||||||
return dtm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePreviewPane() {
|
private void updatePreviewPane() {
|
||||||
|
@ -550,6 +528,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
void dispose() {
|
void dispose() {
|
||||||
previewUpdateManager.dispose();
|
previewUpdateManager.dispose();
|
||||||
archiveGTree.dispose();
|
archiveGTree.dispose();
|
||||||
|
navigationHistory.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -861,6 +840,10 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
return conflictMode.getHandler();
|
return conflictMode.getHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataTypeManagerPlugin getPlugin() {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
|
||||||
private DataType getDataTypeFrom(TreePath path) {
|
private DataType getDataTypeFrom(TreePath path) {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -876,7 +859,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
return dt;
|
return dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryList<DataType> getNavigationHistory() {
|
HistoryList<DataTypeIdUrl> getNavigationHistory() {
|
||||||
return navigationHistory;
|
return navigationHistory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -898,7 +881,11 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
return; // Ignore events from the GTree's housekeeping
|
return; // Ignore events from the GTree's housekeeping
|
||||||
}
|
}
|
||||||
|
|
||||||
navigationHistory.add(dt);
|
if (dt == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationHistory.add(new DataTypeIdUrl(dt));
|
||||||
contextChanged();
|
contextChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import javax.swing.Icon;
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.menu.MultiActionDockingAction;
|
import docking.menu.MultiActionDockingAction;
|
||||||
|
import ghidra.app.util.datatype.DataTypeIdUrl;
|
||||||
import ghidra.base.actions.HorizontalRuleAction;
|
import ghidra.base.actions.HorizontalRuleAction;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
@ -38,7 +39,7 @@ class NextPreviousDataTypeAction extends MultiActionDockingAction {
|
||||||
private boolean isNext;
|
private boolean isNext;
|
||||||
private String owner;
|
private String owner;
|
||||||
private DataTypesProvider provider;
|
private DataTypesProvider provider;
|
||||||
private HistoryList<DataType> history;
|
private HistoryList<DataTypeIdUrl> history;
|
||||||
|
|
||||||
public NextPreviousDataTypeAction(DataTypesProvider provider, String owner, boolean isNext) {
|
public NextPreviousDataTypeAction(DataTypesProvider provider, String owner, boolean isNext) {
|
||||||
super(isNext ? "Next Data Type in History" : "Previous Data Type in History", owner);
|
super(isNext ? "Next Data Type in History" : "Previous Data Type in History", owner);
|
||||||
|
@ -92,19 +93,19 @@ class NextPreviousDataTypeAction extends MultiActionDockingAction {
|
||||||
|
|
||||||
DataTypeManager lastDtm = null;
|
DataTypeManager lastDtm = null;
|
||||||
List<DockingActionIf> results = new ArrayList<>();
|
List<DockingActionIf> results = new ArrayList<>();
|
||||||
List<DataType> types =
|
List<DataTypeIdUrl> types =
|
||||||
isNext ? history.getNextHistoryItems() : history.getPreviousHistoryItems();
|
isNext ? history.getNextHistoryItems() : history.getPreviousHistoryItems();
|
||||||
|
|
||||||
for (DataType dt : types) {
|
for (DataTypeIdUrl url : types) {
|
||||||
|
|
||||||
|
DataType dt = url.getDataType(provider.getPlugin());
|
||||||
DataTypeManager dtm = dt.getDataTypeManager();
|
DataTypeManager dtm = dt.getDataTypeManager();
|
||||||
|
|
||||||
if (dtm != lastDtm && !results.isEmpty()) {
|
if (dtm != lastDtm && !results.isEmpty()) {
|
||||||
// add a separator to show the user they are navigating across managers
|
// add a separator to show the user they are navigating across managers
|
||||||
results.add(createHorizontalRule(lastDtm, dtm));
|
results.add(createHorizontalRule(lastDtm, dtm));
|
||||||
}
|
}
|
||||||
|
|
||||||
results.add(new NavigationAction(dt));
|
results.add(new NavigationAction(dt.getDisplayName()));
|
||||||
lastDtm = dtm;
|
lastDtm = dtm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,10 +123,10 @@ class NextPreviousDataTypeAction extends MultiActionDockingAction {
|
||||||
|
|
||||||
private class NavigationAction extends DockingAction {
|
private class NavigationAction extends DockingAction {
|
||||||
|
|
||||||
private NavigationAction(DataType dt) {
|
private NavigationAction(String dtDisplayName) {
|
||||||
super("DataTypeNavigationAction_" + ++navigationActionIdCount, owner);
|
super("DataTypeNavigationAction_" + ++navigationActionIdCount, owner);
|
||||||
|
|
||||||
setMenuBarData(new MenuData(new String[] { dt.getDisplayName() }));
|
setMenuBarData(new MenuData(new String[] { dtDisplayName }));
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Navigation_Actions"));
|
setHelpLocation(new HelpLocation("DataTypeManagerPlugin", "Navigation_Actions"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,7 +223,7 @@ public class ArchiveNode extends CategoryNode {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
CategoryNode node = findCategoryNode(parentCategory);
|
CategoryNode node = findCategoryNode(parentCategory, loadChildren);
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,40 +15,52 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.datatype;
|
package ghidra.app.util.datatype;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to produce and parse URLs of the form:
|
* A class to produce and parse URLs of the form:
|
||||||
* <pre>
|
* <pre>
|
||||||
* datatype:/12345678/12345678
|
* datatype:/12345678?uid=12345678&name=Bob
|
||||||
* </pre>
|
* </pre>
|
||||||
* where the first number is the ID of the {@link DataTypeManager} and the second number is
|
* where the first number is the ID of the {@link DataTypeManager} and the second number is
|
||||||
* the {@link DataType} ID.
|
* the {@link DataType} ID.
|
||||||
*/
|
*/
|
||||||
public class DataTypeIdUrl {
|
public class DataTypeIdUrl {
|
||||||
|
|
||||||
|
// see javadoc for format
|
||||||
private static String PROTOCOL = "datatype";
|
private static String PROTOCOL = "datatype";
|
||||||
private static Pattern URL_PATTERN = Pattern.compile(PROTOCOL + ":/(\\d+)/(\\d+)");
|
private static Pattern URL_PATTERN =
|
||||||
|
Pattern.compile(PROTOCOL + ":/(\\d+)\\?uid=(\\d*)&name=(\\w+)");
|
||||||
|
|
||||||
private UniversalID dataTypeManagerId;
|
private UniversalID dataTypeManagerId;
|
||||||
private UniversalID dataTypeId;
|
private UniversalID dataTypeId;
|
||||||
|
private String dataTypeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a url from the given data type
|
||||||
|
* @param dt the data type; cannot be null
|
||||||
|
*/
|
||||||
public DataTypeIdUrl(DataType dt) {
|
public DataTypeIdUrl(DataType dt) {
|
||||||
DataTypeManager dtm = dt.getDataTypeManager();
|
DataTypeManager dtm = dt.getDataTypeManager();
|
||||||
if (dtm == null) {
|
dataTypeManagerId = Objects.requireNonNull(dtm.getUniversalID());
|
||||||
Msg.debug(this, "");
|
|
||||||
}
|
|
||||||
dataTypeManagerId = dtm.getUniversalID();
|
|
||||||
dataTypeId = dt.getUniversalID();
|
dataTypeId = dt.getUniversalID();
|
||||||
|
dataTypeName = Objects.requireNonNull(dt.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataTypeIdUrl(String url) {
|
/**
|
||||||
|
* Constructs a url from the given url string
|
||||||
|
*
|
||||||
|
* @param url the url
|
||||||
|
* @throws IllegalArgumentException if the url does not match the expected {@link #URL_PATTERN}
|
||||||
|
* or if there is an issue parsing the id within the given url
|
||||||
|
*/
|
||||||
|
public DataTypeIdUrl(String url) throws IllegalArgumentException {
|
||||||
|
|
||||||
Matcher matcher = URL_PATTERN.matcher(url);
|
Matcher matcher = URL_PATTERN.matcher(url);
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
|
@ -57,8 +69,21 @@ public class DataTypeIdUrl {
|
||||||
|
|
||||||
String dtmId = matcher.group(1);
|
String dtmId = matcher.group(1);
|
||||||
String dtId = matcher.group(2);
|
String dtId = matcher.group(2);
|
||||||
dataTypeManagerId = new UniversalID(Long.parseLong(dtmId));
|
|
||||||
dataTypeId = new UniversalID(Long.parseLong(dtId));
|
try {
|
||||||
|
dataTypeManagerId = new UniversalID(Long.parseLong(dtmId));
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Exception parsing Data Type Manager ID: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
dataTypeId = new UniversalID(Long.parseLong(dtId));
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Exception parsing Data Type ID: ", e);
|
||||||
|
}
|
||||||
|
dataTypeName = matcher.group(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UniversalID getDataTypeManagerId() {
|
public UniversalID getDataTypeManagerId() {
|
||||||
|
@ -69,8 +94,86 @@ public class DataTypeIdUrl {
|
||||||
return dataTypeId;
|
return dataTypeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the given service and its {@link DataTypeManager}s to find the data type
|
||||||
|
* represented by this url
|
||||||
|
*
|
||||||
|
* @param service the service
|
||||||
|
* @return the data type; null if there was an error restoring the type, such as if the
|
||||||
|
* parent {@link DataTypeManager} has been closed
|
||||||
|
*/
|
||||||
|
public DataType getDataType(DataTypeManagerService service) {
|
||||||
|
|
||||||
|
DataTypeManager manager = findManager(service);
|
||||||
|
if (manager == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataTypeId == null) {
|
||||||
|
// The ID will be null for built-in types. In that case, the name will not be
|
||||||
|
// null. Further, built-in types live at the root, so we can just ask for the
|
||||||
|
// type by name.
|
||||||
|
return manager.getDataType(new DataTypePath(CategoryPath.ROOT, dataTypeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType dt = manager.findDataTypeForID(dataTypeId);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataTypeManager findManager(DataTypeManagerService service) {
|
||||||
|
return getManagerById(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataTypeManager getManagerById(DataTypeManagerService service) {
|
||||||
|
DataTypeManager[] mgs = service.getDataTypeManagers();
|
||||||
|
for (DataTypeManager dtm : mgs) {
|
||||||
|
if (dtm.getUniversalID().equals(dataTypeManagerId)) {
|
||||||
|
return dtm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((dataTypeId == null) ? 0 : dataTypeId.hashCode());
|
||||||
|
result = prime * result + ((dataTypeManagerId == null) ? 0 : dataTypeManagerId.hashCode());
|
||||||
|
result = prime * result + ((dataTypeName == null) ? 0 : dataTypeName.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeIdUrl other = (DataTypeIdUrl) obj;
|
||||||
|
if (!Objects.equals(dataTypeId, other.dataTypeId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(dataTypeManagerId, other.dataTypeManagerId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(dataTypeName, other.dataTypeName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return PROTOCOL + ":/" + dataTypeManagerId.toString() + '/' + dataTypeId.toString();
|
return PROTOCOL + ":/" + dataTypeManagerId.toString() + "?uid=" +
|
||||||
|
Objects.toString(dataTypeId, "") + "&name=" + dataTypeName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr;
|
package ghidra.app.plugin.core.datamgr;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.startsWith;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
|
@ -24,7 +24,8 @@ import java.awt.event.KeyEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
@ -39,10 +40,6 @@ import docking.action.DockingActionIf;
|
||||||
import docking.action.ToggleDockingActionIf;
|
import docking.action.ToggleDockingActionIf;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.combobox.GhidraComboBox;
|
|
||||||
import docking.widgets.dialogs.InputWithChoicesDialog;
|
|
||||||
import docking.widgets.fieldpanel.support.Highlight;
|
|
||||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.context.ProgramActionContext;
|
import ghidra.app.context.ProgramActionContext;
|
||||||
import ghidra.app.plugin.core.datamgr.actions.CreateTypeDefDialog;
|
import ghidra.app.plugin.core.datamgr.actions.CreateTypeDefDialog;
|
||||||
|
@ -50,23 +47,15 @@ import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||||
import ghidra.app.plugin.core.function.EditFunctionSignatureDialog;
|
import ghidra.app.plugin.core.function.EditFunctionSignatureDialog;
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesPlugin;
|
|
||||||
import ghidra.app.plugin.core.navigation.locationreferences.LocationReferencesProvider;
|
|
||||||
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
|
||||||
import ghidra.app.services.CodeViewerService;
|
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.app.util.HighlightProvider;
|
|
||||||
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
||||||
import ghidra.app.util.viewer.field.*;
|
|
||||||
import ghidra.app.util.viewer.format.FormatManager;
|
|
||||||
import ghidra.framework.plugintool.Plugin;
|
import ghidra.framework.plugintool.Plugin;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.database.data.ProgramDataTypeManager;
|
import ghidra.program.database.data.ProgramDataTypeManager;
|
||||||
import ghidra.program.model.address.*;
|
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.*;
|
|
||||||
import ghidra.test.*;
|
import ghidra.test.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.classfinder.ClassFilter;
|
import ghidra.util.classfinder.ClassFilter;
|
||||||
|
@ -856,85 +845,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
return builtinNode;
|
return builtinNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findReferencesToField(String choice) {
|
|
||||||
DockingActionIf searchAction = getAction(plugin, "Find Uses of Field");
|
|
||||||
assertTrue(searchAction.isEnabledForContext(treeContext));
|
|
||||||
DataTypeTestUtils.performAction(searchAction, tree, false);
|
|
||||||
|
|
||||||
InputWithChoicesDialog d = waitForDialogComponent(InputWithChoicesDialog.class);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
GhidraComboBox<String> combo = (GhidraComboBox<String>) getInstanceField("combo", d);
|
|
||||||
setComboBoxSelection(combo, choice);
|
|
||||||
pressButtonByText(d, "OK");
|
|
||||||
|
|
||||||
waitForSearchResults();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private LocationReferencesProvider getLocationReferencesProvider() {
|
|
||||||
LocationReferencesPlugin locationRefsPlugin =
|
|
||||||
getPlugin(tool, LocationReferencesPlugin.class);
|
|
||||||
|
|
||||||
List<LocationReferencesProvider> providerList =
|
|
||||||
(List<LocationReferencesProvider>) getInstanceField("providerList", locationRefsPlugin);
|
|
||||||
if (providerList.size() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return providerList.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ThreadedTableModel<?, ?> getTableModel() {
|
|
||||||
|
|
||||||
waitForCondition(() -> getLocationReferencesProvider() != null);
|
|
||||||
|
|
||||||
LocationReferencesProvider refsProvider = getLocationReferencesProvider();
|
|
||||||
Object referencesPanel = getInstanceField("referencesPanel", refsProvider);
|
|
||||||
return (ThreadedTableModel<?, ?>) getInstanceField("tableModel", referencesPanel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitForSearchResults() {
|
|
||||||
ThreadedTableModel<?, ?> model = getTableModel();
|
|
||||||
waitForTableModel(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HighlightProvider getHighlightProvider() {
|
|
||||||
CodeViewerService service = tool.getService(CodeViewerService.class);
|
|
||||||
FormatManager fm = (FormatManager) getInstanceField("formatMgr", service);
|
|
||||||
return (HighlightProvider) getInstanceField("highlightProvider", fm);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertOperandHighlight(String rep, Address addr) {
|
|
||||||
assertHighlight(OperandFieldFactory.class, rep, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertFieldNameHighlight(String rep, Address addr) {
|
|
||||||
assertHighlight(FieldNameFieldFactory.class, rep, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertHighlight(Class<? extends FieldFactory> clazz, String rep, Address addr) {
|
|
||||||
Listing listing = program.getListing();
|
|
||||||
CodeUnit cu = listing.getCodeUnitContaining(addr);
|
|
||||||
if (cu instanceof Data) {
|
|
||||||
Data data = (Data) cu;
|
|
||||||
Address minAddress = data.getMinAddress();
|
|
||||||
long offset = addr.subtract(minAddress);
|
|
||||||
if (offset != 0) {
|
|
||||||
Data subData = data.getComponentAt((int) offset);
|
|
||||||
cu = subData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HighlightProvider highlighter = getHighlightProvider();
|
|
||||||
Highlight[] highlights = highlighter.getHighlights(rep, cu, clazz, -1);
|
|
||||||
assertNotNull(highlights);
|
|
||||||
assertTrue(highlights.length != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Address addr(long offset) {
|
|
||||||
AddressFactory addrMap = program.getAddressFactory();
|
|
||||||
AddressSpace space = addrMap.getDefaultAddressSpace();
|
|
||||||
return space.getAddress(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertSingleFilterMatch(String[] path) {
|
private void assertSingleFilterMatch(String[] path) {
|
||||||
GTreeNode rootNode = tree.getRootNode();
|
GTreeNode rootNode = tree.getRootNode();
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import utilities.util.FileUtilities;
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
|
@ -146,20 +147,18 @@ public class DataTypeTestUtils {
|
||||||
static void closeArchive(final ArchiveNode archiveNode, final boolean deleteFile)
|
static void closeArchive(final ArchiveNode archiveNode, final boolean deleteFile)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
final Exception[] container = new Exception[1];
|
Exception exception = Swing.runNow(() -> {
|
||||||
|
|
||||||
SwingUtilities.invokeAndWait(() -> {
|
|
||||||
try {
|
try {
|
||||||
doCloseArchive(archiveNode, deleteFile);
|
doCloseArchive(archiveNode, deleteFile);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
container[0] = e;
|
return e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (container[0] != null) {
|
if (exception != null) {
|
||||||
throw new RuntimeException("Exception closing archive on Swing thread!: ",
|
throw new RuntimeException("Exception closing archive on Swing thread!: ", exception);
|
||||||
container[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,10 @@ public class HistoryList<T> {
|
||||||
* allow duplicates, but to also move the position of an item if it is re-added to the
|
* allow duplicates, but to also move the position of an item if it is re-added to the
|
||||||
* list.
|
* list.
|
||||||
*
|
*
|
||||||
|
* <p>For correct behavior when not allowing duplicates, ensure you have defined an
|
||||||
|
* <code>equals</code> method to work as you expect. If two different items are considered
|
||||||
|
* equal, then this class will only remove the duplicate if the equals method returns true.
|
||||||
|
*
|
||||||
* <p>The default is false
|
* <p>The default is false
|
||||||
*
|
*
|
||||||
* @param allowDuplicates true to allow duplicates
|
* @param allowDuplicates true to allow duplicates
|
||||||
|
@ -162,7 +166,6 @@ public class HistoryList<T> {
|
||||||
* <p>No action is taken if the current pointer is already at the beginning of the list.
|
* <p>No action is taken if the current pointer is already at the beginning of the list.
|
||||||
*/
|
*/
|
||||||
public void goBack() {
|
public void goBack() {
|
||||||
|
|
||||||
if (historyIndex == 0) {
|
if (historyIndex == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -217,7 +220,7 @@ public class HistoryList<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all items in the history that come after the current history item. They are
|
* Get all items in the history that come after the current history item. They are
|
||||||
* returned in navigation order, as traversed if {@link #goForward() is called.
|
* returned in navigation order, as traversed if {@link #goForward()} is called.
|
||||||
*
|
*
|
||||||
* @return the items
|
* @return the items
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,8 +31,8 @@ import ghidra.util.task.TaskMonitorAdapter;
|
||||||
* DataTypeManager for a file. Can import categories from a file, or export
|
* DataTypeManager for a file. Can import categories from a file, or export
|
||||||
* categories to a packed database.
|
* categories to a packed database.
|
||||||
*/
|
*/
|
||||||
public class FileDataTypeManager extends StandAloneDataTypeManager implements
|
public class FileDataTypeManager extends StandAloneDataTypeManager
|
||||||
FileArchiveBasedDataTypeManager {
|
implements FileArchiveBasedDataTypeManager {
|
||||||
|
|
||||||
public final static String EXTENSION = "gdt"; // Ghidra Data Types
|
public final static String EXTENSION = "gdt"; // Ghidra Data Types
|
||||||
/**
|
/**
|
||||||
|
@ -108,16 +108,15 @@ public class FileDataTypeManager extends StandAloneDataTypeManager implements
|
||||||
* @param outputFilename filename for output
|
* @param outputFilename filename for output
|
||||||
* @param databaseId new databaseId
|
* @param databaseId new databaseId
|
||||||
*/
|
*/
|
||||||
public void saveAs(File saveFile, UniversalID newUniversalId) throws DuplicateFileException,
|
public void saveAs(File saveFile, UniversalID newUniversalId)
|
||||||
IOException {
|
throws DuplicateFileException, IOException {
|
||||||
ResourceFile resourceSaveFile = new ResourceFile(saveFile);
|
ResourceFile resourceSaveFile = new ResourceFile(saveFile);
|
||||||
// TODO: this should really be a package method and not public!
|
// TODO: this should really be a package method and not public!
|
||||||
validateFilename(resourceSaveFile);
|
validateFilename(resourceSaveFile);
|
||||||
try {
|
try {
|
||||||
universalID = newUniversalId;
|
universalID = newUniversalId;
|
||||||
packedDB =
|
packedDB = ((PackedDBHandle) dbHandle).saveAs("DTArchive", saveFile.getParentFile(),
|
||||||
((PackedDBHandle) dbHandle).saveAs("DTArchive", saveFile.getParentFile(),
|
saveFile.getName(), newUniversalId.getValue(), TaskMonitorAdapter.DUMMY_MONITOR);
|
||||||
saveFile.getName(), newUniversalId.getValue(), TaskMonitorAdapter.DUMMY_MONITOR);
|
|
||||||
file = resourceSaveFile;
|
file = resourceSaveFile;
|
||||||
updateRootCategoryName(resourceSaveFile, getRootCategory());
|
updateRootCategoryName(resourceSaveFile, getRootCategory());
|
||||||
}
|
}
|
||||||
|
@ -134,9 +133,8 @@ public class FileDataTypeManager extends StandAloneDataTypeManager implements
|
||||||
ResourceFile resourceSaveFile = new ResourceFile(saveFile);
|
ResourceFile resourceSaveFile = new ResourceFile(saveFile);
|
||||||
validateFilename(resourceSaveFile);
|
validateFilename(resourceSaveFile);
|
||||||
try {
|
try {
|
||||||
packedDB =
|
packedDB = ((PackedDBHandle) dbHandle).saveAs("DTArchive", saveFile.getParentFile(),
|
||||||
((PackedDBHandle) dbHandle).saveAs("DTArchive", saveFile.getParentFile(),
|
saveFile.getName(), TaskMonitorAdapter.DUMMY_MONITOR);
|
||||||
saveFile.getName(), TaskMonitorAdapter.DUMMY_MONITOR);
|
|
||||||
file = resourceSaveFile;
|
file = resourceSaveFile;
|
||||||
updateRootCategoryName(resourceSaveFile, getRootCategory());
|
updateRootCategoryName(resourceSaveFile, getRootCategory());
|
||||||
}
|
}
|
||||||
|
@ -268,4 +266,8 @@ public class FileDataTypeManager extends StandAloneDataTypeManager implements
|
||||||
return ArchiveType.FILE;
|
return ArchiveType.FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + " - " + getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue