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