Merge remote-tracking branch 'origin/GP-4719_ghidra1_StandaloneDTMUndoRedo--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-07-01 14:46:43 -04:00
commit c1f8312c56
64 changed files with 1681 additions and 897 deletions

View File

@ -899,4 +899,10 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
super.close();
objectManager.waitWbWorkers();
}
@Override
protected void domainObjectRestored() {
super.domainObjectRestored();
dataTypeManager.notifyRestored();
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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);

View File

@ -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)) {

View File

@ -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) {

View File

@ -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

View File

@ -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());
}

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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());
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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();

View File

@ -220,5 +220,10 @@ public class DataTypeIndexer {
public void programArchitectureChanged(DataTypeManager dataTypeManager) {
markStale();
}
@Override
public void restored(DataTypeManager dataTypeManager) {
markStale();
}
}
}

View File

@ -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();

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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();
}
}
}

View File

@ -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());
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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());

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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 {

View File

@ -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
}
}
}

View File

@ -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();

View File

@ -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();

View File

@ -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 {

View File

@ -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();
}
}

View File

@ -2495,4 +2495,10 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
}
((ProgramCompilerSpec) compilerSpec).registerProgramOptions();
}
@Override
protected void domainObjectRestored() {
super.domainObjectRestored();
getDataTypeManager().notifyRestored();
}
}

View File

@ -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

View File

@ -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();

View File

@ -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) {

View File

@ -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

View File

@ -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);
}

View File

@ -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) {
}
}

View File

@ -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);
}
});
}
}

View File

@ -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();
}
}
/**

View File

@ -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();
}

View File

@ -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);
}
}
}