GP-5586 Refactored and fixed function stack frame editor

This commit is contained in:
ghidra1 2025-04-11 10:40:36 -04:00
parent 4a46edc9fe
commit 72a94daa1d
20 changed files with 1920 additions and 1949 deletions

View file

@ -248,17 +248,17 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
return true;
}
@Override
public DataTypeInstance validateComponentDataType(int rowIndex, String dtString)
throws UsrException {
dtString = DataTypeHelper.stripWhiteSpace(dtString);
if ((dtString == null) || (dtString.length() < 1)) {
if (rowIndex == getNumComponents()) {
return null;
}
}
return super.validateComponentDataType(rowIndex, dtString);
}
// @Override
// public DataTypeInstance validateComponentDataType(int rowIndex, String dtString)
// throws UsrException {
// dtString = DataTypeHelper.stripWhiteSpace(dtString);
// if ((dtString == null) || (dtString.length() < 1)) {
// if (rowIndex == getNumComponents()) {
// return null;
// }
// }
// return super.validateComponentDataType(rowIndex, dtString);
// }
@Override
public boolean isAddAllowed(DataType dataType) {
@ -894,18 +894,18 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
protected abstract void replaceOriginalComponents();
@Override
protected void checkIsAllowableDataType(DataType datatype) throws InvalidDataTypeException {
protected void checkIsAllowableDataType(DataType dataType) throws InvalidDataTypeException {
super.checkIsAllowableDataType(datatype);
super.checkIsAllowableDataType(dataType);
// Verify that we aren't adding this structure or anything that it is
// part of to this editable structure.
if (datatype.equals(viewComposite)) {
String msg = "Data type \"" + datatype.getDisplayName() + "\" can't contain itself.";
if (dataType.equals(viewComposite)) {
String msg = "Data type \"" + dataType.getDisplayName() + "\" can't contain itself.";
throw new InvalidDataTypeException(msg);
}
else if (DataTypeUtilities.isSecondPartOfFirst(datatype, viewComposite)) {
String msg = "Data type \"" + datatype.getDisplayName() + "\" has \"" +
else if (DataTypeUtilities.isSecondPartOfFirst(dataType, viewComposite)) {
String msg = "Data type \"" + dataType.getDisplayName() + "\" has \"" +
viewComposite.getDisplayName() + "\" within it.";
throw new InvalidDataTypeException(msg);
}
@ -1314,25 +1314,25 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
return;
}
DataType dataType = viewDTM.getDataType(path.getCategoryPath(), path.getDataTypeName());
DataType dataType = viewDTM.getDataType(path);
if (dataType == null) {
return;
}
if (!path.equals(originalDataTypePath)) {
DataType dt = viewDTM.getDataType(path);
if (dt != null) {
if (hasSubDt(viewComposite, path)) {
String msg = "Removed sub-component data type \"" + path;
setStatus(msg, true);
}
viewDTM.withTransaction("Removed Dependency", () -> {
viewDTM.clearUndoOnChange();
viewDTM.remove(dt, TaskMonitor.DUMMY);
});
fireTableDataChanged();
componentDataChanged();
if (!viewDTM.isViewDataTypeFromOriginalDTM(dataType)) {
return;
}
if (hasSubDt(viewComposite, path)) {
String msg = "Removed sub-component data type \"" + path;
setStatus(msg, true);
}
viewDTM.withTransaction("Removed Dependency", () -> {
viewDTM.clearUndoOnChange();
viewDTM.remove(dataType, TaskMonitor.DUMMY);
});
fireTableDataChanged();
componentDataChanged();
return;
}
@ -1366,56 +1366,7 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
@Override
public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
if (dtm != originalDTM) {
throw new AssertException("Listener only supports original DTM");
}
if (!isLoaded()) {
return;
}
if (oldPath.getDataTypeName().equals(newPath.getDataTypeName())) {
return;
}
String newName = newPath.getDataTypeName();
String oldName = oldPath.getDataTypeName();
// Does the old name match our original name.
// Check originalCompositeId to ensure original type is managed
if (originalCompositeId != DataTypeManager.NULL_DATATYPE_ID &&
oldPath.equals(originalDataTypePath)) {
originalDataTypePath = newPath;
try {
if (viewComposite.getName().equals(oldName)) {
setName(newName);
}
}
catch (InvalidNameException | DuplicateNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
return;
}
// Check for managed datatype changing
DataType dt = viewDTM.getDataType(oldPath);
if (dt == null) {
return;
}
viewDTM.withTransaction("Renamed Dependency", () -> {
viewDTM.clearUndoOnChange();
try {
dt.setName(newPath.getDataTypeName());
}
catch (InvalidNameException | DuplicateNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
});
fireTableDataChanged();
componentDataChanged();
dataTypeMoved(dtm, oldPath, newPath);
}
@Override
@ -1429,31 +1380,64 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
return;
}
DataType dt = viewDTM.getDataType(oldPath);
if (dt == null) {
if (oldPath.equals(newPath)) {
return;
}
try {
viewDTM.withTransaction("Moved " + oldPath, () -> {
viewDTM.clearUndoOnChange();
Category newDtCat = viewDTM.createCategory(newPath.getCategoryPath());
newDtCat.moveDataType(dt, null);
});
}
catch (DataTypeDependencyException e) {
throw new AssertException(e);
}
String newName = newPath.getDataTypeName();
String oldName = oldPath.getDataTypeName();
if (originalDataTypePath.getDataTypeName().equals(newPath.getDataTypeName()) &&
originalDataTypePath.getCategoryPath().equals(oldPath.getCategoryPath())) {
originalDataTypePath = newPath;
CategoryPath newCategoryPath = newPath.getCategoryPath();
CategoryPath oldCategoryPath = oldPath.getCategoryPath();
// Does the old name match our original name.
// Check originalCompositeId to ensure original type is managed
if (originalCompositeId != DataTypeManager.NULL_DATATYPE_ID &&
oldPath.equals(originalDataTypePath)) {
viewDTM.withTransaction("Name Changed", () -> {
viewDTM.clearUndoOnChange();
originalDataTypePath = newPath;
try {
if (viewComposite.getName().equals(oldName)) {
setName(newName);
}
if (!newCategoryPath.equals(oldCategoryPath)) {
viewComposite.setCategoryPath(newCategoryPath);
}
}
catch (InvalidNameException | DuplicateNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
});
compositeInfoChanged();
}
else {
fireTableDataChanged();
componentDataChanged();
// Check for managed datatype changing
DataType originalDt = originalDTM.getDataType(newPath);
if (!(originalDt instanceof DatabaseObject)) {
return;
}
DataType dt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(originalDt));
if (dt == null) {
return;
}
viewDTM.withTransaction("Renamed Dependency", () -> {
viewDTM.clearUndoOnChange();
try {
dt.setName(newName);
if (!newCategoryPath.equals(oldCategoryPath)) {
dt.setCategoryPath(newCategoryPath);
}
}
catch (InvalidNameException | DuplicateNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
});
}
fireTableDataChanged();
componentDataChanged();
}
@Override
@ -1517,11 +1501,10 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
// potentially many types getting changed by one change.
DataType changedDt = originalDTM.getDataType(path);
if (!(changedDt instanceof DatabaseObject)) {
// NOTE: viewDTM only maps view-to-original IDs for DataTypeDB
return;
}
long originalId = originalDTM.getID(changedDt);
DataType viewDt = viewDTM.findMyDataTypeFromOriginalID(originalId);
DataType viewDt =
viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(changedDt));
if (viewDt == null) {
return;
}
@ -1562,30 +1545,32 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
if (!oldPath.equals(originalDataTypePath)) {
// Check for type which may be referenced by viewComposite
DataType dt = viewDTM.getDataType(oldPath);
if (dt != null) {
if (hasSubDt(viewComposite, oldPath)) {
String msg = "Replaced data type \"" + oldPath +
"\", which is a sub-component of \"" + getOriginalDataTypeName() + "\".";
setStatus(msg, true);
}
// NOTE: depending upon event sequence and handling a
// re-load may have occurred and replacement may be unnecessary
try {
viewDTM.withTransaction("Replaced Dependency", () -> {
viewDTM.clearUndoOnChange();
viewDTM.replaceDataType(dt, newDataType, true);
});
}
catch (DataTypeDependencyException e) {
throw new AssertException(e);
}
// Clear undo/redo stack to avoid inconsistency with originalDTM
viewDTM.clearUndo();
fireTableDataChanged();
componentDataChanged();
if (dt == null || !viewDTM.isViewDataTypeFromOriginalDTM(dt)) {
return;
}
if (hasSubDt(viewComposite, oldPath)) {
String msg = "Replaced data type \"" + oldPath +
"\", which is a sub-component of \"" + getOriginalDataTypeName() + "\".";
setStatus(msg, true);
}
// NOTE: depending upon event sequence and handling a
// re-load may have occurred and replacement may be unnecessary
try {
viewDTM.withTransaction("Replaced Dependency", () -> {
viewDTM.clearUndoOnChange();
viewDTM.replaceDataType(dt, newDataType, true);
});
}
catch (DataTypeDependencyException e) {
throw new AssertException(e);
}
// Clear undo/redo stack to avoid inconsistency with originalDTM
viewDTM.clearUndo();
fireTableDataChanged();
componentDataChanged();
return;
}

View file

@ -157,7 +157,7 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
originalDataTypePath = originalComposite.getDataTypePath();
currentName = dataType.getName();
createViewCompositeFromOriginalComposite(originalComposite);
createViewCompositeFromOriginalComposite();
// Listen so we can update editor if name changes for this structure.
originalDTM.addDataTypeManagerListener(this);
@ -204,12 +204,10 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
}
/**
* Create {@code viewComposite} and associated view datatype manager ({@code viewDTM}) and
* changes listener(s) if required.
*
* @param original original composite being loaded
* Create {@code viewComposite} and {@link CompositeViewerDataTypeManager viewDTM} for this
* editor and the {@code originalComposite}.
*/
protected void createViewCompositeFromOriginalComposite(T original) {
protected void createViewCompositeFromOriginalComposite() {
if (viewDTM != null) {
viewDTM.close();
@ -217,8 +215,9 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
}
// Use temporary standalone view datatype manager
viewDTM = new CompositeViewerDataTypeManager<>(original.getDataTypeManager().getName(),
original, this::componentEdited, this::restoreEditor);
viewDTM =
new CompositeViewerDataTypeManager<>(originalComposite.getDataTypeManager().getName(),
originalComposite, this::componentEdited, this::restoreEditor);
viewComposite = viewDTM.getResolvedViewComposite();
@ -230,7 +229,7 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
// underlying datatype default setting value being presented when adjusting component
// default settings.
viewDTM.withTransaction("Load Settings",
() -> cloneAllComponentSettings(original, viewComposite));
() -> cloneAllComponentSettings(originalComposite, viewComposite));
viewDTM.clearUndo();
}
@ -317,7 +316,6 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
throw new InvalidDataTypeException("Data types of size 0 are not allowed.");
}
// TODO: Need to handle proper placement for big-endian within a larger component (i.e., right-justified)
return DataTypeInstance.getDataTypeInstance(resultDt, resultLen,
viewComposite.isPackingEnabled());
}
@ -481,13 +479,13 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
dtName = previousDt.getDisplayName();
}
DataType newDt = null;
int newLength = -1;
int newLength;
if (dataTypeObject instanceof DataTypeInstance dti) {
newDt = resolve(dti.getDataType());
newDt = dti.getDataType();
newLength = dti.getLength();
}
else if (dataTypeObject instanceof DataType dt) {
newDt = resolve(dt);
newDt = dt;
newLength = newDt.getLength();
}
else if (dataTypeObject instanceof String dtString) {
@ -506,6 +504,9 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
newLength = 0;
}
DataType dataType = newDt.clone(originalDTM);
newLength = newDt.getLength();
checkIsAllowableDataType(newDt);
if (newLength < 0) {
@ -535,8 +536,7 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
}
// Set component datatype and length on view composite
DataType dataType = resolve(newDt); // probably already resolved
setComponentDataTypeInstance(rowIndex, dataType, newLength);
setComponentDataTypeInstance(rowIndex, newDt, newLength);
return true;
});
@ -1434,51 +1434,51 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
* @return a valid data type instance or null if at blank line with no data type name.
* @throws UsrException indicating that the data type is not valid.
*/
protected DataTypeInstance validateComponentDataType(int rowIndex, String dtString)
throws UsrException {
DataType dt = null;
String dtName = "";
dtString = DataTypeHelper.stripWhiteSpace(dtString);
DataTypeComponent element = getComponent(rowIndex);
if (element != null) {
dt = element.getDataType();
dtName = dt.getDisplayName();
if (dtString.equals(dtName)) {
return DataTypeInstance.getDataTypeInstance(element.getDataType(),
element.getLength(), usesAlignedLengthComponents());
}
}
int newLength = 0;
DataType newDt = DataTypeHelper.parseDataType(rowIndex, dtString, this, originalDTM,
provider.dtmService);
if (newDt == null) {
if (dt != null) {
throw new UsrException("No data type was specified.");
}
throw new AssertException("Can't set data type to null.");
}
checkIsAllowableDataType(newDt);
newLength = newDt.getLength();
if (newLength < 0) {
DataTypeInstance sizedDataType = DataTypeHelper.getSizedDataType(provider, newDt,
lastNumBytes, getMaxReplaceLength(rowIndex));
newLength = sizedDataType.getLength();
}
newDt = viewDTM.resolve(newDt, null);
int maxLength = getMaxReplaceLength(rowIndex);
if (newLength <= 0) {
throw new UsrException("Can't currently add this data type.");
}
if (maxLength > 0 && newLength > maxLength) {
throw new UsrException(newDt.getDisplayName() + " doesn't fit.");
}
return DataTypeInstance.getDataTypeInstance(newDt, newLength,
usesAlignedLengthComponents());
}
// protected DataTypeInstance validateComponentDataType(int rowIndex, String dtString)
// throws UsrException {
// DataType dt = null;
// String dtName = "";
// dtString = DataTypeHelper.stripWhiteSpace(dtString);
// DataTypeComponent element = getComponent(rowIndex);
// if (element != null) {
// dt = element.getDataType();
// dtName = dt.getDisplayName();
// if (dtString.equals(dtName)) {
// return DataTypeInstance.getDataTypeInstance(element.getDataType(),
// element.getLength(), usesAlignedLengthComponents());
// }
// }
//
// int newLength = 0;
// DataType newDt = DataTypeHelper.parseDataType(rowIndex, dtString, this, originalDTM,
// provider.dtmService);
// if (newDt == null) {
// if (dt != null) {
// throw new UsrException("No data type was specified.");
// }
// throw new AssertException("Can't set data type to null.");
// }
//
// checkIsAllowableDataType(newDt);
//
// newLength = newDt.getLength();
// if (newLength < 0) {
// DataTypeInstance sizedDataType = DataTypeHelper.getSizedDataType(provider, newDt,
// lastNumBytes, getMaxReplaceLength(rowIndex));
// newLength = sizedDataType.getLength();
// }
//
// newDt = viewDTM.resolve(newDt, null);
// int maxLength = getMaxReplaceLength(rowIndex);
// if (newLength <= 0) {
// throw new UsrException("Can't currently add this data type.");
// }
// if (maxLength > 0 && newLength > maxLength) {
// throw new UsrException(newDt.getDisplayName() + " doesn't fit.");
// }
// return DataTypeInstance.getDataTypeInstance(newDt, newLength,
// usesAlignedLengthComponents());
// }
@SuppressWarnings("unused") // the exception is thrown by subclasses
protected void validateComponentName(int rowIndex, String name) throws UsrException {

View file

@ -47,8 +47,7 @@ import utilities.util.reflection.ReflectionUtilities;
* @param <M> Specific {@link CompositeEditorModel} implementation which supports editing T
*/
public abstract class CompositeEditorProvider<T extends Composite, M extends CompositeEditorModel<T>>
extends ComponentProviderAdapter
implements EditorProvider, EditorActionListener {
extends ComponentProviderAdapter implements EditorProvider, EditorActionListener {
protected static final Icon EDITOR_ICON = new GIcon("icon.plugin.composite.editor.provider");
@ -317,7 +316,7 @@ public abstract class CompositeEditorProvider<T extends Composite, M extends Com
// Check for changes and prompt user to check if saving them.
if (editorModel.isValidName() && editorModel.hasChanges()) {
String question = "The " + editorModel.getTypeName() + " Editor is closing.\n" +
"Save the changes to " + getDtPath() + "?";
"Save the changes to " + getDisplayName() + "?";
String title = "Save " + editorModel.getTypeName() + " Editor Changes?";
int response;
if (allowCancel) {
@ -337,6 +336,10 @@ public abstract class CompositeEditorProvider<T extends Composite, M extends Com
return 2;
}
protected String getDisplayName() {
return getDtPath().toString();
}
@Override
public String getWindowSubMenuName() {
return getName();

View file

@ -19,6 +19,8 @@ import java.io.IOException;
import java.util.Iterator;
import java.util.TreeSet;
import javax.help.UnsupportedOperationException;
import db.util.ErrorHandler;
import ghidra.program.database.DatabaseObject;
import ghidra.program.model.data.*;
@ -66,16 +68,15 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
private TreeSet<Long> orphanIds = new TreeSet<>();
/**
* Creates a data type manager that the structure editor will use internally for managing
* dependencies for an unmanaged structure being edited. A single transaction will be started
* with this instantiation and held open until this instance is closed. Undo/redo is
* not be supported.
* Creates a data type manager that the composite editor will use internally for managing
* dependencies without resolving the actual composite being edited. A single transaction
* will be started with this instantiation and held open until this instance is closed.
* Undo/redo and datatype pruning is not be supported.
* @param rootName the root name for this data type manager (usually the program name).
* @param originalDTM the original data type manager.
*/
public CompositeViewerDataTypeManager(String rootName, DataTypeManager originalDTM) {
this(rootName, originalDTM, null, null, null);
clearUndo();
transactionId = startTransaction("Composite Edit");
}
@ -123,8 +124,7 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
@SuppressWarnings("unchecked")
private T resolveViewComposite() {
return originalComposite != null ? (T) super.resolve(originalComposite, null)
: null;
return originalComposite != null ? (T) super.resolve(originalComposite, null) : null;
}
private void initializeArchitecture() {
@ -158,12 +158,18 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
@Override
public void undo() {
if (!isUndoRedoAllowed()) {
throw new UnsupportedOperationException();
}
dataTypeIDMap.invalidate();
super.undo();
}
@Override
public void redo() {
if (!isUndoRedoAllowed()) {
throw new UnsupportedOperationException();
}
dataTypeIDMap.invalidate();
super.redo();
}
@ -237,10 +243,12 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
public DataType replaceDataType(DataType existingViewDt, DataType replacementDt,
boolean updateCategoryPath) throws DataTypeDependencyException {
long viewDtId = getID(existingViewDt);
if (existingViewDt.getDataTypeManager() != this) {
throw new IllegalArgumentException("datatype is not from this manager");
}
if (existingViewDt instanceof DatabaseObject) {
dataTypeIDMap.remove(viewDtId);
dataTypeIDMap.remove(getID(existingViewDt));
}
DataType newResolvedDt =
@ -259,10 +267,12 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
@Override
public boolean remove(DataType existingViewDt, TaskMonitor monitor) {
long viewDtId = getID(existingViewDt);
if (existingViewDt.getDataTypeManager() != this) {
throw new IllegalArgumentException("datatype is not from this manager");
}
if (existingViewDt instanceof DatabaseObject) {
dataTypeIDMap.remove(viewDtId);
dataTypeIDMap.remove(getID(existingViewDt));
}
return super.remove(existingViewDt, monitor);
@ -432,14 +442,53 @@ public class CompositeViewerDataTypeManager<T extends Composite> extends StandAl
}
}
public DataType findOriginalDataTypeFromMyID(long myId) {
Long originalId = dataTypeIDMap.getOriginalIDFromViewID(myId);
return originalId != null ? originalDTM.getDataType(originalId) : null;
}
/**
* Find a resolved DB-datatype within this manager based upon its source datatype's ID
* within the original datatype manager associated with this manager. This method is
* useful when attempting to matchup a datatype within this manager to one which has changed
* within the original datatype manager.
*
* @param originalId datatype ID within original datatype manager
* @return matching DB-datatype or null if not found
*/
public DataType findMyDataTypeFromOriginalID(long originalId) {
Long myId = dataTypeIDMap.getViewIDFromOriginalID(originalId);
return myId != null ? getDataType(myId) : null;
}
/**
* Find a resolved DB-datatype within the original datatype manager based upon a resolved
* datatype's ID within this manager. This method is useful when attempting to matchup a
* datatype within this manager to one which has possibly changed within the original
* datatype manager.
*
* @param myId resolved datatype ID within this datatype manager
* @return matching DB-datatype or null if not found
*/
public DataType findOriginalDataTypeFromMyID(long myId) {
Long originalId = dataTypeIDMap.getOriginalIDFromViewID(myId);
return originalId != null ? originalDTM.getDataType(originalId) : null;
}
/**
* Determine if the specified datatype which has previsouly been resolved to this datatype
* manager originated from original composite's source (e.g., program).
* <P>
* NOTE: Non-DB datatypes will always return false.
*
* @param existingViewDt existing datatype which has previously been resolved to this
* datatype manager.
* @return true if specified datatype originated from this manager's associated original
* datatype manager.
*/
public boolean isViewDataTypeFromOriginalDTM(DataType existingViewDt) {
if (existingViewDt.getDataTypeManager() != this) {
throw new IllegalArgumentException("datatype is not from this manager");
}
if (!(existingViewDt instanceof DatabaseObject)) {
return false;
}
return dataTypeIDMap.getOriginalIDFromViewID(getID(existingViewDt)) != null;
}
}

View file

@ -199,35 +199,35 @@ abstract class CompositeViewerModel<T extends Composite> extends AbstractTableMo
}
}
/**
* Resolves the data type against the indicated data type manager using the specified
* conflictHandler. In general, a transaction should have already been initiated prior to
* calling this method so that the true nature of the transaction may be established for
* use with undo/redo (e.g., Set Datatype).
*
* @param dataType the data type to be resolved
* @param resolveDtm the data type manager to resolve the data type against
* @param conflictHandler the handler to be used for any conflicts encountered while resolving
* @return the resolved data type
*/
protected final DataType resolveDataType(DataType dataType, DataTypeManager resolveDtm,
DataTypeConflictHandler conflictHandler) {
if (resolveDtm == null || dataType == DataType.DEFAULT) {
return DataType.DEFAULT;
}
return resolveDtm.withTransaction("Resolve " + dataType.getPathName(), () -> {
return resolveDtm.resolve(dataType, conflictHandler);
});
}
/**
* Resolves the indicated data type against the working copy in the viewer's data type manager.
* @param dataType the data type
* @return the working copy of the data type.
*/
public DataType resolve(DataType dataType) {
return resolveDataType(dataType, viewDTM, null);
}
// /**
// * Resolves the data type against the indicated data type manager using the specified
// * conflictHandler. In general, a transaction should have already been initiated prior to
// * calling this method so that the true nature of the transaction may be established for
// * use with undo/redo (e.g., Set Datatype).
// *
// * @param dataType the data type to be resolved
// * @param resolveDtm the data type manager to resolve the data type against
// * @param conflictHandler the handler to be used for any conflicts encountered while resolving
// * @return the resolved data type
// */
// protected final DataType resolveDataType(DataType dataType, DataTypeManager resolveDtm,
// DataTypeConflictHandler conflictHandler) {
// if (resolveDtm == null || dataType == DataType.DEFAULT) {
// return DataType.DEFAULT;
// }
// return resolveDtm.withTransaction("Resolve " + dataType.getPathName(), () -> {
// return resolveDtm.resolve(dataType, conflictHandler);
// });
// }
//
// /**
// * Resolves the indicated data type against the working copy in the viewer's data type manager.
// * @param dataType the data type
// * @return the working copy of the data type.
// */
// public DataType resolve(DataType dataType) {
// return resolveDataType(dataType, viewDTM, null);
// }
/**
* Gets the current row

View file

@ -1,726 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.stackeditor;
import java.util.*;
import javax.help.UnsupportedOperationException;
import ghidra.program.model.data.*;
import ghidra.util.exception.AssertException;
/**
* {@link BiDirectionDataType} is a special structure data type that allows both positive and
* negative offset values.
* <P>
* NOTE: This special purpose datatype does not support resolving with a {@link DataTypeManager}
*/
public abstract class BiDirectionDataType extends StructureDataType
implements BiDirectionStructure {
protected static Comparator<Object> ordinalComparator = new OrdinalComparator();
protected static Comparator<Object> offsetComparator = new OffsetComparator();
protected int negativeLength;
protected int positiveLength;
protected int splitOffset; // division offset between negative/positive halves
/**
* Construct {@link BiDirectionDataType}
* @param name data type display name
* @param negativeLength negative allocation size
* @param positiveLength positive allocation size
* @param splitOffset division offset between negative/positive halves
* @param dtm associated datatype manager for component datatypes
*/
protected BiDirectionDataType(String name, int negativeLength, int positiveLength,
int splitOffset, DataTypeManager dtm) {
super(CategoryPath.ROOT, name, negativeLength + positiveLength, dtm);
this.negativeLength = negativeLength;
this.positiveLength = positiveLength;
this.splitOffset = splitOffset;
}
@Override
protected DataType validateDataType(DataType dataType) {
if (DataTypeComponent.usesZeroLengthComponent(dataType)) {
throw new IllegalArgumentException(
"Zero-length datatype not permitted: " + dataType.getName());
}
if (dataType instanceof BitFieldDataType) {
throw new IllegalArgumentException("Bitfield not permitted: " + dataType.getName());
}
return super.validateDataType(dataType);
}
@Override
public int getAlignment() {
throw new UnsupportedOperationException(
"BiDirectionDataType.getAlignment() not implemented.");
}
@Override
public boolean repack(boolean notify) {
throw new AssertException();
}
@Override
public void setToDefaultAligned() {
// ignore
}
@Override
public void setToMachineAligned() {
// ignore
}
@Override
public void setPackingEnabled(boolean aligned) {
// ignore
}
@Override
public void setExplicitPackingValue(int packingValue) {
// ignore
}
@Override
public void setExplicitMinimumAlignment(int minimumAlignment) {
// ignore
}
protected DataTypeComponent getDefinedComponentAt(int offset) {
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
return null;
}
int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator);
if (index >= 0) {
return components.get(index);
}
return null;
}
@Override
public DataTypeComponent getComponentAt(int offset) {
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
return null;
}
int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator);
if (index >= 0) {
return components.get(index);
}
int ordinal = 0;
index = -index - 1;
int prevIndex = index - 1;
if (prevIndex < 0) {
ordinal = offset + negativeLength - splitOffset;
}
else {
DataTypeComponent prevComp = components.get(prevIndex);
int prevOrdinal = prevComp.getOrdinal();
int prevOffset = prevComp.getOffset();
int endOffset = prevComp.getEndOffset();
if (offset > prevOffset && offset <= endOffset) {
return null;
}
ordinal = prevOrdinal + offset - endOffset;
}
return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset);
}
@Override
public int getSplitOffset() {
return splitOffset;
}
@Override
public int getNegativeLength() {
return negativeLength;
}
@Override
public int getPositiveLength() {
return positiveLength;
}
@Override
public void delete(int index) {
if (index < 0 || index >= numComponents) {
throw new IndexOutOfBoundsException(index);
}
DataTypeComponent comp = getComponent(index);
int offset = comp.getOffset();
int length = comp.getLength();
int idx = Collections.binarySearch(components, index, ordinalComparator);
if (idx >= 0) {
DataTypeComponent dtc = components.remove(idx);
dtc.getDataType().removeParent(this);
length = dtc.getLength();
}
else {
idx = -idx - 1;
length = 1;
}
adjustOffsets(idx, offset, -1, -length);
numComponents--;
notifySizeChanged();
}
@Override
public void delete(Set<Integer> ordinals) {
for (int ordinal : ordinals) {
delete(ordinal);
}
}
/**
*
* @param idx the min index in the defined component arraylist
* @param offset
* @param deltaOrdinal
* @param deltaLength
*/
protected void adjustOffsets(int idx, int offset, int deltaOrdinal, int deltaLength) {
if (offset >= splitOffset) {
// component was in positive offsets
shiftOffsets(idx, deltaOrdinal, deltaLength);
positiveLength += deltaLength;
}
else {
if (offset - deltaLength > splitOffset) {
// The deleted component straddled negative/zero boudary.
shiftOffsets(0, idx - 1, 0, offset);
shiftOffsets(idx, deltaOrdinal, offset - deltaLength);
// TODO: this seems wrong
negativeLength += offset;
positiveLength -= (offset - deltaLength);
}
else {
// component was in negative offsets
shiftOffsets(0, idx - 1, 0, -deltaLength);
shiftOffsets(idx, deltaOrdinal, 0);
negativeLength += deltaLength;
}
}
structLength += deltaLength;
// nonpackedAlignedStructLength = -1;
}
/*
*
*/
private void shiftOffsets(int index, int deltaOrdinal, int deltaOffset) {
shiftOffsets(index, components.size() - 1, deltaOrdinal, deltaOffset);
}
/**
*
* @param startIndex the min index in the defined component arraylist
* @param endIndex the max index in the defined component arraylist
* @param deltaOrdinal
* @param deltaOffset
*/
protected void shiftOffsets(int startIndex, int endIndex, int deltaOrdinal, int deltaOffset) {
for (int i = startIndex; i <= endIndex && i < components.size(); i++) {
DataTypeComponentImpl dtc = components.get(i);
shiftOffset(dtc, deltaOrdinal, deltaOffset);
}
}
protected DataTypeComponent getDefinedComponent(int ordinal) {
if (ordinal < 0 || ordinal >= numComponents) {
throw new IndexOutOfBoundsException(ordinal);
}
int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator);
if (idx >= 0) {
return components.get(idx);
}
return null;
}
@Override
public DataTypeComponentImpl getComponent(int ordinal) {
if (ordinal < 0 || ordinal >= numComponents) {
throw new IndexOutOfBoundsException(ordinal);
}
int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator);
if (idx >= 0) {
return components.get(idx);
}
idx = -idx - 1;
int prevIndex = idx - 1;
int offset = computeOffset(ordinal, prevIndex);
return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset);
}
protected int computeOffset(int ordinal, int prevIndex) {
int offset;
if (prevIndex < 0) {
offset = splitOffset - negativeLength + ordinal;
}
else {
DataTypeComponent prevElement = components.get(prevIndex);
int prevOrdinal = prevElement.getOrdinal();
int endOffset = prevElement.getEndOffset();
offset = endOffset + ordinal - prevOrdinal;
}
return offset;
}
@Override
public int getNumComponents() {
return numComponents;
}
@Override
public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length,
String newName, String comment) throws IllegalArgumentException {
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
throw new IllegalArgumentException(
"Offset " + offset + " is not in " + getDisplayName() + ".");
}
validateDataType(dataType);
int nextOffset = offset + length;
if (offset > positiveLength) {
int deltaLength = offset - positiveLength;
numComponents += deltaLength;
positiveLength += deltaLength;
structLength += deltaLength;
// nonpackedAlignedStructLength = -1;
}
if (nextOffset < splitOffset - negativeLength) {
int deltaLength = splitOffset - nextOffset - negativeLength;
numComponents += deltaLength;
negativeLength += deltaLength;
structLength += deltaLength;
// nonpackedAlignedStructLength = -1;
}
checkAncestry(dataType);
dataType = dataType.clone(getDataTypeManager());
int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator);
int additionalShift = 0;
if (index >= 0) {
DataTypeComponent dtc = components.get(index);
if (offset < 0) {
additionalShift = offset - dtc.getEndOffset();
}
else {
additionalShift = offset - dtc.getOffset();
}
}
else {
index = -index - 1;
}
// TODO: ??
int ordinal = negativeLength + offset;
if (index > 0) {
DataTypeComponent dtc = components.get(index - 1);
ordinal = dtc.getOrdinal() + offset - dtc.getEndOffset();
}
DataTypeComponentImpl dtc =
new DataTypeComponentImpl(dataType, this, length, ordinal, offset, newName, comment);
dataType.addParent(this);
adjustOffsets(index, offset, 1 + additionalShift, dtc.getLength() + additionalShift);
components.add(index, dtc);
numComponents++;
notifySizeChanged();
return dtc;
}
@Override
public DataTypeComponent add(DataType dataType, int length, String newName, String comment) {
return addPositive(dataType, length, newName, comment);
}
@Override
public DataTypeComponent addPositive(DataType dataType, int length, String newName,
String comment) throws IllegalArgumentException {
validateDataType(dataType);
checkAncestry(dataType);
dataType = dataType.clone(getDataTypeManager());
// int dtLength = dataType.getLength();
int offset = positiveLength;
DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, numComponents,
offset, newName, comment);
dataType.addParent(this);
components.add(dtc);
numComponents++;
positiveLength += length;
structLength += length;
// nonpackedAlignedStructLength = -1;
notifySizeChanged();
return dtc;
}
@Override
public DataTypeComponent addNegative(DataType dataType, int length, String newName,
String comment) throws IllegalArgumentException {
validateDataType(dataType);
checkAncestry(dataType);
dataType = dataType.clone(getDataTypeManager());
// int dtLength = dataType.getLength();
shiftOffsets(0, numComponents - 1, 1, 0);
int offset = splitOffset - negativeLength - length;
DataTypeComponentImpl dtc =
new DataTypeComponentImpl(dataType, this, length, 0, offset, newName, comment);
dataType.addParent(this);
components.add(dtc);
numComponents++;
negativeLength += length;
structLength += length;
// nonpackedAlignedStructLength = -1;
notifySizeChanged();
return dtc;
}
@Override
public void setLength(int len) {
throw new UnsupportedOperationException("setLength not supported");
}
/**
* Increases the size of the bidirectional data type If amount is positive then the positive
* offset side will grow by the indicated amount. If amount is negative, the data type grows on
* the negative offsets side.
*
* @param amount Positive value indicates number of bytes to add to positive side. Negative
* value indicates number of bytes to add to negative side.
*/
@Override
public void growStructure(int amount) {
int absAmount;
if (amount < 0) {
absAmount = -amount;
negativeLength -= amount;
adjustOffsets(0, negativeLength, absAmount, 0);
}
else {
absAmount = amount;
positiveLength += amount;
}
numComponents += absAmount;
structLength += absAmount;
// nonpackedAlignedStructLength = -1;
notifySizeChanged();
}
@Override
public DataTypeComponent insert(int index, DataType dataType, int length, String newName,
String comment) {
throw new UnsupportedOperationException("BiDirectionDataType.insert() not implemented.");
}
protected void insertAtOffset(int offset, int numBytes) {
if (offset < splitOffset - negativeLength || offset > splitOffset + positiveLength) {
throw new IllegalArgumentException("Offset " + offset +
" is not a valid insertion point in " + getDisplayName() + ".");
}
DataTypeComponent dtc = getComponentAt(offset);
int numDefinedComponents = components.size();
int definedIndex = 0;
if (dtc == null) {
if (offset == positiveLength) {
definedIndex = numDefinedComponents;
}
else {
throw new IllegalArgumentException("Offset " + offset +
" is not a valid insertion point in " + getDisplayName() + ".");
}
}
else if (dtc.getOffset() != offset) {
throw new IllegalArgumentException("Cannot insert at offset " + offset +
" within a defined component in " + getDisplayName() + ".");
}
else {
definedIndex = Collections.binarySearch(components, Integer.valueOf(dtc.getOrdinal()),
ordinalComparator);
if (definedIndex < 0) {
definedIndex = -definedIndex - 1;
}
}
if (offset <= 0) {
shiftOffsets(0, definedIndex - 1, 0, -numBytes);
shiftOffsets(definedIndex, numDefinedComponents - 1, numBytes, 0);
negativeLength += numBytes;
}
else {
shiftOffsets(definedIndex, numDefinedComponents - 1, numBytes, numBytes);
positiveLength += numBytes;
}
numComponents += numBytes;
structLength += numBytes;
// nonpackedAlignedStructLength = -1;
notifySizeChanged();
}
@Override
public void deleteAtOffset(int offset) {
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
throw new IllegalArgumentException(
"Offset " + offset + " is not in " + getDisplayName() + ".");
}
int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator);
int length = 1;
if (index < 0) {
index = -index - 1;
}
else {
DataTypeComponent dtc = components.remove(index);
dtc.getDataType().removeParent(this);
length = dtc.getLength();
}
adjustOffsets(index, offset, -1, -length);
numComponents--;
}
@Override
public void clearAtOffset(int offset) {
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
throw new IllegalArgumentException(
"Offset " + offset + " is not in " + getDisplayName() + ".");
}
int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator);
if (index >= 0) {
DataTypeComponent dtc = components.remove(index);
dtc.getDataType().removeParent(this);
int len = dtc.getLength();
if (len > 1) {
int deltaLength = len - 1;
shiftOffsets(index, deltaLength, 0);
numComponents += deltaLength;
}
}
}
@Override
public boolean isEquivalent(DataType dataType) {
if (dataType == this) {
return true;
}
if (dataType == null) {
return false;
}
if (dataType instanceof BiDirectionStructure) {
BiDirectionStructure biDir = (BiDirectionStructure) dataType;
if ((splitOffset != biDir.getSplitOffset()) ||
(negativeLength != biDir.getNegativeLength()) ||
(positiveLength != biDir.getPositiveLength()) ||
(getLength() != biDir.getLength())) {
return false;
}
DataTypeComponent[] myComps = getDefinedComponents();
DataTypeComponent[] otherComps = biDir.getDefinedComponents();
if (myComps.length != otherComps.length) {
return false;
}
for (int i = 0; i < myComps.length; i++) {
if (!myComps[i].isEquivalent(otherComps[i])) {
return false;
}
}
return true;
}
return false;
}
@Override
public void dataTypeSizeChanged(DataType dt) {
// ignore
}
@Override
public void dataTypeAlignmentChanged(DataType dt) {
// ignore
}
@Override
public abstract BiDirectionDataType clone(DataTypeManager dtm);
@Override
public void clearComponent(int ordinal) {
if (ordinal < 0 || ordinal >= numComponents) {
throw new IndexOutOfBoundsException(ordinal);
}
int index =
Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator);
if (index >= 0) {
DataTypeComponent dtc = components.remove(index);
dtc.getDataType().removeParent(this);
int len = dtc.getLength();
if (len > 1) {
int deltaLength = len - 1;
shiftOffsets(index, deltaLength, 0);
numComponents += deltaLength;
}
}
}
public void replaceWith(Structure struct) {
throw new UnsupportedOperationException(
"BiDirectionDataType.replaceWith() not implemented.");
}
@Override
public void dataTypeDeleted(DataType dt) {
throw new UnsupportedOperationException(
"BiDirectionDataType.dataTypeDeleted() not implemented.");
}
@Override
public void dataTypeReplaced(DataType oldDt, DataType newDt) {
throw new UnsupportedOperationException(
"BiDirectionDataType.dataTypeReplaced() not implemented.");
}
@Override
public DataTypeComponent[] getDefinedComponents() {
return components.toArray(new DataTypeComponent[components.size()]);
}
@Override
public DataTypeComponent[] getComponents() {
DataTypeComponent[] comps = new DataTypeComponent[numComponents];
for (int i = 0; i < comps.length; i++) {
comps[i] = getComponent(i);
}
return comps;
}
@Override
public DataTypeComponent replace(int index, DataType dataType, int length, String newName,
String comment) throws IndexOutOfBoundsException, IllegalArgumentException {
if (index < 0 || index >= numComponents) {
throw new IndexOutOfBoundsException(index);
}
validateDataType(dataType);
checkAncestry(dataType);
dataType = dataType.clone(getDataTypeManager());
DataTypeComponent origDtc = getComponent(index);
return replace(origDtc, dataType, length, newName, comment);
}
@Override
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length,
String newName, String comment) throws IllegalArgumentException {
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
throw new IllegalArgumentException(
"Offset " + offset + " is not in " + getDisplayName() + ".");
}
validateDataType(dataType);
checkAncestry(dataType);
dataType = dataType.clone(getDataTypeManager());
DataTypeComponent origDtc = getComponentAt(offset);
DataTypeComponent newDtc = replace(origDtc, dataType, length, newName, comment);
return newDtc;
}
/**
* Replace the indicated component with a new component containing the specified data type.
*
* @param origDtc the original data type component in this structure.
* @param dataType the data type of the new component
* @param length the length of the new component
* @param newName the field name of the new component
* @param comment the comment for the new component
* @return the new component or null if the new component couldn't fit.
* @throws IllegalArgumentException if the dataType.getLength() is positive and does not match
* the given length parameter.
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
* component in this composite data type. For example, suppose dt1 contains dt2.
* Therefore it is not valid to replace a dt2 component with dt1 since this would
* cause a cyclic dependency.
*/
private DataTypeComponent replace(DataTypeComponent origDtc, DataType dataType, int length,
String newName, String comment) {
int ordinal = origDtc.getOrdinal();
int newOffset = origDtc.getOffset();
int dtcLength = origDtc.getLength();
int bytesNeeded = length - dtcLength;
int deltaOrdinal = -bytesNeeded;
if (bytesNeeded > 0) {
int bytesAvailable = getNumUndefinedBytes(ordinal + 1);
if (bytesAvailable < bytesNeeded) {
// throw new IllegalArgumentException("Not enough undefined bytes.");
deltaOrdinal = -bytesAvailable;
length -= (bytesNeeded - bytesAvailable);
}
}
origDtc.getDataType().removeParent(this);
DataTypeComponentImpl newDtc =
new DataTypeComponentImpl(dataType, this, length, ordinal, newOffset, newName, comment);
dataType.addParent(this);
int index =
Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator);
if (index < 0) {
index = -index - 1;
}
else {
components.remove(index);
}
if (deltaOrdinal != 0) {
adjustOffsets(index, newOffset, deltaOrdinal, 0);
}
components.add(index, newDtc);
if (deltaOrdinal != 0) {
numComponents += deltaOrdinal;
}
return newDtc;
}
}
class OffsetComparator implements Comparator<Object> {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Integer) {
return -compare(o2, o1);
}
DataTypeComponent dtc = (DataTypeComponent) o1;
int offset = ((Integer) o2).intValue();
if (offset < dtc.getOffset()) {
return 1;
}
else if (offset > dtc.getEndOffset()) {
return -1;
}
return 0;
}
}
class OrdinalComparator implements Comparator<Object> {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Integer) {
return -compare(o2, o1);
}
DataTypeComponent dtc = (DataTypeComponent) o1;
int ordinal = ((Integer) o2).intValue();
return dtc.getOrdinal() - ordinal;
}
}

