mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-10-22 21:21:02 +00:00
Merge remote-tracking branch 'origin/GP-4719_ghidra1_StandaloneDTMUndoRedo--SQUASHED'
This commit is contained in:
commit
c1f8312c56
|
@ -899,4 +899,10 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
|||
super.close();
|
||||
objectManager.waitWbWorkers();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void domainObjectRestored() {
|
||||
super.domainObjectRestored();
|
||||
dataTypeManager.notifyRestored();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,14 +92,24 @@ public abstract class CompEditorModel extends CompositeEditorModel {
|
|||
throw new IllegalStateException(
|
||||
"Can't apply edits without a data type or data type manager.");
|
||||
}
|
||||
int transactionID = originalDTM.startTransaction("Edit " + getCompositeName());
|
||||
boolean originalDtExists = originalDTM.contains(originalDt);
|
||||
boolean renamed = false;
|
||||
if (originalDtExists) {
|
||||
String origName = originalDt.getName();
|
||||
String editName = getCompositeName();
|
||||
renamed = !origName.equals(editName);
|
||||
}
|
||||
String action = originalDtExists ? "Edit" : "Create";
|
||||
if (renamed) {
|
||||
action += "/Rename";
|
||||
}
|
||||
String type = (originalDt instanceof Union) ? " Union " : " Structure ";
|
||||
int transactionID = originalDTM.startTransaction(action + type + getCompositeName());
|
||||
try {
|
||||
if (originalDTM.contains(originalDt)) {
|
||||
|
||||
if (originalDtExists) {
|
||||
// Update the original structure.
|
||||
String origName = originalDt.getName();
|
||||
String editName = getCompositeName();
|
||||
if (!origName.equals(editName)) {
|
||||
if (renamed) {
|
||||
String editName = getCompositeName();
|
||||
try {
|
||||
originalDt.setName(editName);
|
||||
}
|
||||
|
|
|
@ -545,20 +545,21 @@ public abstract class CompositeEditorPanel extends JPanel
|
|||
}
|
||||
}
|
||||
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
public void dataTypeManagerRestored() {
|
||||
DataTypeManager originalDTM = model.getOriginalDataTypeManager();
|
||||
if (originalDTM == null) {
|
||||
// editor unloaded
|
||||
return;
|
||||
}
|
||||
boolean reload = true;
|
||||
String objectType = "domain object";
|
||||
if (domainObject instanceof Program) {
|
||||
objectType = "program";
|
||||
String objectType;
|
||||
if (originalDTM instanceof ProgramBasedDataTypeManager) {
|
||||
objectType = "Program";
|
||||
}
|
||||
else if (domainObject instanceof DataTypeArchive) {
|
||||
objectType = "data type archive";
|
||||
else {
|
||||
objectType = "Archive";
|
||||
}
|
||||
String archiveName = originalDTM.getName();
|
||||
DataType dt = originalDTM.getDataType(model.getCompositeID());
|
||||
if (dt instanceof Composite) {
|
||||
Composite composite = (Composite) dt;
|
||||
|
@ -570,10 +571,9 @@ public abstract class CompositeEditorPanel extends JPanel
|
|||
Composite originalDt = model.getOriginalComposite();
|
||||
if (originalDt == null) {
|
||||
provider.show();
|
||||
String info =
|
||||
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
|
||||
"\"" + model.getCompositeName() + "\" may no longer exist outside the editor.";
|
||||
Msg.showWarn(this, this, "Program Restored", info);
|
||||
String info = "The " + objectType + " \"" + archiveName + "\" has been restored.\n" +
|
||||
"\"" + model.getCompositeName() + "\" may no longer exist outside the editor.";
|
||||
Msg.showWarn(this, this, objectType + " Restored", info);
|
||||
return;
|
||||
}
|
||||
else if (originalDt.isDeleted()) {
|
||||
|
@ -586,8 +586,8 @@ public abstract class CompositeEditorPanel extends JPanel
|
|||
// The user has modified the structure so prompt for whether or
|
||||
// not to reload the structure.
|
||||
String question =
|
||||
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
|
||||
"\"" + model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
|
||||
model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"Discard edits & reload the " + model.getTypeName() + "?";
|
||||
String title = "Reload " + model.getTypeName() + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
|
||||
|
|
|
@ -261,9 +261,8 @@ public abstract class CompositeEditorProvider extends ComponentProviderAdapter
|
|||
return editorModel.hasChanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
editorPanel.domainObjectRestored(domainObject);
|
||||
public void dataTypeManagerRestored() {
|
||||
editorPanel.dataTypeManagerRestored();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,7 +24,7 @@ import ghidra.util.exception.CancelledException;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
||||
|
||||
|
||||
/**
|
||||
* The data type manager for original composite data type being edited.
|
||||
* This is where the edited datatype will be written back to.
|
||||
|
@ -43,7 +43,7 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
|||
public CompositeViewerDataTypeManager(String rootName, Composite originalComposite) {
|
||||
super(rootName, originalComposite.getDataTypeManager().getDataOrganization());
|
||||
this.originalComposite = originalComposite;
|
||||
transactionID = startTransaction("");
|
||||
transactionID = super.startTransaction("");
|
||||
originalDTM = originalComposite.getDataTypeManager();
|
||||
|
||||
ProgramArchitecture arch = originalDTM.getProgramArchitecture();
|
||||
|
@ -68,11 +68,11 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
endTransaction(transactionID, true);
|
||||
public void close() {
|
||||
super.endTransaction(transactionID, true);
|
||||
super.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@link DataTypeManager} associated with the original composite datatype being edited.
|
||||
* @return original datatype manager
|
||||
|
@ -82,7 +82,7 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ArchiveType getType() {
|
||||
public ArchiveType getType() {
|
||||
return originalDTM.getType();
|
||||
}
|
||||
|
||||
|
@ -103,4 +103,33 @@ public class CompositeViewerDataTypeManager extends StandAloneDataTypeManager {
|
|||
return super.resolve(dataType, handler);
|
||||
}
|
||||
|
||||
//
|
||||
// Transaction support has been disabled since a single open transaction is maintained
|
||||
// until this DTM is closed.
|
||||
//
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public int startTransaction(String description) {
|
||||
// ignore - not yet supported
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransaction(int txId, boolean commit) {
|
||||
// ignore - not yet supported
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public boolean canUndo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public boolean canRedo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -959,6 +959,16 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
|||
// Don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
provider.dataTypeManagerRestored();
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Helper methods for CategoryChangeListener methods.
|
||||
//=================================================================================================
|
||||
|
@ -1354,8 +1364,4 @@ abstract class CompositeViewerModel extends AbstractTableModel
|
|||
return viewComposite.isPackingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,13 +47,6 @@ public interface EditorProvider {
|
|||
*/
|
||||
public DataTypeManager getDataTypeManager();
|
||||
|
||||
/**
|
||||
* Notification that the data type manager domain object (program or data type archive) was
|
||||
* restored.
|
||||
* @param domainObject the program or data type archive that was restored.
|
||||
*/
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject);
|
||||
|
||||
/**
|
||||
* Return whether this editor is editing the data type with the given path.
|
||||
* @param dtPath path of a data type
|
||||
|
|
|
@ -1264,7 +1264,8 @@ class StructureEditorModel extends CompEditorModel {
|
|||
private DataType createDataTypeInOriginalDTM(StructureDataType structureDataType) {
|
||||
boolean commit = false;
|
||||
DataTypeManager originalDTM = getOriginalDataTypeManager();
|
||||
int transactionID = originalDTM.startTransaction("Creating " + structureDataType.getName());
|
||||
int transactionID =
|
||||
originalDTM.startTransaction("Create structure " + structureDataType.getName());
|
||||
try {
|
||||
DataType addedDataType =
|
||||
originalDTM.addDataType(structureDataType, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
|
|
|
@ -290,7 +290,8 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
|||
DataTypeManagerDomainObject domainObject = (DataTypeManagerDomainObject) source;
|
||||
provider.domainObjectRestored(domainObject);
|
||||
dataTypePropertyManager.domainObjectRestored(domainObject);
|
||||
editorManager.domainObjectRestored(domainObject);
|
||||
// NOTE: each editor that cares about a restored DataTypeManager must establish
|
||||
// a DataTypeManagerChangeListener and will be notified via the restored method.
|
||||
}
|
||||
}
|
||||
else if (event.contains(DomainObjectEvent.RENAMED)) {
|
||||
|
|
|
@ -99,7 +99,7 @@ public class DataTypeSynchronizer {
|
|||
}
|
||||
|
||||
private static void update(DataTypeManager refDTM, DataType sourceDT) {
|
||||
int transactionID = refDTM.startTransaction("Update Datatype");
|
||||
int transactionID = refDTM.startTransaction("Update Datatype " + sourceDT.getName());
|
||||
try {
|
||||
updateAssumingTransactionsOpen(refDTM, sourceDT);
|
||||
}
|
||||
|
@ -184,8 +184,7 @@ public class DataTypeSynchronizer {
|
|||
}
|
||||
|
||||
public void markSynchronized() {
|
||||
int transactionID =
|
||||
dataTypeManager.startTransaction("Clear dirty flag for data type manager.");
|
||||
int transactionID = dataTypeManager.startTransaction("Clear Dirty Flag");
|
||||
try {
|
||||
sourceArchive.setDirtyFlag(false);
|
||||
sourceArchive.setLastSyncTime(sourceDTM.getLastChangeTimeForMyManager());
|
||||
|
@ -457,8 +456,8 @@ public class DataTypeSynchronizer {
|
|||
return;
|
||||
}
|
||||
|
||||
int transactionID = dataTypeManager
|
||||
.startTransaction("re-sync '" + sourceArchive.getName() + "' data types");
|
||||
int transactionID =
|
||||
dataTypeManager.startTransaction("Sync '" + sourceArchive.getName() + "' data types");
|
||||
try {
|
||||
reSyncOutOfSyncInTimeOnlyDataTypes();
|
||||
fixSyncForDifferingDataTypes();
|
||||
|
@ -525,7 +524,7 @@ public class DataTypeSynchronizer {
|
|||
|
||||
private void autoUpdateDataTypesThatHaveNoRealChanges(
|
||||
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
|
||||
int transactionID = dataTypeManager.startTransaction("auto sync datatypes");
|
||||
int transactionID = dataTypeManager.startTransaction("Sync datatypes");
|
||||
try {
|
||||
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
|
||||
dataTypeSyncInfo.syncTimes();
|
||||
|
@ -539,16 +538,16 @@ public class DataTypeSynchronizer {
|
|||
}
|
||||
}
|
||||
|
||||
public void performBulkOperation(String actionName, List<DataTypeSyncInfo> selectedList,
|
||||
public void performBulkOperation(String actionName, List<DataTypeSyncInfo> selectedList,
|
||||
ExceptionalConsumer<DataTypeSyncInfo, CancelledException> infoApplier,
|
||||
Consumer<List<DataTypeSyncInfo>> handleOutOfSync,
|
||||
boolean sourceRequiresTransaction) throws CancelledException {
|
||||
Consumer<List<DataTypeSyncInfo>> handleOutOfSync, boolean sourceRequiresTransaction)
|
||||
throws CancelledException {
|
||||
if (sourceDTM == null) {
|
||||
throw new RuntimeException("Source archive required");
|
||||
}
|
||||
|
||||
int sourceTransactionId = sourceRequiresTransaction ?
|
||||
sourceDTM.startTransaction(actionName) : 0;
|
||||
|
||||
int sourceTransactionId =
|
||||
sourceRequiresTransaction ? sourceDTM.startTransaction(actionName) : 0;
|
||||
int transactionID = dataTypeManager.startTransaction(actionName);
|
||||
try {
|
||||
for (DataTypeSyncInfo info : selectedList) {
|
||||
|
|
|
@ -181,6 +181,8 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||
// FileEdit group
|
||||
addLocalAction(new LockArchiveAction(plugin)); // Archive
|
||||
addLocalAction(new UnlockArchiveAction(plugin)); // Archive
|
||||
addLocalAction(new UndoArchiveTransactionAction(plugin)); // Archive
|
||||
addLocalAction(new RedoArchiveTransactionAction(plugin)); // Archive
|
||||
|
||||
// Arch group
|
||||
addLocalAction(new SetArchiveArchitectureAction(plugin)); // Archive
|
||||
|
|
|
@ -74,7 +74,7 @@ abstract class AbstractTypeDefAction extends DockingAction {
|
|||
private DataType createNewTypeDef(Component parentComponent, TypeDef typedef,
|
||||
CategoryPath categoryPath, DataTypeManager dataTypeManager) {
|
||||
DataType newdt = null;
|
||||
int transactionID = dataTypeManager.startTransaction("Create Typedef");
|
||||
int transactionID = dataTypeManager.startTransaction("Create Typedef " + typedef.getName());
|
||||
try {
|
||||
newdt = dataTypeManager.addDataType(typedef, plugin.getConflictHandler());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/* ###
|
||||
* 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 javax.swing.tree.TreePath;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager;
|
||||
|
||||
public abstract class AbstractUndoRedoArchiveTransactionAction extends DockingAction {
|
||||
|
||||
private String actionName; // Undo / Redo
|
||||
|
||||
/**
|
||||
* Construct Undo/Redo action
|
||||
* @param actionName "Undo" or "Redo" action name
|
||||
* @param plugin {@link DataTypeManagerPlugin}
|
||||
*/
|
||||
public AbstractUndoRedoArchiveTransactionAction(String actionName,
|
||||
DataTypeManagerPlugin plugin) {
|
||||
super(actionName + " Archive Change", plugin.getName());
|
||||
this.actionName = actionName;
|
||||
setPopupMenuData(getMenuData(null));
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
private MenuData getMenuData(String txName) {
|
||||
String name = actionName + " Change";
|
||||
if (!StringUtils.isEmpty(txName)) {
|
||||
name += ": " + txName;
|
||||
}
|
||||
return new MenuData(new String[] { name }, null, "FileEdit");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
return getModifiableProjectOrFileDTM(selectionPaths) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the corresponding undo/redo can be performed
|
||||
* @param dtm archive datatype manager
|
||||
* @return true if action can be performed on archive
|
||||
*/
|
||||
abstract protected boolean canExecute(StandAloneDataTypeManager dtm);
|
||||
|
||||
/**
|
||||
* Determine the next undo/redo transaction name
|
||||
* @param dtm archive datatype manager
|
||||
* @return next undo/redo transaction name
|
||||
*/
|
||||
abstract protected String getNextName(StandAloneDataTypeManager dtm);
|
||||
|
||||
/**
|
||||
* Execute the undo/redo operation on the specified archive datatype manager.
|
||||
* @param dtm archive datatype manager
|
||||
*/
|
||||
abstract protected void execute(StandAloneDataTypeManager dtm);
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths);
|
||||
if (dtm != null && canExecute(dtm)) {
|
||||
setPopupMenuData(getMenuData(getNextName(dtm)));
|
||||
return true;
|
||||
}
|
||||
setPopupMenuData(getMenuData(null));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreePath[] selectionPaths = getSelectionPaths(context);
|
||||
StandAloneDataTypeManager dtm = getModifiableProjectOrFileDTM(selectionPaths);
|
||||
if (dtm != null && canExecute(dtm)) {
|
||||
execute(dtm);
|
||||
}
|
||||
}
|
||||
|
||||
private TreePath[] getSelectionPaths(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gtree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||
return selectionPaths;
|
||||
}
|
||||
|
||||
private StandAloneDataTypeManager getModifiableProjectOrFileDTM(TreePath[] selectionPaths) {
|
||||
// only valid if single file or project archive node is selected
|
||||
if (selectionPaths.length != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TreePath path = selectionPaths[0];
|
||||
if (path.getPathCount() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GTreeNode node = (GTreeNode) path.getPathComponent(1);
|
||||
if (!(node instanceof FileArchiveNode) && !(node instanceof ProjectArchiveNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArchiveNode archiveNode = (ArchiveNode) node;
|
||||
if (archiveNode.isModifiable()) {
|
||||
DataTypeManager dtm = archiveNode.getArchive().getDataTypeManager();
|
||||
if (dtm instanceof StandAloneDataTypeManager archiveDtm) {
|
||||
return archiveDtm;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -26,8 +26,7 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
|||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.program.model.data.Category;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.InvalidNameException;
|
||||
|
||||
public class CreateCategoryAction extends DockingAction {
|
||||
|
@ -90,10 +89,10 @@ public class CreateCategoryAction extends DockingAction {
|
|||
Archive archive = archiveNode.getArchive();
|
||||
DataTypeManager dataTypeManager = archive.getDataTypeManager();
|
||||
|
||||
String newNodeName = null;
|
||||
int transactionID = dataTypeManager.startTransaction("Create Category");
|
||||
String newNodeName = getUniqueCategoryName(category);
|
||||
String path = category.toString() + newNodeName;
|
||||
int transactionID = dataTypeManager.startTransaction("Create " + path);
|
||||
try {
|
||||
newNodeName = getUniqueCategoryName(category);
|
||||
category.createCategory(newNodeName);
|
||||
}
|
||||
catch (InvalidNameException ie) {
|
||||
|
|
|
@ -78,7 +78,8 @@ public class CreatePointerAction extends DockingAction {
|
|||
|
||||
private DataType createNewDataType(Component parentComponent, DataType dataType,
|
||||
CategoryPath categoryPath, DataTypeManager dataTypeManager) {
|
||||
int transactionID = dataTypeManager.startTransaction("Create Typedef");
|
||||
int transactionID =
|
||||
dataTypeManager.startTransaction("Create Pointer " + dataType.getName());
|
||||
try {
|
||||
return dataTypeManager.addDataType(dataType, plugin.getConflictHandler());
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ public class Pack1DataTypeAction extends DockingAction {
|
|||
boolean commit = false;
|
||||
try {
|
||||
// start a transaction
|
||||
transactionID = dataTypeManager.startTransaction("pack of " + dataType.getName());
|
||||
transactionID = dataTypeManager.startTransaction("Pack(1) " + dataType.getName());
|
||||
packDataType(dataType);
|
||||
commit = true;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import ghidra.util.Msg;
|
|||
|
||||
public class PackDataTypeAction extends DockingAction {
|
||||
|
||||
|
||||
public PackDataTypeAction(DataTypeManagerPlugin plugin) {
|
||||
super("Pack Data Type", plugin.getName());
|
||||
setPopupMenuData(new MenuData(new String[] { "Pack (default)" }, "Edit"));
|
||||
|
@ -100,20 +99,20 @@ public class PackDataTypeAction extends DockingAction {
|
|||
private void alignDataType(DataType dataType, DataOrganization dataOrganization) {
|
||||
DataTypeManager dataTypeManager = dataType.getDataTypeManager();
|
||||
if (dataTypeManager == null) {
|
||||
Msg.error(this, "Can't align data type " + dataType.getName() +
|
||||
" without a data type manager.");
|
||||
Msg.error(this,
|
||||
"Can't align data type " + dataType.getName() + " without a data type manager.");
|
||||
return;
|
||||
}
|
||||
if (!(dataType instanceof Structure)) {
|
||||
Msg.error(this, "Can't align data type " + dataType.getName() +
|
||||
". It's not a structure.");
|
||||
Msg.error(this,
|
||||
"Can't align data type " + dataType.getName() + ". It's not a structure.");
|
||||
return;
|
||||
}
|
||||
int transactionID = -1;
|
||||
boolean commit = false;
|
||||
try {
|
||||
// start a transaction
|
||||
transactionID = dataTypeManager.startTransaction("align " + dataType.getName());
|
||||
transactionID = dataTypeManager.startTransaction("Pack " + dataType.getName());
|
||||
((Structure) dataType).setPackingEnabled(true);
|
||||
commit = true;
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public class PackSizeDataTypeAction extends DockingAction {
|
|||
try {
|
||||
// start a transaction
|
||||
transactionID =
|
||||
dataTypeManager.startTransaction("pack(" + packSize + ") of " + dataType.getName());
|
||||
dataTypeManager.startTransaction("Pack(" + packSize + ") " + dataType.getName());
|
||||
packDataType(dataType, packSize);
|
||||
commit = true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* ###
|
||||
* 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 ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager;
|
||||
|
||||
public class RedoArchiveTransactionAction extends AbstractUndoRedoArchiveTransactionAction {
|
||||
|
||||
public RedoArchiveTransactionAction(DataTypeManagerPlugin plugin) {
|
||||
super("Redo", plugin);
|
||||
// Key-bind disabled by default to activation context concerns
|
||||
//setKeyBindingData(new KeyBindingData("ctrl shift Z"));
|
||||
setDescription("Redo last undone change made to data type archive");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canExecute(StandAloneDataTypeManager dtm) {
|
||||
return dtm.canRedo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNextName(StandAloneDataTypeManager dtm) {
|
||||
return dtm.getRedoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(StandAloneDataTypeManager dtm) {
|
||||
dtm.redo();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/* ###
|
||||
* 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 ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.program.model.data.StandAloneDataTypeManager;
|
||||
|
||||
public class UndoArchiveTransactionAction extends AbstractUndoRedoArchiveTransactionAction {
|
||||
|
||||
public UndoArchiveTransactionAction(DataTypeManagerPlugin plugin) {
|
||||
super("Undo", plugin);
|
||||
// Key-bind disabled by default to activation context concerns
|
||||
//setKeyBindingData(new KeyBindingData("ctrl Z"));
|
||||
setDescription("Undo last change made to data type archive");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canExecute(StandAloneDataTypeManager dtm) {
|
||||
return dtm.canUndo();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNextName(StandAloneDataTypeManager dtm) {
|
||||
return dtm.getUndoName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(StandAloneDataTypeManager dtm) {
|
||||
dtm.undo();
|
||||
}
|
||||
|
||||
}
|
|
@ -282,7 +282,8 @@ public class AssociateDataTypeAction extends DockingAction {
|
|||
}
|
||||
|
||||
boolean noErrors = false;
|
||||
int tx = dtm.startTransaction("Create Category");
|
||||
String path = archive.getName() + categoryPath;
|
||||
int tx = dtm.startTransaction("Create " + path);
|
||||
try {
|
||||
category = dtm.createCategory(categoryPath);
|
||||
noErrors = true;
|
||||
|
|
|
@ -131,7 +131,8 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
|||
monitor.setMessage("Finding out-of-sync types");
|
||||
List<DataTypeSyncInfo> outOfSynchDataTypes = synchronizer.findOutOfSynchDataTypes();
|
||||
|
||||
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer, synchronizer.findOutOfSynchDataTypes());
|
||||
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer,
|
||||
synchronizer.findOutOfSynchDataTypes());
|
||||
if (outOfSynchDataTypes.isEmpty()) {
|
||||
showNoDataTypesToSyncMessage();
|
||||
return;
|
||||
|
@ -194,20 +195,20 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
|||
protected void processSelectedDataTypes(DataTypeSynchronizer synchronizer,
|
||||
List<DataTypeSyncInfo> selectedList, List<DataTypeSyncInfo> outOfSynchDataTypes,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
|
||||
synchronizer.performBulkOperation(getName(), selectedList, info -> {
|
||||
monitor.checkCancelled();
|
||||
monitor.setMessage("Syncing " + info.getName());
|
||||
applyOperation(info);
|
||||
outOfSynchDataTypes.remove(info);
|
||||
monitor.incrementProgress(1);
|
||||
|
||||
|
||||
}, outOfSyncList -> {
|
||||
// dataTypeChanged can cause other related data types to become updated
|
||||
// and their times will appear out of sync. So clean up any that actually
|
||||
// are the same.
|
||||
removeAndUpdateOutOfSyncInTimeOnlyDataTypes(synchronizer, outOfSyncList);
|
||||
|
||||
|
||||
}, requiresArchiveOpenForEditing());
|
||||
}
|
||||
|
||||
|
@ -326,7 +327,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
|
|||
private void autoUpdateDataTypesThatHaveNoRealChanges(DataTypeSynchronizer synchronizer,
|
||||
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
|
||||
|
||||
int transactionID = dtm.startTransaction("Auto-sync data types");
|
||||
int transactionID = dtm.startTransaction("Sync data types");
|
||||
try {
|
||||
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
|
||||
dataTypeSyncInfo.syncTimes();
|
||||
|
|
|
@ -220,5 +220,10 @@ public class DataTypeIndexer {
|
|||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
markStale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
markStale();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -640,7 +640,6 @@ public class DataTypeManagerHandler {
|
|||
|
||||
void dataTypeManagerChanged(FileArchive archive, DataTypeManager oldManager,
|
||||
DataTypeManager newManager) {
|
||||
|
||||
oldManager.removeDataTypeManagerListener(listenerDelegate);
|
||||
newManager.addDataTypeManagerListener(listenerDelegate);
|
||||
dataTypeIndexer.removeDataTypeManager(oldManager);
|
||||
|
@ -1239,6 +1238,13 @@ public class DataTypeManagerHandler {
|
|||
listener.programArchitectureChanged(dataTypeManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
for (DataTypeManagerChangeListener listener : dataTypeManagerListeners) {
|
||||
listener.restored(dataTypeManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1412,8 +1418,7 @@ public class DataTypeManagerHandler {
|
|||
}
|
||||
|
||||
private DataTreeDialog getSaveDialog() {
|
||||
DataTreeDialog dialog =
|
||||
new DataTreeDialog(null, "Save As", SAVE, createArchiveFileFilter);
|
||||
DataTreeDialog dialog = new DataTreeDialog(null, "Save As", SAVE, createArchiveFileFilter);
|
||||
|
||||
ActionListener listener = event -> {
|
||||
DomainFolder folder = dialog.getDomainFolder();
|
||||
|
|
|
@ -300,6 +300,12 @@ public class FileArchive implements Archive {
|
|||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
setChanged(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
archiveManager.dataTypeManagerChanged(FileArchive.this, dataTypeManager,
|
||||
dataTypeManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,7 +29,7 @@ public class ProjectArchive implements DomainFileArchive {
|
|||
|
||||
private static Icon CLOSED_ICON = new GIcon("icon.plugin.datatypes.archive.project.closed");
|
||||
private static Icon OPEN_ICON = new GIcon("icon.plugin.datatypes.archive.project.open");
|
||||
|
||||
|
||||
private DataTypeArchive dataTypeArchive;
|
||||
private DomainFile sourceDomainFile;
|
||||
private DataTypeManagerChangeListener categoryListener; // hold on to since it is stored in a weak set
|
||||
|
@ -67,6 +67,7 @@ public class ProjectArchive implements DomainFileArchive {
|
|||
return -1; // Project Archives appear between the ProgramArchive and FileArchives.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExclusiveAccess() {
|
||||
return dataTypeArchive.hasExclusiveAccess();
|
||||
}
|
||||
|
@ -202,5 +203,10 @@ public class ProjectArchive implements DomainFileArchive {
|
|||
public void programArchitectureChanged(DataTypeManager dtm) {
|
||||
fireStateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dtm) {
|
||||
fireStateChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,34 +390,6 @@ public class DataTypeEditorManager implements EditorListener {
|
|||
return false;
|
||||
}
|
||||
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
// Create a copy of the list since restore may remove an editor from the original list.
|
||||
ArrayList<EditorProvider> list = new ArrayList<>(editorList);
|
||||
// notify the editors
|
||||
for (EditorProvider editor : list) {
|
||||
DataTypeManager dataTypeManager = editor.getDataTypeManager();
|
||||
DataTypeManager programDataTypeManager = domainObject.getDataTypeManager();
|
||||
if (dataTypeManager == programDataTypeManager) {
|
||||
/*
|
||||
|
||||
It is not clear why this check was added. It seem reasonable to always let the
|
||||
editor know about the event. With this code enabled, editors with new, unsaved
|
||||
types will be closed.
|
||||
|
||||
DataTypePath dtPath = editor.getDtPath();
|
||||
CategoryPath categoryPath = dtPath.getCategoryPath();
|
||||
String name = dtPath.getDataTypeName();
|
||||
DataType dataType = programDataTypeManager.getDataType(categoryPath, name);
|
||||
if (dataType == null || dataType.isDeleted()) {
|
||||
dismissEditor(editor);
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
editor.domainObjectRestored(domainObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the specified data type is being edited for the indicated category, this gets that editor.
|
||||
* @param dataType the data type
|
||||
|
|
|
@ -36,6 +36,7 @@ import docking.widgets.textfield.GValidatedTextField.ValidationMessageListener;
|
|||
import generic.theme.Gui;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.*;
|
||||
|
@ -120,30 +121,40 @@ class EnumEditorPanel extends JPanel {
|
|||
});
|
||||
}
|
||||
|
||||
void domainObjectRestored(DataTypeManagerDomainObject domainObject, EnumDataType enuum) {
|
||||
void domainObjectRestored(EnumDataType enuum, boolean exists) {
|
||||
|
||||
stopCellEditing();
|
||||
this.originalEnumDT = enuum;
|
||||
this.editedEnumDT = (EnumDataType) enuum.copy(enuum.getDataTypeManager());
|
||||
DataTypeManager objectDataTypeManager = domainObject.getDataTypeManager();
|
||||
DataTypeManager providerDataTypeManager = provider.getDataTypeManager();
|
||||
if (objectDataTypeManager != providerDataTypeManager) {
|
||||
return; // The editor isn't associated with the restored domain object.
|
||||
}
|
||||
|
||||
DataTypeManager enumDtMgr = enuum.getDataTypeManager();
|
||||
String objectType = "domain object";
|
||||
if (domainObject instanceof Program) {
|
||||
if (enumDtMgr instanceof ProgramBasedDataTypeManager) {
|
||||
objectType = "program";
|
||||
}
|
||||
else if (domainObject instanceof DataTypeArchive) {
|
||||
else {
|
||||
objectType = "data type archive";
|
||||
}
|
||||
String archiveName = enumDtMgr.getName();
|
||||
this.originalEnumDT = enuum;
|
||||
|
||||
if (tableModel.hasChanges()) {
|
||||
if (!exists) {
|
||||
if (OptionDialog.showOptionNoCancelDialog(this, "Close Enum Editor?",
|
||||
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
|
||||
enuum.getDisplayName() + "\" may no longer exist outside the editor.\n" +
|
||||
"Do you want to close editor?",
|
||||
"Close", "Continue Edit",
|
||||
OptionDialog.WARNING_MESSAGE) == OptionDialog.OPTION_ONE) {
|
||||
provider.dispose();
|
||||
}
|
||||
else {
|
||||
provider.stateChanged(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (exists && tableModel.hasChanges()) {
|
||||
if (OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, "Reload Enum Editor?",
|
||||
"The " + objectType + " \"" + objectDataTypeManager.getName() +
|
||||
"\" has been restored.\n" + "\"" + tableModel.getEnum().getDisplayName() +
|
||||
"\" may have changed outside this editor.\n" +
|
||||
"The " + objectType + " \"" + archiveName + "\" has been restored.\n" + "\"" +
|
||||
enuum.getDisplayName() + "\" may have changed outside this editor.\n" +
|
||||
"Do you want to discard edits and reload the Enum?") == OptionDialog.OPTION_TWO) {
|
||||
|
||||
// 'No'; do not discard
|
||||
|
@ -153,6 +164,7 @@ class EnumEditorPanel extends JPanel {
|
|||
}
|
||||
|
||||
// reload the enum
|
||||
this.editedEnumDT = (EnumDataType) enuum.copy(enuum.getDataTypeManager());
|
||||
setFieldInfo(editedEnumDT);
|
||||
tableModel.setEnum(editedEnumDT, false);
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
}
|
||||
originalCategoryPath = categoryPath;
|
||||
originalEnum = enumDT;
|
||||
|
||||
originalEnumName = enumDT.getDisplayName();
|
||||
dataTypeManager = enumDTM;
|
||||
|
||||
|
@ -213,25 +214,6 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
return editorPanel.needsSave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
|
||||
if (originalEnumID == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Enum enuum = (Enum) dataTypeManager.getDataType(originalEnumID);
|
||||
if (enuum != null) {
|
||||
EnumDataType dt = (EnumDataType) enuum.copy(dataTypeManager);
|
||||
originalEnumName = dt.getDisplayName();
|
||||
updateTitle(dt);
|
||||
Category category = dataTypeManager.getCategory(enuum.getCategoryPath());
|
||||
originalCategoryPath = category.getCategoryPath();
|
||||
editorPanel.domainObjectRestored(domainObject, dt);
|
||||
}
|
||||
tool.setStatusInfo("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransient() {
|
||||
return true;
|
||||
|
@ -348,11 +330,21 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
setStatusMessage("Empty enum is not allowed");
|
||||
return false;
|
||||
}
|
||||
int txID = startTransaction();
|
||||
|
||||
boolean originalDtExists = dataTypeManager.contains(originalEnum);
|
||||
boolean renamed = false;
|
||||
if (originalDtExists) {
|
||||
String editorName = editorPanel.getEnumName().trim();
|
||||
renamed = !originalEnumName.equals(editorName);
|
||||
}
|
||||
String action = originalDtExists ? "Edit" : "Create";
|
||||
if (renamed) {
|
||||
action += "/Rename";
|
||||
}
|
||||
int txID = dataTypeManager.startTransaction(action + " Enum " + editedEnum.getName());
|
||||
try {
|
||||
DataTypeManager dtm = editedEnum.getDataTypeManager();
|
||||
boolean userSaved = resolveEquateConflicts(editedEnum, dtm);
|
||||
|
||||
boolean userSaved = resolveEquateConflicts(editedEnum);
|
||||
if (!userSaved) {
|
||||
return false;
|
||||
}
|
||||
|
@ -364,11 +356,12 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
newEnuum.replaceWith(editedEnum);
|
||||
|
||||
originalEnum = newEnuum;
|
||||
originalEnumID = dataTypeManager.getID(newEnuum);
|
||||
editorPanel.setEnum((EnumDataType) newEnuum.copy(dataTypeManager));
|
||||
applyAction.setEnabled(hasChanges());
|
||||
}
|
||||
finally {
|
||||
endTransaction(txID);
|
||||
dataTypeManager.endTransaction(txID, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -381,10 +374,9 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
/**
|
||||
* Checks to see if the new changes to the enum will affect equates based off of it.
|
||||
* @param editedEnum the enum to check for conflicts with
|
||||
* @param dtm the data type manager that this enum lies within
|
||||
* @return true if the enum should save its changes; otherwise, false
|
||||
*/
|
||||
private boolean resolveEquateConflicts(Enum editedEnum, DataTypeManager dtm) {
|
||||
private boolean resolveEquateConflicts(Enum editedEnum) {
|
||||
|
||||
Program program = plugin.getProgram();
|
||||
if (program == null) {
|
||||
|
@ -500,14 +492,6 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
}
|
||||
}
|
||||
|
||||
private int startTransaction() {
|
||||
return dataTypeManager.startTransaction("Edit Enum");
|
||||
}
|
||||
|
||||
private void endTransaction(int transID) {
|
||||
dataTypeManager.endTransaction(transID, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user if the editor has unsaved changes. Saves the changes if
|
||||
* the user indicates to do so.
|
||||
|
@ -692,6 +676,36 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
|||
dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dtm) {
|
||||
if (originalEnumID <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeManager originalDTM = originalEnum.getDataTypeManager();
|
||||
DataType dt = originalDTM.getDataType(originalEnumID);
|
||||
|
||||
boolean exists = false;
|
||||
if (dt instanceof Enum) {
|
||||
originalEnum = (Enum) dt;
|
||||
exists = true;
|
||||
}
|
||||
else {
|
||||
// original enum no longer exists
|
||||
originalEnumID = -1;
|
||||
EnumDataType enuum = editorPanel.getEnum();
|
||||
originalEnum = new EnumDataType(enuum.getCategoryPath(), enuum.getName(),
|
||||
enuum.getLength(), originalDTM);
|
||||
}
|
||||
|
||||
originalEnumName = originalEnum.getDisplayName();
|
||||
updateTitle(originalEnum);
|
||||
originalCategoryPath = originalEnum.getCategoryPath();
|
||||
|
||||
editorPanel.domainObjectRestored((EnumDataType) originalEnum.copy(originalDTM), exists);
|
||||
tool.setStatusInfo("");
|
||||
}
|
||||
|
||||
private boolean isMyCategory(DataTypePath path) {
|
||||
CategoryPath parentPath = path.getCategoryPath();
|
||||
return parentPath.equals(originalCategoryPath);
|
||||
|
|
|
@ -480,5 +480,13 @@ public class ArchiveNode extends CategoryNode {
|
|||
unloadChildren();
|
||||
nodeChangedUpdater.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager manager) {
|
||||
// need to force all cached datatype tooltips to be cleared
|
||||
// due to potential changes (e.g., undo/redo)
|
||||
unloadChildren();
|
||||
nodeChangedUpdater.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -253,18 +253,20 @@ public class CategoryNode extends DataTypeTreeNode {
|
|||
|
||||
@Override
|
||||
public void valueChanged(Object newValue) {
|
||||
int transactionID = category.getDataTypeManager().startTransaction("rename");
|
||||
String newName = newValue.toString();
|
||||
int transactionID =
|
||||
category.getDataTypeManager().startTransaction("Rename Category " + newName);
|
||||
try {
|
||||
category.setName(newValue.toString());
|
||||
category.setName(newName);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.showError(getClass(), null, "Rename Failed",
|
||||
"Category by the name " + newValue + " already exists in this category.");
|
||||
"Category by the name " + newName + " already exists in this category.");
|
||||
}
|
||||
catch (InvalidNameException exc) {
|
||||
String msg = exc.getMessage();
|
||||
if (msg == null) {
|
||||
msg = "Invalid name specified: " + newValue;
|
||||
msg = "Invalid name specified: " + newName;
|
||||
}
|
||||
Msg.showError(getClass(), null, "Invalid name specified", exc.getMessage());
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ public class DataTypeNode extends DataTypeTreeNode {
|
|||
return;
|
||||
}
|
||||
|
||||
int transactionID = dataType.getDataTypeManager().startTransaction("rename");
|
||||
int transactionID = dataType.getDataTypeManager().startTransaction("Rename DataType");
|
||||
|
||||
try {
|
||||
dataType.setName(newName);
|
||||
|
|
|
@ -182,7 +182,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
|
|||
|
||||
DataTypeManager newDtm = createLayeredDataTypeManager();
|
||||
|
||||
int transactionId = newDtm.startTransaction("add datatypes");
|
||||
int transactionId = newDtm.startTransaction("Add Datatypes");
|
||||
try {
|
||||
Iterator<DataType> allDataTypes = dataTypeManager.getAllDataTypes();
|
||||
while (allDataTypes.hasNext()) {
|
||||
|
@ -343,7 +343,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
|
|||
return;
|
||||
}
|
||||
|
||||
int transactionID = dataTypeManager.startTransaction("Add dataType");
|
||||
int transactionID = dataTypeManager.startTransaction("Add " + dt.getName());
|
||||
try {
|
||||
DataType resolvedDt = dataTypeManager.resolve(dt, null);
|
||||
model.add(resolvedDt);
|
||||
|
@ -354,7 +354,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin {
|
|||
}
|
||||
|
||||
private void removeDataType(DataType dt) {
|
||||
int transactionID = dataTypeManager.startTransaction("Remove dataType");
|
||||
int transactionID = dataTypeManager.startTransaction("Remove " + dt.getName());
|
||||
try {
|
||||
model.removeAll(dt);
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ import javax.swing.*;
|
|||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.compositeeditor.CompositeEditorPanel;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Composite;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
|
@ -273,15 +274,9 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
public void dataTypeManagerRestored() {
|
||||
boolean reload = true;
|
||||
String objectType = "domain object";
|
||||
if (domainObject instanceof Program) {
|
||||
objectType = "program";
|
||||
}
|
||||
else if (domainObject instanceof DataTypeArchive) {
|
||||
objectType = "data type archive";
|
||||
}
|
||||
String objectType = "program";
|
||||
DataTypeManager dtm = ((StackEditorModel) model).getOriginalDataTypeManager();
|
||||
Composite originalDt = ((StackEditorModel) model).getOriginalComposite();
|
||||
if (originalDt instanceof StackFrameDataType) {
|
||||
|
@ -306,8 +301,8 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
|||
// The user has modified the structure so prompt for whether or
|
||||
// not to reload the structure.
|
||||
String question =
|
||||
"The " + objectType + " \"" + domainObject.getName() + "\" has been restored.\n" +
|
||||
"\"" + model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"The " + objectType + " \"" + dtm.getName() + "\" has been restored.\n" + "\"" +
|
||||
model.getCompositeName() + "\" may have changed outside the editor.\n" +
|
||||
"Discard edits & reload the " + name + " Editor?";
|
||||
String title = "Reload " + name + " Editor?";
|
||||
int response = OptionDialog.showYesNoDialogWithNoAsDefaultButton(this, title, question);
|
||||
|
|
|
@ -23,7 +23,8 @@ import ghidra.app.plugin.core.compositeeditor.*;
|
|||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.CategoryPath;
|
||||
import ghidra.program.model.data.DataTypePath;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
@ -144,12 +145,6 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
|
|||
return actionMgr.getAllActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectRestored(DataTypeManagerDomainObject domainObject) {
|
||||
refreshName();
|
||||
editorPanel.domainObjectRestored(domainObject);
|
||||
}
|
||||
|
||||
private void refreshName() {
|
||||
StackFrameDataType origDt = (StackFrameDataType) stackModel.getOriginalComposite();
|
||||
StackFrameDataType viewDt = stackModel.getViewComposite();
|
||||
|
@ -187,11 +182,9 @@ public class StackEditorProvider extends CompositeEditorProvider implements Doma
|
|||
DomainObjectChangeRecord rec = event.getChangeRecord(i);
|
||||
EventType eventType = rec.getEventType();
|
||||
if (eventType == DomainObjectEvent.RESTORED) {
|
||||
Object source = event.getSource();
|
||||
if (source instanceof Program) {
|
||||
Program restoredProgram = (Program) source;
|
||||
domainObjectRestored(restoredProgram);
|
||||
}
|
||||
refreshName();
|
||||
// NOTE: editorPanel should be notified of restored datatype manager via the
|
||||
// CompositeViewerModel's DataTypeManagerChangeListener restored method
|
||||
return;
|
||||
}
|
||||
if (eventType instanceof ProgramEvent type) {
|
||||
|
|
|
@ -460,7 +460,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the golang version
|
||||
* @return {@link GoVer}
|
||||
|
@ -842,9 +841,8 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
// gdt data base. This method only leaves the target gdt filename + ".step1" in the db.
|
||||
File tmpGDTFile = new File(gdtFile.getParentFile(), gdtFile.getName() + ".step1.gdt");
|
||||
FileDataTypeManager tmpFdtm = FileDataTypeManager.createFileArchive(tmpGDTFile);
|
||||
int tx = -1;
|
||||
int tx = tmpFdtm.startTransaction("Import");
|
||||
try {
|
||||
tx = tmpFdtm.startTransaction("Import");
|
||||
tmpFdtm.addDataTypes(registeredStructDTs, DataTypeConflictHandler.DEFAULT_HANDLER,
|
||||
monitor);
|
||||
if (runtimeFuncSnapshot) {
|
||||
|
@ -879,17 +877,14 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
Msg.error(this, "Error when exporting types to file: %s".formatted(gdtFile), e);
|
||||
}
|
||||
finally {
|
||||
if (tx != -1) {
|
||||
tmpFdtm.endTransaction(tx, true);
|
||||
}
|
||||
tmpFdtm.endTransaction(tx, true);
|
||||
}
|
||||
|
||||
tmpFdtm.save();
|
||||
|
||||
FileDataTypeManager fdtm = FileDataTypeManager.createFileArchive(gdtFile);
|
||||
tx = -1;
|
||||
tx = fdtm.startTransaction("Import");
|
||||
try {
|
||||
tx = fdtm.startTransaction("Import");
|
||||
tmpFdtm.getAllDataTypes()
|
||||
.forEachRemaining(
|
||||
dt -> fdtm.addDataType(dt, DataTypeConflictHandler.DEFAULT_HANDLER));
|
||||
|
@ -898,9 +893,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
}
|
||||
}
|
||||
finally {
|
||||
if (tx != -1) {
|
||||
fdtm.endTransaction(tx, true);
|
||||
}
|
||||
fdtm.endTransaction(tx, true);
|
||||
}
|
||||
|
||||
fdtm.save();
|
||||
|
@ -926,7 +919,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
return existingDT;
|
||||
}
|
||||
|
||||
|
||||
private List<DataType> createBootstrapFuncDefs(DataTypeManager destDTM, CategoryPath destCP,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
List<Function> funcs = getAllFunctions().stream()
|
||||
|
@ -959,7 +951,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
return results;
|
||||
}
|
||||
|
||||
|
||||
private void moveAllDataTypesTo(DataTypeManager dtm, CategoryPath srcCP, CategoryPath destCP)
|
||||
throws DuplicateNameException, DataTypeDependencyException, InvalidNameException {
|
||||
Category srcCat = dtm.getCategory(srcCP);
|
||||
|
@ -1182,7 +1173,7 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
.map(Entry::getKey)
|
||||
.collect(toSet());
|
||||
typeDupCount.clear();
|
||||
|
||||
|
||||
for (GoType goType : goTypes.values()) {
|
||||
String typeName = goType.getNameWithPackageString();
|
||||
if (dupedTypeNames.contains(typeName)) {
|
||||
|
@ -1270,10 +1261,11 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
|
||||
StructureContext<T> structContext = getStructureContextOfInstance(structInstance);
|
||||
String fallbackName = defaultValue;
|
||||
fallbackName = fallbackName == null && structContext != null
|
||||
? "%s_%x".formatted(structContext.getMappingInfo().getStructureName(),
|
||||
structContext.getStructureStart())
|
||||
: "invalid_object";
|
||||
fallbackName =
|
||||
fallbackName == null && structContext != null
|
||||
? "%s_%x".formatted(structContext.getMappingInfo().getStructureName(),
|
||||
structContext.getStructureStart())
|
||||
: "invalid_object";
|
||||
return GoName.createFakeInstance(fallbackName);
|
||||
}
|
||||
|
||||
|
@ -1296,7 +1288,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
return "unknown_type_%x".formatted(offset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link GoType} corresponding to an offset that is relative to the controlling
|
||||
* GoModuledata's typesOffset.
|
||||
|
@ -1441,15 +1432,13 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
|
||||
private AddressRange getPclntabSearchRange() {
|
||||
MemoryBlock memBlock = getFirstGoSection(program, "noptrdata", "rdata");
|
||||
return memBlock != null
|
||||
? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
|
||||
return memBlock != null ? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
|
||||
: null;
|
||||
}
|
||||
|
||||
private AddressRange getModuledataSearchRange() {
|
||||
MemoryBlock memBlock = getFirstGoSection(program, "noptrdata", "data");
|
||||
return memBlock != null
|
||||
? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
|
||||
return memBlock != null ? new AddressRangeImpl(memBlock.getStart(), memBlock.getEnd())
|
||||
: null;
|
||||
}
|
||||
|
||||
|
@ -1489,7 +1478,6 @@ public class GoRttiMapper extends DataTypeMapper implements DataTypeMapperContex
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
public Symbol getGoSymbol(String symbolName) {
|
||||
return getGoSymbol(program, symbolName);
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public class DataTypeCleaner implements Closeable {
|
|||
this.targetDtm = targetDtm;
|
||||
this.retainExistingComposites = retainExistingComposites;
|
||||
this.cleanerDtm = new StandAloneDataTypeManager("CleanerDTM");
|
||||
txId = cleanerDtm.startTransaction("CleanerTx");
|
||||
txId = cleanerDtm.startTransaction("Clean Datatypes");
|
||||
|
||||
ProgramArchitecture arch = targetDtm.getProgramArchitecture();
|
||||
if (arch != null) {
|
||||
|
|
|
@ -40,7 +40,6 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog;
|
|||
import ghidra.app.plugin.core.stackeditor.StackEditorModel;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginException;
|
||||
|
@ -488,20 +487,6 @@ public abstract class AbstractEditorTest extends AbstractGhidraHeadedIntegration
|
|||
program.endTransaction(txId, saveChanges);
|
||||
}
|
||||
|
||||
protected class RestoreListener implements DomainObjectListener {
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent event) {
|
||||
if (event.contains(DomainObjectEvent.RESTORED)) {
|
||||
Object source = event.getSource();
|
||||
if (source instanceof DataTypeManagerDomainObject) {
|
||||
DataTypeManagerDomainObject restoredDomainObject =
|
||||
(DataTypeManagerDomainObject) source;
|
||||
provider.domainObjectRestored(restoredDomainObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class StatusListener extends CompositeEditorModelAdapter {
|
||||
String status = null;
|
||||
boolean beep = false;
|
||||
|
|
|
@ -108,11 +108,9 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testModifiedDtAndProgramRestored() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
|
@ -165,7 +163,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,7 +170,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
// This should close the edit session.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedDt() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
Structure s1 = new StructureDataType("s1", 0);
|
||||
|
@ -197,7 +193,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
final Structure myS1Structure = s1Struct;
|
||||
|
||||
init(myS1Structure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Change the structure.
|
||||
runSwingLater(() -> {
|
||||
|
@ -232,7 +227,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,7 +234,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
// program so it goes away. This should close the edit session.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedDtComp() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
Structure s1 = new StructureDataType("s1", 0);
|
||||
|
@ -268,7 +261,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
assertTrue(s2.isEquivalent(myS2Structure));
|
||||
|
||||
init(myS2Structure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Change the structure.
|
||||
runSwing(() -> {
|
||||
|
@ -303,7 +295,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,14 +302,12 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
// so it goes away. The editor stays since the structure existed previously, but editor reloads.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedComponentDtYes() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
Structure myStruct = new StructureDataType("myStruct", 0);
|
||||
myStruct.add(new WordDataType());
|
||||
|
||||
init(emptyStructure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Add the data type so that we can undo its add.
|
||||
boolean commit = true;
|
||||
|
@ -371,7 +360,6 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,14 +367,12 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
// so it goes away. The editor stays since the structure existed previously, but doesn't reload.
|
||||
@Test
|
||||
public void testProgramRestoreRemovesEditedComponentDtNo() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
Structure myStruct = new StructureDataType("myStruct", 0);
|
||||
myStruct.add(new WordDataType());
|
||||
|
||||
init(emptyStructure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Add the data type so that we can undo its add.
|
||||
boolean commit = true;
|
||||
|
@ -438,45 +424,37 @@ public class StructureEditorProviderTest extends AbstractStructureEditorTest {
|
|||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testUnModifiedDtAndProgramRestored() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
try {
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
init(complexStructure, pgmTestCat, false);
|
||||
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
setSelection(new int[] { 4, 5 });
|
||||
deleteAction.actionPerformed(new DefaultActionContext());
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
}
|
||||
finally {
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
// Change the structure
|
||||
runSwingLater(() -> {
|
||||
getTable().requestFocus();
|
||||
setSelection(new int[] { 4, 5 });
|
||||
deleteAction.actionPerformed(new DefaultActionContext());
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
assertTrue(complexStructure.isEquivalent(model.viewComposite));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -31,8 +31,7 @@ import ghidra.program.model.data.*;
|
|||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
public class StructureEditorUnlockedActions5Test
|
||||
extends AbstractStructureEditorTest {
|
||||
public class StructureEditorUnlockedActions5Test extends AbstractStructureEditorTest {
|
||||
|
||||
@Test
|
||||
public void testApplyDuplicateName() throws Exception {
|
||||
|
@ -614,14 +613,14 @@ public class StructureEditorUnlockedActions5Test
|
|||
undo(program, false);
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
runSwing(() -> provider.domainObjectRestored(program), true);
|
||||
runSwing(() -> provider.dataTypeManagerRestored(), true);
|
||||
waitForSwing();
|
||||
|
||||
assertEquals("myStruct", model.getCompositeName());
|
||||
redo(program, false);
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
runSwing(() -> provider.domainObjectRestored(program), true);
|
||||
runSwing(() -> provider.dataTypeManagerRestored(), true);
|
||||
waitForSwing();
|
||||
assertEquals("myStruct2", model.getCompositeName());
|
||||
|
||||
|
|
|
@ -83,11 +83,9 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
|
|||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testModifiedDtAndProgramRestored() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
Window dialog;
|
||||
try {
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
|
||||
// Change the union.
|
||||
Swing.runLater(() -> {
|
||||
|
@ -138,47 +136,39 @@ public class UnionEditorProviderTest extends AbstractUnionEditorTest {
|
|||
}
|
||||
finally {
|
||||
dialog = null;
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
}
|
||||
|
||||
// Test Undo / Redo of program.
|
||||
@Test
|
||||
public void testUnModifiedDtAndProgramRestored() throws Exception {
|
||||
RestoreListener restoreListener = new RestoreListener();
|
||||
try {
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
program.addListener(restoreListener);
|
||||
init(complexUnion, pgmTestCat, false);
|
||||
|
||||
// Change the union.
|
||||
Swing.runLater(() -> {
|
||||
delete(4, 5);
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
// Change the union.
|
||||
Swing.runLater(() -> {
|
||||
delete(4, 5);
|
||||
try {
|
||||
model.add(new WordDataType());
|
||||
}
|
||||
catch (UsrException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
assertFalse(complexUnion.isEquivalent(model.viewComposite));
|
||||
waitForTasks();
|
||||
assertFalse(complexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
// Apply the changes
|
||||
invoke(applyAction);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
// Undo the apply
|
||||
undo(program);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
}
|
||||
finally {
|
||||
program.removeListener(restoreListener);
|
||||
}
|
||||
// Redo the apply
|
||||
redo(program);
|
||||
assertTrue(complexUnion.isEquivalent(model.viewComposite));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
/* ###
|
||||
* 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.editor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.DefaultActionContext;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.*;
|
||||
|
||||
/**
|
||||
* {@link AbstractEnumEditorUndoRedoTest} contains tests which should be applied to the various
|
||||
* {@link DataTypeManager} implementations which are responsible for setting {@code dtm} during
|
||||
* the setUp phase.
|
||||
*/
|
||||
public abstract class AbstractEnumEditorUndoRedoTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
protected Program program;
|
||||
protected DataTypeManagerPlugin plugin;
|
||||
protected PluginTool tool;
|
||||
protected TestEnv env;
|
||||
|
||||
protected DataTypeManager dtm; // must be set by test implementation during setUp
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
ToyProgramBuilder builder = new ToyProgramBuilder("notepad", true);
|
||||
builder.addCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
|
||||
program = builder.getProgram();
|
||||
|
||||
env = new TestEnv();
|
||||
tool = env.showTool(program);
|
||||
tool.addPlugin(DataTypeManagerPlugin.class.getName());
|
||||
plugin = getPlugin(tool, DataTypeManagerPlugin.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedo() throws Exception {
|
||||
|
||||
Enum enumDt = editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
// delete a row
|
||||
table.setRowSelectionInterval(0, 0);
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getDeleteAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
applyChanges(true);
|
||||
assertNull(enumDt.getName(0));
|
||||
|
||||
// undo
|
||||
undo(true);
|
||||
assertEquals("Red", model.getValueAt(0, EnumTableModel.NAME_COL));
|
||||
|
||||
//redo
|
||||
redo(true);
|
||||
assertEquals("Pink", model.getValueAt(0, EnumTableModel.NAME_COL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRemoval() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
assertFalse(model.hasChanges());
|
||||
|
||||
undo(true); // will remove enum from DTM
|
||||
|
||||
DataType dt = dtm.getDataType("/Category1/Colors");
|
||||
assertNull(dt);
|
||||
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
assertEquals("Close Enum Editor?", d.getTitle());
|
||||
|
||||
JButton button = findButtonByText(d.getComponent(), "Continue Edit");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(panel.needsSave());
|
||||
|
||||
DockingActionIf applyAction = getApplyAction();
|
||||
assertTrue(applyAction.isEnabled());
|
||||
|
||||
applyChanges(true);
|
||||
|
||||
dt = dtm.getDataType("/Category1/Colors");
|
||||
assertNotNull(dt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangesBeforeUndoYes() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
int origRowCount = model.getRowCount();
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
applyChanges(true);
|
||||
// make more changes
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
undo(false);
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
// yes to reload the enum data type
|
||||
JButton button = findButtonByText(d.getComponent(), "Yes");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
assertEquals(origRowCount, model.getRowCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangesBeforeUndoNo() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
runSwing(() -> {
|
||||
int lastRow = model.getRowCount() - 1;
|
||||
if (lastRow >= 0) {
|
||||
table.addRowSelectionInterval(lastRow, lastRow);
|
||||
}
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
applyChanges(true);
|
||||
// make more changes
|
||||
runSwing(() -> {
|
||||
int lastRow = model.getRowCount() - 1;
|
||||
if (lastRow >= 0) {
|
||||
table.addRowSelectionInterval(lastRow, lastRow);
|
||||
}
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
int rowCount = model.getRowCount();
|
||||
undo(false);
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
// not to not reload the enum data type
|
||||
JButton button = findButtonByText(d.getComponent(), "No");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
assertEquals(rowCount, model.getRowCount());
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private EnumEditorPanel findEditorPanel(Window w) {
|
||||
Window[] windows = w.getOwnedWindows();
|
||||
for (Window window : windows) {
|
||||
if (window.isVisible() && JDialog.class.isAssignableFrom(window.getClass())) {
|
||||
Container c =
|
||||
findContainer(((JDialog) window).getContentPane(), EnumEditorPanel.class);
|
||||
if (c != null) {
|
||||
return (EnumEditorPanel) c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Container findContainer(Container parent, Class<?> theClass) {
|
||||
Component[] c = parent.getComponents();
|
||||
for (Component element : c) {
|
||||
if (theClass.isAssignableFrom(element.getClass())) {
|
||||
return (Container) element;
|
||||
}
|
||||
if (element instanceof Container) {
|
||||
Container container = findContainer((Container) element, theClass);
|
||||
if (container != null) {
|
||||
return container;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void applyChanges(boolean doWait) throws Exception {
|
||||
|
||||
DockingActionIf applyAction = getApplyAction();
|
||||
assertTrue(applyAction.isEnabled());
|
||||
Runnable r = () -> applyAction.actionPerformed(new DefaultActionContext());
|
||||
if (doWait) {
|
||||
runSwing(r);
|
||||
dtm.flushEvents();
|
||||
}
|
||||
else {
|
||||
runSwingLater(r);
|
||||
}
|
||||
waitForSwing();
|
||||
|
||||
}
|
||||
|
||||
private DockingActionIf getAddAction() {
|
||||
return getAction(plugin, "Add Enum Value");
|
||||
}
|
||||
|
||||
private DockingActionIf getApplyAction() {
|
||||
return getAction(plugin, "Apply Enum Changes");
|
||||
}
|
||||
|
||||
private DockingActionIf getDeleteAction() {
|
||||
return getAction(plugin, "Delete Enum Value");
|
||||
}
|
||||
|
||||
private Enum editSampleEnum() {
|
||||
|
||||
AtomicReference<Enum> enumRef = new AtomicReference<>();
|
||||
|
||||
dtm.withTransaction("Create Test Enum", () -> {
|
||||
|
||||
Category cat = dtm.createCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
|
||||
|
||||
Enum enumm = new EnumDataType("Colors", 1);
|
||||
enumm.add("Red", 0);
|
||||
enumm.add("Green", 0x10);
|
||||
enumm.add("Blue", 0x20);
|
||||
enumm.add("Purple", 5);
|
||||
enumm.add("Turquoise", 0x22);
|
||||
enumm.add("Pink", 2);
|
||||
enumm.setDescription("This is a set of Colors");
|
||||
|
||||
Enum enumDt = (Enum) cat.addDataType(enumm, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
enumRef.set(enumDt);
|
||||
|
||||
dtm.flushEvents();
|
||||
waitForSwing();
|
||||
|
||||
runSwingLater(() -> plugin.edit(enumDt));
|
||||
});
|
||||
|
||||
waitForSwing();
|
||||
return enumRef.get();
|
||||
|
||||
}
|
||||
|
||||
private void undo(boolean doWait) throws Exception {
|
||||
Runnable r = () -> {
|
||||
try {
|
||||
undo();
|
||||
dtm.flushEvents();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
};
|
||||
if (doWait) {
|
||||
runSwing(r);
|
||||
}
|
||||
else {
|
||||
runSwingLater(r);
|
||||
}
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void redo(boolean doWait) throws Exception {
|
||||
Runnable r = () -> {
|
||||
try {
|
||||
redo();
|
||||
dtm.flushEvents();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
};
|
||||
if (doWait) {
|
||||
runSwing(r);
|
||||
}
|
||||
else {
|
||||
runSwingLater(r);
|
||||
}
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
abstract void undo() throws IOException;
|
||||
|
||||
abstract void redo() throws IOException;
|
||||
}
|
|
@ -668,110 +668,6 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUndoRedo() throws Exception {
|
||||
|
||||
Enum enumDt = editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
// delete a row
|
||||
table.setRowSelectionInterval(0, 0);
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getDeleteAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
applyChanges(true);
|
||||
assertNull(enumDt.getName(0));
|
||||
// undo
|
||||
undo(program);
|
||||
assertEquals("Red", model.getValueAt(0, EnumTableModel.NAME_COL));
|
||||
|
||||
//redo
|
||||
redo(program);
|
||||
assertEquals("Pink", model.getValueAt(0, EnumTableModel.NAME_COL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangesBeforeUndoYes() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
int origRowCount = model.getRowCount();
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
applyChanges(true);
|
||||
// make more changes
|
||||
runSwing(() -> {
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
undo(false);
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
// yes to reload the enum data type
|
||||
JButton button = findButtonByText(d.getComponent(), "Yes");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
assertEquals(origRowCount, model.getRowCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangesBeforeUndoNo() throws Exception {
|
||||
|
||||
editSampleEnum();
|
||||
|
||||
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
|
||||
JTable table = panel.getTable();
|
||||
EnumTableModel model = (EnumTableModel) table.getModel();
|
||||
|
||||
runSwing(() -> {
|
||||
int lastRow = model.getRowCount() - 1;
|
||||
if (lastRow >= 0) {
|
||||
table.addRowSelectionInterval(lastRow, lastRow);
|
||||
}
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
applyChanges(true);
|
||||
// make more changes
|
||||
runSwing(() -> {
|
||||
int lastRow = model.getRowCount() - 1;
|
||||
if (lastRow >= 0) {
|
||||
table.addRowSelectionInterval(lastRow, lastRow);
|
||||
}
|
||||
DockingActionIf action = getAddAction();
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
action.actionPerformed(new DefaultActionContext());
|
||||
});
|
||||
waitForSwing();
|
||||
int rowCount = model.getRowCount();
|
||||
undo(false);
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
assertNotNull(d);
|
||||
// not to not reload the enum data type
|
||||
JButton button = findButtonByText(d.getComponent(), "No");
|
||||
assertNotNull(button);
|
||||
runSwing(() -> button.getActionListeners()[0].actionPerformed(null));
|
||||
waitForSwing();
|
||||
assertEquals(rowCount, model.getRowCount());
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
@ -936,23 +832,4 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
return enumDt;
|
||||
}
|
||||
|
||||
private void undo(boolean doWait) throws Exception {
|
||||
Runnable r = () -> {
|
||||
try {
|
||||
program.undo();
|
||||
program.flushEvents();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
};
|
||||
if (doWait) {
|
||||
runSwing(r);
|
||||
}
|
||||
else {
|
||||
runSwingLater(r);
|
||||
}
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* ###
|
||||
* 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.editor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
|
||||
public class FileArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
|
||||
|
||||
private File tempGdt;
|
||||
private Archive fileArchive;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
tempGdt = createTempFileForTest(".gdt");
|
||||
tempGdt.delete();
|
||||
|
||||
fileArchive = plugin.getDataTypeManagerHandler().createArchive(tempGdt);
|
||||
|
||||
assertTrue(fileArchive.isModifiable());
|
||||
|
||||
dtm = fileArchive.getDataTypeManager();
|
||||
}
|
||||
|
||||
@After
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
if (fileArchive != null) {
|
||||
plugin.getDataTypeManagerHandler().closeArchive(fileArchive);
|
||||
tempGdt.delete();
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
void undo() throws IOException {
|
||||
FileDataTypeManager fileDtm = (FileDataTypeManager) fileArchive.getDataTypeManager();
|
||||
fileDtm.undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
void redo() throws IOException {
|
||||
FileDataTypeManager fileDtm = (FileDataTypeManager) fileArchive.getDataTypeManager();
|
||||
fileDtm.redo();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* ###
|
||||
* 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.editor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
public class ProgramEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
dtm = program.getDataTypeManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
void undo() throws IOException {
|
||||
program.undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
void redo() throws IOException {
|
||||
program.redo();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* ###
|
||||
* 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.editor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.program.database.DataTypeArchiveDB;
|
||||
|
||||
public class ProjectArchiveEnumEditorUndoRedoTest extends AbstractEnumEditorUndoRedoTest {
|
||||
|
||||
Archive projectArchive;
|
||||
DataTypeArchiveDB dataTypeArchiveDB;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
|
||||
|
||||
dataTypeArchiveDB = new DataTypeArchiveDB(rootFolder, "Test", tool);
|
||||
|
||||
projectArchive = plugin.getDataTypeManagerHandler().openArchive(dataTypeArchiveDB);
|
||||
|
||||
assertTrue(projectArchive.isModifiable());
|
||||
|
||||
dtm = dataTypeArchiveDB.getDataTypeManager();
|
||||
}
|
||||
|
||||
@After
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
if (projectArchive != null) {
|
||||
plugin.getDataTypeManagerHandler().closeArchive(projectArchive);
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
void undo() throws IOException {
|
||||
dataTypeArchiveDB.undo();
|
||||
}
|
||||
|
||||
@Override
|
||||
void redo() throws IOException {
|
||||
dataTypeArchiveDB.redo();
|
||||
}
|
||||
|
||||
}
|
|
@ -22,8 +22,6 @@ import javax.swing.JTextField;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.program.model.data.DataTypeManagerDomainObject;
|
||||
import ghidra.program.model.data.Pointer;
|
||||
|
||||
public class PositiveStackEditorProviderTest extends AbstractStackEditorTest {
|
||||
|
@ -113,39 +111,4 @@ public class PositiveStackEditorProviderTest extends AbstractStackEditorTest {
|
|||
assertEquals(0x4, stackModel.getParameterSize());
|
||||
}
|
||||
|
||||
// public void testIncreasePosReturnAddrOffset() throws Exception {
|
||||
// init(SIMPLE_STACK);
|
||||
// assertEquals(0x20, stackModel.getFrameSize());
|
||||
// assertEquals(0x0, stackModel.getReturnAddressOffset());
|
||||
// assertEquals(0x12, stackModel.getLocalSize());
|
||||
// assertEquals(-0x8, stackModel.getParameterOffset());
|
||||
// assertEquals(0x7, stackModel.getParameterSize());
|
||||
// }
|
||||
//
|
||||
// public void testDecreasePosReturnAddrOffset() throws Exception {
|
||||
// init(SIMPLE_STACK);
|
||||
// assertEquals(0x20, stackModel.getFrameSize());
|
||||
// assertEquals(0x0, stackModel.getReturnAddressOffset());
|
||||
// assertEquals(0x12, stackModel.getLocalSize());
|
||||
// assertEquals(-0x8, stackModel.getParameterOffset());
|
||||
// assertEquals(0x7, stackModel.getParameterSize());
|
||||
// }
|
||||
|
||||
protected class RestoreListener implements DomainObjectListener {
|
||||
/**
|
||||
* @see ghidra.framework.model.DomainObjectListener#domainObjectChanged(ghidra.framework.model.DomainObjectChangedEvent)
|
||||
*/
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent event) {
|
||||
if (event.contains(DomainObjectEvent.RESTORED)) {
|
||||
Object source = event.getSource();
|
||||
if (source instanceof DataTypeManagerDomainObject) {
|
||||
DataTypeManagerDomainObject restoredDomainObject =
|
||||
(DataTypeManagerDomainObject) source;
|
||||
provider.domainObjectRestored(restoredDomainObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1119,6 +1119,11 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
|
|||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
// don't care for now
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
// don't care for now
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomDataType extends StructureDataType {
|
||||
|
|
|
@ -910,5 +910,10 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
// don't care
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -476,13 +476,21 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter implemen
|
|||
@Override
|
||||
public void invalidate() {
|
||||
clearCache(false);
|
||||
super.invalidate();
|
||||
super.invalidate(); // fires RESTORED event
|
||||
}
|
||||
|
||||
protected void clearCache(boolean all) {
|
||||
options.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this domain object has been restored to a completely different state due
|
||||
* to a transaction undo/redo/rollback or a database merge operation.
|
||||
*/
|
||||
protected void domainObjectRestored() {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean canSave() {
|
||||
DomainFile df = getDomainFile();
|
||||
|
|
|
@ -89,12 +89,10 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
if (domainObj.changeSet != null) {
|
||||
domainObj.changeSet.endTransaction(!rollback);
|
||||
}
|
||||
domainObj.clearCache(false);
|
||||
}
|
||||
|
||||
domainObj.fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
if (notify) {
|
||||
notifyEndTransaction();
|
||||
domainObj.domainObjectRestored();
|
||||
if (notify) {
|
||||
notifyEndTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,13 +176,12 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
domainObj.changeSet.endTransaction(false);
|
||||
}
|
||||
}
|
||||
domainObj.clearCache(false);
|
||||
domainObj.domainObjectRestored();
|
||||
transaction.restoreToolStates(true);
|
||||
transaction = null;
|
||||
if (notify) {
|
||||
notifyEndTransaction();
|
||||
}
|
||||
domainObj.fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
transaction.restoreToolStates(true);
|
||||
transaction = null;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -272,8 +269,8 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
if (domainObj.changeSet != null) {
|
||||
domainObj.changeSet.redo();
|
||||
}
|
||||
domainObj.fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
undoList.addLast(t);
|
||||
domainObj.domainObjectRestored();
|
||||
t.restoreToolStates(false);
|
||||
if (notify) {
|
||||
notifyUndoRedo();
|
||||
|
@ -290,9 +287,8 @@ class DomainObjectTransactionManager extends AbstractTransactionManager {
|
|||
if (domainObj.changeSet != null) {
|
||||
domainObj.changeSet.undo();
|
||||
}
|
||||
domainObj.clearCache(false);
|
||||
domainObj.fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
redoList.addLast(t);
|
||||
domainObj.domainObjectRestored();
|
||||
t.restoreToolStates(true);
|
||||
if (notify) {
|
||||
notifyUndoRedo();
|
||||
|
|
|
@ -1169,7 +1169,7 @@ public class GhidraFileData {
|
|||
projectLocator.isTransient()));
|
||||
folderItem.setCheckout(checkout.getCheckoutId(), exclusive,
|
||||
checkout.getCheckoutVersion(), folderItem.getCurrentVersion());
|
||||
|
||||
|
||||
if (inUseDomainObj != null) {
|
||||
// Reset source file and change-sets for open database
|
||||
getContentHandler().resetDBSourceFile(folderItem, inUseDomainObj);
|
||||
|
@ -1185,11 +1185,11 @@ public class GhidraFileData {
|
|||
// Ignore - should result in Hijacked file
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // end of synchronized block
|
||||
|
||||
if (inUseDomainObj != null) {
|
||||
inUseDomainObj.invalidate();
|
||||
inUseDomainObj.domainObjectRestored();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
|
@ -1537,7 +1537,7 @@ public class GhidraFileData {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (inUseDomainObj != null) {
|
||||
// Reset source file and change-sets for open database
|
||||
contentHandler.resetDBSourceFile(folderItem, inUseDomainObj);
|
||||
|
@ -1550,7 +1550,7 @@ public class GhidraFileData {
|
|||
} // end of synchronized block
|
||||
|
||||
if (inUseDomainObj != null) {
|
||||
inUseDomainObj.invalidate();
|
||||
inUseDomainObj.domainObjectRestored();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
|
@ -1919,7 +1919,7 @@ public class GhidraFileData {
|
|||
inUseDomainObj = getAndLockInUseDomainObjectForMergeUpdate("merge");
|
||||
|
||||
ContentHandler<?> contentHandler = getContentHandler();
|
||||
|
||||
|
||||
if (!modifiedSinceCheckout()) {
|
||||
// Quick merge
|
||||
folderItem.updateCheckout(versionedFolderItem, true, monitor);
|
||||
|
@ -1999,13 +1999,13 @@ public class GhidraFileData {
|
|||
ClientUtil.getUserName());
|
||||
tmpItem = null;
|
||||
}
|
||||
|
||||
|
||||
Msg.info(this, "Updated checkout completed for " + name);
|
||||
|
||||
if (inUseDomainObj != null) {
|
||||
// Reset source file and change-sets for open database
|
||||
contentHandler.resetDBSourceFile(folderItem, inUseDomainObj);
|
||||
inUseDomainObj.invalidate();
|
||||
inUseDomainObj.domainObjectRestored();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package ghidra.program.database;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -22,7 +23,8 @@ import db.*;
|
|||
import ghidra.framework.Application;
|
||||
import ghidra.framework.data.DomainObjectAdapterDB;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.DataTypeArchive;
|
||||
|
@ -515,12 +517,6 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
clearCache(false);
|
||||
fireEvent(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangeable() {
|
||||
return changeable;
|
||||
|
@ -535,6 +531,27 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
|
|||
this.changeSet = changeSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(String comment, TaskMonitor monitor) throws IOException, CancelledException {
|
||||
try {
|
||||
super.save(comment, monitor);
|
||||
}
|
||||
finally {
|
||||
dataTypeManager.clearUndo();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToPackedFile(File outputFile, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
try {
|
||||
super.saveToPackedFile(outputFile, monitor);
|
||||
}
|
||||
finally {
|
||||
dataTypeManager.clearUndo();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
|
||||
|
@ -568,4 +585,10 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB implements DataType
|
|||
public void updateID() {
|
||||
dataTypeManager.updateID();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void domainObjectRestored() {
|
||||
super.domainObjectRestored();
|
||||
dataTypeManager.notifyRestored();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2495,4 +2495,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||
}
|
||||
((ProgramCompilerSpec) compilerSpec).registerProgramOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void domainObjectRestored() {
|
||||
super.domainObjectRestored();
|
||||
getDataTypeManager().notifyRestored();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
package ghidra.program.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
|
||||
|
@ -196,6 +195,11 @@ public class ProjectDataTypeManager extends StandAloneDataTypeManager
|
|||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initTransactionState() {
|
||||
// do nothing - rely on DataTypeArchiveDB
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction openTransaction(String description) throws IllegalStateException {
|
||||
return dataTypeArchive.openTransaction(description);
|
||||
|
@ -208,14 +212,75 @@ public class ProjectDataTypeManager extends StandAloneDataTypeManager
|
|||
}
|
||||
|
||||
@Override
|
||||
public void flushEvents() {
|
||||
dataTypeArchive.flushEvents();
|
||||
public void endTransaction(int transactionID, boolean commit) {
|
||||
dataTypeArchive.endTransaction(transactionID, commit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
try {
|
||||
dataTypeArchive.undo();
|
||||
}
|
||||
catch (IOException e) {
|
||||
dbError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
try {
|
||||
dataTypeArchive.redo();
|
||||
}
|
||||
catch (IOException e) {
|
||||
dbError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public void endTransaction(int transactionID, boolean commit) {
|
||||
dataTypeArchive.endTransaction(transactionID, commit);
|
||||
public void clearUndo() {
|
||||
dataTypeArchive.clearUndo();
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public boolean canRedo() {
|
||||
return dataTypeArchive.canRedo();
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public boolean canUndo() {
|
||||
return dataTypeArchive.canUndo();
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public String getRedoName() {
|
||||
return dataTypeArchive.getRedoName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public String getUndoName() {
|
||||
return dataTypeArchive.getUndoName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public List<String> getAllUndoNames() {
|
||||
return dataTypeArchive.getAllUndoNames();
|
||||
}
|
||||
|
||||
@SuppressWarnings("sync-override")
|
||||
@Override
|
||||
public List<String> getAllRedoNames() {
|
||||
return dataTypeArchive.getAllRedoNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushEvents() {
|
||||
dataTypeArchive.flushEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -230,7 +230,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
|||
try {
|
||||
dbHandle = new DBHandle();
|
||||
readOnlyMode = false;
|
||||
int id = startTransaction("");
|
||||
long txId = dbHandle.startTransaction();
|
||||
try {
|
||||
init(OpenMode.CREATE, TaskMonitor.DUMMY);
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
|||
throw new AssertException(e); // unexpected
|
||||
}
|
||||
finally {
|
||||
endTransaction(id, true);
|
||||
dbHandle.endTransaction(txId, true);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -319,7 +319,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
|||
|
||||
private void initPackedDatabase(ResourceFile packedDBfile, OpenMode openMode,
|
||||
TaskMonitor monitor) throws CancelledException, IOException {
|
||||
try (Transaction tx = openTransaction("")) {
|
||||
long txId = dbHandle.startTransaction();
|
||||
try {
|
||||
init(openMode, monitor);
|
||||
|
||||
if (openMode != OpenMode.CREATE && hasDataOrganizationChange(true)) {
|
||||
|
@ -343,6 +344,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
|||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
dbHandle.endTransaction(txId, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -951,6 +955,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
|||
return dbHandle.isTransactionActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be invoked following an undo/redo or a transaction rollback situation.
|
||||
* This will notify {@link DataTypeManagerChangeListenerHandler} and its listeners that this
|
||||
* manager has just been restored (e.g., undo/redo/rollback).
|
||||
*/
|
||||
public void notifyRestored() {
|
||||
defaultListener.restored(this);
|
||||
}
|
||||
|
||||
abstract protected String getDomainFileID();
|
||||
|
||||
abstract protected String getPath();
|
||||
|
|
|
@ -99,6 +99,16 @@ public final class BuiltInDataTypeManager extends StandAloneDataTypeManager {
|
|||
super.endTransaction(transactionID, commit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean canUndo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean canRedo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category createCategory(CategoryPath path) {
|
||||
if (path != CategoryPath.ROOT) {
|
||||
|
|
|
@ -27,6 +27,8 @@ import ghidra.util.InvalidNameException;
|
|||
import ghidra.util.UniversalID;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utility.function.ExceptionalCallback;
|
||||
import utility.function.ExceptionalSupplier;
|
||||
|
||||
/**
|
||||
* Interface for Managing data types.
|
||||
|
@ -384,6 +386,72 @@ public interface DataTypeManager {
|
|||
*/
|
||||
public void endTransaction(int transactionID, boolean commit);
|
||||
|
||||
/**
|
||||
* Performs the given callback inside of a transaction. Use this method in place of the more
|
||||
* verbose try/catch/finally semantics.
|
||||
* <p>
|
||||
* <pre>
|
||||
* program.withTransaction("My Description", () -> {
|
||||
* // ... Do something
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Note: the transaction created by this method will always be committed when the call is
|
||||
* finished. If you need the ability to abort transactions, then you need to use the other
|
||||
* methods on this interface.
|
||||
*
|
||||
* @param description brief description of transaction
|
||||
* @param callback the callback that will be called inside of a transaction
|
||||
* @throws E any exception that may be thrown in the given callback
|
||||
*/
|
||||
public default <E extends Exception> void withTransaction(String description,
|
||||
ExceptionalCallback<E> callback) throws E {
|
||||
int id = startTransaction(description);
|
||||
try {
|
||||
callback.call();
|
||||
}
|
||||
finally {
|
||||
endTransaction(id, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the given supplier inside of a transaction. Use this method in place of the more
|
||||
* verbose try/catch/finally semantics.
|
||||
* <p>
|
||||
* <pre>
|
||||
* program.withTransaction("My Description", () -> {
|
||||
* // ... Do something
|
||||
* return result;
|
||||
* });
|
||||
* </pre>
|
||||
* <p>
|
||||
* If you do not need to supply a result, then use
|
||||
* {@link #withTransaction(String, ExceptionalCallback)} instead.
|
||||
*
|
||||
* @param <E> the exception that may be thrown from this method
|
||||
* @param <T> the type of result returned by the supplier
|
||||
* @param description brief description of transaction
|
||||
* @param supplier the supplier that will be called inside of a transaction
|
||||
* @return the result returned by the supplier
|
||||
* @throws E any exception that may be thrown in the given callback
|
||||
*/
|
||||
public default <E extends Exception, T> T withTransaction(String description,
|
||||
ExceptionalSupplier<T, E> supplier) throws E {
|
||||
T t = null;
|
||||
boolean success = false;
|
||||
int id = startTransaction(description);
|
||||
try {
|
||||
t = supplier.get();
|
||||
success = true;
|
||||
}
|
||||
finally {
|
||||
endTransaction(id, success);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force all pending notification events to be flushed
|
||||
* @throws IllegalStateException if the client is holding this object's lock
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import ghidra.framework.model.DomainObjectEvent;
|
||||
|
||||
/**
|
||||
* The listener interface for notification of changes to a DataTypeManager
|
||||
*/
|
||||
|
@ -22,6 +24,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when category is added.
|
||||
*
|
||||
* @param dtm the dataType manager
|
||||
* @param path the categoryPath of the newly added category.
|
||||
*/
|
||||
|
@ -29,6 +32,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when a category is removed.
|
||||
*
|
||||
* @param dtm data type manager associated with the category
|
||||
* @param path the categoryPath of the category that was removed.
|
||||
*/
|
||||
|
@ -36,6 +40,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when category is renamed.
|
||||
*
|
||||
* @param dtm data type manager associated with the category
|
||||
* @param oldPath the path of the category before it was renamed.
|
||||
* @param newPath the path of the category after it was renamed. This path will only differ in
|
||||
|
@ -44,7 +49,8 @@ public interface DataTypeManagerChangeListener {
|
|||
public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath);
|
||||
|
||||
/**
|
||||
* Notification when a category is reparented to new category.
|
||||
* Notification when a category is reparented to new category.
|
||||
*
|
||||
* @param dtm data type manager associated with the category
|
||||
* @param oldPath the path of the category before it was moved.
|
||||
* @param newPath the path of the category after it was moved.
|
||||
|
@ -53,6 +59,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when a data type is added to a category
|
||||
*
|
||||
* @param dtm data type manager for the given category paths.
|
||||
* @param path the DataTypePath of the newly added datatype.
|
||||
*/
|
||||
|
@ -60,6 +67,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when data type is removed.
|
||||
*
|
||||
* @param dtm data type manager for the given category paths.
|
||||
* @param path the DataTypePath of the removed datatype.
|
||||
*/
|
||||
|
@ -67,6 +75,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when data type is renamed.
|
||||
*
|
||||
* @param dtm data type manager for the given category paths.
|
||||
* @param oldPath the path of the datatype before it was renamed.
|
||||
* @param newPath the path of the datatype after it was renamed.
|
||||
|
@ -75,6 +84,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when a data type is moved.
|
||||
*
|
||||
* @param dtm data type manager for the given category paths.
|
||||
* @param oldPath the path of the datatype before it was moved.
|
||||
* @param newPath the path of the datatype after it was moved.
|
||||
|
@ -83,6 +93,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when data type is changed.
|
||||
*
|
||||
* @param dtm data type manager for the given category paths.
|
||||
* @param path the path of the datatype that changed.
|
||||
*/
|
||||
|
@ -90,6 +101,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification when a data type has been replaced.
|
||||
*
|
||||
* @param dtm data type manager for the given category paths.
|
||||
* @param oldPath the path of the datatype that was replaced.
|
||||
* @param newPath the path of the datatype that replaced the existing datatype.
|
||||
|
@ -100,6 +112,7 @@ public interface DataTypeManagerChangeListener {
|
|||
|
||||
/**
|
||||
* Notification the favorite status of a datatype has changed
|
||||
*
|
||||
* @param dtm data type manager for the given category paths.
|
||||
* @param path the DataTypePath of the datatype had its favorite status changed.
|
||||
* @param isFavorite reflects the current favorite status of the datatype.
|
||||
|
@ -109,6 +122,7 @@ public interface DataTypeManagerChangeListener {
|
|||
/**
|
||||
* Notification that the information for a particular source archive has changed. Typically,
|
||||
* this would be because it was renamed or moved.
|
||||
*
|
||||
* @param dataTypeManager data type manager referring to the given source information.
|
||||
* @param sourceArchive the changed data type source information
|
||||
*/
|
||||
|
@ -118,6 +132,7 @@ public interface DataTypeManagerChangeListener {
|
|||
/**
|
||||
* Notification that the information for a source archive has been added. This happens when
|
||||
* a data type from the indicated source archive is added to this data type manager.
|
||||
*
|
||||
* @param dataTypeManager data type manager referring to the given source information.
|
||||
* @param sourceArchive the new data type source information
|
||||
*/
|
||||
|
@ -127,7 +142,17 @@ public interface DataTypeManagerChangeListener {
|
|||
/**
|
||||
* Notification that the program architecture associated with the specified
|
||||
* dataTypeManager has changed.
|
||||
*
|
||||
* @param dataTypeManager data type manager referring to the given source information.
|
||||
*/
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager);
|
||||
|
||||
/**
|
||||
* Notification that the specified datatype manager has been restored to a
|
||||
* previous state. NOTE: this notification may duplicate the {@link DomainObjectEvent#RESTORED}
|
||||
* employed by {@link DataTypeManagerDomainObject} cases.
|
||||
*
|
||||
* @param dataTypeManager data type manager that has been restored
|
||||
*/
|
||||
public void restored(DataTypeManager dataTypeManager);
|
||||
}
|
||||
|
|
|
@ -70,11 +70,15 @@ public class DataTypeManagerChangeListenerAdapter implements DataTypeManagerChan
|
|||
}
|
||||
|
||||
@Override
|
||||
public void sourceArchiveChanged(DataTypeManager dataTypeManager, SourceArchive dataTypeSource) {
|
||||
public void sourceArchiveChanged(DataTypeManager dataTypeManager,
|
||||
SourceArchive dataTypeSource) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dataTypeManager) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.categoryAdded(dtm, path);
|
||||
}
|
||||
|
@ -65,12 +65,11 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
}
|
||||
|
||||
@Override
|
||||
public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath,
|
||||
CategoryPath newPath) {
|
||||
public void categoryMoved(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) {
|
||||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.categoryMoved(dtm, oldPath, newPath);
|
||||
}
|
||||
|
@ -82,7 +81,7 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.categoryRemoved(dtm, path);
|
||||
}
|
||||
|
@ -90,13 +89,12 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
}
|
||||
|
||||
@Override
|
||||
public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath,
|
||||
CategoryPath newPath) {
|
||||
public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) {
|
||||
|
||||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.categoryRenamed(dtm, oldPath, newPath);
|
||||
}
|
||||
|
@ -109,7 +107,7 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.dataTypeAdded(dtm, path);
|
||||
}
|
||||
|
@ -122,7 +120,7 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.dataTypeChanged(dtm, path);
|
||||
}
|
||||
|
@ -130,13 +128,12 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath,
|
||||
DataTypePath newPath) {
|
||||
public void dataTypeMoved(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
|
||||
|
||||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.dataTypeMoved(dtm, oldPath, newPath);
|
||||
}
|
||||
|
@ -149,7 +146,7 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.dataTypeRemoved(dtm, path);
|
||||
}
|
||||
|
@ -157,13 +154,12 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath,
|
||||
DataTypePath newPath) {
|
||||
public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
|
||||
|
||||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.dataTypeRenamed(dtm, oldPath, newPath);
|
||||
listener.favoritesChanged(dtm, oldPath, false);
|
||||
|
@ -176,23 +172,20 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
listenerList = WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
}
|
||||
|
||||
private void invokeRunnable(Runnable r) {
|
||||
// if (SwingUtilities.isEventDispatchThread()) {
|
||||
// r.run();
|
||||
// }
|
||||
// else {
|
||||
private void invokeLater(Runnable r) {
|
||||
// Since this method may be invoked from within a synchronized block it is important that
|
||||
// the runnable be executed later in a non-blocking fashion.
|
||||
SwingUtilities.invokeLater(r);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath,
|
||||
DataTypePath newPath, DataType newDataType) {
|
||||
public void dataTypeReplaced(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath,
|
||||
DataType newDataType) {
|
||||
|
||||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.dataTypeReplaced(dtm, oldPath, newPath, newDataType);
|
||||
}
|
||||
|
@ -204,7 +197,7 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.favoritesChanged(dtm, path, isFavorite);
|
||||
}
|
||||
|
@ -218,7 +211,7 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.sourceArchiveChanged(dataTypeManager, dataTypeSource);
|
||||
}
|
||||
|
@ -226,27 +219,39 @@ public class DataTypeManagerChangeListenerHandler implements DataTypeManagerChan
|
|||
}
|
||||
|
||||
@Override
|
||||
public void sourceArchiveAdded(DataTypeManager dataTypeManager,
|
||||
SourceArchive dataTypeSource) {
|
||||
public void sourceArchiveAdded(DataTypeManager dataTypeManager, SourceArchive dataTypeSource) {
|
||||
|
||||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.sourceArchiveAdded(dataTypeManager, dataTypeSource);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
|
||||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeRunnable(() -> {
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.programArchitectureChanged(dataTypeManager);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restored(DataTypeManager dtm) {
|
||||
if (listenerList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
invokeLater(() -> {
|
||||
for (DataTypeManagerChangeListener listener : listenerList) {
|
||||
listener.restored(dtm);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,8 +155,8 @@ public class FileDataTypeManager extends StandAloneDataTypeManager
|
|||
* match another existing archive database.
|
||||
* @param saveFile the file to save
|
||||
* @param newUniversalId the new id to use
|
||||
* @throws DuplicateFileException
|
||||
* @throws IOException
|
||||
* @throws DuplicateFileException if save file already exists
|
||||
* @throws IOException if IO error occurs
|
||||
*/
|
||||
public void saveAs(File saveFile, UniversalID newUniversalId)
|
||||
throws DuplicateFileException, IOException {
|
||||
|
@ -173,11 +173,16 @@ public class FileDataTypeManager extends StandAloneDataTypeManager
|
|||
catch (CancelledException e) {
|
||||
// Cancel can't happen because we are using a dummy monitor
|
||||
}
|
||||
finally {
|
||||
clearUndo();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the data type manager to the given file
|
||||
* @param saveFile the file to save
|
||||
* @throws DuplicateFileException if save file already exists
|
||||
* @throws IOException if IO error occurs
|
||||
*/
|
||||
public void saveAs(File saveFile) throws DuplicateFileException, IOException {
|
||||
ResourceFile resourceSaveFile = new ResourceFile(saveFile);
|
||||
|
@ -191,10 +196,14 @@ public class FileDataTypeManager extends StandAloneDataTypeManager
|
|||
catch (CancelledException e) {
|
||||
// Cancel can't happen because we are using a dummy monitor
|
||||
}
|
||||
finally {
|
||||
clearUndo();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the category to source file.
|
||||
* @throws IOException if IO error occurs
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
|
||||
|
@ -208,6 +217,9 @@ public class FileDataTypeManager extends StandAloneDataTypeManager
|
|||
catch (CancelledException e) {
|
||||
// Cancel can't happen because we are using a dummy monitor
|
||||
}
|
||||
finally {
|
||||
clearUndo();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,14 +17,14 @@ package ghidra.program.model.data;
|
|||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import db.*;
|
||||
import db.buffers.BufferMgr;
|
||||
import db.util.ErrorHandler;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
|
@ -53,9 +53,16 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
private static final String LANGUAGE_ID = "Language ID";
|
||||
private static final String COMPILER_SPEC_ID = "Compiler Spec ID";
|
||||
|
||||
private static final int NUM_UNDOS = 50;
|
||||
|
||||
private LinkedList<String> undoList = new LinkedList<>();
|
||||
private LinkedList<String> redoList = new LinkedList<>();
|
||||
|
||||
private int transactionCount;
|
||||
private Long transaction;
|
||||
private boolean commitTransaction;
|
||||
private String transactionName;
|
||||
|
||||
private boolean isImmutable;
|
||||
|
||||
private LanguageTranslator languageUpgradeTranslator;
|
||||
|
@ -147,6 +154,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
public StandAloneDataTypeManager(String rootName) throws RuntimeIOException {
|
||||
super(DataOrganizationImpl.getDefaultOrganization());
|
||||
this.name = rootName;
|
||||
initTransactionState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,6 +168,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
throws RuntimeIOException {
|
||||
super(dataOrganzation);
|
||||
this.name = rootName;
|
||||
initTransactionState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,11 +190,12 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
protected StandAloneDataTypeManager(ResourceFile packedDbfile, OpenMode openMode,
|
||||
TaskMonitor monitor) throws IOException, CancelledException {
|
||||
super(packedDbfile, openMode, monitor);
|
||||
initTransactionState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a data-type manager using a specified DBHandle.
|
||||
* <p>
|
||||
* <br>
|
||||
* <B>NOTE:</B> {@link #logWarning()} should be invoked immediately after
|
||||
* instantiating a {@link StandAloneDataTypeManager} for an existing database after
|
||||
* {@link #getName()} and {@link #getPath()} can be invoked safely. In addition, it
|
||||
|
@ -207,6 +217,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
if (openMode != OpenMode.CREATE && hasDataOrganizationChange(true)) {
|
||||
handleDataOrganizationChange(openMode, monitor);
|
||||
}
|
||||
initTransactionState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -824,6 +835,10 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
defaultListener.categoryRenamed(this, CategoryPath.ROOT, CategoryPath.ROOT);
|
||||
}
|
||||
|
||||
protected void initTransactionState() {
|
||||
dbHandle.setMaxUndos(NUM_UNDOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction openTransaction(String description) throws IllegalStateException {
|
||||
return new Transaction() {
|
||||
|
@ -850,6 +865,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
}
|
||||
if (transaction == null) {
|
||||
transaction = dbHandle.startTransaction();
|
||||
transactionName = description;
|
||||
commitTransaction = true;
|
||||
}
|
||||
transactionCount++;
|
||||
|
@ -857,30 +873,156 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
}
|
||||
|
||||
@Override
|
||||
public void flushEvents() {
|
||||
// do nothing
|
||||
public void endTransaction(int transactionID, boolean commit) {
|
||||
boolean restored = false;
|
||||
synchronized (this) {
|
||||
if (transaction == null) {
|
||||
throw new IllegalStateException("No Transaction Open");
|
||||
}
|
||||
if (transaction.intValue() != transactionID) {
|
||||
throw new IllegalArgumentException(
|
||||
"Transaction id does not match current transaction");
|
||||
}
|
||||
if (!commit) {
|
||||
commitTransaction = false;
|
||||
}
|
||||
if (--transactionCount == 0) {
|
||||
try {
|
||||
if (dbHandle.endTransaction(transaction.longValue(), commitTransaction)) {
|
||||
redoList.clear();
|
||||
undoList.addLast(transactionName);
|
||||
if (undoList.size() > NUM_UNDOS) {
|
||||
undoList.removeFirst();
|
||||
}
|
||||
}
|
||||
else if (!commitTransaction) {
|
||||
restored = true;
|
||||
}
|
||||
transaction = null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
dbError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (restored) {
|
||||
notifyRestored();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void endTransaction(int transactionID, boolean commit) {
|
||||
if (transaction == null) {
|
||||
throw new IllegalStateException("No Transaction Open");
|
||||
}
|
||||
if (transaction.intValue() != transactionID) {
|
||||
throw new IllegalArgumentException("Transaction id does not match current transaction");
|
||||
}
|
||||
if (!commit) {
|
||||
commitTransaction = false;
|
||||
}
|
||||
if (--transactionCount == 0) {
|
||||
public void undo() {
|
||||
synchronized (this) {
|
||||
if (!canUndo()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dbHandle.endTransaction(transaction.longValue(), commitTransaction);
|
||||
transaction = null;
|
||||
dbHandle.undo();
|
||||
redoList.addLast(undoList.removeLast());
|
||||
}
|
||||
catch (IOException e) {
|
||||
dbError(e);
|
||||
}
|
||||
}
|
||||
invalidateCache();
|
||||
notifyRestored();
|
||||
}
|
||||
|
||||
public void redo() {
|
||||
synchronized (this) {
|
||||
if (!canRedo()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dbHandle.redo();
|
||||
undoList.addLast(redoList.removeLast());
|
||||
}
|
||||
catch (IOException e) {
|
||||
dbError(e);
|
||||
}
|
||||
}
|
||||
invalidateCache();
|
||||
notifyRestored();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear undo/redo stack.
|
||||
* <br>
|
||||
* NOTE: It is important that this always be invoked following any save operation that
|
||||
* compacts the checkpoints within the database {@link BufferMgr}.
|
||||
*/
|
||||
protected synchronized void clearUndo() {
|
||||
undoList.clear();
|
||||
redoList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if there is a transaction previously undone (see {@link #undo()}) that can be
|
||||
* redone (see {@link #redo()}).
|
||||
*
|
||||
* @return true if there is a transaction previously undone that can be redone, else false
|
||||
*/
|
||||
public synchronized boolean canRedo() {
|
||||
return transaction == null && !redoList.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if there is a previous transaction that can be reverted/undone (see {@link #undo()}).
|
||||
*
|
||||
* @return true if there is a previous transaction that can be reverted/undone, else false.
|
||||
*/
|
||||
public synchronized boolean canUndo() {
|
||||
return transaction == null && !undoList.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transaction name that is available for {@link #redo()} (see {@link #canRedo()}).
|
||||
* @return transaction name that is available for {@link #redo()} or empty String.
|
||||
*/
|
||||
public synchronized String getRedoName() {
|
||||
if (canRedo()) {
|
||||
return redoList.getLast();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transaction name that is available for {@link #undo()} (see {@link #canUndo()}).
|
||||
* @return transaction name that is available for {@link #undo()} or empty String.
|
||||
*/
|
||||
public synchronized String getUndoName() {
|
||||
if (canUndo()) {
|
||||
return undoList.getLast();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transaction names that are available within the {@link #undo()} stack.
|
||||
*
|
||||
* @return all transaction names that are available within the {@link #undo()} stack.
|
||||
*/
|
||||
public synchronized List<String> getAllUndoNames() {
|
||||
if (canUndo()) {
|
||||
return new ArrayList<>(undoList);
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transaction names that are available within the {@link #redo()} stack.
|
||||
*
|
||||
* @return all transaction names that are available within the {@link #redo()} stack.
|
||||
*/
|
||||
public synchronized List<String> getAllRedoNames() {
|
||||
if (canRedo()) {
|
||||
return new ArrayList<>(redoList);
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushEvents() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -894,7 +1036,8 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
|||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
public synchronized void close() {
|
||||
clearUndo();
|
||||
if (!dbHandle.isClosed()) {
|
||||
dbHandle.close();
|
||||
}
|
||||
|
|
|
@ -43,293 +43,70 @@ import ghidra.util.Msg;
|
|||
public class CreateAVR8GDTArchiveScript extends GhidraScript {
|
||||
|
||||
private File outputDirectory;
|
||||
|
||||
|
||||
private static String headerFilePath = "/data/HeaderFiles";
|
||||
|
||||
private static String filenames[] = {
|
||||
"stdint.h",
|
||||
"avr/io.h",
|
||||
};
|
||||
|
||||
private static String orig_args[] = {
|
||||
"-I"+headerFilePath+"/avr/include",
|
||||
"-I"+headerFilePath+"/avr/include/avr",
|
||||
"-D__STDC",
|
||||
"-D_GNU_SOURCE",
|
||||
"-D__GLIBC_HAVE_LONG_LONG=1",
|
||||
"-D__DOXYGEN__=true", // header files have special __attributes__ if not defined
|
||||
};
|
||||
|
||||
private static String processorVariants[] = {
|
||||
"AT94K",
|
||||
"AT43USB320",
|
||||
"AT43USB355",
|
||||
"AT76C711",
|
||||
"AT86RF401",
|
||||
"AT90PWM1",
|
||||
"AT90PWM2",
|
||||
"AT90PWM2B",
|
||||
"AT90PWM3",
|
||||
"AT90PWM3B",
|
||||
"AT90PWM216",
|
||||
"AT90PWM316",
|
||||
"AT90PWM161",
|
||||
"AT90PWM81",
|
||||
"ATmega8U2",
|
||||
"ATmega16M1",
|
||||
"ATmega16U2",
|
||||
"ATmega16U4",
|
||||
"ATmega32C1",
|
||||
"ATmega32M1",
|
||||
"ATmega32U2",
|
||||
"ATmega32U4",
|
||||
"ATmega32U6",
|
||||
"ATmega64C1",
|
||||
"ATmega64M1",
|
||||
"ATmega128",
|
||||
"ATmega128A",
|
||||
"ATmega1280",
|
||||
"ATmega1281",
|
||||
"ATmega1284",
|
||||
"ATmega1284P",
|
||||
"ATmega128RFA1",
|
||||
"ATmega1284RFR2",
|
||||
"ATmega128RFR2",
|
||||
"ATmega2564RFR2",
|
||||
"ATmega256RFR2",
|
||||
"ATmega2560",
|
||||
"ATmega2561",
|
||||
"AT90CAN32",
|
||||
"AT90CAN64",
|
||||
"AT90CAN128",
|
||||
"AT90USB82",
|
||||
"AT90USB162",
|
||||
"AT90USB646",
|
||||
"AT90USB647",
|
||||
"AT90USB1286",
|
||||
"AT90USB1287",
|
||||
"ATmega644RFR2",
|
||||
"ATmega64RFR2",
|
||||
"ATmega64",
|
||||
"ATmega64A",
|
||||
"ATmega640",
|
||||
"ATmega644",
|
||||
"ATmega644A",
|
||||
"ATmega644P",
|
||||
"ATmega644PA",
|
||||
"ATmega645",
|
||||
"ATmega645A",
|
||||
"ATmega645P",
|
||||
"ATmega6450",
|
||||
"ATmega6450A",
|
||||
"ATmega6450P",
|
||||
"ATmega649",
|
||||
"ATmega649A",
|
||||
"ATmega6490",
|
||||
"ATmega6490A",
|
||||
"ATmega6490P",
|
||||
"ATmega649P",
|
||||
"ATmega64HVE",
|
||||
"ATmega64HVE2",
|
||||
"ATmega103",
|
||||
"ATmega32",
|
||||
"ATmega32A",
|
||||
"ATmega323",
|
||||
"ATmega324P",
|
||||
"ATmega324A",
|
||||
"ATmega324PA",
|
||||
"ATmega325",
|
||||
"ATmega325A",
|
||||
"ATmega325P",
|
||||
"ATmega325PA",
|
||||
"ATmega3250",
|
||||
"ATmega3250A",
|
||||
"ATmega3250P",
|
||||
"ATmega3250PA",
|
||||
"ATmega328P",
|
||||
"ATmega328",
|
||||
"ATmega329",
|
||||
"ATmega329A",
|
||||
"ATmega329P",
|
||||
"ATmega329PA",
|
||||
"ATmega3290PA",
|
||||
"ATmega3290",
|
||||
"ATmega3290A",
|
||||
"ATmega3290P",
|
||||
"ATmega32HVB",
|
||||
"ATmega32HVBREVB",
|
||||
"ATmega406",
|
||||
"ATmega16",
|
||||
"ATmega16A",
|
||||
"ATmega161",
|
||||
"ATmega162",
|
||||
"ATmega163",
|
||||
"ATmega164P",
|
||||
"ATmega164A",
|
||||
"ATmega164PA",
|
||||
"ATmega165",
|
||||
"ATmega165A",
|
||||
"ATmega165P",
|
||||
"ATmega165PA",
|
||||
"ATmega168",
|
||||
"ATmega168A",
|
||||
"ATmega168P",
|
||||
"ATmega168PA",
|
||||
"ATmega168PB",
|
||||
"ATmega169",
|
||||
"ATmega169A",
|
||||
"ATmega169P",
|
||||
"ATmega169PA",
|
||||
"ATmega8HVA",
|
||||
"ATmega16HVA",
|
||||
"ATmega16HVA2",
|
||||
"ATmega16HVB",
|
||||
"ATmega16HVBREVB",
|
||||
"ATmega8",
|
||||
"ATmega8A",
|
||||
"ATmega48",
|
||||
"ATmega48A",
|
||||
"ATmega48PA",
|
||||
"ATmega48PB",
|
||||
"ATmega48P",
|
||||
"ATmega88",
|
||||
"ATmega88A",
|
||||
"ATmega88P",
|
||||
"ATmega88PA",
|
||||
"ATmega88PB",
|
||||
"ATmega8515",
|
||||
"ATmega8535",
|
||||
"AT90S8535",
|
||||
"AT90C8534",
|
||||
"AT90S8515",
|
||||
"AT90S4434",
|
||||
"AT90S4433",
|
||||
"AT90S4414",
|
||||
"ATtiny22",
|
||||
"ATtiny26",
|
||||
"AT90S2343",
|
||||
"AT90S2333",
|
||||
"AT90S2323",
|
||||
"AT90S2313",
|
||||
"ATtiny4",
|
||||
"ATtiny5",
|
||||
"ATtiny9",
|
||||
"ATtiny10",
|
||||
"ATtiny20",
|
||||
"ATtiny40",
|
||||
"ATtiny2313",
|
||||
"ATtiny2313A",
|
||||
"ATtiny13",
|
||||
"ATtiny13A",
|
||||
"ATtiny25",
|
||||
"ATtiny4313",
|
||||
"ATtiny45",
|
||||
"ATtiny85",
|
||||
"ATtiny24",
|
||||
"ATtiny24A",
|
||||
"ATtiny44",
|
||||
"ATtiny44A",
|
||||
"ATtiny441",
|
||||
"ATtiny84",
|
||||
"ATtiny84A",
|
||||
"ATtiny841",
|
||||
"ATtiny261",
|
||||
"ATtiny261A",
|
||||
"ATtiny461",
|
||||
"ATtiny461A",
|
||||
"ATtiny861",
|
||||
"ATtiny861A",
|
||||
"ATtiny43U",
|
||||
"ATtiny48",
|
||||
"ATtiny88",
|
||||
"ATtiny828",
|
||||
"ATtiny87",
|
||||
"ATtiny167",
|
||||
"ATtiny1634",
|
||||
"AT90SCR100",
|
||||
"ATxmega8E5",
|
||||
"ATxmega16A4",
|
||||
"ATxmega16A4U",
|
||||
"ATxmega16C4",
|
||||
"ATxmega16D4",
|
||||
"ATxmega16E5",
|
||||
"ATxmega32A4",
|
||||
"ATxmega32A4U",
|
||||
"ATxmega32C3",
|
||||
"ATxmega32C4",
|
||||
"ATxmega32D3",
|
||||
"ATxmega32D4",
|
||||
"ATxmega32E5",
|
||||
"ATxmega64A1",
|
||||
"ATxmega64A1U",
|
||||
"ATxmega64A3",
|
||||
"ATxmega64A3U",
|
||||
"ATxmega64A4U",
|
||||
"ATxmega64B1",
|
||||
"ATxmega64B3",
|
||||
"ATxmega64C3",
|
||||
"ATxmega64D3",
|
||||
"ATxmega64D4",
|
||||
"ATxmega128A1",
|
||||
"ATxmega128A1U",
|
||||
"ATxmega128A4U",
|
||||
"ATxmega128A3",
|
||||
"ATxmega128A3U",
|
||||
"ATxmega128B1",
|
||||
"ATxmega128B3",
|
||||
"ATxmega128C3",
|
||||
"ATxmega128D3",
|
||||
"ATxmega128D4",
|
||||
"ATxmega192A3",
|
||||
"ATxmega192A3U",
|
||||
"ATxmega192C3",
|
||||
"ATxmega192D3",
|
||||
"ATxmega256A3",
|
||||
"ATxmega256A3U",
|
||||
"ATxmega256A3B",
|
||||
"ATxmega256A3BU",
|
||||
"ATxmega256C3",
|
||||
"ATxmega256D3",
|
||||
"ATxmega384C3",
|
||||
"ATxmega384D3",
|
||||
"ATA5702M322",
|
||||
"ATA5782",
|
||||
"ATA5790",
|
||||
"ATA5790N",
|
||||
"ATA5791",
|
||||
"ATA5831",
|
||||
"ATA5272",
|
||||
"ATA5505",
|
||||
"ATA5795",
|
||||
"ATA6285",
|
||||
"ATA6286",
|
||||
"ATA6289",
|
||||
"ATA6612C",
|
||||
"ATA6613C",
|
||||
"ATA6614Q",
|
||||
"ATA6616C",
|
||||
"ATA6617C",
|
||||
"ATA664251",
|
||||
"ATA8210",
|
||||
"ATA8510",
|
||||
"ATtiny28",
|
||||
"AT90S1200",
|
||||
"ATtiny15",
|
||||
"ATtiny12",
|
||||
"ATtiny11",
|
||||
"M3000",
|
||||
};
|
||||
|
||||
private static String filenames[] = { "stdint.h", "avr/io.h", };
|
||||
|
||||
private static String orig_args[] =
|
||||
{ "-I" + headerFilePath + "/avr/include", "-I" + headerFilePath + "/avr/include/avr",
|
||||
"-D__STDC", "-D_GNU_SOURCE", "-D__GLIBC_HAVE_LONG_LONG=1", "-D__DOXYGEN__=true", // header files have special __attributes__ if not defined
|
||||
};
|
||||
|
||||
private static String processorVariants[] = { "AT94K", "AT43USB320", "AT43USB355", "AT76C711",
|
||||
"AT86RF401", "AT90PWM1", "AT90PWM2", "AT90PWM2B", "AT90PWM3", "AT90PWM3B", "AT90PWM216",
|
||||
"AT90PWM316", "AT90PWM161", "AT90PWM81", "ATmega8U2", "ATmega16M1", "ATmega16U2",
|
||||
"ATmega16U4", "ATmega32C1", "ATmega32M1", "ATmega32U2", "ATmega32U4", "ATmega32U6",
|
||||
"ATmega64C1", "ATmega64M1", "ATmega128", "ATmega128A", "ATmega1280", "ATmega1281",
|
||||
"ATmega1284", "ATmega1284P", "ATmega128RFA1", "ATmega1284RFR2", "ATmega128RFR2",
|
||||
"ATmega2564RFR2", "ATmega256RFR2", "ATmega2560", "ATmega2561", "AT90CAN32", "AT90CAN64",
|
||||
"AT90CAN128", "AT90USB82", "AT90USB162", "AT90USB646", "AT90USB647", "AT90USB1286",
|
||||
"AT90USB1287", "ATmega644RFR2", "ATmega64RFR2", "ATmega64", "ATmega64A", "ATmega640",
|
||||
"ATmega644", "ATmega644A", "ATmega644P", "ATmega644PA", "ATmega645", "ATmega645A",
|
||||
"ATmega645P", "ATmega6450", "ATmega6450A", "ATmega6450P", "ATmega649", "ATmega649A",
|
||||
"ATmega6490", "ATmega6490A", "ATmega6490P", "ATmega649P", "ATmega64HVE", "ATmega64HVE2",
|
||||
"ATmega103", "ATmega32", "ATmega32A", "ATmega323", "ATmega324P", "ATmega324A",
|
||||
"ATmega324PA", "ATmega325", "ATmega325A", "ATmega325P", "ATmega325PA", "ATmega3250",
|
||||
"ATmega3250A", "ATmega3250P", "ATmega3250PA", "ATmega328P", "ATmega328", "ATmega329",
|
||||
"ATmega329A", "ATmega329P", "ATmega329PA", "ATmega3290PA", "ATmega3290", "ATmega3290A",
|
||||
"ATmega3290P", "ATmega32HVB", "ATmega32HVBREVB", "ATmega406", "ATmega16", "ATmega16A",
|
||||
"ATmega161", "ATmega162", "ATmega163", "ATmega164P", "ATmega164A", "ATmega164PA",
|
||||
"ATmega165", "ATmega165A", "ATmega165P", "ATmega165PA", "ATmega168", "ATmega168A",
|
||||
"ATmega168P", "ATmega168PA", "ATmega168PB", "ATmega169", "ATmega169A", "ATmega169P",
|
||||
"ATmega169PA", "ATmega8HVA", "ATmega16HVA", "ATmega16HVA2", "ATmega16HVB",
|
||||
"ATmega16HVBREVB", "ATmega8", "ATmega8A", "ATmega48", "ATmega48A", "ATmega48PA",
|
||||
"ATmega48PB", "ATmega48P", "ATmega88", "ATmega88A", "ATmega88P", "ATmega88PA", "ATmega88PB",
|
||||
"ATmega8515", "ATmega8535", "AT90S8535", "AT90C8534", "AT90S8515", "AT90S4434", "AT90S4433",
|
||||
"AT90S4414", "ATtiny22", "ATtiny26", "AT90S2343", "AT90S2333", "AT90S2323", "AT90S2313",
|
||||
"ATtiny4", "ATtiny5", "ATtiny9", "ATtiny10", "ATtiny20", "ATtiny40", "ATtiny2313",
|
||||
"ATtiny2313A", "ATtiny13", "ATtiny13A", "ATtiny25", "ATtiny4313", "ATtiny45", "ATtiny85",
|
||||
"ATtiny24", "ATtiny24A", "ATtiny44", "ATtiny44A", "ATtiny441", "ATtiny84", "ATtiny84A",
|
||||
"ATtiny841", "ATtiny261", "ATtiny261A", "ATtiny461", "ATtiny461A", "ATtiny861",
|
||||
"ATtiny861A", "ATtiny43U", "ATtiny48", "ATtiny88", "ATtiny828", "ATtiny87", "ATtiny167",
|
||||
"ATtiny1634", "AT90SCR100", "ATxmega8E5", "ATxmega16A4", "ATxmega16A4U", "ATxmega16C4",
|
||||
"ATxmega16D4", "ATxmega16E5", "ATxmega32A4", "ATxmega32A4U", "ATxmega32C3", "ATxmega32C4",
|
||||
"ATxmega32D3", "ATxmega32D4", "ATxmega32E5", "ATxmega64A1", "ATxmega64A1U", "ATxmega64A3",
|
||||
"ATxmega64A3U", "ATxmega64A4U", "ATxmega64B1", "ATxmega64B3", "ATxmega64C3", "ATxmega64D3",
|
||||
"ATxmega64D4", "ATxmega128A1", "ATxmega128A1U", "ATxmega128A4U", "ATxmega128A3",
|
||||
"ATxmega128A3U", "ATxmega128B1", "ATxmega128B3", "ATxmega128C3", "ATxmega128D3",
|
||||
"ATxmega128D4", "ATxmega192A3", "ATxmega192A3U", "ATxmega192C3", "ATxmega192D3",
|
||||
"ATxmega256A3", "ATxmega256A3U", "ATxmega256A3B", "ATxmega256A3BU", "ATxmega256C3",
|
||||
"ATxmega256D3", "ATxmega384C3", "ATxmega384D3", "ATA5702M322", "ATA5782", "ATA5790",
|
||||
"ATA5790N", "ATA5791", "ATA5831", "ATA5272", "ATA5505", "ATA5795", "ATA6285", "ATA6286",
|
||||
"ATA6289", "ATA6612C", "ATA6613C", "ATA6614Q", "ATA6616C", "ATA6617C", "ATA664251",
|
||||
"ATA8210", "ATA8510", "ATtiny28", "AT90S1200", "ATtiny15", "ATtiny12", "ATtiny11",
|
||||
"M3000", };
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
outputDirectory = askDirectory("Select Directory for GDT files", "Select GDT Output Dir");
|
||||
|
||||
|
||||
parseGDT_AVR8();
|
||||
}
|
||||
|
||||
public void parseGDT_AVR8() throws Exception {
|
||||
|
||||
public void parseGDT_AVR8() throws Exception {
|
||||
// If need data types from other archives can add other archives
|
||||
|
||||
|
||||
// Using another archive while parsing will cause:
|
||||
// - a dependence on the other archive
|
||||
// - any missing data types while parsing are supplied if present from existingDTMgr
|
||||
|
@ -345,21 +122,21 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript {
|
|||
// by defaults, don't want to be dependent on other archives if have all necessary definitions
|
||||
// comment out if missing data types
|
||||
openTypes = null;
|
||||
|
||||
|
||||
String dataTypeFile = outputDirectory + File.separator + "avr8.gdt";
|
||||
|
||||
File f = getArchiveFile(dataTypeFile);
|
||||
|
||||
FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f);
|
||||
|
||||
// Parse each processor variant as an individual parse that gets added to the data
|
||||
// type manager. If all header files were parsed at once, there are conflicting
|
||||
// macro definitions that will cause the parse to fail.
|
||||
//
|
||||
for (String variantName : processorVariants) {
|
||||
parseProcessorDefs(variantName, dtMgr, openTypes);
|
||||
}
|
||||
|
||||
|
||||
FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f);
|
||||
|
||||
// Parse each processor variant as an individual parse that gets added to the data
|
||||
// type manager. If all header files were parsed at once, there are conflicting
|
||||
// macro definitions that will cause the parse to fail.
|
||||
//
|
||||
for (String variantName : processorVariants) {
|
||||
parseProcessorDefs(variantName, dtMgr, openTypes);
|
||||
}
|
||||
|
||||
dtMgr.save();
|
||||
dtMgr.close();
|
||||
}
|
||||
|
@ -394,15 +171,17 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript {
|
|||
* @throws ghidra.app.util.cparser.C.ParseException
|
||||
* @throws IOException io exception
|
||||
*/
|
||||
private void parseProcessorDefs(String procName, FileDataTypeManager dtMgr, DataTypeManager[] openTypes)
|
||||
private void parseProcessorDefs(String procName, FileDataTypeManager dtMgr,
|
||||
DataTypeManager[] openTypes)
|
||||
throws ParseException, ghidra.app.util.cparser.C.ParseException, IOException {
|
||||
|
||||
String args[] = Arrays.append(orig_args, "-D__AVR_"+procName+"__");
|
||||
|
||||
CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, "avr8:LE:16:atmega256", "gcc", monitor);
|
||||
|
||||
|
||||
String args[] = Arrays.append(orig_args, "-D__AVR_" + procName + "__");
|
||||
|
||||
CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr,
|
||||
"avr8:LE:16:atmega256", "gcc", monitor);
|
||||
|
||||
Msg.info(this, results.getFormattedParseMessage(null));
|
||||
|
||||
|
||||
storeExtraDefinitions(procName, dtMgr, openTypes, results.preProcessor());
|
||||
}
|
||||
|
||||
|
@ -413,48 +192,54 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript {
|
|||
* @param dtMgr add data types to dtMgr
|
||||
* @param cpp pre-processor holds macros/defines from parsing
|
||||
*/
|
||||
private void storeExtraDefinitions(String procName, FileDataTypeManager dtMgr, DataTypeManager[] openTypes, PreProcessor cpp) {
|
||||
private void storeExtraDefinitions(String procName, FileDataTypeManager dtMgr,
|
||||
DataTypeManager[] openTypes, PreProcessor cpp) {
|
||||
int transactionID = dtMgr.startTransaction("Add Extra Equates");
|
||||
|
||||
DefineTable definitions = cpp.getDefinitions();
|
||||
Iterator<String> defineNames = definitions.getDefineNames();
|
||||
while (defineNames.hasNext()) {
|
||||
String defName = defineNames.next();
|
||||
String rawDefValue = definitions.getValue(defName);
|
||||
String expandValue = definitions.expandDefine(defName);
|
||||
|
||||
if (expandValue == null || expandValue.length()==0) {
|
||||
// can't expand, must be a macro
|
||||
continue;
|
||||
try {
|
||||
DefineTable definitions = cpp.getDefinitions();
|
||||
Iterator<String> defineNames = definitions.getDefineNames();
|
||||
while (defineNames.hasNext()) {
|
||||
String defName = defineNames.next();
|
||||
String rawDefValue = definitions.getValue(defName);
|
||||
String expandValue = definitions.expandDefine(defName);
|
||||
|
||||
if (expandValue == null || expandValue.length() == 0) {
|
||||
// can't expand, must be a macro
|
||||
continue;
|
||||
}
|
||||
|
||||
// look at string and see if if the definition of an SFR, register
|
||||
String PTR_PREFIX_16 = "(*(volatile uint16_t *)";
|
||||
String PTR_PREFIX_8 = "(*(volatile uint8_t *)";
|
||||
|
||||
Long lvalue = null;
|
||||
if (expandValue.startsWith(PTR_PREFIX_16)) {
|
||||
// ptr to 16 bit address in SFR
|
||||
expandValue = expandValue.replace(PTR_PREFIX_16, "");
|
||||
expandValue = expandValue.substring(0, expandValue.lastIndexOf(')'));
|
||||
}
|
||||
else if (expandValue.startsWith(PTR_PREFIX_8)) {
|
||||
// ptr to 8 bit address in SFR
|
||||
expandValue = expandValue.replace(PTR_PREFIX_8, "");
|
||||
expandValue = expandValue.substring(0, expandValue.lastIndexOf(')'));
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (expandValue == null || expandValue.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lvalue = AddressEvaluator.evaluateToLong(expandValue);
|
||||
if (lvalue == null) {
|
||||
continue;
|
||||
}
|
||||
definitions.populateDefineEquate(openTypes, dtMgr, "memory", "", defName, lvalue);
|
||||
}
|
||||
|
||||
// look at string and see if if the definition of an SFR, register
|
||||
String PTR_PREFIX_16 = "(*(volatile uint16_t *)";
|
||||
String PTR_PREFIX_8 = "(*(volatile uint8_t *)";
|
||||
|
||||
Long lvalue = null;
|
||||
if (expandValue.startsWith(PTR_PREFIX_16)) {
|
||||
// ptr to 16 bit address in SFR
|
||||
expandValue = expandValue.replace(PTR_PREFIX_16, "");
|
||||
expandValue = expandValue.substring(0,expandValue.lastIndexOf(')'));
|
||||
} else if (expandValue.startsWith(PTR_PREFIX_8) ) {
|
||||
// ptr to 8 bit address in SFR
|
||||
expandValue = expandValue.replace(PTR_PREFIX_8, "");
|
||||
expandValue = expandValue.substring(0,expandValue.lastIndexOf(')'));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (expandValue == null || expandValue.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lvalue = AddressEvaluator.evaluateToLong(expandValue);
|
||||
if (lvalue == null) {
|
||||
continue;
|
||||
}
|
||||
definitions.populateDefineEquate(openTypes, dtMgr, "memory", "", defName, lvalue);
|
||||
}
|
||||
dtMgr.endTransaction(transactionID, true);
|
||||
finally {
|
||||
dtMgr.endTransaction(transactionID, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user