View file

@ -1,44 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.stackeditor;
import ghidra.program.model.data.*;
public interface BiDirectionStructure extends Structure {
/**
* Get the length of this DataType in the negative direction.
* @return the length of this DataType in the negative direction.
*/
public abstract int getNegativeLength();
/**
* Get the length of this DataType in the positive direction.
* @return the length of this DataType in the positive direction.
*/
public abstract int getPositiveLength();
/**
* Get the component offset which represents the division point
* between the positive and negative halves of the structure.
* @return split offset
*/
public abstract int getSplitOffset();
public DataTypeComponent addNegative(DataType dataType, int length, String name, String comment);
public DataTypeComponent addPositive(DataType dataType, int length, String name, String comment);
}

View file

@ -17,8 +17,6 @@ package ghidra.app.plugin.core.stackeditor;
import java.util.*;
import javax.swing.JOptionPane;
import docking.widgets.OptionDialog;
import docking.widgets.fieldpanel.support.FieldRange;
import docking.widgets.fieldpanel.support.FieldSelection;
@ -32,7 +30,9 @@ import docking.widgets.fieldpanel.support.FieldSelection;
* When edit actions occur and there is a selection, the listener's are notified
* of the new selection via the listener's overrideSelection method.
*/
import ghidra.app.plugin.core.compositeeditor.*;
import ghidra.app.plugin.core.compositeeditor.CompositeEditorModel;
import ghidra.app.plugin.core.compositeeditor.CompositeViewerDataTypeManager;
import ghidra.app.plugin.core.stackeditor.StackFrameDataType.StackComponentWrapper;
import ghidra.app.util.datatype.EmptyCompositeException;
import ghidra.framework.plugintool.Plugin;
import ghidra.framework.plugintool.PluginTool;
@ -57,7 +57,8 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
private static final int MAX_LOCAL_SIZE = Integer.MAX_VALUE;
private static final int MAX_PARAM_SIZE = Integer.MAX_VALUE;
private StackFrame originalStack;
private Function function;
private StackFrameDataType originalStackFrameDataType;
private boolean stackChangedExternally;
@ -91,16 +92,23 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
return false;
}
void stackChangedExternally(boolean changed) {
stackChangedExternally = changed;
@Override
protected boolean usesAlignedLengthComponents() {
// NOTE: It is assumed that aligned-length is not used by stack variables
return false;
}
void load(Function function) {
originalStack = function.getStackFrame();
ProgramBasedDataTypeManager dtm = function.getProgram().getDataTypeManager();
StackFrameDataType stackFrameDataType = new StackFrameDataType(originalStack, dtm);
stackFrameDataType.setCategoryPath(dtm.getRootCategory().getCategoryPath());
load(stackFrameDataType);
void stackChangedExternally(boolean changed) {
stackChangedExternally = changed;
if (changed) {
setStatus("Stack may have been changed externally -- data may be stale.");
}
}
void load(Function func) {
function = func;
originalStackFrameDataType = new StackFrameDataType(function);
load(originalStackFrameDataType);
}
@Override
@ -110,18 +118,27 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
}
@Override
protected void createViewCompositeFromOriginalComposite(StackFrameDataType original) {
protected void createViewCompositeFromOriginalComposite() {
if (viewDTM != null) {
viewDTM.close();
viewDTM = null;
}
// Use temporary standalone view datatype manager which will not manage the viewComposite
// Establish editor's datatype manager which will manage datatype dependencies.
viewDTM = new CompositeViewerDataTypeManager<>(originalDTM.getName(), originalDTM);
// NOTE: StackFrameDataType cannot be resolved but all of its element datatypes must be
viewComposite = original.copy(viewDTM);
// Create a copy of the original stack frame datatype and force the resolving of its
// datatype dependencies. A round-about approach is used since the StackFrameDataType
// itself cannot be resolved and cannot be treated in the same fashion as a normal
// Structure edit. It relies on wrapping a normal structure to serve as a proxy of sorts
// for the purpose of managing datatype dependencies. It is only through the use of
// a StructureDB that component datatypes are forced to be resolved into the viewDTM.
// NOTE: Since the StackEditorDataTypeManager keeps a single transaction open unused
// datatype pruning is never performed.
viewComposite = originalComposite.copy(originalDTM);
originalComposite.resolveWrappedComposite(viewDTM);
originalComposite = originalStackFrameDataType; // use true original
}
@Override
@ -131,18 +148,15 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
@Override
public boolean updateAndCheckChangeState() {
if (originalIsChanging) {
return false;
}
StackFrameDataType sfdt = viewComposite;
int editReturnAddressOffset = sfdt.getReturnAddressOffset();
int editLocalSize = sfdt.getLocalSize();
int editParamOffset = sfdt.getParameterOffset();
int editParamSize = sfdt.getParameterSize();
int stackReturnAddressOffset = originalStack.getReturnAddressOffset();
int stackLocalSize = originalStack.getLocalSize();
int stackParamOffset = originalStack.getParameterOffset();
int stackParamSize = originalStack.getParameterSize();
int stackReturnAddressOffset = originalStackFrameDataType.getReturnAddressOffset();
int stackLocalSize = originalStackFrameDataType.getLocalSize();
int stackParamOffset = originalStackFrameDataType.getParameterOffset();
int stackParamSize = originalStackFrameDataType.getParameterSize();
hasChanges = (editReturnAddressOffset != stackReturnAddressOffset) ||
(editLocalSize != stackLocalSize) || (editParamOffset != stackParamOffset) ||
(editParamSize != stackParamSize) || super.updateAndCheckChangeState();
@ -185,7 +199,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
(rowIndex < 0) || (columnIndex < 0) || (columnIndex >= getColumnCount())) {
return "";
}
DataTypeComponent element = viewComposite.getComponent(rowIndex);
StackComponentWrapper element = viewComposite.getComponent(rowIndex);
DataType dt;
int dtLen;
switch (columnIndex) {
@ -214,10 +228,6 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
if (fieldName == null) {
fieldName = "";
}
// if ((fieldName.length() == 0)
// && (element.getOffset() == ((StackFrameDataType)viewComposite).getReturnAddressOffset())) {
// return "<RETURN_ADDRESS>";
// }
return fieldName;
case COMMENT:
return element.getComment();
@ -226,13 +236,13 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
}
}
private String getFieldNameAtRow(int rowIndex, StackFrameDataType stackFrameDataType) {
DataTypeComponent dataType = stackFrameDataType.getComponent(rowIndex);
String fieldName = dataType.getFieldName();
private String getFieldNameAtRow(int rowIndex, StackFrameDataType stackDt) {
StackComponentWrapper stackDtc = stackDt.getComponent(rowIndex);
String fieldName = stackDtc.getFieldName();
if (fieldName == null) {
// If the component is a defined stack variable with no name, use default name.
if (stackFrameDataType.isStackVariable(rowIndex)) {
fieldName = stackFrameDataType.getDefaultName(dataType);
if (stackDt.isStackVariable(rowIndex)) {
fieldName = stackDt.getDefaultName(stackDtc);
}
}
return fieldName;
@ -249,23 +259,12 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
public void setValueAt(Object aValue, int rowIndex, int modelColumnIndex) {
try {
settingValueAt = true;
OffsetPairs offsetSelection = getRelOffsetSelection();
Object originalValue = getValueAt(rowIndex, modelColumnIndex);
if (SystemUtilities.isEqual(originalValue, aValue)) {
return;
}
if (fieldEdited(aValue, rowIndex, modelColumnIndex)) {
if (modelColumnIndex == OFFSET) {
int svOffset = Integer.decode((String) aValue).intValue();
DataTypeComponent dtc =
(viewComposite).getComponentAt(svOffset);
offsetSelection = new OffsetPairs();
offsetSelection.addPair(svOffset, dtc.getEndOffset());
}
}
setRelOffsetSelection(offsetSelection);
fieldEdited(aValue, rowIndex, modelColumnIndex);
setSelection(new int[] { rowIndex });
}
finally {
settingValueAt = false;
@ -292,9 +291,6 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
setComponentOffset(rowIndex, (String) value);
break;
case DATATYPE:
if (value instanceof StackPieceDataType) {
return true; // no change
}
setComponentDataType(rowIndex, value);
break;
case NAME:
@ -424,10 +420,6 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
return viewComposite.getDefinedComponentAtOffset(offset) != null;
}
StackFrame getOriginalStack() {
return originalStack;
}
StackFrameDataType getEditorStack() {
return viewComposite;
}
@ -603,10 +595,13 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
*/
@Override
public boolean isAddAllowed(int currentIndex, DataType dataType) {
if (currentIndex < 0 || currentIndex >= getRowCount()) {
return false;
}
try {
if (currentIndex < 0 || currentIndex >= getRowCount()) {
return false;
}
checkIsAllowableDataType(dataType);
}
catch (InvalidDataTypeException e) {
@ -627,18 +622,6 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
newLength = compDt.getLength();
}
int offset = comp.getOffset();
// TODO: not sure we need to prevent creating local variables in 'save' area,
// since doing so just leads to confusion when using stack frame editor
// if (((StackFrameDataType) viewComposite).growsNegative()) {
// if (offset >= 0 && offset < getParameterOffset()) {
// return false;
// }
// }
// else {
// if (offset < 0 && offset > getParameterOffset()) {
// return false;
// }
// }
int maxBytes = viewComposite.getMaxLength(offset);
if (newLength > maxBytes) {
return false;
@ -660,7 +643,8 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
if (index < 0 || index >= viewComposite.getNumComponents()) {
return false;
}
return viewComposite.getDefinedComponentAtOrdinal(index) != null;
StackComponentWrapper dtc = viewComposite.getDefinedComponentAtOrdinal(index);
return dtc != null;
}
@Override
@ -683,17 +667,8 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
return false;
}
int paramOffset = getParameterOffset();
if (paramOffset >= 0) {
// grows negative
if (startOffset < paramOffset && endOffset >= paramOffset) {
return false;
}
}
else {
// grows positive
if (startOffset <= paramOffset && endOffset > paramOffset) {
return false;
}
if (startOffset < paramOffset && endOffset >= paramOffset) {
return false;
}
return true;
}
@ -723,59 +698,6 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
return true;
}
private void adjustComponents(DataType dataType) {
DataTypeComponent[] comps = viewComposite.getDefinedComponents();
String msg = "";
for (DataTypeComponent component : comps) {
DataType compDt = component.getDataType();
if (compDt == dataType) {
int len = compDt.getLength();
if (len <= 0) {
len = component.getLength();
}
try {
viewComposite.replace(component.getOrdinal(), compDt, len,
component.getFieldName(),
component.getComment());
}
catch (IllegalArgumentException e) {
msg += "Adjusting variable at offset " +
getHexString(component.getOffset(), true) + ". " + e.getMessage() + "\n";
}
}
}
if (msg.length() > 0) {
JOptionPane.showMessageDialog(provider.getComponent(), msg,
"Stack Editor Adjustment Warning", JOptionPane.WARNING_MESSAGE);
}
}
private void replaceComponents(DataType oldDataType, DataType newDataType) {
DataTypeComponent[] comps = viewComposite.getDefinedComponents();
String msg = "";
for (DataTypeComponent component : comps) {
DataType compDt = component.getDataType();
if (compDt == oldDataType) {
int len = newDataType.getLength();
if (len <= 0) {
len = component.getLength();
}
try {
viewComposite.replace(component.getOrdinal(), newDataType, len,
component.getFieldName(), component.getComment());
}
catch (IllegalArgumentException e) {
msg += "Replacing variable at offset " +
getHexString(component.getOffset(), true) + ". " + e.getMessage() + "\n";
}
}
}
if (msg.length() > 0) {
JOptionPane.showMessageDialog(provider.getComponent(), msg,
"Stack Editor Replacement Warning", JOptionPane.WARNING_MESSAGE);
}
}
@Override
public void setComponentDataTypeInstance(int index, DataType dt, int length)
throws UsrException {
@ -800,15 +722,14 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
// prevent user names that are default values, unless the value is the original name
String nameInEditor = (String) getValueAt(rowIndex, NAME);
StackFrameDataType stackFrameDataType = viewComposite;
if (stackFrameDataType.isDefaultName(newName) && !isOriginalFieldName(newName, rowIndex)) {
if (viewComposite.isDefaultName(newName) && !isOriginalFieldName(newName, rowIndex)) {
if (Objects.equals(nameInEditor, newName)) {
return false; // same as current name in the table; do nothing
}
throw new InvalidNameException("Cannot set a stack variable name to a default value");
}
if (stackFrameDataType.setName(rowIndex, newName)) {
if (viewComposite.setName(rowIndex, newName)) {
updateAndCheckChangeState();
fireTableCellUpdated(rowIndex, getNameColumn());
notifyCompositeChanged();
@ -819,8 +740,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
/** Gets the original field name within the parent data type for a given row in the editor */
private boolean isOriginalFieldName(String testName, int rowIndex) {
StackFrameDataType dataType = getOriginalComposite();
String fieldName = getFieldNameAtRow(rowIndex, dataType);
String fieldName = getFieldNameAtRow(rowIndex, originalStackFrameDataType);
return SystemUtilities.isEqual(fieldName, testName);
}
@ -857,6 +777,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
*/
@Override
public DataTypeComponent add(int index, DataType dt) throws UsrException {
// NOTE: Unused method
return replace(index, dt);
}
@ -909,16 +830,14 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
if (!isValidName() || !hasChanges()) {
return false;
}
StackFrame original = getOriginalStack(); // FIXME: Not Needed - use originalStack
Function function = original.getFunction();
StackFrameDataType edited = getEditorStack();
Variable[] newVars = edited.getStackVariables();
Variable[] newVars = viewComposite.getStackVariables();
List<Variable> newVarsList = Arrays.asList(newVars);
Collections.sort(newVarsList, StackVariableComparator.get()); // sort for use with getVariableContaining
original.setLocalSize(edited.getLocalSize());
original.setReturnAddressOffset(edited.getReturnAddressOffset());
StackFrame functionStackFrame = function.getStackFrame();
functionStackFrame.setLocalSize(viewComposite.getLocalSize());
functionStackFrame.setReturnAddressOffset(viewComposite.getReturnAddressOffset());
// first-pass: remove deleted params from end of param list if possible
// to avoid custom storage enablement
@ -959,7 +878,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
Variable newSv = null;
try {
DataType dt = originalDTM.resolve(sv.getDataType(), null);
Variable var = original.getVariableContaining(sv.getStackOffset());
Variable var = functionStackFrame.getVariableContaining(sv.getStackOffset());
// TODO: Handle case where new size is smaller but stack alignment will prevent variable shuffle on setDataType - could be problamatic
if (var != null && var.getStackOffset() == sv.getStackOffset() &&
var.getLength() == sv.getLength()) {
@ -972,16 +891,16 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
}
}
else {
if (original.isParameterOffset(sv.getStackOffset()) ||
if (functionStackFrame.isParameterOffset(sv.getStackOffset()) ||
(var instanceof Parameter)) {
// about to make param change - must enable custom storage
original.getFunction().setCustomVariableStorage(true);
functionStackFrame.getFunction().setCustomVariableStorage(true);
}
if (var != null) {
original.clearVariable(var.getStackOffset());
functionStackFrame.clearVariable(var.getStackOffset());
}
newSv = original.createVariable(sv.getName(), sv.getStackOffset(), dt,
SourceType.USER_DEFINED);
newSv = functionStackFrame.createVariable(sv.getName(), sv.getStackOffset(),
dt, SourceType.USER_DEFINED);
}
newSv.setComment(sv.getComment());
}
@ -1002,7 +921,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
newSv.setComment(comment);
}
}
load(new StackFrameDataType(original, originalDTM));
load(function);
clearStatus();
return true;
}
@ -1081,14 +1000,6 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
return false;
}
public DataTypeComponent replace(DataType dataType) throws UsrException {
int rowIndex = getMinIndexSelected();
if (rowIndex < 0) {
throw new UsrException("A component must be selected.");
}
return replace(rowIndex, dataType);
}
private DataTypeComponent replace(int index, DataType dataType) throws UsrException {
try {
DataTypeInstance dti = getDropDataType(index, dataType);
@ -1102,11 +1013,10 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
@Override
public DataTypeComponent replace(int index, DataType dt, int dtLength) throws UsrException {
OffsetPairs offsetSelection = getRelOffsetSelection();
fieldEdited(
DataTypeInstance.getDataTypeInstance(dt, dtLength, usesAlignedLengthComponents()),
index, getDataTypeColumn());
setRelOffsetSelection(offsetSelection);
setSelection(new int[] { index });
return getComponent(index);
}
@ -1135,14 +1045,16 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
@Override
public void restored(DataTypeManager dataTypeManager) {
functionChanged(true);
}
void functionChanged(boolean isRestore) {
StackFrameDataType sfdt = getOriginalComposite();
Function function = sfdt.getFunction();
if (function.isDeleted()) {
// Cancel Editor.
provider.dispose();
// Close the Editor.
PluginTool tool = ((StackEditorProvider) provider).getPlugin().getTool();
tool.setStatusInfo("Stack Editor was closed for " + provider.getName());
provider.dispose();
return;
}
@ -1152,9 +1064,9 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
if (hasChanges) {
// The user has modified the structure so prompt for whether or
// not to reload the structure.
String question = "The program \"dtm.getName()\" has been restored.\n" + "\"" +
currentName + "\" may have changed outside the editor.\n" +
"Discard edits and reload the Stack Editor?";
String text = isRestore ? "may have " : "";
String question = "The function \"" + currentName + "\" " + text +
"changed outside the editor.\n" + "Discard edits and reload the Stack Editor?";
String title = "Reload Stack Editor?";
int response = OptionDialog
.showYesNoDialogWithNoAsDefaultButton(provider.getComponent(), title, question);
@ -1166,10 +1078,83 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
load(function);
}
else {
stackChangedExternally(true);
refresh();
}
}
@Override
public void dataTypeRemoved(DataTypeManager dataTypeManager, DataTypePath path) {
if (dataTypeManager != originalDTM) {
throw new AssertException("Listener only supports original DTM");
}
if (!isLoaded()) {
return;
}
DataType dataType = viewDTM.getDataType(path.getCategoryPath(), path.getDataTypeName());
if (dataType == null || !viewDTM.isViewDataTypeFromOriginalDTM(dataType)) {
return;
}
OffsetPairs offsetSelection = getRelOffsetSelection();
viewDTM.remove(dataType, TaskMonitor.DUMMY);
fireTableDataChanged();
componentDataChanged();
setRelOffsetSelection(offsetSelection);
}
@Override
public void dataTypeRenamed(DataTypeManager dataTypeManager, DataTypePath oldPath,
DataTypePath newPath) {
dataTypeMoved(dataTypeManager, oldPath, newPath);
}
@Override
public void dataTypeMoved(DataTypeManager dataTypeManager, DataTypePath oldPath,
DataTypePath newPath) {
if (dataTypeManager != originalDTM) {
throw new AssertException("Listener only supports original DTM");
}
if (!isLoaded()) {
return;
}
if (oldPath.getDataTypeName().equals(newPath.getDataTypeName())) {
return;
}
// Check for managed datatype changing
DataType originalDt = originalDTM.getDataType(newPath);
if (!(originalDt instanceof DatabaseObject)) {
return;
}
DataType dt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(originalDt));
if (dt == null) {
return;
}
OffsetPairs offsetSelection = getRelOffsetSelection();
try {
dt.setName(newPath.getDataTypeName());
CategoryPath newCategoryPath = newPath.getCategoryPath();
CategoryPath oldCategoryPath = oldPath.getCategoryPath();
if (!newCategoryPath.equals(oldCategoryPath)) {
dt.setCategoryPath(newCategoryPath);
}
}
catch (InvalidNameException | DuplicateNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
fireTableDataChanged();
componentDataChanged();
setRelOffsetSelection(offsetSelection);
}
@Override
public void dataTypeChanged(DataTypeManager dataTypeManager, DataTypePath path) {
if (dataTypeManager != originalDTM) {
@ -1178,65 +1163,25 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
if (!isLoaded()) {
return;
}
DataType dataType = dataTypeManager.getDataType(path);
DataType changedDt = originalDTM.getDataType(path);
if (!(changedDt instanceof DatabaseObject)) {
return;
}
DataType viewDt = viewDTM.findMyDataTypeFromOriginalID(originalDTM.getID(changedDt));
if (viewDt == null) {
return;
}
OffsetPairs offsetSelection = getRelOffsetSelection();
adjustComponents(dataType);
fireTableDataChanged();
componentDataChanged();
setRelOffsetSelection(offsetSelection);
}
@Override
public void dataTypeMoved(DataTypeManager dataTypeManager, DataTypePath oldPath,
DataTypePath newPath) {
if (dataTypeManager != originalDTM) {
throw new AssertException("Listener only supports original DTM");
try {
viewDTM.replaceDataType(viewDt, changedDt, true);
viewComposite.checkForStackGrowth();
}
if (!isLoaded()) {
return;
catch (DataTypeDependencyException e) {
throw new AssertException(e);
}
if (originalDataTypePath != null &&
originalDataTypePath.getDataTypeName().equals(newPath.getDataTypeName()) &&
originalDataTypePath.getCategoryPath().equals(oldPath.getCategoryPath())) {
originalDataTypePath = newPath;
compositeInfoChanged();
}
}
@Override
public void dataTypeRemoved(DataTypeManager dataTypeManager, DataTypePath path) {
if (dataTypeManager != originalDTM) {
throw new AssertException("Listener only supports original DTM");
}
if (!isLoaded()) {
return;
}
OffsetPairs offsetSelection = getRelOffsetSelection();
DataType dataType = dataTypeManager.getDataType(path);
replaceComponents(dataType, DataType.DEFAULT);
fireTableDataChanged();
componentDataChanged();
setRelOffsetSelection(offsetSelection);
}
@Override
public void dataTypeRenamed(DataTypeManager dataTypeManager, DataTypePath oldPath,
DataTypePath newPath) {
if (dataTypeManager != originalDTM) {
throw new AssertException("Listener only supports original DTM");
}
if (!isLoaded()) {
return;
}
DataTypePath originalPath = getOriginalDataTypePath();
if (originalPath == null || !oldPath.equals(originalPath)) {
return;
}
// Don't try to actually rename, since we shouldn't get name change on a
// fabricated stack data type.
OffsetPairs offsetSelection = getRelOffsetSelection();
compositeInfoChanged(); // size info may have changed
fireTableDataChanged();
componentDataChanged();
setRelOffsetSelection(offsetSelection);
@ -1251,9 +1196,21 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
if (!isLoaded()) {
return;
}
DataType oldDataType = viewDTM.getDataType(oldPath);
DataType dt = viewDTM.getDataType(oldPath);
if (dt == null || !viewDTM.isViewDataTypeFromOriginalDTM(dt)) {
return;
}
OffsetPairs offsetSelection = getRelOffsetSelection();
replaceComponents(oldDataType, newDataType);
try {
viewDTM.replaceDataType(dt, newDataType, true);
viewComposite.checkForStackGrowth();
}
catch (DataTypeDependencyException e) {
throw new AssertException(e);
}
compositeInfoChanged(); // size info may have changed
fireTableDataChanged();
componentDataChanged();
setRelOffsetSelection(offsetSelection);
@ -1325,54 +1282,56 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
//**************************************************************************
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
@Override
public DataTypeInstance validateComponentDataType(int index, String dtString)
throws CancelledException, UsrException {
DataType dt = null;
String dtName = "";
dtString = DataTypeHelper.stripWhiteSpace(dtString);
if (index < getNumComponents()) {
DataTypeComponent element = viewComposite.getComponent(index);
dt = element.getDataType();
dtName = dt.getDisplayName();
if (dtString.equals(dtName)) {
return DataTypeInstance.getDataTypeInstance(element.getDataType(),
element.getLength(), usesAlignedLengthComponents());
}
}
DataType newDt = DataTypeHelper.parseDataType(index, dtString, this, originalDTM,
provider.getDtmService());
if (newDt == null) {
if (dt != null) {
throw new UsrException("No data type was specified.");
}
throw new CancelledException();
}
int newLength = newDt.getLength();
checkIsAllowableDataType(newDt);
newDt = resolveDataType(newDt, viewDTM, null);
int maxLength = getMaxReplaceLength(index);
if (newLength <= 0) {
throw new UsrException("Can't currently add this data type--not enough space.");
}
if (maxLength > 0 && newLength > maxLength) {
throw new UsrException(newDt.getDisplayName() + " doesn't fit.");
}
return DataTypeInstance.getDataTypeInstance(newDt, newLength,
usesAlignedLengthComponents());
}
// @Override
// public DataTypeInstance validateComponentDataType(int index, String dtString)
// throws CancelledException, UsrException {
// DataType dt = null;
// String dtName = "";
// dtString = DataTypeHelper.stripWhiteSpace(dtString);
// if (index < getNumComponents()) {
// DataTypeComponent element = viewComposite.getComponent(index);
// dt = element.getDataType();
// dtName = dt.getDisplayName();
// if (dtString.equals(dtName)) {
// return DataTypeInstance.getDataTypeInstance(element.getDataType(),
// element.getLength(), usesAlignedLengthComponents());
// }
// }
//
// DataType newDt = DataTypeHelper.parseDataType(index, dtString, this, originalDTM,
// provider.getDtmService());
//
// if (newDt == null) {
// if (dt != null) {
// throw new UsrException("No data type was specified.");
// }
// throw new CancelledException();
// }
//
// int newLength = newDt.getLength();
//
// checkIsAllowableDataType(newDt);
// newDt = resolveDataType(newDt, viewDTM, null);
// int maxLength = getMaxReplaceLength(index);
// if (newLength <= 0) {
// throw new UsrException("Can't currently add this data type--not enough space.");
// }
// if (maxLength > 0 && newLength > maxLength) {
// throw new UsrException(newDt.getDisplayName() + " doesn't fit.");
// }
// return DataTypeInstance.getDataTypeInstance(newDt, newLength,
// usesAlignedLengthComponents());
// }
@Override
protected void deleteComponent(int rowIndex) {
viewComposite.delete(rowIndex);
compositeInfoChanged(); // info may have changed
fireTableDataChanged();
}
@Override
public DataTypeComponent getComponent(int rowIndex) {
public StackComponentWrapper getComponent(int rowIndex) {
if (viewComposite == null) {
return null;
}

View file

@ -23,16 +23,13 @@ import ghidra.app.plugin.core.compositeeditor.*;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataTypePath;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolType;
import ghidra.program.util.ProgramChangeRecord;
import ghidra.program.util.ProgramEvent;
import ghidra.util.InvalidNameException;
import ghidra.util.Msg;
import ghidra.util.task.SwingUpdateManager;
/**
* Editor for a Function Stack.
@ -45,6 +42,31 @@ public class StackEditorProvider
private Function function;
private StackEditorModel stackModel;
boolean scheduleRefreshName = false;
boolean scheduleReload = false;
/**
* Delay model update caused by Program change events.
*/
SwingUpdateManager delayedUpdateMgr = new SwingUpdateManager(200, 200, () -> {
try {
if (function.isDeleted()) {
stackModel.functionChanged(false);
return;
}
if (scheduleRefreshName) {
updateTitle();
}
if (scheduleReload) {
stackModel.functionChanged(false);
}
}
finally {
scheduleRefreshName = false;
scheduleReload = false;
}
});
public StackEditorProvider(Plugin plugin, Function function) {
super(plugin);
this.program = function.getProgram();
@ -56,15 +78,22 @@ public class StackEditorProvider
initializeActions();
editorPanel = new StackEditorPanel(program, stackModel, this);
setTitle(getName() + " - " + getProviderSubTitle(function));
updateTitle();
plugin.getTool().addComponentProvider(this, true);
addActionsToTool();
editorPanel.getTable().requestFocus();
}
@Override
protected void updateTitle() {
setTabText(function.getName());
setTitle(getName() + " - " + getProviderSubTitle(function));
}
@Override
public void dispose() {
delayedUpdateMgr.dispose();
program.removeListener(this);
super.dispose();
}
@ -84,6 +113,11 @@ public class StackEditorProvider
return "Stack Editor";
}
@Override
protected String getDisplayName() {
return "stack frame: " + function.getName();
}
@Override
public String getHelpName() {
return "Stack_Editor";
@ -147,32 +181,6 @@ public class StackEditorProvider
return actionMgr.getAllActions();
}
private void refreshName() {
StackFrameDataType origDt = stackModel.getOriginalComposite();
StackFrameDataType viewDt = stackModel.getEditorStack();
String oldName = origDt.getName();
String newName = function.getName();
if (oldName.equals(newName)) {
return;
}
setTitle("Stack Editor: " + newName);
try {
origDt.setName(newName);
if (viewDt.getName().equals(oldName)) {
viewDt.setName(newName);
}
}
catch (InvalidNameException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
CategoryPath oldCategoryPath = origDt.getCategoryPath();
DataTypePath oldDtPath = new DataTypePath(oldCategoryPath, oldName);
DataTypePath newDtPath = new DataTypePath(oldCategoryPath, newName);
stackModel.dataTypeRenamed(stackModel.getOriginalDataTypeManager(), oldDtPath, newDtPath);
}
@Override
public void domainObjectChanged(DomainObjectChangedEvent event) {
if (!isVisible()) {
@ -181,35 +189,40 @@ public class StackEditorProvider
int recordCount = event.numRecords();
for (int i = 0; i < recordCount; i++) {
DomainObjectChangeRecord rec = event.getChangeRecord(i);
EventType eventType = rec.getEventType();
if (eventType == DomainObjectEvent.RESTORED) {
refreshName();
// NOTE: editorPanel should be notified of restored datatype manager via the
// CompositeViewerModel's DataTypeManagerChangeListener restored method
return;
// NOTE: RESTORED event can be ignored here since the model will be notified
// of restored datatype manager via the CompositeViewerModel's
// DataTypeManagerChangeListener restored method.
if (eventType == DomainObjectEvent.FILE_CHANGED) {
scheduleRefreshName = true;
delayedUpdateMgr.updateLater();
continue;
}
if (eventType instanceof ProgramEvent type) {
switch (type) {
case FUNCTION_REMOVED:
Function func = (Function) ((ProgramChangeRecord) rec).getObject();
if (func == function) {
this.dispose();
// Close the Editor.
tool.setStatusInfo("Stack Editor was closed for " + getName());
dispose();
return;
}
return;
break;
case SYMBOL_RENAMED:
case SYMBOL_DATA_CHANGED:
Symbol sym = (Symbol) ((ProgramChangeRecord) rec).getObject();
SymbolType symType = sym.getSymbolType();
if (symType == SymbolType.LABEL) {
if (sym.isPrimary() &&
sym.getAddress().equals(function.getEntryPoint())) {
refreshName();
}
if (sym.isPrimary() && sym.getAddress().equals(function.getEntryPoint())) {
scheduleRefreshName = true;
delayedUpdateMgr.updateLater();
}
else if (inCurrentFunction(rec)) {
reloadFunction();
scheduleReload = true;
delayedUpdateMgr.updateLater();
}
break;
case FUNCTION_CHANGED:
@ -217,15 +230,15 @@ public class StackEditorProvider
case SYMBOL_REMOVED:
case SYMBOL_ADDRESS_CHANGED:
if (inCurrentFunction(rec)) {
reloadFunction();
scheduleReload = true;
delayedUpdateMgr.updateLater();
}
break;
case SYMBOL_PRIMARY_STATE_CHANGED:
sym = (Symbol) ((ProgramChangeRecord) rec).getNewValue();
symType = sym.getSymbolType();
if (symType == SymbolType.LABEL &&
sym.getAddress().equals(function.getEntryPoint())) {
refreshName();
if (sym.getAddress().equals(function.getEntryPoint())) {
scheduleRefreshName = true;
delayedUpdateMgr.updateLater();
}
break;
default:
@ -234,16 +247,6 @@ public class StackEditorProvider
}
}
private void reloadFunction() {
if (!stackModel.hasChanges()) {
stackModel.load(function);
}
else {
stackModel.stackChangedExternally(true);
editorPanel.setStatus("Stack may have been changed externally--data may be stale.");
}
}
private boolean inCurrentFunction(DomainObjectChangeRecord record) {
if (!(record instanceof ProgramChangeRecord)) {
return false;

View file

@ -1,131 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.stackeditor;
import ghidra.docking.settings.Settings;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Variable;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.DuplicateNameException;
public class StackPieceDataType extends DataTypeImpl {
private final Variable variable;
StackPieceDataType(Variable var, DataTypeManager dataMgr) {
super(CategoryPath.ROOT, getPieceName(var), dataMgr);
variable = var;
}
private static String getPieceName(Variable var) {
VariableStorage storage = var.getVariableStorage();
Varnode stackVarnode = storage.getLastVarnode();
int pieceLen = stackVarnode.getSize();
return var.getDataType().getName() + ":" + pieceLen + " (piece)";
}
@Override
public DataType clone(DataTypeManager dtm) {
if (dtm == getDataTypeManager()) {
return this;
}
throw new IllegalArgumentException("May not be cloned with new DataTypeManager");
}
@Override
public DataType copy(DataTypeManager dtm) {
throw new UnsupportedOperationException();
}
@Override
public void setCategoryPath(CategoryPath path) throws DuplicateNameException {
throw new UnsupportedOperationException();
}
@Override
public void setName(String name) throws InvalidNameException {
throw new UnsupportedOperationException();
}
@Override
public void setNameAndCategory(CategoryPath path, String name)
throws InvalidNameException, DuplicateNameException {
throw new UnsupportedOperationException();
}
@Override
public String getMnemonic(Settings settings) {
DataType dt = variable.getDataType();
return dt.getMnemonic(settings) + ":" + getLength();
}
@Override
public int getLength() {
VariableStorage storage = variable.getVariableStorage();
Varnode stackVarnode = storage.getLastVarnode();
return stackVarnode.getSize();
}
@Override
public String getDescription() {
// We could provide a description if needed
return null;
}
@Override
public Object getValue(MemBuffer buf, Settings settings, int length) {
return null;
}
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
return null;
}
@Override
public boolean isEquivalent(DataType dt) {
return false;
}
@Override
public void dataTypeSizeChanged(DataType dt) {
// ignore
}
@Override
public void dataTypeDeleted(DataType dt) {
// ignore
}
@Override
public void dataTypeReplaced(DataType oldDt, DataType newDt) {
// ignore
}
@Override
public void dataTypeNameChanged(DataType dt, String oldName) {
// ignore
}
@Override
public boolean dependsOn(DataType dt) {
return false;
}
}

View file

@ -44,8 +44,6 @@ public abstract class AbstractStackEditorProviderTest extends AbstractStackEdito
env.showTool();
}
//==================================================================================================
// Private Methods
//==================================================================================================
@ -140,7 +138,7 @@ public abstract class AbstractStackEditorProviderTest extends AbstractStackEdito
Parameter parameter = function.getParameter(parameterIndex);
SetVariableNameCmd cmd =
new SetVariableNameCmd(parameter, newName, SourceType.USER_DEFINED);
applyCmd(program, cmd);
program.withTransaction("Rename Parameter", () -> cmd.applyTo(program)); // avoid blocking
return parameter;
}

View file

@ -384,11 +384,29 @@ public class StackEditorCellEditTest extends AbstractStackEditorTest {
typeInCellEditor("testName1\n");
assertTrue(applyAction.isEnabled());
// Change name back and apply disables
// Attempt use of another default name
clickTableCell(getTable(), 0, colNum, 2);
assertIsEditingField(0, colNum);
selectAllInCellEditor();
typeInCellEditor("local_8\n");
assertStatus("Cannot set a stack variable name to a default value");
assertCellString("testName1", 0, colNum);
assertTrue(applyAction.isEnabled());
// Change name back to original and apply disables
clickTableCell(getTable(), 0, colNum, 2);
assertIsEditingField(0, colNum);
selectAllInCellEditor();
typeInCellEditor("local_10\n");
assertCellString("local_10", 0, colNum);
assertTrue(!applyAction.isEnabled());
// Change name back to original and apply disables
clickTableCell(getTable(), 0, colNum, 2);
assertIsEditingField(0, colNum);
selectAllInCellEditor();
typeInCellEditor("\b\n"); // clear entry
assertCellString("local_10", 0, colNum);
assertTrue(!applyAction.isEnabled());
}

View file

@ -200,7 +200,7 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest {
public void testCentralComponentSelectedEnablement() throws Exception {
init(SIMPLE_STACK);
// Check enablement on central component selected.
// Check enablement on central defined-component selected which has undefined datatype
runSwing(() -> model.setSelection(new int[] { 1 }));
int numBytes = getModel().getMaxReplaceLength(1);

View file

@ -19,8 +19,7 @@ import static org.junit.Assert.*;
import java.awt.Window;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.*;
import org.junit.Test;
@ -324,6 +323,8 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
int parameterIndex = 0;
Parameter parameter = renameParameterInListing(parameterIndex, "listing.test.name");
waitForSwing();
// Verify the provider's name for that parameter is updated
String modelParameterName = getParameterNameFromModel(parameterIndex);
assertEquals(parameter.getName(), modelParameterName);
@ -342,6 +343,9 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
// Change the name of a parameter in the Listing
renameParameterInListing(parameterIndex, "listing.test.name");
JDialog reloadDialog = waitForJDialog("Reload Stack Editor?");
pressButton(reloadDialog, "No");
// Verify the name of the parameter in the provider is not changed from the original edit
String currentModelName = getParameterNameFromModel(parameterIndex);
assertEquals(newModelName, currentModelName);
@ -360,6 +364,9 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
// Change the name of a parameter in the Listing
renameParameterInListing(parameterIndex, "listing.test.name");
JDialog reloadDialog = waitForJDialog("Reload Stack Editor?");
pressButton(reloadDialog, "No");
// Press Apply
apply();
@ -386,6 +393,9 @@ public class StackEditorProvider1Test extends AbstractStackEditorProviderTest {
String newListingText = "listing.test.name";
renameParameterInListing(parameterIndex, newListingText);
JDialog reloadDialog = waitForJDialog("Reload Stack Editor?");
pressButton(reloadDialog, "No");
// Press Apply
apply();

View file

@ -36,8 +36,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
private final ComponentDBAdapter adapter;
private final DBRecord record; // null record -> immutable component
private final CompositeDB parent;
private DataType cachedDataType; // required for bit-fields during packing process
private final DataType cachedDataType; // used by immutable defined component (no record)
private int ordinal;
private int offset;
@ -56,9 +55,15 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
*/
DataTypeComponentDB(DataTypeManagerDB dataMgr, CompositeDB parent, int ordinal, int offset,
DataType datatype, int length) {
this(dataMgr, parent, ordinal, offset);
this.dataMgr = dataMgr;
this.parent = parent;
this.cachedDataType = datatype;
this.ordinal = ordinal;
this.offset = offset;
this.length = length;
this.record = null;
this.adapter = null;
}
/**
@ -73,6 +78,8 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
this.dataMgr = dataMgr;
this.parent = parent;
this.ordinal = ordinal;
this.cachedDataType = null;
this.offset = offset;
this.length = 1;
this.record = null;
@ -90,11 +97,13 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
DBRecord record) {
this.dataMgr = dataMgr;
this.adapter = adapter;
this.cachedDataType = null;
this.record = record;
this.parent = parent;
ordinal = record.getIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL);
offset = record.getIntValue(ComponentDBAdapter.COMPONENT_OFFSET_COL);
length = record.getIntValue(ComponentDBAdapter.COMPONENT_SIZE_COL);
this.ordinal = record.getIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL);
this.offset = record.getIntValue(ComponentDBAdapter.COMPONENT_OFFSET_COL);
this.length = record.getIntValue(ComponentDBAdapter.COMPONENT_SIZE_COL);
if (isZeroBitFieldComponent()) {
length = 0; // previously stored as 1, force to 0
}

View file

@ -1378,11 +1378,13 @@ class StructureDB extends CompositeDB implements StructureInternal {
* @param componentName name of component replacement (may be null)
* @param comment comment for component replacement (may be null)
* @return new/updated component (may be null if replacement is not a defined component)
* @throws IllegalArgumentException if unable to identify/make sufficient space
* @throws IOException if an IO error occurs
*/
private DataTypeComponent doComponentReplacement(
LinkedList<DataTypeComponentDB> replacedComponents, int offset, DataType dataType,
int length, String componentName, String comment) throws IOException {
int length, String componentName, String comment)
throws IllegalArgumentException, IOException {
// Attempt quick update of a single defined component if possible.
// A quick update requires that component characteristics including length, offset,
@ -1416,13 +1418,14 @@ class StructureDB extends CompositeDB implements StructureInternal {
@Override
public final DataTypeComponent replace(int ordinal, DataType dataType, int length)
throws IllegalArgumentException {
throws IllegalArgumentException, IndexOutOfBoundsException {
return replace(ordinal, dataType, length, null, null);
}
@Override
public DataTypeComponent replace(int ordinal, DataType dataType, int length,
String componentName, String comment) {
String componentName, String comment)
throws IllegalArgumentException, IndexOutOfBoundsException {
lock.acquire();
try {
checkDeleted();

View file

@ -277,16 +277,18 @@ class FunctionStackFrame implements StackFrame {
function.setLocalSize(size);
}
/*
* (non-Javadoc)
* @see ghidra.program.model.listing.StackFrame#getParameterSize()
*/
@Override
public int getParameterSize() {
function.manager.lock.acquire();
try {
checkIsValid();
// NOTE: This logic is sensitive to the existance of Local variables at the incorrect
// stack offset placed before parameters. This can occur when adjustments are made to
// the prototype model's stack pentry specification. Unfortunately, the distinction
// between a parameter and a local is locked-in at the time of creation due to the
// use of distinct symbol types.
int baseOffset = 0;
Integer base = VariableUtilities.getBaseStackParamOffset(function);
if (base != null) {

View file

@ -44,7 +44,8 @@ public interface Structure extends Composite {
public DataTypeComponent getComponent(int ordinal) throws IndexOutOfBoundsException;
/**
* Gets the first defined component located at or after the specified offset.
* Gets the first defined component located at or after the specified offset. If a
* component contains the specified offset that component will be returned.
* Note: The returned component may be a zero-length component.
*
* @param offset the byte offset into this structure

View file

@ -107,13 +107,6 @@ public interface StackFrame {
*/
public int getParameterOffset();
// /**
// * Set the offset on the stack of the parameters.
// *
// * @param offset the start offset of parameters on the stack
// */
// public void setParameterOffset(int offset) throws InvalidInputException;
/**
* Returns true if specified offset could correspond to a parameter
* @param offset