GP-862 Refactor of Composite interface and internals. Changes made to

packing and alignment methods (see WhatsNew.html for API changes).
This commit is contained in:
ghidra1 2021-02-02 20:43:39 -05:00
parent 3b867b3444
commit da800b6e41
166 changed files with 6316 additions and 5486 deletions

View file

@ -201,7 +201,9 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB
@Override
protected void close() {
super.close();
dataTypeManager.dispose();
if (dataTypeManager != null) {
dataTypeManager.dispose();
}
}
@Override
@ -283,11 +285,15 @@ public class DataTypeArchiveDB extends DomainObjectAdapterDB
* notification the a data type has changed
* @param dataTypeID the id of the data type that changed.
* @param type the type of the change (moved, renamed, etc.)
* @param isAutoResponseChange true if change is an auto-response change caused by
* another datatype's change (e.g., size, alignment), else false in which case this
* change will be added to archive change-set to aid merge conflict detection.
* @param oldValue the old data type.
* @param newValue the new data type.
*/
public void dataTypeChanged(long dataTypeID, int type, Object oldValue, Object newValue) {
if (recordChanges) {
public void dataTypeChanged(long dataTypeID, int type, boolean isAutoResponseChange,
Object oldValue, Object newValue) {
if (recordChanges && !isAutoResponseChange) {
((DataTypeArchiveDBChangeSet) changeSet).dataTypeChanged(dataTypeID);
}
changed = true;

View file

@ -813,11 +813,17 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* notification the a datatype has changed
* @param dataTypeID the id of the datatype that changed.
* @param type the type of the change (moved, renamed, etc.)
* @param isAutoChange true if change was an automatic change in response to
* another datatype's change (e.g., size, alignment), else false in which case this
* change will be added to program change-set to aid merge conflict detection.
* @param oldValue the old datatype.
* @param newValue the new datatype.
*/
public void dataTypeChanged(long dataTypeID, int type, Object oldValue, Object newValue) {
if (recordChanges) {
public void dataTypeChanged(long dataTypeID, int type, boolean isAutoChange,
Object oldValue, Object newValue) {
// TODO: do not need to record type changes for packed composite change which is in repsonse
// to component size or alignment change.
if (recordChanges && !isAutoChange) {
((ProgramDBChangeSet) changeSet).dataTypeChanged(dataTypeID);
}
changed = true;

View file

@ -115,6 +115,11 @@ class ArrayDB extends DataTypeDB implements Array {
return getDataType().isDynamicallySized();
}
@Override
public boolean isZeroLength() {
return getDataType().isZeroLength();
}
@Override
public int getLength() {
checkIsValid();
@ -244,10 +249,10 @@ class ArrayDB extends DataTypeDB implements Array {
notifyNameChanged(myOldName);
}
if (getLength() != oldLength) {
notifySizeChanged();
notifySizeChanged(false);
}
else {
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
}
}
@ -265,10 +270,21 @@ class ArrayDB extends DataTypeDB implements Array {
public void dataTypeSizeChanged(DataType dt) {
lock.acquire();
try {
checkIsValid();
if (checkIsValid() && dt == getDataType()) {
notifySizeChanged(true);
}
}
finally {
lock.release();
}
}
if (dt == getDataType()) {
notifySizeChanged();
@Override
public void dataTypeAlignmentChanged(DataType dt) {
lock.acquire();
try {
if (checkIsValid() && dt == getDataType()) {
notifyAlignmentChanged(true);
}
}
finally {

View file

@ -28,17 +28,7 @@ import ghidra.util.exception.AssertException;
/**
* Database implementation for a structure or union.
*/
abstract class CompositeDB extends DataTypeDB implements Composite {
// Internal Alignment Constants
protected static final int UNALIGNED = CompositeDBAdapter.UNALIGNED;
protected static final int ALIGNED_NO_PACKING = CompositeDBAdapter.ALIGNED_NO_PACKING;
// Otherwise the packing value (1 to (2**32 - 1)).
// External (Minimum) Alignment Constants
protected static final int MACHINE_ALIGNED = CompositeDBAdapter.MACHINE_ALIGNED;
protected static final int DEFAULT_ALIGNED = CompositeDBAdapter.DEFAULT_ALIGNED;
// Otherwise the alignment value (1 to (2**32 - 1)).
abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
protected CompositeDBAdapter compositeAdapter;
protected ComponentDBAdapter componentAdapter;
@ -81,7 +71,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
* @return preferred component length
*/
protected int getPreferredComponentLength(DataType dataType, int length) {
if ((isInternallyAligned() || (this instanceof Union)) && !(dataType instanceof Dynamic)) {
if ((isPackingEnabled() || (this instanceof Union)) && !(dataType instanceof Dynamic)) {
length = -1; // force use of datatype size
}
int dtLength = dataType.getLength();
@ -114,7 +104,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
* @param bitfieldComponent bitfield component
* @param oldDt affected datatype which has been removed or replaced
* @param newDt replacement datatype
* @param true if bitfield component was modified
* @return true if bitfield component was modified
* @throws InvalidDataTypeException if bitfield was based upon oldDt but new
* datatype is invalid for a bitfield
*/
@ -200,7 +190,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
@Override
public boolean isDynamicallySized() {
return isInternallyAligned();
return isPackingEnabled();
}
@Override
@ -301,7 +291,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
* @throws IllegalArgumentException if the data type is invalid.
*/
protected void validateDataType(DataType dataType) {
if (isInternallyAligned() && dataType == DataType.DEFAULT) {
if (isPackingEnabled() && dataType == DataType.DEFAULT) {
throw new IllegalArgumentException(
"The DEFAULT data type is not allowed in an aligned composite data type.");
}
@ -335,7 +325,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
checkDeleted();
record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime);
compositeAdapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -352,7 +342,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
checkDeleted();
record.setLongValue(CompositeDBAdapter.COMPOSITE_SOURCE_SYNC_TIME_COL, lastChangeTime);
compositeAdapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -374,7 +364,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
checkDeleted();
record.setLongValue(CompositeDBAdapter.COMPOSITE_UNIVERSAL_DT_ID, id.getValue());
compositeAdapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -398,7 +388,7 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
checkDeleted();
record.setLongValue(CompositeDBAdapter.COMPOSITE_SOURCE_ARCHIVE_ID_COL, id.getValue());
compositeAdapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -409,56 +399,134 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
}
@Override
public int getPackingValue() {
int dbValue = record.getIntValue(CompositeDBAdapter.COMPOSITE_INTERNAL_ALIGNMENT_COL);
if (dbValue == CompositeDB.UNALIGNED || dbValue == CompositeDB.ALIGNED_NO_PACKING) {
return 0;
protected final int getNonPackedAlignment() {
int alignment;
int minimumAlignment = getStoredMinimumAlignment();
if (minimumAlignment == DEFAULT_ALIGNMENT) {
alignment = 1;
}
else if (minimumAlignment == MACHINE_ALIGNMENT) {
alignment = getDataOrganization().getMachineAlignment();
}
else {
alignment = minimumAlignment;
}
return alignment;
}
/**
* Get computed alignment and optionally update record. May only be invoked with
* lock acquired.
* @param updateRecord if true record should be updated without timestamp change
* @return computed alignment
*/
protected abstract int getComputedAlignment(boolean updateRecord);
@Override
public final int getAlignment() {
lock.acquire();
try {
return getComputedAlignment(checkIsValid() && dataMgr.isTransactionActive());
}
finally {
lock.release();
}
return dbValue;
}
@Override
public void setPackingValue(int packingValue) {
boolean changed = false;
if (!isInternallyAligned()) {
doSetInternallyAligned(true);
changed = true;
public final void repack() {
lock.acquire();
try {
checkDeleted();
repack(false, true);
}
if (packingValue != getPackingValue()) {
doSetPackingValue(packingValue);
changed = true;
}
if (changed) {
adjustInternalAlignment(false);
notifyAlignmentChanged();
finally {
lock.release();
}
}
public void doSetPackingValue(int packingValue) {
if (packingValue < 0) {
packingValue = NOT_PACKING;
/**
* Repack components within this composite based on the current packing, alignment
* and {@link DataOrganization} settings. Non-packed Structures: change detection
* is limited to component count and length is assumed to already be correct.
* May only be invoked with lock acquired.
* <p>
* NOTE: If modifications to stored length are made prior to invoking this method,
* detection of a size change may not be possible.
* <p>
* NOTE: Currently a change in calculated alignment can not be provided since
* this value is not stored.
*
* @param isAutoChange true if changes are in response to another another datatype's change.
* @param notify if true notification will be sent to parents if a size change
* or component placement change is detected.
* @return true if a layout change was detected.
*/
protected abstract boolean repack(boolean isAutoChange, boolean notify);
@Override
public int getStoredPackingValue() {
lock.acquire();
try {
checkIsValid();
return record.getIntValue(CompositeDBAdapter.COMPOSITE_PACKING_COL);
}
finally {
lock.release();
}
}
@Override
public PackingType getPackingType() {
int packing = getStoredPackingValue();
if (packing < DEFAULT_PACKING) {
return PackingType.DISABLED;
}
if (packing == DEFAULT_PACKING) {
return PackingType.DEFAULT;
}
return PackingType.EXPLICIT;
}
@Override
public int getExplicitPackingValue() {
return getStoredPackingValue();
}
@Override
public void setExplicitPackingValue(int packingValue) {
if (packingValue <= 0) {
throw new IllegalArgumentException(
"explicit packing value must be positive: " + packingValue);
}
setStoredPackingValue(packingValue);
}
@Override
public void setToDefaultPacking() {
setStoredPackingValue(DEFAULT_PACKING);
}
private void setStoredPackingValue(int packingValue) {
if (packingValue < NO_PACKING) {
throw new IllegalArgumentException("invalid packing value: " + packingValue);
}
lock.acquire();
try {
checkDeleted();
if (packingValue == getPackingValue()) {
int oldPackingValue = getStoredPackingValue();
if (packingValue == oldPackingValue) {
return;
}
int dbPackingValue;
if (packingValue == NOT_PACKING) {
if (isInternallyAligned()) {
dbPackingValue = ALIGNED_NO_PACKING;
}
else {
dbPackingValue = UNALIGNED;
}
if (oldPackingValue == NO_PACKING || packingValue == NO_PACKING) {
// force default alignment when transitioning to or from disabled packing
record.setIntValue(CompositeDBAdapter.COMPOSITE_MIN_ALIGN_COL, DEFAULT_ALIGNMENT);
}
else {
dbPackingValue = packingValue;
}
record.setIntValue(CompositeDBAdapter.COMPOSITE_INTERNAL_ALIGNMENT_COL, dbPackingValue);
record.setIntValue(CompositeDBAdapter.COMPOSITE_PACKING_COL, packingValue);
compositeAdapter.updateRecord(record, true);
if (!repack(false, true)) {
dataMgr.dataTypeChanged(this, false);
}
}
catch (IOException e) {
dataMgr.dbError(e);
@ -469,125 +537,72 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
}
@Override
public boolean isDefaultAligned() {
int dbValue = record.getIntValue(CompositeDBAdapter.COMPOSITE_EXTERNAL_ALIGNMENT_COL);
return (dbValue == CompositeDB.DEFAULT_ALIGNED);
public AlignmentType getAlignmentType() {
int minimumAlignment = getStoredMinimumAlignment();
if (minimumAlignment < DEFAULT_ALIGNMENT) {
return AlignmentType.MACHINE;
}
if (minimumAlignment == DEFAULT_ALIGNMENT) {
return AlignmentType.DEFAULT;
}
return AlignmentType.EXPLICIT;
}
@Override
public boolean isMachineAligned() {
int dbValue = record.getIntValue(CompositeDBAdapter.COMPOSITE_EXTERNAL_ALIGNMENT_COL);
return (dbValue == CompositeDB.MACHINE_ALIGNED);
public void setToDefaultAligned() {
setStoredMinimumAlignment(DEFAULT_ALIGNMENT);
}
@Override
public int getMinimumAlignment() {
int dbValue = record.getIntValue(CompositeDBAdapter.COMPOSITE_EXTERNAL_ALIGNMENT_COL);
if (dbValue == CompositeDB.MACHINE_ALIGNED) {
return getMachineAlignment();
}
if (dbValue == CompositeDB.DEFAULT_ALIGNED) {
return getDefaultAlignment();
}
return dbValue;
}
private int getDefaultAlignment() {
return Composite.DEFAULT_ALIGNMENT_VALUE;
}
private int getMachineAlignment() {
return dataMgr.getDataOrganization().getMachineAlignment();
public void setToMachineAligned() {
setStoredMinimumAlignment(MACHINE_ALIGNMENT);
}
@Override
public void setMinimumAlignment(int externalAlignment) {
boolean changed = false;
if (!isInternallyAligned()) {
doSetInternallyAligned(true);
changed = true;
}
if (doSetMinimumAlignment(externalAlignment)) {
changed = true;
}
if (changed) {
adjustInternalAlignment(false);
notifyAlignmentChanged();
}
}
public boolean doSetMinimumAlignment(int externalAlignment) {
if (externalAlignment < 1) {
externalAlignment = DEFAULT_ALIGNED;
}
return modifyAlignment(externalAlignment);
public int getExplicitMinimumAlignment() {
return getStoredMinimumAlignment();
}
@Override
public void setToDefaultAlignment() {
boolean changed = false;
if (!isInternallyAligned()) {
doSetInternallyAligned(true);
changed = true;
public int getStoredMinimumAlignment() {
lock.acquire();
try {
checkIsValid();
return record.getIntValue(CompositeDBAdapter.COMPOSITE_MIN_ALIGN_COL);
}
if (doSetToDefaultAlignment()) {
changed = true;
finally {
lock.release();
}
if (changed) {
adjustInternalAlignment(false);
notifyAlignmentChanged();
}
}
public boolean doSetToDefaultAlignment() {
return modifyAlignment(CompositeDB.DEFAULT_ALIGNED);
}
@Override
public void setToMachineAlignment() {
boolean changed = false;
if (!isInternallyAligned()) {
doSetInternallyAligned(true);
changed = true;
}
if (doSetToMachineAlignment()) {
changed = true;
}
if (changed) {
adjustInternalAlignment(false);
notifyAlignmentChanged();
public void setExplicitMinimumAlignment(int minimumAlignment) {
if (minimumAlignment <= 0) {
throw new IllegalArgumentException(
"explicit minimum alignment must be positive: " + minimumAlignment);
}
setStoredMinimumAlignment(minimumAlignment);
}
public boolean doSetToMachineAlignment() {
return modifyAlignment(CompositeDB.MACHINE_ALIGNED);
}
private boolean modifyAlignment(int dbExternalAlignment) {
private void setStoredMinimumAlignment(int minimumAlignment) {
if (minimumAlignment < MACHINE_ALIGNMENT) {
throw new IllegalArgumentException(
"invalid minimum alignment value: " + minimumAlignment);
}
lock.acquire();
try {
checkDeleted();
if (isMachineAligned()) {
if (dbExternalAlignment == MACHINE_ALIGNED) {
return false;
}
if (minimumAlignment == getStoredMinimumAlignment()) {
return;
}
if (isDefaultAligned()) {
if (dbExternalAlignment == DEFAULT_ALIGNED) {
return false;
}
}
else if (dbExternalAlignment == getMinimumAlignment()) {
return false;
}
record.setIntValue(CompositeDBAdapter.COMPOSITE_EXTERNAL_ALIGNMENT_COL,
dbExternalAlignment);
record.setIntValue(CompositeDBAdapter.COMPOSITE_MIN_ALIGN_COL, minimumAlignment);
compositeAdapter.updateRecord(record, true);
return true;
if (!repack(false, true)) {
dataMgr.dataTypeChanged(this, false);
}
}
catch (IOException e) {
dataMgr.dbError(e);
return false;
}
finally {
lock.release();
@ -618,184 +633,40 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
}
}
/**
* Notification that this composite data type's alignment has changed.
*/
protected void notifyAlignmentChanged() {
// TODO: This method is not properly invoked when new components are
// added which could change the alignment of this composite
for (DataType dt : dataMgr.getParentDataTypes(key)) {
if (dt instanceof Composite) {
Composite composite = (Composite) dt;
composite.dataTypeAlignmentChanged(this);
}
}
dataMgr.dataTypeChanged(this);
}
@Override
public boolean isInternallyAligned() {
int dbValue = record.getIntValue(CompositeDBAdapter.COMPOSITE_INTERNAL_ALIGNMENT_COL);
return dbValue != UNALIGNED;
}
@Override
public void setInternallyAligned(boolean aligned) {
if (aligned == isInternallyAligned()) {
public void setPackingEnabled(boolean enabled) {
if (enabled == isPackingEnabled()) {
return;
}
doSetInternallyAligned(aligned);
adjustInternalAlignment(true);
notifyAlignmentChanged();
}
protected void doSetInternallyAligned(boolean aligned) {
lock.acquire();
try {
checkDeleted();
if (aligned == isInternallyAligned()) {
return;
}
int dbValue = aligned ? CompositeDB.ALIGNED_NO_PACKING : CompositeDB.UNALIGNED;
record.setIntValue(CompositeDBAdapter.COMPOSITE_INTERNAL_ALIGNMENT_COL, dbValue);
if (!aligned) {
int dbExternalAlignment = CompositeDB.DEFAULT_ALIGNED;
record.setIntValue(CompositeDBAdapter.COMPOSITE_EXTERNAL_ALIGNMENT_COL,
dbExternalAlignment);
}
compositeAdapter.updateRecord(record, true);
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
lock.release();
}
}
protected void setAlignment(Composite composite, boolean notify) {
doSetInternallyAligned(composite.isInternallyAligned());
doSetPackingValue(composite.getPackingValue());
if (composite.isDefaultAligned()) {
doSetToDefaultAlignment();
}
else if (composite.isMachineAligned()) {
doSetToMachineAlignment();
}
else {
doSetMinimumAlignment(composite.getMinimumAlignment());
}
adjustInternalAlignment(notify);
setStoredPackingValue(enabled ? DEFAULT_PACKING : NO_PACKING);
}
/**
* Adjusts the internal alignment of components within this composite based on
* the current settings of the internal alignment, packing, alignment type and
* minimum alignment value. This method should be called whenever any of the
* above settings are changed or whenever a components data type is changed or a
* component is added or removed.
*
* @param notify
* Copy packing and alignment settings from specified composite without
* repacking or notification.
* @param composite instance whose packing and alignment are to be copied
* @throws IOException if database IO error occured
*/
protected abstract void adjustInternalAlignment(boolean notify);
@Override
public int getAlignment() {
// TODO: use cached value if available (requires DB change to facilitate)
return CompositeAlignmentHelper.getAlignment(getDataOrganization(), this);
}
/**
* Dump all components for use in {@link #toString()} representation.
*
* @param buffer string buffer
* @param pad padding to be used with each component output line
*/
protected void dumpComponents(StringBuilder buffer, String pad) {
// limit output of filler components for unaligned structures
DataTypeComponent[] components = getDefinedComponents();
for (DataTypeComponent dtc : components) {
DataType dataType = dtc.getDataType();
buffer.append(pad + dtc.getOffset());
buffer.append(pad + dataType.getName());
if (dataType instanceof BitFieldDataType) {
BitFieldDataType bfDt = (BitFieldDataType) dataType;
buffer.append("(");
buffer.append(Integer.toString(bfDt.getBitOffset()));
buffer.append(")");
}
buffer.append(pad + dtc.getLength());
buffer.append(pad + dtc.getFieldName());
String comment = dtc.getComment();
if (comment == null) {
comment = "";
}
buffer.append(pad + "\"" + comment + "\"");
buffer.append("\n");
}
protected void doSetPackingAndAlignment(CompositeInternal composite) throws IOException {
record.setIntValue(CompositeDBAdapter.COMPOSITE_MIN_ALIGN_COL,
composite.getStoredMinimumAlignment());
record.setIntValue(CompositeDBAdapter.COMPOSITE_PACKING_COL,
composite.getStoredPackingValue());
compositeAdapter.updateRecord(record, true);
}
@Override
public String toString() {
StringBuilder stringBuffer = new StringBuilder();
stringBuffer.append(getPathName() + "\n");
stringBuffer.append(getAlignmentSettingsString() + "\n");
stringBuffer.append(getTypeName() + " " + getDisplayName() + " {\n");
dumpComponents(stringBuffer, " ");
stringBuffer.append("}\n");
stringBuffer.append(
"Size = " + getLength() + " Actual Alignment = " + getAlignment() + "\n");
return stringBuffer.toString();
return CompositeDataTypeImpl.toString(this);
}
private String getTypeName() {
if (this instanceof Structure) {
return "Structure";
}
else if (this instanceof Union) {
return "Union";
}
return "";
}
private String getAlignmentSettingsString() {
StringBuffer stringBuffer = new StringBuffer();
if (!isInternallyAligned()) {
stringBuffer.append("Unaligned");
}
else if (isDefaultAligned()) {
stringBuffer.append("Aligned");
}
else if (isMachineAligned()) {
stringBuffer.append("Machine aligned");
}
else {
long alignment = getMinimumAlignment();
stringBuffer.append("align(" + alignment + ")");
}
stringBuffer.append(getPackingString());
return stringBuffer.toString();
}
private String getPackingString() {
if (!isInternallyAligned()) {
return "";
}
long packingValue = getPackingValue();
if (packingValue == Composite.NOT_PACKING) {
return "";
}
return " pack(" + packingValue + ")";
}
/**
* Perform any neccessary component adjustments based on
* sizes and alignment of components differing from their
* specification which may be influenced by the data organization.
* If this composite changes parents will not be
* notified - handling this is the caller's responsibility.
* @throws IOException if database IO error occurs
*/
protected abstract void fixupComponents();
protected abstract void fixupComponents() throws IOException;
}

View file

@ -18,8 +18,10 @@ package ghidra.program.database.data;
import java.io.IOException;
import db.*;
import ghidra.program.model.data.CompositeInternal;
import ghidra.util.UniversalID;
import ghidra.util.exception.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
/**
@ -28,38 +30,29 @@ import ghidra.util.task.TaskMonitor;
abstract class CompositeDBAdapter {
static final String COMPOSITE_TABLE_NAME = "Composite Data Types";
static final Schema COMPOSITE_SCHEMA = CompositeDBAdapterV2V3.V2_COMPOSITE_SCHEMA;
static final Schema COMPOSITE_SCHEMA = CompositeDBAdapterV5.V5_COMPOSITE_SCHEMA;
static final int COMPOSITE_NAME_COL = CompositeDBAdapterV2V3.V2_COMPOSITE_NAME_COL;
static final int COMPOSITE_COMMENT_COL = CompositeDBAdapterV2V3.V2_COMPOSITE_COMMENT_COL;
static final int COMPOSITE_IS_UNION_COL = CompositeDBAdapterV2V3.V2_COMPOSITE_IS_UNION_COL;
static final int COMPOSITE_CAT_COL = CompositeDBAdapterV2V3.V2_COMPOSITE_CAT_COL;
static final int COMPOSITE_LENGTH_COL = CompositeDBAdapterV2V3.V2_COMPOSITE_LENGTH_COL;
static final int COMPOSITE_NAME_COL = CompositeDBAdapterV5.V5_COMPOSITE_NAME_COL;
static final int COMPOSITE_COMMENT_COL = CompositeDBAdapterV5.V5_COMPOSITE_COMMENT_COL;
static final int COMPOSITE_IS_UNION_COL = CompositeDBAdapterV5.V5_COMPOSITE_IS_UNION_COL;
static final int COMPOSITE_CAT_COL = CompositeDBAdapterV5.V5_COMPOSITE_CAT_COL;
static final int COMPOSITE_LENGTH_COL = CompositeDBAdapterV5.V5_COMPOSITE_LENGTH_COL;
static final int COMPOSITE_ALIGNMENT_COL = CompositeDBAdapterV5.V5_COMPOSITE_ALIGNMENT_COL;
static final int COMPOSITE_NUM_COMPONENTS_COL =
CompositeDBAdapterV2V3.V2_COMPOSITE_NUM_COMPONENTS_COL;
CompositeDBAdapterV5.V5_COMPOSITE_NUM_COMPONENTS_COL;
static final int COMPOSITE_SOURCE_ARCHIVE_ID_COL =
CompositeDBAdapterV2V3.V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL;
CompositeDBAdapterV5.V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL;
static final int COMPOSITE_UNIVERSAL_DT_ID =
CompositeDBAdapterV2V3.V2_COMPOSITE_UNIVERSAL_DT_ID_COL;
CompositeDBAdapterV5.V5_COMPOSITE_UNIVERSAL_DT_ID_COL;
static final int COMPOSITE_SOURCE_SYNC_TIME_COL =
CompositeDBAdapterV2V3.V2_COMPOSITE_SOURCE_SYNC_TIME_COL;
CompositeDBAdapterV5.V5_COMPOSITE_SOURCE_SYNC_TIME_COL;
static final int COMPOSITE_LAST_CHANGE_TIME_COL =
CompositeDBAdapterV2V3.V2_COMPOSITE_LAST_CHANGE_TIME_COL;
static final int COMPOSITE_INTERNAL_ALIGNMENT_COL =
CompositeDBAdapterV2V3.V2_COMPOSITE_INTERNAL_ALIGNMENT_COL;
static final int COMPOSITE_EXTERNAL_ALIGNMENT_COL =
CompositeDBAdapterV2V3.V2_COMPOSITE_EXTERNAL_ALIGNMENT_COL;
CompositeDBAdapterV5.V5_COMPOSITE_LAST_CHANGE_TIME_COL;
static final int COMPOSITE_PACKING_COL =
CompositeDBAdapterV5.V5_COMPOSITE_PACK_COL;
static final int COMPOSITE_MIN_ALIGN_COL = CompositeDBAdapterV5.V5_COMPOSITE_MIN_ALIGN_COL;
// Internal Alignment Constants
static final byte UNALIGNED = (byte) -1;
static final byte ALIGNED_NO_PACKING = (byte) 0;
// Otherwise the packing value.
// External Alignment Constants
static final byte MACHINE_ALIGNED = (byte) -1;
static final byte DEFAULT_ALIGNED = (byte) 0;
// Otherwise the external minimum alignment value.
// Stored Packing and Minimum Alignment values are consistent with CompositeInternal
/**
* Gets an adapter for working with the composite data type database table.
@ -75,18 +68,21 @@ abstract class CompositeDBAdapter {
*/
static CompositeDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor)
throws VersionException, IOException, CancelledException {
if (openMode == DBConstants.CREATE) {
return new CompositeDBAdapterV5(handle, true);
}
try {
return new CompositeDBAdapterV2V3(handle, openMode);
return new CompositeDBAdapterV5(handle, false);
}
catch (VersionException e) {
if (openMode == DBConstants.CREATE) {
throw new AssertException();
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
throw e;
}
CompositeDBAdapter adapter = findReadOnlyAdapter(handle);
if (openMode == DBConstants.UPGRADE) {
CompositeDBAdapter adapter = findReadOnlyAdapter(handle);
return upgrade(handle, adapter, monitor);
}
throw e;
return adapter;
}
}
@ -100,7 +96,7 @@ abstract class CompositeDBAdapter {
static CompositeDBAdapter findReadOnlyAdapter(DBHandle handle)
throws VersionException, IOException {
try {
return new CompositeDBAdapterV2V3(handle);
return new CompositeDBAdapterV2V4(handle);
}
catch (VersionException e) {
// ignore
@ -132,7 +128,7 @@ abstract class CompositeDBAdapter {
long id = tmpHandle.startTransaction();
CompositeDBAdapter tmpAdapter = null;
try {
tmpAdapter = new CompositeDBAdapterV2V3(tmpHandle, DBConstants.CREATE);
tmpAdapter = new CompositeDBAdapterV5(tmpHandle, true);
RecordIterator it = oldAdapter.getRecords();
while (it.hasNext()) {
monitor.checkCanceled();
@ -140,7 +136,7 @@ abstract class CompositeDBAdapter {
tmpAdapter.updateRecord(rec, false);
}
oldAdapter.deleteTable(handle);
CompositeDBAdapter newAdapter = new CompositeDBAdapterV2V3(handle, DBConstants.CREATE);
CompositeDBAdapter newAdapter = new CompositeDBAdapterV5(handle, true);
it = tmpAdapter.getRecords();
while (it.hasNext()) {
monitor.checkCanceled();
@ -162,19 +158,20 @@ abstract class CompositeDBAdapter {
* @param isUnion true indicates this data type is a union and all component offsets are at zero.
* @param categoryID the ID for the category that contains this array.
* @param length the total length or size of this data type.
* @param computedAlignment computed alignment for composite or -1 if not yet computed
* @param sourceArchiveID the ID for the source archive where this data type originated.
* @param sourceDataTypeID the ID of the associated data type in the source archive.
* @param lastChangeTime the time this data type was last changed.
* @param internalAlignment UNALIGNED, ALIGNED_NO_PACKING or the packing value
* currently in use by this data type.
* @param externalAlignment DEFAULT_ALIGNED, MACHINE_ALIGNED or the minimum alignment value
* currently in use by this data type.
* @param packValue {@link CompositeInternal#NO_PACKING}, {@link CompositeInternal#DEFAULT_PACKING}
* or the explicit pack value currently in use by this data type (positive value).
* @param minAlignment {@link CompositeInternal#DEFAULT_ALIGNMENT}, {@link CompositeInternal#MACHINE_ALIGNMENT}
* or the minimum alignment value currently in use by this data type (positive value).
* @return the database record for this data type.
* @throws IOException if the database can't be accessed.
*/
abstract DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID,
int length, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime,
int internalAlignment, int externalAlignment) throws IOException;
int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID,
long lastChangeTime, int packValue, int minAlignment) throws IOException;
/**
* Gets a composite data type record from the database based on its ID.

View file

@ -18,8 +18,7 @@ package ghidra.program.database.data;
import java.io.IOException;
import db.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.*;
import ghidra.util.UniversalID;
import ghidra.util.UniversalIdGenerator;
import ghidra.util.exception.VersionException;
@ -69,8 +68,8 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato
@Override
public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID,
int length, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime,
int internalAlignment, int externalAlignment) throws IOException {
int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID,
long lastChangeTime, int packValue, int minAlignment) throws IOException {
throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION +
" of " + COMPOSITE_TABLE_NAME + " table.");
}
@ -92,7 +91,8 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato
@Override
public boolean removeRecord(long compositeID) throws IOException {
return compositeTable.deleteRecord(compositeID);
throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION +
" of " + COMPOSITE_TABLE_NAME + " table.");
}
@Override
@ -129,8 +129,8 @@ class CompositeDBAdapterV0 extends CompositeDBAdapter implements RecordTranslato
rec.setLongValue(COMPOSITE_UNIVERSAL_DT_ID, UniversalIdGenerator.nextID().getValue());
rec.setLongValue(COMPOSITE_SOURCE_SYNC_TIME_COL, DataType.NO_SOURCE_SYNC_TIME);
rec.setLongValue(COMPOSITE_LAST_CHANGE_TIME_COL, DataType.NO_LAST_CHANGE_TIME);
rec.setIntValue(COMPOSITE_INTERNAL_ALIGNMENT_COL, CompositeDBAdapter.UNALIGNED);
rec.setIntValue(COMPOSITE_EXTERNAL_ALIGNMENT_COL, CompositeDBAdapter.DEFAULT_ALIGNED);
rec.setIntValue(COMPOSITE_PACKING_COL, CompositeInternal.NO_PACKING);
rec.setIntValue(COMPOSITE_MIN_ALIGN_COL, CompositeInternal.DEFAULT_ALIGNMENT);
return rec;
}

View file

@ -18,6 +18,7 @@ package ghidra.program.database.data;
import java.io.IOException;
import db.*;
import ghidra.program.model.data.CompositeInternal;
import ghidra.util.UniversalID;
import ghidra.util.exception.VersionException;
@ -37,13 +38,13 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato
static final int V1_COMPOSITE_SOURCE_SYNC_TIME_COL = 8;
static final int V1_COMPOSITE_LAST_CHANGE_TIME_COL = 9;
static final Schema V1_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID",
new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE,
LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE,
LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE },
new String[] { "Name", "Comment", "Is Union", "Category ID", "Length",
"Number Of Components", "Source Archive ID", "Source Data Type ID", "Source Sync Time",
"Last Change Time" });
// static final Schema V1_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID",
// new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE,
// LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, LongField.INSTANCE,
// LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE },
// new String[] { "Name", "Comment", "Is Union", "Category ID", "Length",
// "Number Of Components", "Source Archive ID", "Source Data Type ID", "Source Sync Time",
// "Last Change Time" });
private Table compositeTable;
@ -72,8 +73,8 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato
@Override
public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID,
int length, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime,
int internalAlignment, int externalAlignment) throws IOException {
int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID,
long lastChangeTime, int packValue, int minAlignment) throws IOException {
throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION +
" of " + COMPOSITE_TABLE_NAME + " table.");
}
@ -95,7 +96,8 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato
@Override
public boolean removeRecord(long compositeID) throws IOException {
return compositeTable.deleteRecord(compositeID);
throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION +
" of " + COMPOSITE_TABLE_NAME + " table.");
}
@Override
@ -130,6 +132,7 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato
oldRec.getBooleanValue(V1_COMPOSITE_IS_UNION_COL));
rec.setLongValue(COMPOSITE_CAT_COL, oldRec.getLongValue(V1_COMPOSITE_CAT_COL));
rec.setIntValue(COMPOSITE_LENGTH_COL, oldRec.getIntValue(V1_COMPOSITE_LENGTH_COL));
rec.setIntValue(COMPOSITE_ALIGNMENT_COL, oldRec.getIntValue(-1));
rec.setIntValue(COMPOSITE_NUM_COMPONENTS_COL,
oldRec.getIntValue(V1_COMPOSITE_NUM_COMPONENTS_COL));
rec.setLongValue(COMPOSITE_SOURCE_ARCHIVE_ID_COL,
@ -140,8 +143,8 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato
oldRec.getLongValue(V1_COMPOSITE_SOURCE_SYNC_TIME_COL));
rec.setLongValue(COMPOSITE_LAST_CHANGE_TIME_COL,
oldRec.getLongValue(V1_COMPOSITE_LAST_CHANGE_TIME_COL));
rec.setIntValue(COMPOSITE_INTERNAL_ALIGNMENT_COL, CompositeDBAdapter.UNALIGNED);
rec.setIntValue(COMPOSITE_EXTERNAL_ALIGNMENT_COL, CompositeDBAdapter.DEFAULT_ALIGNED);
rec.setIntValue(COMPOSITE_PACKING_COL, CompositeInternal.NO_PACKING);
rec.setIntValue(COMPOSITE_MIN_ALIGN_COL, CompositeInternal.DEFAULT_ALIGNMENT);
return rec;
}
@ -150,8 +153,8 @@ class CompositeDBAdapterV1 extends CompositeDBAdapter implements RecordTranslato
Field[] keys = compositeTable.findRecords(new LongField(datatypeID.getValue()),
V1_COMPOSITE_UNIVERSAL_DT_ID_COL);
for (int i = 0; i < keys.length; i++) {
DBRecord record = compositeTable.getRecord(keys[i]);
for (Field key : keys) {
DBRecord record = compositeTable.getRecord(key);
if (record.getLongValue(V1_COMPOSITE_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) {
return translateRecord(record);
}

View file

@ -16,17 +16,15 @@
package ghidra.program.database.data;
import java.io.IOException;
import java.util.Date;
import db.*;
import ghidra.util.ReadOnlyException;
import ghidra.util.UniversalID;
import ghidra.util.exception.VersionException;
/**
* Version 2-3 implementation for accessing the Composite database table.
* Version 2-4 implementation for accessing the Composite database table.
*/
class CompositeDBAdapterV2V3 extends CompositeDBAdapter {
class CompositeDBAdapterV2V4 extends CompositeDBAdapter implements RecordTranslator {
// While the addition of flex-array and bitfields does not impact the
// actual schema the presence of such components can not be supported
@ -50,8 +48,8 @@ class CompositeDBAdapterV2V3 extends CompositeDBAdapter {
static final int V2_COMPOSITE_UNIVERSAL_DT_ID_COL = 7;
static final int V2_COMPOSITE_SOURCE_SYNC_TIME_COL = 8;
static final int V2_COMPOSITE_LAST_CHANGE_TIME_COL = 9;
static final int V2_COMPOSITE_INTERNAL_ALIGNMENT_COL = 10;
static final int V2_COMPOSITE_EXTERNAL_ALIGNMENT_COL = 11;
static final int V2_COMPOSITE_PACK_COL = 10; // renamed from Internal Alignment
static final int V2_COMPOSITE_MIN_ALIGN_COL = 11; // renamed from External Alignment
static final Schema V2_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID",
new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE,
@ -60,44 +58,9 @@ class CompositeDBAdapterV2V3 extends CompositeDBAdapter {
IntField.INSTANCE },
new String[] { "Name", "Comment", "Is Union", "Category ID", "Length",
"Number Of Components", "Source Archive ID", "Source Data Type ID", "Source Sync Time",
"Last Change Time", "Internal Alignment", "External Alignment" });
"Last Change Time", "Pack", "MinAlign" });
private Table compositeTable;
private boolean readOnly;
/**
* Gets an adapter for the Composite database table.
* @param handle handle to the database containing the table.
* @param openMode the open mode
* @throws VersionException if the the table's version does not match the expected version
* for this adapter.
*/
public CompositeDBAdapterV2V3(DBHandle handle, int openMode)
throws VersionException, IOException {
readOnly = (openMode == DBConstants.READ_ONLY);
if (openMode == DBConstants.CREATE) {
compositeTable = handle.createTable(COMPOSITE_TABLE_NAME, V2_COMPOSITE_SCHEMA,
new int[] { V2_COMPOSITE_CAT_COL, V2_COMPOSITE_UNIVERSAL_DT_ID_COL });
}
else {
compositeTable = handle.getTable(COMPOSITE_TABLE_NAME);
if (compositeTable == null) {
throw new VersionException("Missing Table: " + COMPOSITE_TABLE_NAME);
}
int version = compositeTable.getSchema().getVersion();
if (version != VERSION) {
if (version < VERSION && version >= MIN_READ_ONLY_VERSION) {
if (openMode == DBConstants.READ_ONLY) {
return; // allow read-only immutable use
}
throw new VersionException(VersionException.OLDER_VERSION, true);
}
String msg = "Expected version 2-" + VERSION + " for table " +
COMPOSITE_TABLE_NAME + " but got " + version;
throw new VersionException(msg, VersionException.NEWER_VERSION, false);
}
}
}
/**
* Gets a read-only adapter for the Composite database table.
@ -105,8 +68,7 @@ class CompositeDBAdapterV2V3 extends CompositeDBAdapter {
* @throws VersionException if the the table's version does not match the expected version
* for this adapter.
*/
public CompositeDBAdapterV2V3(DBHandle handle) throws VersionException {
readOnly = true;
public CompositeDBAdapterV2V4(DBHandle handle) throws VersionException {
compositeTable = handle.getTable(COMPOSITE_TABLE_NAME);
if (compositeTable == null) {
throw new VersionException("Missing Table: " + COMPOSITE_TABLE_NAME);
@ -124,65 +86,31 @@ class CompositeDBAdapterV2V3 extends CompositeDBAdapter {
@Override
public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID,
int length, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime,
int internalAlignment, int externalAlignment) throws IOException {
if (readOnly) {
throw new ReadOnlyException();
}
if (internalAlignment == UNALIGNED) {
length = 0; // aligned structures always start empty
}
long tableKey = compositeTable.getKey();
// if (tableKey <= DataManager.VOID_DATATYPE_ID) {
// tableKey = DataManager.VOID_DATATYPE_ID +1;
// }
long key = DataTypeManagerDB.createKey(DataTypeManagerDB.COMPOSITE, tableKey);
DBRecord record = CompositeDBAdapter.COMPOSITE_SCHEMA.createRecord(key);
record.setString(V2_COMPOSITE_NAME_COL, name);
record.setString(V2_COMPOSITE_COMMENT_COL, comments);
record.setBooleanValue(V2_COMPOSITE_IS_UNION_COL, isUnion);
record.setLongValue(V2_COMPOSITE_CAT_COL, categoryID);
record.setIntValue(V2_COMPOSITE_LENGTH_COL, length);
record.setIntValue(V2_COMPOSITE_NUM_COMPONENTS_COL, length);
record.setLongValue(V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL, sourceArchiveID);
record.setLongValue(V2_COMPOSITE_UNIVERSAL_DT_ID_COL, sourceDataTypeID);
record.setLongValue(V2_COMPOSITE_SOURCE_SYNC_TIME_COL, lastChangeTime);
record.setLongValue(V2_COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime);
record.setIntValue(V2_COMPOSITE_INTERNAL_ALIGNMENT_COL, internalAlignment);
record.setIntValue(V2_COMPOSITE_EXTERNAL_ALIGNMENT_COL, externalAlignment);
compositeTable.putRecord(record);
return record;
int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID,
long lastChangeTime, int packValue, int minAlignment) throws IOException {
throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION +
" of " + COMPOSITE_TABLE_NAME + " table.");
}
@Override
public DBRecord getRecord(long dataTypeID) throws IOException {
return compositeTable.getRecord(dataTypeID);
return translateRecord(compositeTable.getRecord(dataTypeID));
}
@Override
public RecordIterator getRecords() throws IOException {
return compositeTable.iterator();
return new TranslatedRecordIterator(compositeTable.iterator(), this);
}
@Override
public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException {
if (readOnly) {
throw new ReadOnlyException();
}
if (setLastChangeTime) {
record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL,
(new Date()).getTime());
}
compositeTable.putRecord(record);
throw new UnsupportedOperationException();
}
@Override
public boolean removeRecord(long compositeID) throws IOException {
if (readOnly) {
throw new ReadOnlyException();
}
return compositeTable.deleteRecord(compositeID);
throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION +
" of " + COMPOSITE_TABLE_NAME + " table.");
}
@Override
@ -202,15 +130,46 @@ class CompositeDBAdapterV2V3 extends CompositeDBAdapter {
V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL);
}
/* (non-Javadoc)
* @see db.RecordTranslator#translateRecord(db.Record)
*/
@Override
public DBRecord translateRecord(DBRecord oldRec) {
if (oldRec == null) {
return null;
}
DBRecord rec = CompositeDBAdapter.COMPOSITE_SCHEMA.createRecord(oldRec.getKey());
rec.setString(COMPOSITE_NAME_COL, oldRec.getString(V2_COMPOSITE_NAME_COL));
rec.setString(COMPOSITE_COMMENT_COL, oldRec.getString(V2_COMPOSITE_COMMENT_COL));
rec.setBooleanValue(COMPOSITE_IS_UNION_COL,
oldRec.getBooleanValue(V2_COMPOSITE_IS_UNION_COL));
rec.setLongValue(COMPOSITE_CAT_COL, oldRec.getLongValue(V2_COMPOSITE_CAT_COL));
rec.setIntValue(COMPOSITE_LENGTH_COL, oldRec.getIntValue(V2_COMPOSITE_LENGTH_COL));
rec.setIntValue(COMPOSITE_ALIGNMENT_COL, -1);
rec.setIntValue(COMPOSITE_NUM_COMPONENTS_COL,
oldRec.getIntValue(V2_COMPOSITE_NUM_COMPONENTS_COL));
rec.setLongValue(COMPOSITE_SOURCE_ARCHIVE_ID_COL,
oldRec.getLongValue(V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL));
rec.setLongValue(COMPOSITE_UNIVERSAL_DT_ID,
oldRec.getLongValue(V2_COMPOSITE_UNIVERSAL_DT_ID_COL));
rec.setLongValue(COMPOSITE_SOURCE_SYNC_TIME_COL,
oldRec.getLongValue(V2_COMPOSITE_SOURCE_SYNC_TIME_COL));
rec.setLongValue(COMPOSITE_LAST_CHANGE_TIME_COL,
oldRec.getLongValue(V2_COMPOSITE_LAST_CHANGE_TIME_COL));
rec.setIntValue(COMPOSITE_PACKING_COL, oldRec.getIntValue(V2_COMPOSITE_PACK_COL));
rec.setIntValue(COMPOSITE_MIN_ALIGN_COL, oldRec.getIntValue(V2_COMPOSITE_MIN_ALIGN_COL));
return rec;
}
@Override
DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException {
Field[] keys = compositeTable.findRecords(new LongField(datatypeID.getValue()),
V2_COMPOSITE_UNIVERSAL_DT_ID_COL);
for (int i = 0; i < keys.length; i++) {
DBRecord record = compositeTable.getRecord(keys[i]);
for (Field key : keys) {
DBRecord record = compositeTable.getRecord(key);
if (record.getLongValue(V2_COMPOSITE_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) {
return record;
return translateRecord(record);
}
}
return null;

View file

@ -0,0 +1,176 @@
/* ###
* 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.program.database.data;
import java.io.IOException;
import java.util.Date;
import db.*;
import ghidra.program.model.data.CompositeInternal;
import ghidra.util.UniversalID;
import ghidra.util.exception.VersionException;
/**
* Version 5 implementation for accessing the Composite database table.
* This version introduces the retained computed alignment to reduce
* need for recalculation and to allow for improved change detection.
*/
class CompositeDBAdapterV5 extends CompositeDBAdapter {
static final int VERSION = 5;
static final int V5_COMPOSITE_NAME_COL = 0;
static final int V5_COMPOSITE_COMMENT_COL = 1;
static final int V5_COMPOSITE_IS_UNION_COL = 2;
static final int V5_COMPOSITE_CAT_COL = 3;
static final int V5_COMPOSITE_LENGTH_COL = 4;
static final int V5_COMPOSITE_ALIGNMENT_COL = 5;
static final int V5_COMPOSITE_NUM_COMPONENTS_COL = 6;
static final int V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL = 7;
static final int V5_COMPOSITE_UNIVERSAL_DT_ID_COL = 8;
static final int V5_COMPOSITE_SOURCE_SYNC_TIME_COL = 9;
static final int V5_COMPOSITE_LAST_CHANGE_TIME_COL = 10;
static final int V5_COMPOSITE_PACK_COL = 11;
static final int V5_COMPOSITE_MIN_ALIGN_COL = 12;
static final Schema V5_COMPOSITE_SCHEMA = new Schema(VERSION, "Data Type ID",
new Field[] { StringField.INSTANCE, StringField.INSTANCE, BooleanField.INSTANCE,
LongField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE, IntField.INSTANCE,
LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE,
IntField.INSTANCE, IntField.INSTANCE },
new String[] { "Name", "Comment", "Is Union", "Category ID", "Length", "Alignment",
"Number Of Components", "Source Archive ID", "Source Data Type ID", "Source Sync Time",
"Last Change Time", "Pack", "MinAlign" });
private Table compositeTable;
/**
* Gets an adapter for the Composite database table.
* @param handle handle to the database containing the table.
* @param create true if this constructor should create the table.
* @throws VersionException if the the table's version does not match the expected version
* for this adapter.
* @throws IOException if IO error occurs
*/
public CompositeDBAdapterV5(DBHandle handle, boolean create)
throws VersionException, IOException {
if (create) {
compositeTable = handle.createTable(COMPOSITE_TABLE_NAME, V5_COMPOSITE_SCHEMA,
new int[] { V5_COMPOSITE_CAT_COL, V5_COMPOSITE_UNIVERSAL_DT_ID_COL });
}
else {
compositeTable = handle.getTable(COMPOSITE_TABLE_NAME);
if (compositeTable == null) {
throw new VersionException("Missing Table: " + COMPOSITE_TABLE_NAME);
}
int version = compositeTable.getSchema().getVersion();
if (version != VERSION) {
String msg = "Expected version " + VERSION + " for table " + COMPOSITE_TABLE_NAME +
" but got " + version;
if (version < VERSION) {
throw new VersionException(msg, VersionException.OLDER_VERSION, true);
}
throw new VersionException(msg, VersionException.NEWER_VERSION, false);
}
}
}
@Override
public DBRecord createRecord(String name, String comments, boolean isUnion, long categoryID,
int length, int computedAlignment, long sourceArchiveID, long sourceDataTypeID,
long lastChangeTime, int packValue, int minAlignment) throws IOException {
if (packValue < CompositeInternal.DEFAULT_ALIGNMENT) {
packValue = CompositeInternal.NO_PACKING;
}
else {
length = 0; // aligned structures always start empty
}
long key =
DataTypeManagerDB.createKey(DataTypeManagerDB.COMPOSITE, compositeTable.getKey());
DBRecord record = CompositeDBAdapter.COMPOSITE_SCHEMA.createRecord(key);
record.setString(V5_COMPOSITE_NAME_COL, name);
record.setString(V5_COMPOSITE_COMMENT_COL, comments);
record.setBooleanValue(V5_COMPOSITE_IS_UNION_COL, isUnion);
record.setLongValue(V5_COMPOSITE_CAT_COL, categoryID);
record.setIntValue(V5_COMPOSITE_LENGTH_COL, length);
record.setIntValue(V5_COMPOSITE_ALIGNMENT_COL, computedAlignment);
record.setIntValue(V5_COMPOSITE_NUM_COMPONENTS_COL, length);
record.setLongValue(V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL, sourceArchiveID);
record.setLongValue(V5_COMPOSITE_UNIVERSAL_DT_ID_COL, sourceDataTypeID);
record.setLongValue(V5_COMPOSITE_SOURCE_SYNC_TIME_COL, lastChangeTime);
record.setLongValue(V5_COMPOSITE_LAST_CHANGE_TIME_COL, lastChangeTime);
record.setIntValue(V5_COMPOSITE_PACK_COL, packValue);
record.setIntValue(V5_COMPOSITE_MIN_ALIGN_COL, minAlignment);
compositeTable.putRecord(record);
return record;
}
@Override
public DBRecord getRecord(long dataTypeID) throws IOException {
return compositeTable.getRecord(dataTypeID);
}
@Override
public RecordIterator getRecords() throws IOException {
return compositeTable.iterator();
}
@Override
public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException {
if (setLastChangeTime) {
record.setLongValue(CompositeDBAdapter.COMPOSITE_LAST_CHANGE_TIME_COL,
(new Date()).getTime());
}
compositeTable.putRecord(record);
}
@Override
public boolean removeRecord(long compositeID) throws IOException {
return compositeTable.deleteRecord(compositeID);
}
@Override
void deleteTable(DBHandle handle) throws IOException {
handle.deleteTable(COMPOSITE_TABLE_NAME);
}
@Override
public Field[] getRecordIdsInCategory(long categoryID) throws IOException {
return compositeTable.findRecords(new LongField(categoryID),
CompositeDBAdapter.COMPOSITE_CAT_COL);
}
@Override
Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException {
return compositeTable.findRecords(new LongField(archiveID),
V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL);
}
@Override
DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException {
Field[] keys = compositeTable.findRecords(new LongField(datatypeID.getValue()),
V5_COMPOSITE_UNIVERSAL_DT_ID_COL);
for (Field key : keys) {
DBRecord record = compositeTable.getRecord(key);
if (record.getLongValue(V5_COMPOSITE_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) {
return record;
}
}
return null;
}
}

View file

@ -148,7 +148,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
@Override
public int getOffset() {
if (isFlexibleArrayComponent) {
if (parent.isNotYetDefined()) {
if (parent.isZeroLength()) {
// some structures have only a flexible array defined
return 0;
}
@ -223,7 +223,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
if (record != null) {
record.setString(ComponentDBAdapter.COMPONENT_COMMENT_COL, comment);
adapter.updateRecord(record);
notifyChanged();
dataMgr.dataTypeChanged(getParent(), false);
}
}
catch (IOException e) {
@ -268,7 +268,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
}
record.setString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL, name);
adapter.updateRecord(record);
notifyChanged();
dataMgr.dataTypeChanged(getParent(), false);
}
}
catch (IOException e) {
@ -279,7 +279,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
private void checkDuplicateName(String name) throws DuplicateNameException {
DataTypeComponentImpl.checkDefaultFieldName(name);
for (DataTypeComponent comp : parent.getComponents()) {
for (DataTypeComponent comp : parent.getDefinedComponents()) {
if (comp == this) {
continue;
}
@ -344,7 +344,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
}
DataType myParent = getParent();
boolean aligned =
(myParent instanceof Composite) ? ((Composite) myParent).isInternallyAligned() : false;
(myParent instanceof Composite) ? ((Composite) myParent).isPackingEnabled() : false;
// Components don't need to have matching offset when they are aligned
// NOTE: use getOffset() method since returned values will differ from
// stored values for flexible array component
@ -452,11 +452,6 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
}
}
private void notifyChanged() {
DataType dt = getParent();
dataMgr.dataTypeChanged(dt);
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();

View file

@ -19,9 +19,6 @@ import java.io.IOException;
import java.net.URL;
import java.util.List;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import db.DBRecord;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
@ -38,7 +35,7 @@ import ghidra.util.exception.NotYetImplementedException;
*
*
*/
abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeListener {
abstract class DataTypeDB extends DatabaseObject implements DataType {
protected DBRecord record;
protected final DataTypeManagerDB dataMgr;
@ -125,9 +122,11 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
return false;
}
/**
* @see ghidra.program.model.data.DataType#getDisplayName()
*/
@Override
public boolean isZeroLength() {
return false;
}
@Override
public String getDisplayName() {
return getName();
@ -159,9 +158,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
return name;
}
/**
* @see ghidra.program.model.data.DataType#getDefaultSettings()
*/
@Override
public Settings getDefaultSettings() {
Settings localDefaultSettings = defaultSettings;
@ -182,9 +178,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
}
/**
* @see ghidra.program.model.data.DataType#getDocs()
*/
@Override
public URL getDocs() {
return null;
@ -203,49 +196,31 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
throw new NotYetImplementedException("setValue() not implemented");
}
/**
* @see ghidra.program.model.data.DataType#getSettingsDefinitions()
*/
@Override
public SettingsDefinition[] getSettingsDefinitions() {
return EMPTY_DEFINITIONS;
}
/**
* @see ghidra.program.model.data.DataType#isDeleted()
*/
@Override
public boolean isDeleted() {
return isDeleted(lock);
}
/**
* @see ghidra.program.model.data.DataType#update(ghidra.program.model.data.DataType)
*/
@Override
public void dataTypeSizeChanged(DataType dt) {
// no-op
// do nothing
}
/**
* @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
*/
@Override
public void stateChanged(ChangeEvent e) {
dataMgr.dataTypeChanged(this);
public void dataTypeAlignmentChanged(DataType dt) {
// do nothing
}
/**
* @see ghidra.program.model.data.DataType#getDataTypeManager()
*/
@Override
public DataTypeManager getDataTypeManager() {
return dataMgr;
}
/**
* @see ghidra.program.model.data.DataType#setDefaultSettings(ghidra.docking.settings.Settings)
*/
@Override
public void setDefaultSettings(Settings settings) {
checkIsValid();
@ -259,12 +234,9 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
return 1;
}
DataOrganization dataOrganization = dataMgr.getDataOrganization();
return dataOrganization.getAlignment(this, length);
return dataOrganization.getAlignment(this);
}
/**
* @see ghidra.program.model.data.DataType#getPathName()
*/
@Override
public String getPathName() {
return getDataTypePath().getPath();
@ -320,9 +292,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
return new DataTypePath(getCategoryPath(), getName());
}
/**
* @see ghidra.program.model.data.DataType#setName(java.lang.String)
*/
@Override
public void setName(String name) throws InvalidNameException, DuplicateNameException {
lock.acquire();
@ -357,9 +326,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
}
/**
* @see ghidra.program.model.data.DataType#setCategoryPath(ghidra.program.model.data.CategoryPath)
*/
@Override
public void setCategoryPath(CategoryPath path) throws DuplicateNameException {
lock.acquire();
@ -450,11 +416,28 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
}
}
protected void notifySizeChanged() {
/**
* Notify all parents that the size of this datatype has changed or
* other significant change that may affect a parent containing this
* datatype.
* @param isAutoChange true if changes are in response to another datatype's change.
*/
protected void notifySizeChanged(boolean isAutoChange) {
for (DataType dt : dataMgr.getParentDataTypes(key)) {
dt.dataTypeSizeChanged(this);
}
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, isAutoChange);
}
/**
* Notification that this composite data type's alignment has changed.
* @param isAutoChange true if changes are in response to another datatype's change.
*/
protected void notifyAlignmentChanged(boolean isAutoChange) {
for (DataType dt : dataMgr.getParentDataTypes(key)) {
dt.dataTypeAlignmentChanged(this);
}
dataMgr.dataTypeChanged(this, isAutoChange);
}
protected void notifyNameChanged(String oldName) {

View file

@ -490,6 +490,16 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
}
/**
* Determine if transaction is active. With proper lock established
* this method may be useful for determining if a lazy record update
* may be performed.
* @return true if database transaction if active, else false
*/
protected final boolean isTransactionActive() {
return dbHandle.isTransactionActive();
}
abstract protected String getDomainFileID();
abstract protected String getPath();
@ -983,19 +993,19 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
try {
if (existingDataType instanceof StructureDB) {
if (!(dataType instanceof Structure)) {
if (!(dataType instanceof StructureInternal)) {
return false;
}
StructureDB existingStruct = (StructureDB) existingDataType;
existingStruct.doReplaceWith((Structure) dataType, true);
existingStruct.doReplaceWith((StructureInternal) dataType, true);
return true;
}
else if (existingDataType instanceof UnionDB) {
if (!(dataType instanceof Union)) {
if (!(dataType instanceof UnionInternal)) {
return false;
}
UnionDB existingUnion = (UnionDB) existingDataType;
existingUnion.doReplaceWith((Union) dataType, true);
existingUnion.doReplaceWith((UnionInternal) dataType, true);
return true;
}
}
@ -1121,7 +1131,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
long lastChangeTime = dataType.getLastChangeTime();
existingDataType.setLastChangeTime(lastChangeTime);
existingDataType.setLastChangeTimeInSourceArchive(lastChangeTime);
dataTypeChanged(existingDataType);
dataTypeChanged(existingDataType, false);
return existingDataType;
}
@ -1725,7 +1735,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
idsToDataTypeMap.removeDataType(sourceArchive, oldDtID);
}
dataTypeChanged(dataType);
dataTypeChanged(dataType, false);
}
finally {
lock.release();
@ -2307,8 +2317,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
int len = ptr.isDynamicallySized() ? -1 : ptr.getLength();
newDataType = createPointer(ptr.getDataType(), cat, (byte) len, handler);
}
else if (dt instanceof Structure) {
Structure structure = (Structure) dt;
else if (dt instanceof StructureInternal) {
StructureInternal structure = (StructureInternal) dt;
newDataType = createStructure(structure, name, cat, sourceArchiveIdValue,
id.getValue());
}
@ -2317,8 +2327,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
newDataType =
createTypeDef(typedef, name, cat, sourceArchiveIdValue, id.getValue());
}
else if (dt instanceof Union) {
Union union = (Union) dt;
else if (dt instanceof UnionInternal) {
UnionInternal union = (UnionInternal) dt;
newDataType =
createUnion(union, name, cat, sourceArchiveIdValue, id.getValue());
}
@ -2352,7 +2362,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
return null;
}
private Structure createStructure(Structure struct, String name, CategoryDB category,
private Structure createStructure(StructureInternal struct, String name, CategoryDB category,
long sourceArchiveIdValue, long universalIdValue)
throws IOException {
try {
@ -2361,13 +2371,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
creatingDataType++;
int len = struct.getLength();
if (struct.isNotYetDefined() || struct.isInternallyAligned()) {
if (struct.isZeroLength() || struct.isPackingEnabled()) {
len = 0;
}
DBRecord record = compositeAdapter.createRecord(name, struct.getDescription(), false,
category.getID(), len, sourceArchiveIdValue, universalIdValue,
struct.getLastChangeTime(), getInternalAlignment(struct),
getExternalAlignment(struct));
category.getID(), len, -1, sourceArchiveIdValue,
universalIdValue, struct.getLastChangeTime(),
struct.getStoredPackingValue(), struct.getStoredMinimumAlignment());
StructureDB structDB =
new StructureDB(this, dtCache, compositeAdapter, componentAdapter, record);
@ -2378,7 +2388,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
structDB.doReplaceWith(struct, false);
structDB.setDescription(struct.getDescription());
// structDB.notifySizeChanged();
// doReplaceWith updated the last change time so set it back to what we want.
// doReplaceWith may have updated the last change time so set it back to what we want.
structDB.setLastChangeTime(struct.getLastChangeTime());
return structDB;
@ -2395,32 +2405,32 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
return dbHandle.isChanged();
}
private int getExternalAlignment(Composite struct) {
if (struct.isDefaultAligned()) {
return CompositeDB.DEFAULT_ALIGNED;
}
else if (struct.isMachineAligned()) {
return CompositeDB.MACHINE_ALIGNED;
}
else {
int alignment = struct.getMinimumAlignment();
if (alignment == 0) {
return CompositeDB.DEFAULT_ALIGNED;
}
return alignment;
}
}
// private int getExternalAlignment(Composite struct) {
// if (struct.isDefaultAligned()) {
// return CompositeDB.DEFAULT_ALIGNED;
// }
// else if (struct.isMachineAligned()) {
// return CompositeDB.MACHINE_ALIGNED;
// }
// else {
// int alignment = struct.getAlignment();
// if (alignment <= 0) {
// return CompositeDB.DEFAULT_ALIGNED;
// }
// return alignment;
// }
// }
private int getInternalAlignment(Composite struct) {
if (struct.isInternallyAligned()) {
int packingValue = struct.getPackingValue();
if (packingValue == 0) {
return CompositeDB.ALIGNED_NO_PACKING;
}
return packingValue;
}
return CompositeDB.UNALIGNED;
}
// private int getInternalAlignment(Composite struct) {
// if (struct.isPackingEnabled()) {
// int packingValue = struct.getPackingValue();
// if (packingValue == 0) {
// return CompositeDB.ALIGNED_NO_PACKING;
// }
// return packingValue;
// }
// return CompositeDB.UNALIGNED;
// }
private TypeDef createTypeDef(TypeDef typedef, String name, Category cat,
long sourceArchiveIdValue, long universalIdValue)
@ -2437,7 +2447,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
return typedefDB;
}
private Union createUnion(Union union, String name, CategoryDB category,
private Union createUnion(UnionInternal union, String name, CategoryDB category,
long sourceArchiveIdValue, long universalIdValue)
throws IOException {
if (name == null || name.length() == 0) {
@ -2446,8 +2456,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
try {
creatingDataType++;
DBRecord record = compositeAdapter.createRecord(name, null, true, category.getID(), 0,
sourceArchiveIdValue, universalIdValue, union.getLastChangeTime(),
getInternalAlignment(union), getExternalAlignment(union));
-1, sourceArchiveIdValue, universalIdValue,
union.getLastChangeTime(), union.getStoredPackingValue(), union.getStoredMinimumAlignment());
UnionDB unionDB =
new UnionDB(this, dtCache, compositeAdapter, componentAdapter, record);
@ -3585,7 +3595,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
@Override
public void dataTypeChanged(DataType dt) {
public void dataTypeChanged(DataType dt, boolean isAutoChange) {
if (dt instanceof Enum) {
enumValueMap = null;
}
@ -3852,6 +3862,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
}
catch (IOException e) {
dbError(e);
}
finally {
lock.release();
}

View file

@ -19,8 +19,8 @@ import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import db.Field;
import db.DBRecord;
import db.Field;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.database.DBObjectCache;
@ -216,7 +216,7 @@ class EnumDB extends DataTypeDB implements Enum {
valueAdapter.createRecord(key, valueName, value);
adapter.updateRecord(record, true);
addToCache(valueName, value);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
@ -263,7 +263,7 @@ class EnumDB extends DataTypeDB implements Enum {
}
}
adapter.updateRecord(record, true);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -309,10 +309,10 @@ class EnumDB extends DataTypeDB implements Enum {
}
if (oldLength != newLength) {
notifySizeChanged();
notifySizeChanged(false);
}
else {
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
}
catch (IOException e) {
@ -386,7 +386,7 @@ class EnumDB extends DataTypeDB implements Enum {
checkDeleted();
record.setString(EnumDBAdapter.ENUM_COMMENT_COL, description);
adapter.updateRecord(record, true);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -620,7 +620,7 @@ class EnumDB extends DataTypeDB implements Enum {
checkDeleted();
record.setLongValue(EnumDBAdapter.ENUM_UNIVERSAL_DT_ID_COL, id.getValue());
adapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -642,7 +642,7 @@ class EnumDB extends DataTypeDB implements Enum {
checkDeleted();
record.setLongValue(EnumDBAdapter.ENUM_SOURCE_ARCHIVE_ID_COL, id.getValue());
adapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -659,7 +659,7 @@ class EnumDB extends DataTypeDB implements Enum {
checkDeleted();
record.setLongValue(EnumDBAdapter.ENUM_LAST_CHANGE_TIME_COL, lastChangeTime);
adapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -677,7 +677,7 @@ class EnumDB extends DataTypeDB implements Enum {
record.setLongValue(EnumDBAdapter.ENUM_SOURCE_SYNC_TIME_COL,
lastChangeTimeInSourceArchive);
adapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);

View file

@ -18,8 +18,8 @@ package ghidra.program.database.data;
import java.io.IOException;
import java.util.*;
import db.Field;
import db.DBRecord;
import db.Field;
import ghidra.docking.settings.Settings;
import ghidra.program.database.DBObjectCache;
import ghidra.program.model.data.*;
@ -268,7 +268,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
}
loadParameters();
funDefAdapter.updateRecord(record, true); // update last change time
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -293,7 +293,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
dataMgr.getID(resolvedDt));
funDefAdapter.updateRecord(record, true);
resolvedDt.addParent(this);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -310,7 +310,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
checkDeleted();
record.setString(FunctionDefinitionDBAdapter.FUNCTION_DEF_COMMENT_COL, comment);
funDefAdapter.updateRecord(record, true);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -523,7 +523,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
record.setByteValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_FLAGS_COL, flags);
try {
funDefAdapter.updateRecord(record, true);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -552,7 +552,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
record.setByteValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_FLAGS_COL, flags);
try {
funDefAdapter.updateRecord(record, true);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -600,7 +600,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_LAST_CHANGE_TIME_COL,
lastChangeTime);
funDefAdapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -618,7 +618,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_SOURCE_SYNC_TIME_COL,
lastChangeTime);
funDefAdapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -642,7 +642,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_SOURCE_DT_ID_COL,
id.getValue());
funDefAdapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -666,7 +666,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_SOURCE_ARCHIVE_ID_COL,
id.getValue());
funDefAdapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);

View file

@ -76,7 +76,7 @@ final class ParameterDefinitionDB implements ParameterDefinition {
record.setIntValue(FunctionParameterAdapter.PARAMETER_DT_LENGTH_COL, type.getLength());
try {
adapter.updateRecord(record);
dataMgr.dataTypeChanged(parent);
dataMgr.dataTypeChanged(parent, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -109,7 +109,7 @@ final class ParameterDefinitionDB implements ParameterDefinition {
record.setString(FunctionParameterAdapter.PARAMETER_NAME_COL, name);
try {
adapter.updateRecord(record);
dataMgr.dataTypeChanged(parent);
dataMgr.dataTypeChanged(parent, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -126,7 +126,7 @@ final class ParameterDefinitionDB implements ParameterDefinition {
record.setString(FunctionParameterAdapter.PARAMETER_COMMENT_COL, comment);
try {
adapter.updateRecord(record);
dataMgr.dataTypeChanged(parent);
dataMgr.dataTypeChanged(parent, false);
}
catch (IOException e) {
dataMgr.dbError(e);

View file

@ -130,12 +130,13 @@ public class ProgramDataTypeManager extends DataTypeManagerDB
}
@Override
public void dataTypeChanged(DataType dt) {
super.dataTypeChanged(dt);
public void dataTypeChanged(DataType dt, boolean isAutoChange) {
super.dataTypeChanged(dt, isAutoChange);
if (!isCreatingDataType()) {
program.getCodeManager().invalidateCache(false);
program.getFunctionManager().invalidateCache(false);
program.dataTypeChanged(getID(dt), ChangeManager.DOCR_DATA_TYPE_CHANGED, null, dt);
program.dataTypeChanged(getID(dt), ChangeManager.DOCR_DATA_TYPE_CHANGED,
isAutoChange, null, dt);
}
}
@ -149,7 +150,8 @@ public class ProgramDataTypeManager extends DataTypeManagerDB
protected void dataTypeReplaced(long existingDtID, DataTypePath existingPath,
DataType replacementDt) {
super.dataTypeReplaced(existingDtID, existingPath, replacementDt);
program.dataTypeChanged(existingDtID, ChangeManager.DOCR_DATA_TYPE_REPLACED, existingPath,
program.dataTypeChanged(existingDtID, ChangeManager.DOCR_DATA_TYPE_REPLACED, true,
existingPath,
replacementDt);
}
@ -157,20 +159,20 @@ public class ProgramDataTypeManager extends DataTypeManagerDB
protected void dataTypeDeleted(long deletedID, DataTypePath deletedDataTypePath) {
super.dataTypeDeleted(deletedID, deletedDataTypePath);
program.dataTypeChanged(deletedID, ChangeManager.DOCR_DATA_TYPE_REMOVED,
deletedDataTypePath, null);
false, deletedDataTypePath, null);
}
@Override
protected void dataTypeMoved(DataType dt, DataTypePath oldPath, DataTypePath newPath) {
super.dataTypeMoved(dt, oldPath, newPath);
Category category = getCategory(oldPath.getCategoryPath());
program.dataTypeChanged(getID(dt), ChangeManager.DOCR_DATA_TYPE_MOVED, category, dt);
program.dataTypeChanged(getID(dt), ChangeManager.DOCR_DATA_TYPE_MOVED, false, category, dt);
}
@Override
protected void dataTypeNameChanged(DataType dt, String oldName) {
super.dataTypeNameChanged(dt, oldName);
program.dataTypeChanged(getID(dt), ChangeManager.DOCR_DATA_TYPE_RENAMED, oldName, dt);
program.dataTypeChanged(getID(dt), ChangeManager.DOCR_DATA_TYPE_RENAMED, false, oldName, dt);
}
@Override

View file

@ -97,12 +97,12 @@ public class ProjectDataTypeManager extends DataTypeManagerDB
////////////////////
@Override
public void dataTypeChanged(DataType dt) {
super.dataTypeChanged(dt);
public void dataTypeChanged(DataType dt, boolean isAutoChange) {
super.dataTypeChanged(dt, isAutoChange);
// dataTypeArchive.getCodeManager().invalidateCache(false);
// TODO
dataTypeArchive.dataTypeChanged(getID(dt),
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_CHANGED, null, dt);
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_CHANGED, isAutoChange, null, dt);
}
@Override
@ -118,14 +118,15 @@ public class ProjectDataTypeManager extends DataTypeManagerDB
DataType replacementDt) {
super.dataTypeReplaced(existingDtID, existingPath, replacementDt);
dataTypeArchive.dataTypeChanged(existingDtID,
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_REPLACED, existingPath, replacementDt);
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_REPLACED, false, existingPath,
replacementDt);
}
@Override
protected void dataTypeDeleted(long deletedID, DataTypePath deletedDataTypePath) {
super.dataTypeDeleted(deletedID, deletedDataTypePath);
dataTypeArchive.dataTypeChanged(deletedID,
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_REMOVED, deletedDataTypePath, null);
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_REMOVED, false, deletedDataTypePath, null);
}
@Override
@ -133,14 +134,14 @@ public class ProjectDataTypeManager extends DataTypeManagerDB
super.dataTypeMoved(dt, oldPath, newPath);
Category category = getCategory(oldPath.getCategoryPath());
dataTypeArchive.dataTypeChanged(getID(dt),
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_MOVED, category, dt);
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_MOVED, false, category, dt);
}
@Override
protected void dataTypeNameChanged(DataType dt, String oldName) {
super.dataTypeNameChanged(dt, oldName);
dataTypeArchive.dataTypeChanged(getID(dt),
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_RENAMED, oldName, dt);
DataTypeArchiveChangeManager.DOCR_DATA_TYPE_RENAMED, false, oldName, dt);
}
@Override

View file

@ -18,8 +18,8 @@ package ghidra.program.database.data;
import java.io.IOException;
import java.util.*;
import db.Field;
import db.DBRecord;
import db.Field;
import ghidra.docking.settings.Settings;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeComponent;
@ -63,11 +63,13 @@ class SettingsDBManager implements Settings {
}
private void settingsChanged() {
// NOTE: There is currently no merge support for settings so recording within
// domain object change set is unneccessary
if (dtc != null) {
dataMgr.dataTypeChanged(dtc.getParent());
dataMgr.dataTypeChanged(dtc.getParent(), true);
}
else {
dataMgr.dataTypeChanged(dataType);
dataMgr.dataTypeChanged(dataType, true);
}
}
@ -178,11 +180,11 @@ class SettingsDBManager implements Settings {
try {
Field[] keys = adapter.getSettingsKeys(dataTypeID);
for (int i = 0; i < keys.length; i++) {
DBRecord rec = adapter.getSettingsRecord(keys[i].getLongValue());
for (Field key : keys) {
DBRecord rec = adapter.getSettingsRecord(key.getLongValue());
String settingsName = rec.getString(SettingsDBAdapter.SETTINGS_NAME_COL);
if (settingsName.equals(name)) {
adapter.removeSettingsRecord(keys[i].getLongValue());
adapter.removeSettingsRecord(key.getLongValue());
settingsChanged();
return;
}
@ -197,8 +199,8 @@ class SettingsDBManager implements Settings {
public void clearAllSettings() {
try {
Field[] keys = adapter.getSettingsKeys(dataTypeID);
for (int i = 0; i < keys.length; i++) {
adapter.removeSettingsRecord(keys[i].getLongValue());
for (Field key : keys) {
adapter.removeSettingsRecord(key.getLongValue());
}
settingsChanged();
}
@ -212,8 +214,8 @@ class SettingsDBManager implements Settings {
List<String> list = new ArrayList<String>();
try {
Field[] keys = adapter.getSettingsKeys(dataTypeID);
for (int i = 0; i < keys.length; i++) {
DBRecord rec = adapter.getSettingsRecord(keys[i].getLongValue());
for (Field key : keys) {
DBRecord rec = adapter.getSettingsRecord(key.getLongValue());
String name = rec.getString(SettingsDBAdapter.SETTINGS_NAME_COL);
if (!list.contains(name)) {
list.add(name);
@ -242,8 +244,8 @@ class SettingsDBManager implements Settings {
void update(Settings settings) {
clearAllSettings();
String[] names = settings.getNames();
for (int i = 0; i < names.length; i++) {
setValue(names[i], settings.getValue(names[i]));
for (String name : names) {
setValue(name, settings.getValue(name));
}
}
@ -278,8 +280,8 @@ class SettingsDBManager implements Settings {
private DBRecord getRecord(String name) {
try {
Field[] keys = adapter.getSettingsKeys(dataTypeID);
for (int i = 0; i < keys.length; i++) {
DBRecord rec = adapter.getSettingsRecord(keys[i].getLongValue());
for (Field key : keys) {
DBRecord rec = adapter.getSettingsRecord(key.getLongValue());
if (rec.getString(SettingsDBAdapter.SETTINGS_NAME_COL).equals(name)) {
return rec;
}

View file

@ -76,6 +76,11 @@ class TypedefDB extends DataTypeDB implements TypeDef {
return getDisplayName();
}
@Override
public boolean isZeroLength() {
return getDataType().isZeroLength();
}
@Override
public int getLength() {
return getDataType().getLength();
@ -108,7 +113,20 @@ class TypedefDB extends DataTypeDB implements TypeDef {
lock.acquire();
try {
if (checkIsValid() && dt == getDataType()) {
notifySizeChanged();
notifySizeChanged(true);
}
}
finally {
lock.release();
}
}
@Override
public void dataTypeAlignmentChanged(DataType dt) {
lock.acquire();
try {
if (checkIsValid() && dt == getDataType()) {
notifyAlignmentChanged(true);
}
}
finally {
@ -204,10 +222,10 @@ class TypedefDB extends DataTypeDB implements TypeDef {
dataMgr.dbError(e);
}
if (oldLen != getLength()) {
notifySizeChanged();
notifySizeChanged(false);
}
else {
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
}
}
@ -297,7 +315,7 @@ class TypedefDB extends DataTypeDB implements TypeDef {
checkDeleted();
record.setLongValue(TypedefDBAdapter.TYPEDEF_UNIVERSAL_DT_ID_COL, id.getValue());
adapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -319,7 +337,7 @@ class TypedefDB extends DataTypeDB implements TypeDef {
checkDeleted();
record.setLongValue(TypedefDBAdapter.TYPEDEF_SOURCE_ARCHIVE_ID_COL, id.getValue());
adapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -336,7 +354,7 @@ class TypedefDB extends DataTypeDB implements TypeDef {
checkDeleted();
record.setLongValue(TypedefDBAdapter.TYPEDEF_LAST_CHANGE_TIME_COL, lastChangeTime);
adapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);
@ -354,7 +372,7 @@ class TypedefDB extends DataTypeDB implements TypeDef {
record.setLongValue(TypedefDBAdapter.TYPEDEF_SOURCE_SYNC_TIME_COL,
lastChangeTimeInSourceArchive);
adapter.updateRecord(record, false);
dataMgr.dataTypeChanged(this);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
dataMgr.dbError(e);

View file

@ -18,8 +18,8 @@ package ghidra.program.database.data;
import java.io.IOException;
import java.util.*;
import db.Field;
import db.DBRecord;
import db.Field;
import ghidra.docking.settings.Settings;
import ghidra.program.database.DBObjectCache;
import ghidra.program.model.data.*;
@ -29,11 +29,13 @@ import ghidra.util.Msg;
/**
* Database implementation for the Union data type.
*/
class UnionDB extends CompositeDB implements Union {
class UnionDB extends CompositeDB implements UnionInternal {
private ArrayList<DataTypeComponentDB> components;
private int unionLength;
private static MemberComparator comparator = new MemberComparator();
private int unionAlignment; // reflects stored alignment, -1 if not yet stored
private int computedAlignment = -1; // cached alignment if not yet stored
private List<DataTypeComponentDB> components;
/**
* Constructor
@ -65,9 +67,10 @@ class UnionDB extends CompositeDB implements Union {
catch (IOException e) {
dataMgr.dbError(e);
}
Collections.sort(components, comparator);
Collections.sort(components, ComponentComparator.INSTANCE);
unionLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL);
unionAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL);
computedAlignment = -1;
}
@Override
@ -89,8 +92,11 @@ class UnionDB extends CompositeDB implements Union {
lock.acquire();
try {
checkDeleted();
getComputedAlignment(true); // ensure previous alignment has been stored
DataTypeComponent dtc = doAdd(dataType, length, componentName, comment, true);
adjustLength(true, true);
if (!repack(false, true)) {
dataMgr.dataTypeChanged(this, false);
}
return dtc;
}
catch (DataTypeDependencyException e) {
@ -103,7 +109,7 @@ class UnionDB extends CompositeDB implements Union {
private int getBitFieldAllocation(BitFieldDataType bitfieldDt) {
BitFieldPacking bitFieldPacking = getBitFieldPacking();
BitFieldPacking bitFieldPacking = getDataOrganization().getBitFieldPacking();
if (bitFieldPacking.useMSConvention()) {
return bitfieldDt.getBaseTypeSize();
}
@ -113,8 +119,8 @@ class UnionDB extends CompositeDB implements Union {
}
int length = bitfieldDt.getBaseTypeSize();
int packValue = getPackingValue();
if (packValue != NOT_PACKING && length > packValue) {
int packValue = getStoredPackingValue();
if (packValue > 0 && length > packValue) {
length =
DataOrganizationImpl.getLeastCommonMultiple(bitfieldDt.getStorageSize(), packValue);
}
@ -179,6 +185,8 @@ class UnionDB extends CompositeDB implements Union {
dataType = resolve(dataType);
checkAncestry(dataType);
getComputedAlignment(true); // ensure previous alignment has been stored
length = getPreferredComponentLength(dataType, length);
DataTypeComponentDB dtc =
@ -187,7 +195,9 @@ class UnionDB extends CompositeDB implements Union {
shiftOrdinals(ordinal, 1);
components.add(ordinal, dtc);
adjustLength(true, true);
if (!repack(false, true)) {
dataMgr.dataTypeChanged(this, false);
}
return dtc;
}
catch (DataTypeDependencyException e) {
@ -207,10 +217,10 @@ class UnionDB extends CompositeDB implements Union {
@Override
public DataTypeComponent insertBitField(int ordinal, DataType baseDataType, int bitSize,
String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException {
throws InvalidDataTypeException, IndexOutOfBoundsException {
if (ordinal < 0 || ordinal > components.size()) {
throw new ArrayIndexOutOfBoundsException(ordinal);
throw new IndexOutOfBoundsException(ordinal);
}
BitFieldDataType bitFieldDt = new BitFieldDBDataType(baseDataType, bitSize, 0);
@ -223,12 +233,16 @@ class UnionDB extends CompositeDB implements Union {
try {
checkDeleted();
getComputedAlignment(true); // ensure previous alignment has been stored
DataTypeComponentDB dtc = components.remove(ordinal);
dtc.getDataType().removeParent(this);
removeComponent(dtc.getKey());
shiftOrdinals(ordinal, -1);
adjustLength(true, true);
if (!repack(false, true)) {
dataMgr.dataTypeChanged(this, false);
}
}
finally {
lock.release();
@ -236,26 +250,70 @@ class UnionDB extends CompositeDB implements Union {
}
@Override
public void delete(int[] ordinals) {
for (int ordinal : ordinals) {
delete(ordinal);
public void delete(Set<Integer> ordinals) {
if (ordinals.isEmpty()) {
return;
}
lock.acquire();
try {
checkDeleted();
if (isPackingEnabled()) {
getComputedAlignment(true); // ensure previous alignment has been stored
}
List<DataTypeComponentDB> newComponents = new ArrayList<>();
int newLength = 0;
int ordinalAdjustment = 0;
for (DataTypeComponentDB dtc : components) {
int ordinal = dtc.getOrdinal();
if (ordinals.contains(ordinal)) {
// component removed
--ordinalAdjustment;
}
else {
if (ordinalAdjustment != 0) {
dtc.setOrdinal(dtc.getOrdinal() + ordinalAdjustment, true);
}
newComponents.add(dtc);
newLength = Math.max(newLength, dtc.getLength());
}
}
components = newComponents;
if (isPackingEnabled()) {
if (!repack(false, true)) {
dataMgr.dataTypeChanged(this, false);
}
}
else {
unionLength = newLength;
notifySizeChanged(false);
}
}
finally {
lock.release();
}
}
@Override
public void replaceWith(DataType dataType) {
if (!(dataType instanceof Union)) {
if (!(dataType instanceof UnionInternal)) {
throw new IllegalArgumentException();
}
lock.acquire();
boolean isResolveCacheOwner = dataMgr.activateResolveCache();
try {
checkDeleted();
doReplaceWith((Union) dataType, true);
doReplaceWith((UnionInternal) dataType, true);
}
catch (DataTypeDependencyException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
if (isResolveCacheOwner) {
dataMgr.flushResolveQueue(true);
@ -264,8 +322,8 @@ class UnionDB extends CompositeDB implements Union {
}
}
void doReplaceWith(Union union, boolean notify)
throws DataTypeDependencyException {
void doReplaceWith(UnionInternal union, boolean notify)
throws DataTypeDependencyException, IOException {
// pre-resolved component types to catch dependency issues early
DataTypeComponent[] otherComponents = union.getComponents();
@ -275,34 +333,25 @@ class UnionDB extends CompositeDB implements Union {
checkAncestry(resolvedDts[i]);
}
int oldLength = unionLength;
int oldMinAlignment = getMinimumAlignment();
for (DataTypeComponentDB dtc : components) {
dtc.getDataType().removeParent(this);
removeComponent(dtc.getKey());
}
components.clear();
unionAlignment = -1;
computedAlignment = -1;
setAlignment(union, false);
doSetPackingAndAlignment(union);
for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i];
doAdd(resolvedDts[i], dtc.getLength(), dtc.getFieldName(), dtc.getComment(), false);
}
adjustLength(false, false);
repack(false, false);
if (notify) {
if (oldMinAlignment != getMinimumAlignment()) {
notifyAlignmentChanged();
}
else if (oldLength != unionLength) {
notifySizeChanged();
}
else {
dataMgr.dataTypeChanged(this);
}
notifySizeChanged(false); // assume size and/or alignment changed
}
if (pointerPostResolveRequired) {
@ -394,7 +443,7 @@ class UnionDB extends CompositeDB implements Union {
}
@Override
public DataType clone(DataTypeManager dtm) {
public Union clone(DataTypeManager dtm) {
UnionDataType union = new UnionDataType(getCategoryPath(), getName(), getUniversalID(),
getSourceArchive(), getLastChangeTime(), getLastChangeTimeInSourceArchive(), dtm);
union.setDescription(getDescription());
@ -402,6 +451,11 @@ class UnionDB extends CompositeDB implements Union {
return union;
}
@Override
public boolean isZeroLength() {
return unionLength == 0;
}
@Override
public int getLength() {
lock.acquire();
@ -417,6 +471,52 @@ class UnionDB extends CompositeDB implements Union {
}
}
@Override
protected void fixupComponents() {
boolean changed = false;
for (DataTypeComponentDB dtc : components) {
DataType dt = dtc.getDataType();
if (dt instanceof BitFieldDataType) {
dt = adjustBitField(dt); // in case base type changed
}
int dtcLen = dtc.getLength();
int length = dt.getLength();
if (length <= 0) {
length = dtcLen;
}
if (length != dtcLen) {
dtc.setLength(length, true);
changed = true;
}
}
if (changed) {
// NOTE: since we do not retain our external alignment we have no way of knowing if
// it has changed, so we must assume it has if we are an aligned union
// Do not notify parents
if (!repack(false, false)) {
dataMgr.dataTypeChanged(this, false);
}
}
}
@Override
public void dataTypeAlignmentChanged(DataType dt) {
if (!isPackingEnabled()) {
return;
}
if (dt instanceof BitFieldDataType) {
return; // unsupported
}
lock.acquire();
try {
checkDeleted();
repack(true, true);
}
finally {
lock.release();
}
}
@Override
public void dataTypeSizeChanged(DataType dt) {
if (dt instanceof BitFieldDataType) {
@ -436,8 +536,8 @@ class UnionDB extends CompositeDB implements Union {
changed = true;
}
}
if (changed) {
adjustLength(true, false); // notifies parents
if (changed && !repack(true, true)) {
dataMgr.dataTypeChanged(this, true);
}
}
finally {
@ -445,38 +545,6 @@ class UnionDB extends CompositeDB implements Union {
}
}
@Override
protected void fixupComponents() {
boolean changed = false;
for (DataTypeComponentDB dtc : components) {
DataType dt = dtc.getDataType();
if (dt instanceof BitFieldDataType) {
dt = adjustBitField(dt); // in case base type changed
}
int dtcLen = dtc.getLength();
int length = dt.getLength();
if (length <= 0) {
length = dtcLen;
}
if (length != dtcLen) {
dtc.setLength(length, true);
changed = true;
}
}
if (changed || isInternallyAligned()) {
// NOTE: since we do not retain our external alignment we have no way of knowing if
// it has changed, so we must assume it has if we are an aligned union
// Do not notify parents
adjustLength(false, false);
dataMgr.dataTypeChanged(this);
}
}
@Override
public void dataTypeAlignmentChanged(DataType dt) {
adjustInternalAlignment(true);
}
private DataType adjustBitField(DataType dataType) {
if (!(dataType instanceof BitFieldDataType)) {
@ -488,8 +556,8 @@ class UnionDB extends CompositeDB implements Union {
DataType baseDataType = bitfieldDt.getBaseDataType();
baseDataType = resolve(baseDataType);
// Both aligned and unaligned bitfields use same adjustment
// unaligned must force bitfield placement at byte offset 0
// Both aligned and non-packed bitfields use same adjustment
// non-packed must force bitfield placement at byte offset 0
int bitSize = bitfieldDt.getDeclaredBitSize();
int effectiveBitSize =
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
@ -522,32 +590,94 @@ class UnionDB extends CompositeDB implements Union {
return bitfieldDt;
}
private void adjustLength(boolean notify, boolean setLastChangeTime) {
@Override
protected int getComputedAlignment(boolean updateRecord) {
if (unionAlignment > 0) {
return unionAlignment;
}
if (computedAlignment <= 0) {
if (isPackingEnabled()) {
computedAlignment =
CompositeAlignmentHelper.getAlignment(getDataOrganization(), this);
}
else {
computedAlignment = getNonPackedAlignment();
}
}
if (updateRecord) {
// perform lazy update of stored computed alignment
record.setIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL, computedAlignment);
try {
compositeAdapter.updateRecord(record, false);
}
catch (IOException e) {
dataMgr.dbError(e);
}
unionAlignment = computedAlignment;
computedAlignment = -1;
return unionAlignment;
}
return computedAlignment;
}
/**
* Perform union member repack.
* Perform lazy update of stored alignment introduced with v5 adapter.
*/
@Override
protected boolean repack(boolean isAutoChange, boolean notify) {
lock.acquire();
try {
checkDeleted();
int oldLength = unionLength;
boolean storeAlignment = (unionAlignment <= 0); // lazy upgrade for v5 adapter
int oldAlignment = getComputedAlignment(false);
unionLength = 0;
for (DataTypeComponent dtc : components) {
// TODO: compute alignment in this loop
int length = dtc.getLength();
if (isInternallyAligned() && dtc.isBitFieldComponent()) {
if (isPackingEnabled() && dtc.isBitFieldComponent()) {
// revise length to reflect compiler bitfield allocation rules
length = getBitFieldAllocation((BitFieldDataType) dtc.getDataType());
}
unionLength = Math.max(length, unionLength);
}
DataOrganization dataOrganization = getDataOrganization();
int alignment = dataOrganization.getAlignment(this, unionLength);
int amountFilled = unionLength % alignment;
if (amountFilled > 0) {
unionLength += alignment - amountFilled;
computedAlignment = -1; // force recompute of unionAlignment
unionAlignment = -1;
unionAlignment = getComputedAlignment(false);
if (isPackingEnabled()) {
unionLength = DataOrganizationImpl.getAlignedOffset(unionAlignment, unionLength);
}
updateLength(oldLength, notify, setLastChangeTime);
boolean changed = (oldLength != unionLength) || (oldAlignment != unionAlignment);
if (changed || storeAlignment) {
record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, unionLength);
record.setIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL, unionAlignment);
try {
compositeAdapter.updateRecord(record, changed && !isAutoChange);
}
catch (IOException e) {
dataMgr.dbError(e);
}
}
if (changed & notify) {
if (oldLength != unionLength) {
notifySizeChanged(isAutoChange);
}
else if (oldAlignment != unionAlignment) {
notifyAlignmentChanged(isAutoChange);
}
else {
dataMgr.dataTypeChanged(this, isAutoChange);
}
}
return changed;
}
finally {
lock.release();
@ -559,7 +689,7 @@ class UnionDB extends CompositeDB implements Union {
lock.acquire();
try {
checkDeleted();
boolean didChange = false;
boolean changed = false;
for (int i = components.size() - 1; i >= 0; i--) { // reverse order
DataTypeComponentDB dtc = components.get(i);
boolean removeBitFieldComponent = false;
@ -572,11 +702,11 @@ class UnionDB extends CompositeDB implements Union {
components.remove(i);
removeComponent(dtc.getKey());
shiftOrdinals(i, -1);
didChange = true;
changed = true;
}
}
if (didChange) {
adjustLength(true, true);
if (changed && !repack(false, true)) {
dataMgr.dataTypeChanged(this, false);
}
}
finally {
@ -590,7 +720,7 @@ class UnionDB extends CompositeDB implements Union {
if (dataType == this) {
return true;
}
if (!(dataType instanceof Union)) {
if (!(dataType instanceof UnionInternal)) {
return false;
}
@ -609,13 +739,10 @@ class UnionDB extends CompositeDB implements Union {
try {
isEquivalent = false;
Union union = (Union) dataType;
if (isInternallyAligned() != union.isInternallyAligned() ||
isDefaultAligned() != union.isDefaultAligned() ||
isMachineAligned() != union.isMachineAligned() ||
getMinimumAlignment() != union.getMinimumAlignment() ||
getPackingValue() != union.getPackingValue()) {
// rely on component match instead of checking length
UnionInternal union = (UnionInternal) dataType;
if (getStoredPackingValue() != union.getStoredPackingValue() ||
getStoredMinimumAlignment() != union.getStoredMinimumAlignment()) {
// rely on component match instead of checking length
// since dynamic component sizes could affect length
return false;
}
@ -637,24 +764,6 @@ class UnionDB extends CompositeDB implements Union {
return true;
}
private void updateLength(int oldLength, boolean notify, boolean setLastChangeTime) {
if (oldLength != unionLength) {
record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, unionLength);
try {
compositeAdapter.updateRecord(record, setLastChangeTime);
}
catch (IOException e) {
dataMgr.dbError(e);
}
if (notify) {
notifySizeChanged();
}
}
else if (notify) {
dataMgr.dataTypeChanged(this);
}
}
private void shiftOrdinals(int ordinal, int deltaOrdinal) {
for (int i = ordinal; i < components.size(); i++) {
DataTypeComponentDB dtc = components.get(i);
@ -729,8 +838,8 @@ class UnionDB extends CompositeDB implements Union {
changed = true;
}
}
if (changed) {
adjustLength(true, true);
if (changed && !repack(false, true)) {
dataMgr.dataTypeChanged(this, false);
}
}
finally {
@ -759,27 +868,9 @@ class UnionDB extends CompositeDB implements Union {
}
}
private static class MemberComparator implements Comparator<DataTypeComponent> {
@Override
public int compare(DataTypeComponent dtc1, DataTypeComponent dtc2) {
return dtc1.getOrdinal() - dtc2.getOrdinal();
}
}
@Override
public String getDefaultLabelPrefix() {
return "UNION_" + getName();
}
@Override
public void realign() {
if (isInternallyAligned()) {
adjustInternalAlignment(true);
}
}
@Override
public void adjustInternalAlignment(boolean notify) {
adjustLength(notify, false);
}
}

View file

@ -112,6 +112,11 @@ public abstract class AbstractDataType implements DataType {
return false;
}
@Override
public boolean isZeroLength() {
return false;
}
@Override
public String toString() {
return getDisplayName();
@ -138,6 +143,11 @@ public abstract class AbstractDataType implements DataType {
// do nothing
}
@Override
public void dataTypeAlignmentChanged(DataType dt) {
// do nothing
}
@Override
public void dataTypeDeleted(DataType dt) {
// do nothing

View file

@ -42,6 +42,7 @@ public abstract class AbstractFloatDataType extends BuiltIn {
*
* @see ghidra.program.model.data.DataType#getMnemonic(Settings)
*/
@Override
public String getMnemonic(Settings settings) {
return name;
}
@ -49,6 +50,7 @@ public abstract class AbstractFloatDataType extends BuiltIn {
/**
* @see ghidra.program.model.data.DataType#isDynamicallySized()
*/
@Override
public boolean isDynamicallySized() {
return false;
}
@ -57,6 +59,7 @@ public abstract class AbstractFloatDataType extends BuiltIn {
*
* @see ghidra.program.model.data.DataType#getDescription()
*/
@Override
public String getDescription() {
return "IEEE-754 Float";
}
@ -65,6 +68,7 @@ public abstract class AbstractFloatDataType extends BuiltIn {
*
* @see ghidra.program.model.data.DataType#getValue(ghidra.program.model.mem.MemBuffer, ghidra.docking.settings.Settings, int)
*/
@Override
public Object getValue(MemBuffer buf, Settings settings, int length) {
try {
int len = getLength(); // use type length (ignore length arg)
@ -97,10 +101,12 @@ public abstract class AbstractFloatDataType extends BuiltIn {
*
* @see ghidra.program.model.data.DataType#getRepresentation(MemBuffer, Settings, int)
*/
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
Object obj = getValue(buf, settings, length);
if (obj == null)
if (obj == null) {
return "??";
}
return obj.toString();
}

View file

@ -43,7 +43,7 @@ class AlignedComponentPacker {
private InternalDataTypeComponent lastComponent;
private int externalAlignment = 1;
private int defaultAlignment = 1;
private boolean componentsChanged;
AlignedComponentPacker(int packValue, DataOrganization dataOrganization) {
@ -68,7 +68,7 @@ class AlignedComponentPacker {
lastComponent = dtc;
++nextOrdinal;
externalAlignment = getComponentAlignmentLCM(externalAlignment);
defaultAlignment = getComponentAlignmentLCM(defaultAlignment);
}
/**
@ -80,13 +80,13 @@ class AlignedComponentPacker {
}
/**
* Get the external structure alignment after all components have been added.
* This value does not factor in the affects of any trailing flexible array component
* which may exist.
* Get the external structure alignment after all components have been packed.
* NOTE: This value does not factor in the affects of any trailing flexible array component
* which may exist or the alignment which may have been explicitly set on the structure.
* @return external alignment
*/
int getExternalAlignment() {
return externalAlignment;
int getDefaultAlignment() {
return defaultAlignment;
}
/**
@ -108,12 +108,11 @@ class AlignedComponentPacker {
offset = lastComponent.getOffset() + lastComponent.getLength();
if (!bitFieldPacking.useMSConvention() && lastComponent.isZeroBitFieldComponent()) {
// factor in trailing zero-length bitfield
// TODO: does pack setting need to be considered?
BitFieldDataType bitfieldDt = (BitFieldDataType) lastComponent.getDataType();
int sizeAlignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization,
Composite.NOT_PACKING, bitfieldDt.getBaseDataType(),
bitfieldDt.getBaseTypeSize());
int sizeAlignment = bitfieldDt.getBaseDataType().getAlignment();
getBitFieldAlignment((BitFieldDataType) lastComponent.getDataType());
offset += DataOrganizationImpl.getPaddingSize(sizeAlignment, offset);
offset = DataOrganizationImpl.getAlignedOffset(sizeAlignment, offset);
}
}
return offset;
@ -129,13 +128,13 @@ class AlignedComponentPacker {
private int getBitFieldAlignment(BitFieldDataType bitfieldDt) {
if (!bitFieldPacking.useMSConvention() && packValue != Composite.NOT_PACKING) {
if (!bitFieldPacking.useMSConvention() && packValue != CompositeInternal.DEFAULT_PACKING) {
// GCC always uses 1 when packing regardless of pack value
return 1;
}
return CompositeAlignmentHelper.getPackedAlignment(dataOrganization, packValue,
bitfieldDt.getBaseDataType(), bitfieldDt.getBaseTypeSize());
return CompositeAlignmentHelper
.getPackedAlignment(bitfieldDt.getBaseDataType().getAlignment(), packValue);
}
private boolean isIgnoredZeroBitField(BitFieldDataType zeroBitFieldDt) {
@ -167,11 +166,11 @@ class AlignedComponentPacker {
if (!bitFieldPacking.useMSConvention() && !isLastComponent) {
// GCC ignores pack value for :0 bitfield alignment but considers it when
// passing alignment along to structure
pack = Composite.NOT_PACKING;
pack = CompositeInternal.NO_PACKING;
}
return CompositeAlignmentHelper.getPackedAlignment(dataOrganization, pack,
zeroBitFieldDt.getBaseDataType(), zeroBitFieldDt.getBaseTypeSize());
return CompositeAlignmentHelper
.getPackedAlignment(zeroBitFieldDt.getBaseDataType().getAlignment(), pack);
}
private void initGroup(InternalDataTypeComponent dataTypeComponent, boolean isLastComponent) {
@ -248,8 +247,8 @@ class AlignedComponentPacker {
*/
private void adjustZeroLengthBitField(int ordinal, int minimumAlignment) {
int minOffset = DataOrganizationImpl.getOffset(minimumAlignment, groupOffset);
int zeroAlignmentOffset = DataOrganizationImpl.getOffset(zeroAlignment, groupOffset);
int minOffset = DataOrganizationImpl.getAlignedOffset(minimumAlignment, groupOffset);
int zeroAlignmentOffset = DataOrganizationImpl.getAlignedOffset(zeroAlignment, groupOffset);
// Determine component offset of zero-length bitfield and the component
// which immediately follows it.
@ -312,8 +311,8 @@ class AlignedComponentPacker {
dtSize = dataTypeComponent.getLength();
}
int alignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, packValue,
componentDt, dtSize);
int alignment =
CompositeAlignmentHelper.getPackedAlignment(componentDt.getAlignment(), packValue);
int offset;
if (lastComponent != null && lastComponent.isZeroBitFieldComponent()) {
@ -322,7 +321,7 @@ class AlignedComponentPacker {
offset = groupOffset;
}
else {
offset = DataOrganizationImpl.getOffset(alignment, minOffset);
offset = DataOrganizationImpl.getAlignedOffset(alignment, minOffset);
if (lastComponent == null) {
groupOffset = offset; // establish corrected group offset after alignment
}
@ -345,14 +344,14 @@ class AlignedComponentPacker {
int bitsConsumed;
// update lastAlignment to be conveyed onto structure alignment
int alignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, packValue,
bitfieldDt.getPrimitiveBaseDataType(), bitfieldDt.getBaseTypeSize());
int alignment = CompositeAlignmentHelper.getPackedAlignment(
bitfieldDt.getPrimitiveBaseDataType().getAlignment(), packValue);
// Set conveyed alignment early since bitfield alignment may be reduced below
lastAlignment = Math.max(alignment, lastAlignment);
if (lastComponent == null) {
offset = DataOrganizationImpl.getOffset(alignment, groupOffset);
offset = DataOrganizationImpl.getAlignedOffset(alignment, groupOffset);
bitsConsumed = 0;
groupOffset = offset; // establish corrected group offset after alignment
}
@ -406,10 +405,10 @@ class AlignedComponentPacker {
if (offset % alignment != 0 || byteSize > bitfieldDt.getBaseTypeSize()) {
// offset is not an aligned offset (which may be OK when packing with lastComponent)
int alignedBaseOffset =
DataOrganizationImpl.getOffset(alignment, offset) - alignment;
DataOrganizationImpl.getAlignedOffset(alignment, offset) - alignment;
if (endOffset >= alignedBaseOffset + bitfieldDt.getBaseTypeSize()) {
// skip ahead to next aligned offset
offset = DataOrganizationImpl.getOffset(alignment, offset + 1);
offset = DataOrganizationImpl.getAlignedOffset(alignment, offset + 1);
endOffset = offset + byteSize - 1;
bitsConsumed = 0;
}

View file

@ -31,13 +31,13 @@ import ghidra.util.exception.DuplicateNameException;
*/
public class AlignedStructureInspector extends AlignedStructurePacker {
private AlignedStructureInspector(Structure structure) {
private AlignedStructureInspector(StructureInternal structure) {
super(structure, getComponentWrappers(structure));
}
private static List<ReadOnlyComponentWrapper> getComponentWrappers(Structure structure) {
List<ReadOnlyComponentWrapper> list = new ArrayList<>();
for (DataTypeComponent c : structure.getComponents()) {
for (DataTypeComponent c : structure.getDefinedComponents()) {
list.add(new ReadOnlyComponentWrapper(c));
}
return list;
@ -168,7 +168,7 @@ public class AlignedStructureInspector extends AlignedStructurePacker {
* @param structure
* @return aligned packing result
*/
public static StructurePackResult packComponents(Structure structure) {
public static StructurePackResult packComponents(StructureInternal structure) {
AlignedStructureInspector packer = new AlignedStructureInspector(structure);
return packer.pack();
}

View file

@ -27,7 +27,7 @@ import java.util.List;
*/
public class AlignedStructurePacker {
private final Structure structure;
private final StructureInternal structure;
private final List<? extends InternalDataTypeComponent> components;
private final DataOrganization dataOrganization;
@ -38,7 +38,7 @@ public class AlignedStructurePacker {
* during packing (ordinal, offset, length and bit-field datatypes may be modified)
* @param components list of mutable component
*/
protected AlignedStructurePacker(Structure structure,
protected AlignedStructurePacker(StructureInternal structure,
List<? extends InternalDataTypeComponent> components) {
this.structure = structure;
this.components = components;
@ -75,7 +75,7 @@ public class AlignedStructurePacker {
int componentCount = 0;
AlignedComponentPacker packer =
new AlignedComponentPacker(structure.getPackingValue(), dataOrganization);
new AlignedComponentPacker(structure.getStoredPackingValue(), dataOrganization);
// Remove any default components from list
Iterator<? extends InternalDataTypeComponent> componentIterator = components.iterator();
@ -95,7 +95,7 @@ public class AlignedStructurePacker {
packer.addComponent(dataTypeComponent, isLastComponent);
}
int externalAlignment = packer.getExternalAlignment();
int defaultAlignment = packer.getDefaultAlignment();
int length = packer.getLength();
componentsChanged |= packer.componentsChanged();
@ -104,22 +104,25 @@ public class AlignedStructurePacker {
if (flexibleArrayComponent != null) {
// account for flexible array type and any end of structure padding required
int componentAlignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization,
structure.getPackingValue(), flexibleArrayComponent);
length = DataOrganizationImpl.getOffset(componentAlignment, length);
externalAlignment =
DataOrganizationImpl.getLeastCommonMultiple(externalAlignment, componentAlignment);
structure.getStoredPackingValue(), flexibleArrayComponent);
length = DataOrganizationImpl.getAlignedOffset(componentAlignment, length);
defaultAlignment =
DataOrganizationImpl.getLeastCommonMultiple(defaultAlignment, componentAlignment);
}
int alignment = structure.getMinimumAlignment();
if (alignment < externalAlignment) {
alignment = externalAlignment;
int alignment = defaultAlignment;
AlignmentType alignmentType = structure.getAlignmentType();
if (alignmentType != AlignmentType.DEFAULT) {
// Apply minimum alignment if applicable - may be reduced by explicit pack
// Simplified logic assumes pack and align values which are a power of 2 (1,2,4,8,16...)
int minAlign =
alignmentType == AlignmentType.MACHINE ? dataOrganization.getMachineAlignment()
: structure.getExplicitMinimumAlignment();
alignment = Math.max(defaultAlignment, minAlign);
}
if (length != 0) {
int padSize = DataOrganizationImpl.getPaddingSize(alignment, length);
if (padSize > 0) {
length += padSize;
}
length = DataOrganizationImpl.getAlignedOffset(alignment, length);
}
return new StructurePackResult(componentCount, length, alignment, componentsChanged);
@ -135,7 +138,7 @@ public class AlignedStructurePacker {
* @param components structure components (excludes any trailing flexible array).
* @return aligned packing result
*/
public static StructurePackResult packComponents(Structure structure,
public static StructurePackResult packComponents(StructureInternal structure,
List<? extends InternalDataTypeComponent> components) {
AlignedStructurePacker packer = new AlignedStructurePacker(structure, components);
return packer.pack();

View file

@ -0,0 +1,41 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.data;
/**
* <code>AlignmentType</code> specifies the type of alignment which applies to a composite data type.
* This can be DEFAULT, MACHINE, EXPLICIT. For packed composites, the length of the composite
* will be padded to force the length to a multiple of the computed alignment.
*/
public enum AlignmentType {
/**
* <B>DEFAULT</B> - this data type's alignment is computed based upon its current pack setting
* and data organization rules. If packing is disabled the computed alignment will be 1.
*/
DEFAULT,
/**
* <B>MACHINE</B> - this data type's alignment will be a multiple of the machine alignment
* specified by the data organization. In general, and for all non-packed composites, the
* computed alignment will match the machine alignment if this setting is used.
*/
MACHINE,
/**
* <B>MACHINE</B> - this data type's alignment will be a multiple of the explicit alignment
* value specified for the datatype. For all non-packed composites, the
* computed alignment will match the machine alignment if this setting is used.
*/
EXPLICIT;
}

View file

@ -130,6 +130,11 @@ public class ArrayDataType extends DataTypeImpl implements Array {
return DataTypeUtilities.getMnemonic(this, false, settings);
}
@Override
public boolean isZeroLength() {
return dataType.isZeroLength();
}
@Override
public int getLength() {
return numElements * getElementLength();
@ -160,11 +165,18 @@ public class ArrayDataType extends DataTypeImpl implements Array {
@Override
public void dataTypeSizeChanged(DataType dt) {
if (dt.equals(dataType)) {
if (dt == dataType) {
notifySizeChanged();
}
}
@Override
public void dataTypeAlignmentChanged(DataType dt) {
if (dt == dataType) {
notifyAlignmentChanged();
}
}
@Override
public Class<?> getValueClass(Settings settings) {
return getArrayValueClass(settings);

View file

@ -15,56 +15,13 @@
*/
package ghidra.program.model.data;
import java.util.Set;
/**
* Interface for common methods in Structure and Union
*/
public interface Composite extends DataType {
/**
* AlignmentType defined the three states for the type of alignment of a composite data type.
* This can be default aligned, machine aligned or aligned by value.
* <BR>This controls how this data type will be aligned within other data types.
* <BR><B>Default Aligned</B> means to determine this data type's alignment based upon the
* alignments of its components. This is controlled by the Data Organization that is provided
* by its data type manager. If there is no data type manager then a default data organization
* is used.
* <BR><B>Machine Aligned</B> means this data type's alignment will use a minimum alignment that
* is the machine alignment specified by the data organization.
* <BR><B>Align By Value</B> means that a "minimum alignment value", which is a power of 2 and
* specified elsewhere, will affect the alignment so that it will be at least the
* indicated value and will be a multiple of the minimum value and of the default value.
* <BR>Note: If the data organization specifies a maximum alignment, other than 0, then the
* alignment value will not be allowed to exceed the maximum alignment for any of these types.
*/
public static enum AlignmentType implements NamedAlignment {
DEFAULT_ALIGNED("Default Aligned"),
MACHINE_ALIGNED("Machine Aligned"),
ALIGNED_BY_VALUE("Align By Value");
private String name;
AlignmentType(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
}
interface NamedAlignment {
public String getName();
}
public final static int DEFAULT_ALIGNMENT_VALUE = 0;
public final static int NOT_PACKING = 0;
/**
* Sets the string describing this data type.
* @param desc the new description.
@ -74,8 +31,8 @@ public interface Composite extends DataType {
/**
* Gets the number of component data types in this composite.
* The count will include all undefined filler components which may be present
* within an unaligned structure. Structures do not include the
* If this is Structure with packing disabled, the count will include all undefined filler
* components which may be present. In addition, Structures do not include the
* optional trailing flexible array component in this count
* (see {@link Structure#hasFlexibleArrayComponent()}).
* @return the number of components that make up this composite
@ -84,9 +41,13 @@ public interface Composite extends DataType {
/**
* Returns the number of explicitly defined components in this composite.
* For Unions and aligned Structures this is equivalent to {@link #getNumComponents()}
* since they do not contain undefined components. The count will exclude all undefined
* filler components which may be present within an unaligned structure.
* For Unions and packed Structures this is equivalent to {@link #getNumComponents()}
* since they do not contain undefined components.
* This count will always exclude all undefined filler components which may be present
* within a Structure whoose packing is disabled (see {@link #isPackingEnabled()}).
* In addition, Structures do not include the
* optional trailing flexible array component in this count
* (see {@link Structure#hasFlexibleArrayComponent()}).
* @return the number of explicitly defined components in this composite
*/
public abstract int getNumDefinedComponents();
@ -95,27 +56,28 @@ public interface Composite extends DataType {
* Returns the component of this data type with the indicated ordinal.
* @param ordinal the component's ordinal (zero based).
* @return the data type component.
* @throws ArrayIndexOutOfBoundsException if the ordinal is out of bounds
* @throws IndexOutOfBoundsException if the ordinal is out of bounds
*/
public abstract DataTypeComponent getComponent(int ordinal);
public abstract DataTypeComponent getComponent(int ordinal) throws IndexOutOfBoundsException;
/**
* Returns an array of Data Type Components that make up this composite including
* undefined filler components which may be present within an unaligned structure.
* undefined filler components which may be present within a Structure whch has packing disabled.
* Structures do not include the optional trailing flexible array component in the returned array.
* The number of components corresponds to {@link #getNumComponents()}.
* @return list all components
* @return array all components
*/
public abstract DataTypeComponent[] getComponents();
/**
* Returns an array of Data Type Components that make up this composite excluding
* undefined filler components which may be present within an unaligned structure.
* The number of components corresponds to {@link #getNumComponents()}. For Unions and
* aligned Structures this is equivalent to {@link #getComponents()}
* since they do not contain undefined components. Structures do not include the
* optional trailing flexible array component in this list
* undefined filler components which may be present within Structures where packing is disabled.
* The number of components corresponds to {@link #getNumDefinedComponents()}. For Unions and
* packed Structures this is equivalent to {@link #getComponents()}
* since they do not contain undefined filler components. Structures do not include the
* optional trailing flexible array component in the returned array
* (see {@link Structure#getFlexibleArrayComponent()}).
* @return list all explicitly defined components
* @return array all explicitly defined components
*/
public abstract DataTypeComponent[] getDefinedComponents();
@ -164,9 +126,9 @@ public interface Composite extends DataType {
/**
* Adds a new bitfield to the end of this composite. This method is intended
* to be used with aligned structures/unions only where the bitfield will be
* to be used with packed structures/unions only where the bitfield will be
* appropriately packed. The minimum storage storage byte size will be applied.
* It will not provide useful results within unaligned composites.
* It will not provide useful results for composites with packing disabled.
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param bitSize the bitfield size in bits
* @param componentName the field name to associate with this component.
@ -208,9 +170,10 @@ public interface Composite extends DataType {
* allowed to be inserted into this composite data type.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to insert dt1 to dt2 since this would cause a cyclic dependency.
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public DataTypeComponent insert(int ordinal, DataType dataType) throws IllegalArgumentException;
public DataTypeComponent insert(int ordinal, DataType dataType)
throws IndexOutOfBoundsException, IllegalArgumentException;
/**
* Inserts a new datatype at the specified ordinal position in this composite.
@ -226,10 +189,10 @@ public interface Composite extends DataType {
* length is specified.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to insert dt1 to dt2 since this would cause a cyclic dependency.
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public DataTypeComponent insert(int ordinal, DataType dataType, int length)
throws IllegalArgumentException;
throws IndexOutOfBoundsException, IllegalArgumentException;
/**
* Inserts a new datatype at the specified ordinal position in this composite.
@ -247,28 +210,28 @@ public interface Composite extends DataType {
* is specified.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to insert dt1 to dt2 since this would cause a cyclic dependency.
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name,
String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException;
String comment) throws IndexOutOfBoundsException, IllegalArgumentException;
/**
* Deletes the component at the given ordinal position.
* <BR>Note: Removal of bitfields from an unaligned structure will
* not shift other components with vacated bytes reverting to undefined.
* <BR>Note: Removal of bitfields from a structure with packing disabled will
* not shift other components causing vacated bytes to revert to undefined filler.
* @param ordinal the ordinal of the component to be deleted.
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public void delete(int ordinal) throws ArrayIndexOutOfBoundsException;
public void delete(int ordinal) throws IndexOutOfBoundsException;
/**
* Deletes the components at the given ordinal positions.
* <BR>Note: Removal of bitfields from an unaligned structure will
* not shift other components with vacated bytes reverting to undefined.
* Deletes the specified set of components at the given ordinal positions.
* <BR>Note: Removal of bitfields from a structure with packing disabled will
* not shift other components causing vacated bytes to revert to undefined filler.
* @param ordinals the ordinals of the component to be deleted.
* @throws ArrayIndexOutOfBoundsException if any specified component ordinal is out of bounds
* @throws IndexOutOfBoundsException if any specified component ordinal is out of bounds
*/
public void delete(int[] ordinals) throws ArrayIndexOutOfBoundsException;
public void delete(Set<Integer> ordinals) throws IndexOutOfBoundsException;
/**
* Check if a data type is part of this data type. A data type could
@ -283,111 +246,194 @@ public interface Composite extends DataType {
public abstract boolean isPartOf(DataType dataType);
/**
* Updates the composite to any changes in the data organization. If the composite is not
* internally aligned, this method does nothing.
*/
public void realign();
/**
* Determine if this data type has its internal components currently aligned.
* @return true if this data type's components are aligned relative to each other using the
* current data organization. When internally aligned the end of this data type will be padded
* to a multiple of its actual alignment.
*/
public boolean isInternallyAligned();
/**
* Sets whether this data type's internal components are currently aligned or unaligned.
* @param aligned true means align the internal components of this data type.
* false means don't align it. True also causes the end of this data type to be padded
* to a multiple of its actual alignment.
*/
public void setInternallyAligned(boolean aligned);
/**
* The overall (external) alignment changed for the specified data type.
* In other words, the data type has a different alignment when placed inside other structures.
* The alignment changed for the specified data type. If packing is enabled for this
* composite, the placement of the component may be affected by a change in its alignment.
* A non-packed composite can ignore this notification.
* @param dt the data type whose alignment changed.
*/
@Override
public void dataTypeAlignmentChanged(DataType dt);
/**
* Gets the current packing value (typically a power of 2). If this isn't a packed data
* type then NOT_PACKING is returned. The packing value only pertains to internally aligned composite
* data types. Aligned structures allow packing.
* @return the current packing value or NOT_PACKING.
* Updates packed composite to any changes in the data organization. If the composite does
* not have packing enabled this method does nothing.
* <BR>
* NOTE: Changes to data organization is discouraged. Attempts to use this method in such
* cases should be performed on all composites in dependency order (ignoring pointer components).
*/
public int getPackingValue();
public void repack();
/**
* Sets the current packing value (usually a power of 2). A value of NOT_PACKING should be passed
* if this isn't a packed data type. Otherwise this value indicates a maximum alignment
* for any component within this data type. Calling this method will cause the data type to
* become an internally aligned data type.
* <br>Note: If a component's data type has a specific external alignment, it will
* override this value if necessary.
* @param packingValue the new packing value or 0 for NOT_PACKING.
* A negative value will be treated the same as 0.
* @return the packing type set for this composite
*/
public void setPackingValue(int packingValue);
public PackingType getPackingType();
/**
* Get the external alignment (a minimum alignment) for this DataType.
* This controls where this data type will get aligned within other data types.
* It also causes the end of this data type to get padded so its length is a multiple
* of the alignment.
* @return the external alignment for this DataType or DEFAULT_ALIGNMENT_VALUE.
* Determine if this data type has its internal components currently packed
* based upon alignment and packing settings. If disabled, component placement
* is based upon explicit placement by offset.
* @return true if this data type's components auto-packed
*/
public int getMinimumAlignment();
/**
* Sets the external alignment (a minimum alignment) for this DataType.
* This controls where this data type will get aligned within other data types.
* It also causes the end of this data type to get padded so its length is a multiple
* of the alignment. Calling this method will cause the data type to
* become an internally aligned data type.
* @param minimumAlignment the external (minimum) alignment for this DataType.
* Any value less than 1 will revert to default alignment.
*/
public void setMinimumAlignment(int minimumAlignment);
/**
* Sets this data type's external (minimum) alignment to the default alignment. This data type's
* external alignment will be based upon the components it contains. This should be used
* when a data type doesn't have an alignment attribute specified. Calling this method will
* cause the data type to become an internally aligned data type.
*/
public void setToDefaultAlignment();
/**
* Sets this data type's external (minimum) alignment to a multiple of the machine alignment that is
* specified in the DataOrganization. The machine alignment is defined as the maximum useful
* alignment for the target machine. This should be used when a data type has an alignment
* attribute specified without a size (indicating to use the machine alignment).
* Calling this method will cause the data type to become an internally aligned data type.
*/
public void setToMachineAlignment();
/**
* Whether or not this data type is using the default external (minimum) alignment.
* @return true if this data type has the default external alignment.
*/
public boolean isDefaultAligned();
/**
* Whether or not this data type is using the machine alignment value from the
* DataOrganization for its external (minimum) alignment.
* @return true if this data type is using the machine alignment as the minimum alignment.
*/
public boolean isMachineAligned();
/**
* Get the bitfield packing information associated with the underlying
* data organization.
* @return bitfield packing information
*/
public default BitFieldPacking getBitFieldPacking() {
return getDataOrganization().getBitFieldPacking();
public default boolean isPackingEnabled() {
return getPackingType() != PackingType.DISABLED;
}
/**
* Sets whether this data type's internal components are currently packed. The
* affect of disabled packing differs between {@link Structure} and {@link Union}. When
* packing disabled:
* <ul>
* <li>Structures utilize explicit component offsets and produce undefined filler
* components where defined components do not consume space.</li>
* <li>Unions always place components at offset 0 and do not pad for alignment.
* </ul>
* In addition, when packing is disabled the default alignment is always 1 unless a
* different minimum alignment has been set. When packing is enabled the overall
* composite length infleunced by the composite's minimum alignment setting.
* If a change in enablement occurs, the default alignment and packing behavior
* will be used.
* @param enabled true enables packing of components respecting component
* alignment and pack setting, whereas false disables packing.
*/
public void setPackingEnabled(boolean enabled);
/**
* Determine if packing is enabled with an explicit packing value (see {@link #getExplicitPackingValue()}).
* @return true if packing is enabled with an explicit packing value, else false.
*/
public default boolean hasExplicitPackingValue() {
return getPackingType() == PackingType.EXPLICIT;
}
/**
* Determine if default packing is enabled.
* @return true if default packing is enabled.
*/
public default boolean hasDefaultPacking() {
return getPackingType() == PackingType.DEFAULT;
}
/**
* Gets the current packing value (typically a power of 2).
* If this isn't a packed composite with an explicit packing value (see {@link #hasExplicitPackingValue()})
* then the return value is undefined.
* @return the current packing value or an undefined non-positive value
*/
public int getExplicitPackingValue();
/**
* Sets the pack value for this composite (positive value, usually a power of 2).
* If packing was previously disabled, packing will be enabled. This value will
* establish the maximum effective alignment for this composite and each of the
* components during the alignment computation (e.g., a value of 1 will eliminate
* any padding). The overall composite length may be infleunced by the composite's
* minimum alignment setting.
* @param packingValue the new positive packing value.
* @throws IllegalArgumentException if a non-positive value is specified.
*/
public void setExplicitPackingValue(int packingValue);
/**
* Same as {@link #setExplicitPackingValue(int)}.
* @param packingValue the new positive packing value.
* @throws IllegalArgumentException if a non-positive value is specified.
*/
public default void pack(int packingValue) {
setExplicitPackingValue(packingValue);
}
/**
* Enables default packing behavior.
* If packing was previously disabled, packing will be enabled.
* Composite will automatically pack based upon the alignment requirements
* of its components with overall composite length possibly infleunced by the composite's
* minimum alignment setting.
*/
public void setToDefaultPacking();
/**
* Get the computed alignment for this composite based upon packing and minimum
* alignment settings as well as component alignment. If packing is disabled,
* the alignment will always be 1 unless a minimum alignment has been set.
* @return this composites alignment
*/
@Override
abstract int getAlignment();
/**
* @return the alignment type set for this composite
*/
abstract AlignmentType getAlignmentType();
/**
* Whether or not this data type is using the default alignment. When Structure packing
* is disabled the default alignment is always 1 (see {@link Structure#setPackingEnabled(boolean)}.
* @return true if this data type is using its default alignment.
*/
public default boolean isDefaultAligned() {
return getAlignmentType() == AlignmentType.DEFAULT;
}
/**
* Whether or not this data type is using the machine alignment value, specified by
* {@link DataOrganization#getMachineAlignment()}, for its alignment.
* @return true if this data type is using the machine alignment as its alignment.
*/
public default boolean isMachineAligned() {
return getAlignmentType() == AlignmentType.MACHINE;
}
/**
* Determine if an explicit minimum alignment has been set (see {@link #getExplicitMinimumAlignment()}).
* An undefined value is returned if default alignment or machine alignment is enabled.
* @return true if an explicit minimum alignment has been set, else false
*/
public default boolean hasExplicitMinimumAlignment() {
return getAlignmentType() == AlignmentType.EXPLICIT;
}
/**
* Get the explicitminimum alignment setting for this Composite which contributes
* to the actual computed alignment value (see {@link #getAlignment()}.
* @return the minimum alignment setting for this Composite or an undefined
* non-positive value if an explicit minimum alignment has not been set.
*/
public int getExplicitMinimumAlignment();
/**
* Sets this data type's explicit minimum alignment (positive value).
* Together with the pack setting and component alignments will
* affect the actual computed alignment of this composite.
* When packing is enabled, the alignment setting may also affect padding
* at the end of the composite and its length. When packing is disabled,
* this setting will not affect the length of thhis composite.
* @param minAlignment the minimum alignment for this Composite.
* @throws IllegalArgumentException if a non-positive value is specified
*/
public void setExplicitMinimumAlignment(int minAlignment);
/**
* Same as {@link #setExplicitMinimumAlignment(int)}.
* @param minAlignment the explicit minimum alignment for this Composite.
* @throws IllegalArgumentException if a non-positive value is specified
*/
public default void align(int minAlignment) {
setExplicitMinimumAlignment(minAlignment);
}
/**
* Sets this data type's alignment to its default alignment. For packed
* composites, this data type's alignment will be based upon the components it contains and
* its current pack settings. This is the default state and only needs to be used
* when changing from a non-default alignment type.
*/
public void setToDefaultAligned();
/**
* Sets this data type's minimum alignment to the machine alignment which is
* specified by {@link DataOrganization#getMachineAlignment()}. The machine alignment is
* defined as the maximum useful alignment for the target machine.
*/
public void setToMachineAligned();
}

View file

@ -18,14 +18,14 @@ package ghidra.program.model.data;
public class CompositeAlignmentHelper {
private static int getCompositeAlignmentMultiple(DataOrganization dataOrganization,
Composite composite) {
CompositeInternal composite) {
int allComponentsLCM = 1;
int packingAlignment = composite.getPackingValue();
int packingValue = composite.getStoredPackingValue();
DataTypeComponent[] dataTypeComponents = composite.getDefinedComponents();
for (DataTypeComponent dataTypeComponent : dataTypeComponents) {
int impartedAlignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization,
packingAlignment, dataTypeComponent);
packingValue, dataTypeComponent);
if (impartedAlignment != 0) {
allComponentsLCM = DataOrganizationImpl.getLeastCommonMultiple(allComponentsLCM,
impartedAlignment);
@ -36,7 +36,7 @@ public class CompositeAlignmentHelper {
DataTypeComponent flexibleArrayComponent = struct.getFlexibleArrayComponent();
if (flexibleArrayComponent != null) {
allComponentsLCM = getComponentAlignmentLCM(dataOrganization, allComponentsLCM,
packingAlignment, flexibleArrayComponent);
packingValue, flexibleArrayComponent);
}
}
return allComponentsLCM;
@ -58,53 +58,33 @@ public class CompositeAlignmentHelper {
// Zero-length bitfields ignored within unions for non-MSVC cases
return 0;
}
DataType componentDt = component.getDataType();
int dtSize = componentDt.getLength();
if (dtSize <= 0) {
dtSize = component.getLength();
}
return getPackedAlignment(dataOrganization, packingValue, componentDt, dtSize);
return getPackedAlignment(componentDt.getAlignment(), packingValue);
}
private static int getPackedAlignment(int componentAlignment, int forcedAlignment,
int packingAlignment) {
// Only do packing if we are not forcing an alignment.
static int getPackedAlignment(int componentAlignment, int packingValue) {
int alignment = componentAlignment;
if (packingAlignment != Composite.NOT_PACKING) { // TODO Should this be packingValue > 0?
if (forcedAlignment > packingAlignment) {
alignment = forcedAlignment;
}
else if (alignment > packingAlignment) {
alignment = packingAlignment;
}
if (packingValue > 0 && packingValue < componentAlignment) {
alignment = packingValue;
}
return alignment;
}
public static int getPackedAlignment(DataOrganization dataOrganization, int packingAlignment,
DataType componentDt, int dtSize) {
int componentAlignment = dataOrganization.getAlignment(componentDt, dtSize);
int componentForcedAlignment = dataOrganization.getForcedAlignment(componentDt);
boolean componentForcingAlignment = componentForcedAlignment > 0;
if (componentForcingAlignment) {
componentAlignment = DataOrganizationImpl.getLeastCommonMultiple(componentAlignment,
componentForcedAlignment);
}
return getPackedAlignment(componentAlignment, componentForcedAlignment, packingAlignment);
}
public static int getAlignment(DataOrganization dataOrganization, Composite dataType) {
public static int getAlignment(DataOrganization dataOrganization, CompositeInternal composite) {
// TODO: goal is to eliminate this method in favor of pack once and remember alignment
if (!dataType.isInternallyAligned()) {
return 1; // Unaligned
int minimumAlignment = composite.getStoredMinimumAlignment();
if (minimumAlignment < CompositeInternal.DEFAULT_ALIGNMENT) {
minimumAlignment = dataOrganization.getMachineAlignment();
}
if (!composite.isPackingEnabled()) {
return minimumAlignment == CompositeInternal.DEFAULT_ALIGNMENT ? 1 : minimumAlignment;
}
int lcm = getCompositeAlignmentMultiple(dataOrganization, dataType);
int minimumAlignment = dataType.getMinimumAlignment();
if ((minimumAlignment != Composite.DEFAULT_ALIGNMENT_VALUE) &&
int lcm = getCompositeAlignmentMultiple(dataOrganization, composite);
if ((minimumAlignment != CompositeInternal.DEFAULT_ALIGNMENT) &&
(lcm % minimumAlignment != 0)) {
lcm = DataOrganizationImpl.getLeastCommonMultiple(lcm, minimumAlignment);
}

View file

@ -26,17 +26,18 @@ import ghidra.util.exception.NotYetImplementedException;
/**
* Common implementation methods for structure and union
*/
public abstract class CompositeDataTypeImpl extends GenericDataType implements Composite {
private final static long serialVersionUID = 1;
public abstract class CompositeDataTypeImpl extends GenericDataType implements CompositeInternal {
// Strings used for toString formatting
private static final String ALIGN_NAME = "aligned";
private static final String PACKING_NAME = "pack";
private static final String DISABLED_PACKING_NAME = "disabled";
private static final String DEFAULT_PACKING_NAME = "";
private String description;
protected boolean aligned = false; // WARNING, changing the initial value for this will cause
// subtle errors - One I know of is in the StructureDataType
// copyComponent method. It has built in assumptions about this.
protected AlignmentType alignmentType = AlignmentType.DEFAULT_ALIGNED;
protected int packingValue = NOT_PACKING;
protected int externalAlignment = DEFAULT_ALIGNMENT_VALUE;
protected int minimumAlignment = DEFAULT_ALIGNMENT;
protected int packing = NO_PACKING;
/**
* Construct a new composite with the given name
@ -67,6 +68,21 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
description = "";
}
@Override
public int getStoredPackingValue() {
return packing;
}
@Override
public int getStoredMinimumAlignment() {
return minimumAlignment;
}
@Override
public void dataTypeNameChanged(DataType dt, String oldName) {
// ignored
}
/**
* Get the preferred length for a new component. For Unions and internally
* aligned structures the preferred component length for a fixed-length dataType
@ -80,9 +96,6 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
* @return preferred component length
*/
protected int getPreferredComponentLength(DataType dataType, int length) {
if ((isInternallyAligned() || (this instanceof Union)) && !(dataType instanceof Dynamic)) {
length = -1; // force use of datatype size
}
int dtLength = dataType.getLength();
if (length <= 0) {
length = dtLength;
@ -99,7 +112,7 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
@Override
public boolean isDynamicallySized() {
return isInternallyAligned();
return true; // assume dynamically sized component datatype may be present
}
@Override
@ -135,10 +148,6 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
* @throws IllegalArgumentException if the data type is invalid.
*/
protected void validateDataType(DataType dataType) {
if (isInternallyAligned() && dataType == DataType.DEFAULT) {
throw new IllegalArgumentException(
"The DEFAULT data type is not allowed in an aligned composite data type.");
}
if (dataType instanceof FactoryDataType) {
throw new IllegalArgumentException("The \"" + dataType.getName() +
"\" data type is not allowed in a composite data type.");
@ -250,139 +259,161 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
}
@Override
public int getPackingValue() {
return packingValue;
}
@Override
public void setPackingValue(int packingValue) {
if (packingValue < 0) {
packingValue = NOT_PACKING;
}
aligned = true;
this.packingValue = packingValue;
adjustInternalAlignment();
}
@Override
public int getMinimumAlignment() {
if (alignmentType == AlignmentType.MACHINE_ALIGNED) {
return getMachineAlignment();
}
if (alignmentType == AlignmentType.DEFAULT_ALIGNED) {
return Composite.DEFAULT_ALIGNMENT_VALUE;
}
return externalAlignment;
}
@Override
public void setMinimumAlignment(int externalAlignment) {
if (externalAlignment < 1) {
this.externalAlignment = DEFAULT_ALIGNMENT_VALUE;
alignmentType = AlignmentType.DEFAULT_ALIGNED;
}
else {
this.externalAlignment = externalAlignment;
alignmentType = AlignmentType.ALIGNED_BY_VALUE;
}
aligned = true;
adjustInternalAlignment();
}
private int getMachineAlignment() {
return getDataOrganization().getMachineAlignment();
}
@Override
public boolean isInternallyAligned() {
return aligned;
}
@Override
public boolean isDefaultAligned() {
return alignmentType == AlignmentType.DEFAULT_ALIGNED;
}
@Override
public boolean isMachineAligned() {
return alignmentType == AlignmentType.MACHINE_ALIGNED;
}
@Override
public void setInternallyAligned(boolean aligned) {
if (this.aligned != aligned) {
this.aligned = aligned;
if (!aligned) {
alignmentType = AlignmentType.DEFAULT_ALIGNED;
packingValue = Composite.NOT_PACKING;
}
}
adjustInternalAlignment();
}
@Override
public void setToDefaultAlignment() {
aligned = true;
alignmentType = AlignmentType.DEFAULT_ALIGNED;
adjustInternalAlignment();
}
@Override
public void setToMachineAlignment() {
aligned = true;
alignmentType = AlignmentType.MACHINE_ALIGNED;
adjustInternalAlignment();
public final void repack() {
repack(true);
}
/**
* Notify any parent data types that this composite data type's alignment has
* changed.
* Repack components within this composite based on the current packing, alignment
* and {@link DataOrganization} settings. Non-packed Structures: change detection
* is limited to component count and length is assumed to already be correct.
* <p>
* NOTE: If modifications to stored length are made prior to invoking this method,
* detection of a size change may not be possible.
* <p>
* NOTE: Currently a change in calculated alignment can not be provided since
* this value is not stored.
*
* @param notify if true notification will be sent to parents if a size change
* or component placement change is detected.
* @return true if a layout change was detected.
*/
protected void notifyAlignmentChanged() {
DataType[] parents = getParents();
for (DataType dataType : parents) {
if (dataType instanceof Composite) {
Composite composite = (Composite) dataType;
composite.dataTypeAlignmentChanged(this);
}
}
}
/**
* Adjusts the internal alignment of components within this composite based on
* the current settings of the internal alignment, packing, alignment type and
* minimum alignment value. This method should be called whenever any of the
* above settings are changed or whenever a components data type is changed or a
* component is added or removed.
*/
protected abstract void adjustInternalAlignment();
public abstract boolean repack(boolean notify);
@Override
public int getAlignment() {
return CompositeAlignmentHelper.getAlignment(getDataOrganization(), this);
public void setPackingEnabled(boolean enabled) {
if (enabled == isPackingEnabled()) {
return;
}
setStoredPackingValue(enabled ? DEFAULT_PACKING : NO_PACKING);
}
// set my alignment info to the same as the given composite
protected void setAlignment(Composite composite) {
aligned = composite.isInternallyAligned();
if (composite.isDefaultAligned()) {
alignmentType = AlignmentType.DEFAULT_ALIGNED;
@Override
public PackingType getPackingType() {
if (packing < DEFAULT_PACKING) {
return PackingType.DISABLED;
}
else if (composite.isMachineAligned()) {
alignmentType = AlignmentType.MACHINE_ALIGNED;
if (packing == DEFAULT_PACKING) {
return PackingType.DEFAULT;
}
return PackingType.EXPLICIT;
}
@Override
public void setToDefaultPacking() {
setStoredPackingValue(DEFAULT_PACKING);
}
@Override
public int getExplicitPackingValue() {
return packing;
}
@Override
public void setExplicitPackingValue(int packingValue) {
if (packingValue <= 0) {
throw new IllegalArgumentException(
"explicit packing value must be positive: " + packingValue);
}
setStoredPackingValue(packingValue);
}
private void setStoredPackingValue(int packingValue) {
if (minimumAlignment < NO_PACKING) {
throw new IllegalArgumentException("invalid packing value: " + packingValue);
}
if (packingValue == this.packing) {
return;
}
if (this.packing == NO_PACKING || packingValue == NO_PACKING) {
// force default alignment when transitioning to or from disabled packing
this.minimumAlignment = DEFAULT_ALIGNMENT;
}
this.packing = packingValue;
repack(true);
}
@Override
public AlignmentType getAlignmentType() {
if (minimumAlignment < DEFAULT_ALIGNMENT) {
return AlignmentType.MACHINE;
}
if (minimumAlignment == DEFAULT_ALIGNMENT) {
return AlignmentType.DEFAULT;
}
return AlignmentType.EXPLICIT;
}
@Override
public void setToDefaultAligned() {
setStoredMinimumAlignment(DEFAULT_ALIGNMENT);
}
@Override
public void setToMachineAligned() {
setStoredMinimumAlignment(MACHINE_ALIGNMENT);
}
@Override
public int getExplicitMinimumAlignment() {
return minimumAlignment;
}
@Override
public void setExplicitMinimumAlignment(int minimumAlignment) {
if (minimumAlignment <= 0) {
throw new IllegalArgumentException(
"explicit minimum alignment must be positive: " + minimumAlignment);
}
setStoredMinimumAlignment(minimumAlignment);
}
private void setStoredMinimumAlignment(int minimumAlignment) {
if (minimumAlignment < MACHINE_ALIGNMENT) {
throw new IllegalArgumentException(
"invalid minimum alignment value: " + minimumAlignment);
}
if (this.minimumAlignment == minimumAlignment) {
return;
}
this.minimumAlignment = minimumAlignment;
repack(true);
}
protected final int getNonPackedAlignment() {
int alignment;
if (minimumAlignment == DEFAULT_ALIGNMENT) {
alignment = 1;
}
else if (minimumAlignment == MACHINE_ALIGNMENT) {
alignment = getDataOrganization().getMachineAlignment();
}
else {
if (alignmentType != AlignmentType.ALIGNED_BY_VALUE) {
alignmentType = AlignmentType.ALIGNED_BY_VALUE;
}
externalAlignment = composite.getMinimumAlignment();
alignment = minimumAlignment;
}
return alignment;
}
packingValue = composite.getPackingValue();
@Override
public abstract int getAlignment();
@Override
public String toString() {
return toString(this);
}
public static String toString(Composite composite) {
StringBuilder stringBuffer = new StringBuilder();
stringBuffer.append(composite.getPathName() + "\n");
stringBuffer.append(getAlignmentAndPackingString(composite) + "\n");
stringBuffer.append(getTypeName(composite) + " " + composite.getDisplayName() + " {\n");
dumpComponents(composite, stringBuffer, " ");
stringBuffer.append("}\n");
stringBuffer.append("Size = " + composite.getLength() + " Actual Alignment = " +
composite.getAlignment() + "\n");
return stringBuffer.toString();
adjustInternalAlignment();
}
/**
@ -391,11 +422,13 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
* @param buffer string buffer
* @param pad padding to be used with each component output line
*/
protected void dumpComponents(StringBuilder buffer, String pad) {
// limit output of filler components for unaligned structures
DataTypeComponent[] components = getDefinedComponents();
private static void dumpComponents(Composite composite, StringBuilder buffer, String pad) {
// limit output of filler components for non-packed structures
DataTypeComponent[] components = composite.getDefinedComponents();
for (DataTypeComponent dtc : components) {
DataType dataType = dtc.getDataType();
// buffer.append(pad + dtc.getOrdinal());
// buffer.append(") ");
buffer.append(pad + dtc.getOffset());
buffer.append(pad + dataType.getName());
if (dataType instanceof BitFieldDataType) {
@ -413,57 +446,76 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
buffer.append(pad + "\"" + comment + "\"");
buffer.append("\n");
}
if (composite instanceof Structure) {
DataTypeComponent dtc = ((Structure) composite).getFlexibleArrayComponent();
if (dtc != null) {
DataType dataType = dtc.getDataType();
buffer.append(pad + dataType.getDisplayName() + "[0]");
buffer.append(pad + dtc.getLength());
buffer.append(pad + dtc.getFieldName());
String comment = dtc.getComment();
if (comment == null) {
comment = "";
}
buffer.append(pad + "\"" + comment + "\"");
buffer.append("\n");
}
}
}
@Override
public String toString() {
StringBuilder stringBuffer = new StringBuilder();
stringBuffer.append(getPathName() + "\n");
stringBuffer.append(getAlignmentSettingsString() + "\n");
stringBuffer.append(getTypeName() + " " + getDisplayName() + " {\n");
dumpComponents(stringBuffer, " ");
stringBuffer.append("}\n");
stringBuffer.append(
"Size = " + getLength() + " Actual Alignment = " + getAlignment() + "\n");
return stringBuffer.toString();
}
private String getTypeName() {
if (this instanceof Structure) {
private static String getTypeName(Composite composite) {
if (composite instanceof Structure) {
return "Structure";
}
else if (this instanceof Union) {
else if (composite instanceof Union) {
return "Union";
}
return "";
}
private String getAlignmentSettingsString() {
StringBuffer stringBuffer = new StringBuffer();
if (!isInternallyAligned()) {
stringBuffer.append("Unaligned");
public static String getAlignmentAndPackingString(Composite composite) {
StringBuilder buf =
new StringBuilder(getMinAlignmentString(composite));
if (buf.length() != 0) {
buf.append(" ");
}
else if (isDefaultAligned()) {
stringBuffer.append("Aligned");
}
else if (isMachineAligned()) {
stringBuffer.append("Machine aligned");
}
else {
long alignment = getMinimumAlignment();
stringBuffer.append("align(" + alignment + ")");
}
stringBuffer.append(getPackingString());
return stringBuffer.toString();
buf.append(getPackingString(composite));
return buf.toString();
}
private String getPackingString() {
if (!isInternallyAligned()) {
public static String getMinAlignmentString(Composite composite) {
if (composite.isDefaultAligned()) {
return "";
}
if (packingValue == Composite.NOT_PACKING) {
return "";
StringBuilder buf = new StringBuilder(ALIGN_NAME);
buf.append("(");
if (composite.isMachineAligned()) {
buf.append("machine:");
buf.append(composite.getDataOrganization().getMachineAlignment());
}
return " pack(" + packingValue + ")";
else {
buf.append(composite.getExplicitMinimumAlignment());
}
buf.append(")");
return buf.toString();
}
public static String getPackingString(Composite composite) {
StringBuilder buf = new StringBuilder(PACKING_NAME);
buf.append("(");
if (composite.isPackingEnabled()) {
if (composite.hasExplicitPackingValue()) {
buf.append(composite.getExplicitPackingValue());
}
else {
buf.append(DEFAULT_PACKING_NAME);
}
}
else {
buf.append(DISABLED_PACKING_NAME); // NO_PACKING
}
buf.append(")");
return buf.toString();
}
}

View file

@ -0,0 +1,154 @@
/* ###
* 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.program.model.data;
import java.util.Comparator;
/**
* Interface for common methods in Structure and Union
*/
public interface CompositeInternal extends Composite {
/**
* The stored packing value which corresponds to a composite that will automatically pack
* based upon the alignment requirements of its components. A positive pack value will
* also pack in a similar fashion but will use the pack value as a maximum alignment
* for each component.
* See {@link #getStoredPackingValue}.
*/
public final static int DEFAULT_PACKING = 0;
/**
* The stored packing value which corresponds to a composite whoose packing has been disabled.
* In the case of structures this will permit explicit component placement by
* offset within the structure and undefined filler components will be used.
* This is the initial state of all newly instantiated structures.
* See {@link #getStoredPackingValue()}.
*/
public final static int NO_PACKING = -1;
/**
* The stored minimum alignment value which indicates the default alignment
* should be used based upon the packing and component alignment requirements.
* See {@link #getStoredMinimumAlignment}.
*/
public final static int DEFAULT_ALIGNMENT = 0;
/**
* The stored minimum alignment value which indicates the machine alignment
* should be used as the minimum alignment (as defined by the current
* {@link DataOrganization#getMachineAlignment()}).
* See {@link #getStoredMinimumAlignment()}.
*/
public final static int MACHINE_ALIGNMENT = -1;
/**
* Gets the current packing value (typically a power of 2). Other special values
* which may be returned include {@value #DEFAULT_PACKING} and {@value #NO_PACKING}.
* @return the current positive packing value, {@value #DEFAULT_PACKING} or {@value #NO_PACKING}.
*/
public int getStoredPackingValue();
/**
* Sets the current packing behavior (positive value, usually a power of 2). If a positive
* value is specified the use of packing will be enabled if it was previously disabled
* (see {@link #setPackingEnabled(boolean)}. A positive value will set the maximum
* alignment for this composite and each component within a structure
* (e.g., a value of 1 will eliminate any padding).
* <br>
* Special packing values which may be specified include:
* <ul>
* <li>{@value #DEFAULT_PACKING} will perform default packing based upon the alignment
* requirements of the individual components.</li>
* <li>{@link #NO_PACKING} (or any negative value) will disable packing</li>
* </ul>
* @param packingValue the new positive packing value, or {@value #DEFAULT_PACKING} or
* {@link #NO_PACKING}. A negative value will be treated the same as {@link #NO_PACKING}.
*/
// public void setStoredPackingValue(int packingValue);
/**
* Get the minimum alignment setting for this Composite which contributes
* to the actual computed alignment value (see {@link #getAlignment()}.
* @return the minimum alignment setting for this Composite or a reserved value to indicate
* either {@link #DEFAULT_ALIGNMENT} or {@link #MACHINE_ALIGNMENT}.
*/
public int getStoredMinimumAlignment();
/**
* <code>ComponentComparator</code> provides ability to compare two DataTypeComponent objects
* based upon their ordinal. Intended to be used to sort components based upon ordinal.
*/
public static class ComponentComparator implements Comparator<DataTypeComponent> {
public static final ComponentComparator INSTANCE = new ComponentComparator();
@Override
public int compare(DataTypeComponent dtc1, DataTypeComponent dtc2) {
return dtc1.getOrdinal() - dtc2.getOrdinal();
}
}
/**
* <code>OffsetComparator</code> provides ability to compare an Integer offset with a
* DataTypeComponent object. The offset will be consider equal (0) if the component contains the
* offset.
*/
public static class OffsetComparator implements Comparator<Object> {
public static final OffsetComparator INSTANCE = new OffsetComparator();
@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;
}
}
/**
* <code>OrdinalComparator</code> provides ability to compare an Integer ordinal with a
* DataTypeComponent object. The ordinal will be consider equal (0) if the component corresponds
* to the specified ordinal.
* <p>
*/
public static class OrdinalComparator implements Comparator<Object> {
public static final OrdinalComparator INSTANCE = new OrdinalComparator();
@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

@ -132,11 +132,6 @@ public interface DataOrganization {
*/
BitFieldPacking getBitFieldPacking();
/**
* Remove all entries from the size alignment map
*/
void clearSizeAlignmentMap();
/**
* Gets the number of sizes that have an alignment specified.
* @return the number of sizes with an alignment mapped to them.
@ -161,24 +156,20 @@ public interface DataOrganization {
/**
* Determines the alignment value for the indicated data type. (i.e. how the data type gets
* aligned within other data types.) NOTE: the alignment of bitfields is dependent upon packing
* rules which must be considered at the composite level.
* aligned within other data types.) NOTE: this method should not be used for bitfields
* which are highly dependent upon packing for a composite. This method will always return 1
* for Dynamic and FactoryDataTypes.
* @param dataType the data type
* @param dtSize the data type's size or component size
* @return the alignment
* @return the datatype alignment
*/
int getAlignment(DataType dataType, int dtSize);
int getAlignment(DataType dataType);
boolean isForcingAlignment(DataType dataType);
int getForcedAlignment(DataType dataType);
/**
* Determines the offset where the specified data type should be placed to be properly aligned.
* @param minimumOffset the minimum allowable offset where the data type can be placed.
* @param dataType the data type
* @param dtSize the data type's size
* @return the aligned offset for the data type
*/
int getAlignmentOffset(int minimumOffset, DataType dataType, int dtSize);
// /**
// * Determines the offset where the specified data type should be placed to be properly aligned.
// * @param minimumOffset the minimum allowable offset where the data type can be placed.
// * @param dataType the data type
// * @param dtSize the data type's size
// * @return the aligned offset for the data type
// */
// int getAlignmentOffset(int minimumOffset, DataType dataType, int dtSize);
}

View file

@ -15,10 +15,10 @@
*/
package ghidra.program.model.data;
import java.util.Arrays;
import java.util.*;
import ghidra.program.model.lang.Language;
import ghidra.util.datastruct.IntIntHashtable;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.NoValueException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
@ -56,7 +56,7 @@ public class DataOrganizationImpl implements DataOrganization {
/*
* Map for determining the alignment of a data type based upon its size.
*/
private final IntIntHashtable sizeAlignmentMap = new IntIntHashtable();
private final HashMap<Integer, Integer> sizeAlignmentMap = new HashMap<>();
/**
* Creates a new default DataOrganization. This has a mapping which defines the alignment
@ -166,7 +166,7 @@ public class DataOrganizationImpl implements DataOrganization {
/**
* Set data endianess
* @param bigEndian is true to set big endian
* @param bigEndian true if big-endian, false if little-endian
*/
public void setBigEndian(boolean bigEndian) {
this.bigEndian = bigEndian;
@ -411,9 +411,8 @@ public class DataOrganizationImpl implements DataOrganization {
/**
* Remove all entries from the size alignment map
*/
@Override
public void clearSizeAlignmentMap() {
sizeAlignmentMap.removeAll();
sizeAlignmentMap.clear();
}
/**
@ -431,7 +430,12 @@ public class DataOrganizationImpl implements DataOrganization {
*/
@Override
public int[] getSizes() {
int[] keys = sizeAlignmentMap.getKeys();
Set<Integer> keySet = sizeAlignmentMap.keySet();
int[] keys = new int[keySet.size()];
int index = 0;
for (Integer k : keySet) {
keys[index++] = k;
}
Arrays.sort(keys);
return keys;
}
@ -466,28 +470,19 @@ public class DataOrganizationImpl implements DataOrganization {
}
@Override
public int getAlignment(DataType dataType, int dtSize) {
// Don't do alignment on dynamic data types.
if (dataType instanceof Dynamic) {
// throw new AssertException("Dynamic data types don't have an alignment. \"" +
// dataType.getName() + "\" is dynamic.");
public int getAlignment(DataType dataType) {
int dtSize = dataType.getLength();
if (dataType instanceof Dynamic || dataType instanceof FactoryDataType || dtSize <= 0) {
return 1;
}
// Typedef is aligned the same as its underlying data type is aligned.
if (dataType instanceof TypeDef) {
return getAlignment(((TypeDef) dataType).getBaseDataType(), dtSize);
return getAlignment(((TypeDef) dataType).getBaseDataType());
}
// Array alignment is the alignment of its element data type.
if (dataType instanceof Array) {
DataType elementDt = ((Array) dataType).getDataType();
int elementLength = ((Array) dataType).getElementLength();
return getAlignment(elementDt, elementLength);
}
// Pointer alignment is based on its size or default pointer alignment if there is no size????
if (dataType instanceof Pointer) {
if (dtSize <= 0) {
return getDefaultPointerAlignment();
}
return getAlignment(elementDt);
}
// Structure's or Union's alignment is a multiple of the least common multiple of
// the components. It can also be adjusted by packing and alignment attributes.
@ -499,19 +494,14 @@ public class DataOrganizationImpl implements DataOrganization {
// See AlignedStructurePacker.
if (dataType instanceof BitFieldDataType) {
BitFieldDataType bitfieldDt = (BitFieldDataType) dataType;
return getAlignment(bitfieldDt.getBaseDataType(), bitfieldDt.getBaseTypeSize());
return getAlignment(bitfieldDt.getBaseDataType());
}
// Otherwise get the alignment based on the size.
if (sizeAlignmentMap.contains(dtSize)) {
try {
int sizeAlignment = sizeAlignmentMap.get(dtSize);
return ((absoluteMaxAlignment == 0) || (sizeAlignment < absoluteMaxAlignment))
? sizeAlignment
: absoluteMaxAlignment;
}
catch (NoValueException e) {
// Simply fall through to the default value.
}
if (sizeAlignmentMap.containsKey(dtSize)) {
int sizeAlignment = sizeAlignmentMap.get(dtSize);
return ((absoluteMaxAlignment == 0) || (sizeAlignment < absoluteMaxAlignment))
? sizeAlignment
: absoluteMaxAlignment;
}
if (dataType instanceof Pointer) {
return getDefaultPointerAlignment();
@ -519,114 +509,26 @@ public class DataOrganizationImpl implements DataOrganization {
// Otherwise just assume the default alignment.
return getDefaultAlignment();
}
@Override
public boolean isForcingAlignment(DataType dataType) {
return getForcedAlignment(dataType) > 0;
}
@Override
public int getForcedAlignment(DataType dataType) {
// Don't do forced alignment on dynamic data types.
if (dataType instanceof Dynamic) {
return 0;
}
// Typedef is aligned the same as its underlying data type is aligned.
if (dataType instanceof TypeDef) {
return getForcedAlignment(((TypeDef) dataType).getBaseDataType());
}
// Array alignment is the alignment of its element data type.
if (dataType instanceof Array) {
DataType elementDt = ((Array) dataType).getDataType();
return getForcedAlignment(elementDt);
}
// We don't allow alignment attribute on pointers.
if (dataType instanceof Pointer) {
return 0;
}
// Structure's or Union's alignment is a multiple of the least common multiple of
// the components. It can also be adjusted by packing and alignment attributes.
if (dataType instanceof Composite) {
// Check whether this composite forces the alignment.
int forcedLCM = 0;
Composite composite = (Composite) dataType;
if (!composite.isInternallyAligned()) {
return 0;
}
if (!composite.isDefaultAligned()) {
int minimumAlignment = composite.getMinimumAlignment();
forcedLCM = (minimumAlignment > 0) ? minimumAlignment : 0;
}
// Check each component and get the least common multiple of their forced minimum alignments.
int componentForcedLCM = 0;
for (DataTypeComponent dataTypeComponent : composite.getDefinedComponents()) {
if (dataTypeComponent.isBitFieldComponent()) {
continue;
}
DataType componentDt = dataTypeComponent.getDataType();
int forcedAlignment = getForcedAlignment(componentDt);
if (forcedAlignment > 0) {
if (componentForcedLCM > 0) {
componentForcedLCM =
getLeastCommonMultiple(componentForcedLCM, forcedAlignment);
}
else {
componentForcedLCM = forcedAlignment;
}
}
}
if (forcedLCM > 0) {
if (componentForcedLCM > 0) {
// Both this composite and one or more of its children force the alignment.
return getLeastCommonMultiple(forcedLCM, componentForcedLCM);
}
// Children don't force alignment but this composite does.
return forcedLCM;
}
// This composite's forced alignment is based only on its children's forced alignments.
return componentForcedLCM;
}
// Otherwise not forcing alignment.
return 0;
}
/**
* Determines the offset where the specified data type should be placed to be properly aligned.
* @param minimumOffset the minimum allowable offset where the data type can be placed.
* @param dataType the data type
* @param dtSize the data type's size
* @return the aligned offset for the data type
*/
@Override
public int getAlignmentOffset(int minimumOffset, DataType dataType, int dtSize) {
int alignment = getAlignment(dataType, dtSize);
return getOffset(alignment, minimumOffset);
}
/**
* Determines the first offset that is equal to or greater than the minimum offset which
* has the specified alignment.
* @param alignment the desired alignment
* has the specified alignment. If a non-positive alignment is specified the origina
* minimumOffset will be return.
* @param alignment the desired alignment (positive value)
* @param minimumOffset the minimum offset
* @return the aligned offset
*/
public static int getOffset(int alignment, int minimumOffset) {
return alignment + ((minimumOffset - 1) & ~(alignment - 1));
}
/**
* Determines the amount of padding that should be added to a structure at the indicated
* offset in order to get the next component (member) to be aligned with the specified
* alignment within the structure.
* @param alignment the desired alignment
* @param offset the offset that the padding would be placed at to achieve the desired alignment.
* @return the padding needed at the offset.
*/
public static int getPaddingSize(int alignment, int offset) {
return (alignment - (offset % alignment)) % alignment;
public static int getAlignedOffset(int alignment, int minimumOffset) {
if (alignment <= 0) {
return minimumOffset;
}
if ((alignment & 1) == 0) {
// handle alignment which is a power-of-2
return alignment + ((minimumOffset - 1) & ~(alignment - 1));
}
int offcut = (minimumOffset % alignment);
int adj = (offcut != 0) ? (alignment - offcut) : 0;
return minimumOffset + adj;
}
/**
@ -731,17 +633,10 @@ public class DataOrganizationImpl implements DataOrganization {
buffer.append("/>\n");
}
if (sizeAlignmentMap.size() != 0) {
int[] keys = sizeAlignmentMap.getKeys();
buffer.append("<size_alignment_map>\n");
for (int key : keys) {
for (int key : sizeAlignmentMap.keySet()) {
buffer.append("<entry");
int value;
try {
value = sizeAlignmentMap.get(key);
}
catch (NoValueException e) {
value = 0;
}
int value = sizeAlignmentMap.get(key);
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "size", key);
SpecXmlUtils.encodeSignedIntegerAttribute(buffer, "alignment", value);
buffer.append("/>\n");
@ -884,21 +779,16 @@ public class DataOrganizationImpl implements DataOrganization {
if (pointerSize != op2.pointerSize || pointerShift != op2.pointerShift) {
return false;
}
int[] keys = sizeAlignmentMap.getKeys();
int[] op2keys = op2.sizeAlignmentMap.getKeys();
if (keys.length != op2keys.length) {
Set<Integer> keys = sizeAlignmentMap.keySet();
Set<Integer> op2keys = op2.sizeAlignmentMap.keySet();
if (keys.size() != op2keys.size()) {
return false;
}
try {
for (int i = 0; i < keys.length; ++i) {
if (sizeAlignmentMap.get(keys[i]) != op2.sizeAlignmentMap.get(op2keys[i])) {
return false;
}
for (int k : keys) {
if (!SystemUtilities.isEqual(sizeAlignmentMap.get(k), op2.sizeAlignmentMap.get(k))) {
return false;
}
}
catch (NoValueException ex) {
return false;
}
return true;
}
@ -922,14 +812,8 @@ public class DataOrganizationImpl implements DataOrganization {
hash = 79 * hash + pointerSize;
hash = 79 * hash + shortSize;
hash = 79 * hash + wideCharSize;
int[] keys = sizeAlignmentMap.getKeys();
try {
for (int key : keys) {
hash = 79 * hash + sizeAlignmentMap.get(key);
}
}
catch (NoValueException ex) {
hash = 0;
for (int k : sizeAlignmentMap.keySet()) {
hash = 79 * hash + sizeAlignmentMap.get(k);
}
return hash;
}

View file

@ -64,16 +64,20 @@ public interface DataType {
public Settings getDefaultSettings();
/**
* Returns a new instance of this DataType with its universalID and SourceArchive identity retained.
* Note: for built-in DataType's, clone and copy should have the same affect.
* Returns an instance of this DataType with its universalID and SourceArchive identity retained.
* The current instanceof will be returned if this datatype's DataTypeManager matches
* the specified dtm. The recursion depth of a clone will stop on any datatype whose
* DataTypeManager matches the specified dtm and simply use the existing datatype instance.
* @param dtm the data-type manager instance whose data-organization should apply.
* @return cloned instance which may be the same as this instance
*/
public DataType clone(DataTypeManager dtm);
/**
* Returns a new instance of this DataType with a new identity.
* Note: for built-in DataType's, clone and copy should have the same affect.
* Returns a new instance (shallow copy) of this DataType with a new identity.
* Any reference to other datatypes will use {@link #clone(DataTypeManager)}.
* @param dtm the data-type manager instance whose data-organization should apply.
* @return new instanceof of this datatype
*/
public DataType copy(DataTypeManager dtm);
@ -91,12 +95,14 @@ public interface DataType {
/**
* @param path set the categoryPath associated with this data type
* @throws DuplicateNameException
* @throws DuplicateNameException if an attempt to place this datatype into the
* specified category resulted in a name collision. This should not occur for non-DB
* DataType instances.
*/
public void setCategoryPath(CategoryPath path) throws DuplicateNameException;
/**
* Returns the DataTypeManager that is associated with this dataType.
* @return the DataTypeManager that is associated with this dataType.
* This association should not be used to indicate whether this DataType has been
* resolved, but is intended to indicate whether the appropriate DataOrganization
* is being used.
@ -110,13 +116,13 @@ public interface DataType {
public String getDisplayName();
/**
* Return that name of the data type
* @return the name of this data type
*/
public String getName();
/**
* Returns the full category path name that includes this dataType's name. If
* the category is null, then this just returns the dataType's name.
* @return the full category path name that includes this dataType's name. If
* the category is null, then this just the dataType's name is returned.
*/
public String getPathName();
@ -144,7 +150,7 @@ public interface DataType {
/**
* Get the mnemonic for this DataType.
*
* @param settings settings which may influence the result or null
* @return the mnemonic for this DataType.
*/
public String getMnemonic(Settings settings);
@ -155,6 +161,17 @@ public interface DataType {
*/
public int getLength();
/**
* Indicates is this datatype is defined with a zero length.
* This method should not be confused with {@link #isNotYetDefined()}
* which indicates that nothing but the name and basic type is known.
* NOTE: Support for zero-length datatypes is not yet fully supported, as a result
* they will generally return a non-zero length.
* @return true if type definition has a length of 0 even though it may report
* a length of 1, else false.
*/
public boolean isZeroLength();
/**
* Get a String briefly describing this DataType.
*
@ -240,7 +257,7 @@ public interface DataType {
* @param settings the Settings object
* @param len the length of the data.
* @param options options for how to format the default label prefix.
* @param offcutOffset
* @param offcutOffset offset into datatype
* @return the default label prefix.
*/
public String getDefaultOffcutLabelPrefix(MemBuffer buf, Settings settings, int len,
@ -277,18 +294,26 @@ public interface DataType {
* Notification that the given dataType's size has changed. DataTypes may
* need to make internal changes in response.
* <br>
* TODO: This method is reserved for internal DB use and should be removed
* from the public DataType interface!!
* TODO: This method is reserved for internal DB use.
* <br>
* @param dt the dataType that has changed.
*/
public void dataTypeSizeChanged(DataType dt);
/**
* Notification that the given dataType's alignment has changed. DataTypes may
* need to make internal changes in response.
* <br>
* TODO: This method is reserved for internal DB use.
* <br>
* @param dt the dataType that has changed.
*/
public void dataTypeAlignmentChanged(DataType dt);
/**
* Informs this dataType that the given dataType has been deleted.
* <br>
* TODO: This method is reserved for internal DB use and should be removed
* from the public DataType interface!!
* TODO: This method is reserved for internal DB use.
* <br>
* @param dt the dataType that has been deleted.
*/
@ -297,8 +322,7 @@ public interface DataType {
/**
* Informs this data type that the given oldDT has been replaced with newDT
* <br>
* TODO: This method is reserved for internal DB use and should be removed
* from the public DataType interface!!
* TODO: This method is reserved for internal DB use.
* <br>
* @param oldDt old data type
* @param newDt new data type
@ -308,8 +332,7 @@ public interface DataType {
/**
* Set the default settings for this data type.
* <br>
* TODO: This method is reserved for internal DB use and should be removed
* from the public DataType interface!!
* TODO: This method is reserved for internal DB use.
* <br>
* @param settings the settings to be used as this dataTypes default settings.
*/
@ -318,8 +341,7 @@ public interface DataType {
/**
* Inform this data type that it has the given parent
* <br>
* TODO: This method is reserved for internal DB use and should be removed
* from the public DataType interface!!
* TODO: This method is reserved for internal DB use.
* <br>
* @param dt parent data type
*/
@ -328,8 +350,7 @@ public interface DataType {
/**
* Remove a parent data type
* <br>
* TODO: This method is reserved for internal DB use and should be removed
* from the public DataType interface!!
* TODO: This method is reserved for internal DB use.
* <br>
* @param dt parent data type
*/
@ -338,8 +359,7 @@ public interface DataType {
/**
* Informs this data type that its name has changed from the indicated old name.
* <br>
* TODO: This method is reserved for internal DB use and should be removed
* from the public DataType interface!!
* TODO: This method is reserved for internal DB use.
* <br>
* @param dt the data type whose name changed
* @param oldName the data type's old name
@ -362,6 +382,8 @@ public interface DataType {
* For example byte[] depends on byte. If byte were deleted, then byte[] would
* also be deleted.
* @param dt the dataType to test that this dataType depends on.
* @return true if the existence of this datatype relies on the existence
* of the specified datatype dt.
*/
public boolean dependsOn(DataType dt);

View file

@ -107,7 +107,7 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
@Override
public int getOffset() {
if (isFlexibleArrayComponent) {
if (parent.isNotYetDefined()) {
if (parent.isZeroLength()) {
// some structures have only a flexible array defined
return 0;
}
@ -179,7 +179,7 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
if (parent == null) {
return; // Bad situation
}
for (DataTypeComponent comp : parent.getComponents()) {
for (DataTypeComponent comp : parent.getDefinedComponents()) {
if (comp != this && name.equals(comp.getFieldName())) {
throw new DuplicateNameException("Duplicate field name: " + name);
}
@ -328,7 +328,7 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
DataType otherDt = dtc.getDataType();
DataType myParent = getParent();
boolean aligned =
(myParent instanceof Composite) ? ((Composite) myParent).isInternallyAligned() : false;
(myParent instanceof Composite) ? ((Composite) myParent).isPackingEnabled() : false;
// Components don't need to have matching offset when they are aligned
// NOTE: use getOffset() method since returned values will differ from
// stored values for flexible array component

View file

@ -15,8 +15,9 @@
*/
package ghidra.program.model.data;
import java.util.ArrayList;
import java.util.Iterator;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.function.Consumer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
@ -31,7 +32,7 @@ public abstract class DataTypeImpl extends AbstractDataType implements ChangeLis
private final static SettingsDefinition[] EMPTY_DEFINITIONS = new SettingsDefinition[0];
protected Settings defaultSettings;
private ArrayList<DataType> parentList;
private List<WeakReference<DataType>> parentList;
private UniversalID universalID;
private SourceArchive sourceArchive;
private long lastChangeTime;
@ -102,59 +103,78 @@ public abstract class DataTypeImpl extends AbstractDataType implements ChangeLis
if (length < 0) {
return 1;
}
return getDataOrganization().getAlignment(this, length);
return getDataOrganization().getAlignment(this);
}
@Override
public void addParent(DataType dt) {
parentList.add(dt);
parentList.add(new WeakReference<>(dt));
}
@Override
public void removeParent(DataType dt) {
parentList.remove(dt);
Iterator<WeakReference<DataType>> iterator = parentList.iterator();
while (iterator.hasNext()) {
WeakReference<DataType> ref = iterator.next();
DataType dataType = ref.get();
if (dataType == null) {
iterator.remove();
}
else if (dt == dataType) {
iterator.remove();
break;
}
}
}
@Override
public DataType[] getParents() {
DataType[] dts = new DataType[parentList.size()];
return parentList.toArray(dts);
List<DataType> parents = new ArrayList<>();
Iterator<WeakReference<DataType>> iterator = parentList.iterator();
while (iterator.hasNext()) {
WeakReference<DataType> ref = iterator.next();
DataType dataType = ref.get();
if (dataType == null) {
iterator.remove();
}
else {
parents.add(dataType);
}
}
DataType[] array = new DataType[parents.size()];
return parents.toArray(array);
}
/**
* Notify any parent data types that my size has changed.
* Notify all parents that the size of this datatype has changed or
* other significant change that may affect a parent containing this
* datatype.
*/
protected void notifySizeChanged() {
Iterator<DataType> it = parentList.iterator();
while (it.hasNext()) {
DataType dt = it.next();
dt.dataTypeSizeChanged(this);
}
notifyParents(dt -> dt.dataTypeSizeChanged(this));
}
/**
* Notify any parents that my name has changed.
* Notify all parents that this datatype's alignment has changed
*/
protected void notifyAlignmentChanged() {
notifyParents(dt -> dt.dataTypeAlignmentChanged(this));
}
/**
* Notify all parents that this datatype's name has changed
*
* @param oldName
*/
protected void notifyNameChanged(String oldName) {
Iterator<DataType> it = parentList.iterator();
while (it.hasNext()) {
DataType dt = it.next();
dt.dataTypeNameChanged(this, oldName);
}
notifyParents(dt -> dt.dataTypeNameChanged(this, oldName));
}
/**
* Notify any parents that I am deleted.
* Notify all parents that this datatype has been deleted
*/
protected void notifyDeleted() {
Iterator<DataType> it = parentList.iterator();
while (it.hasNext()) {
DataType dt = it.next();
dt.dataTypeDeleted(this);
}
notifyParents(dt -> dt.dataTypeDeleted(this));
}
/**
@ -162,10 +182,20 @@ public abstract class DataTypeImpl extends AbstractDataType implements ChangeLis
* @param replacement replacement data type
*/
protected void notifyReplaced(DataType replacement) {
Iterator<DataType> it = parentList.iterator();
while (it.hasNext()) {
DataType dt = it.next();
dt.dataTypeReplaced(this, replacement);
notifyParents(dt -> dt.dataTypeReplaced(this, replacement));
}
protected final void notifyParents(Consumer<DataType> consumer) {
Iterator<WeakReference<DataType>> iterator = parentList.iterator();
while (iterator.hasNext()) {
WeakReference<DataType> ref = iterator.next();
DataType dataType = ref.get();
if (dataType == null) {
iterator.remove();
}
else {
consumer.accept(dataType);
}
}
}

View file

@ -249,8 +249,10 @@ public interface DataTypeManager {
/**
* Notification when data type is changed.
* @param dataType data type that is changed
* @param isAutoChange true if change was an automatic change in response to
* another datatype's change (e.g., size, alignment).
*/
public void dataTypeChanged(DataType dataType);
public void dataTypeChanged(DataType dataType, boolean isAutoChange);
/**
* Add a listener that is notified when the dataTypeManger changes.

View file

@ -383,37 +383,12 @@ public class EnumDataType extends GenericDataType implements Enum {
valueMap = new HashMap<>();
setLength(enumm.getLength());
String[] names = enumm.getNames();
for (int i = 0; i < names.length; i++) {
add(names[i], enumm.getValue(names[i]));
for (String name2 : names) {
add(name2, enumm.getValue(name2));
}
stateChanged(null);
}
@Override
public void dataTypeSizeChanged(DataType dt) {
// not applicable
}
@Override
public void dataTypeDeleted(DataType dt) {
// not applicable
}
@Override
public void dataTypeNameChanged(DataType dt, String oldName) {
// not applicable
}
@Override
public void dataTypeReplaced(DataType oldDt, DataType newDt) {
// not applicable
}
@Override
public boolean dependsOn(DataType dt) {
return false;
}
@Override
public String getDefaultLabelPrefix() {
return name == null ? null : name.toUpperCase();

View file

@ -24,8 +24,6 @@ import ghidra.util.exception.DuplicateNameException;
*/
public abstract class GenericDataType extends DataTypeImpl {
protected boolean packed = false;
protected GenericDataType(CategoryPath path, String name, DataTypeManager dataMgr) {
super(path, name, dataMgr);
if (!DataUtilities.isValidDataTypeName(name)) {
@ -72,8 +70,9 @@ public abstract class GenericDataType extends DataTypeImpl {
}
private void doSetCategoryPath(CategoryPath path) {
if (path == null)
if (path == null) {
path = CategoryPath.ROOT;
}
categoryPath = path;
}

View file

@ -0,0 +1,42 @@
/* ###
* 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.program.model.data;
/**
* <code>PackingType</code> specifies the pack setting which applies to a composite data type.
* This can be DISABLED, DEFAULT, EXPLICIT.
*/
public enum PackingType {
/**
* <B>DISABLED</B> - indicates that automatic component placement should not be performed, with
* components placed at specified offsets and <code>undefined</code> components used to
* reflects padding/unused bytes. This mode is commonly used when reverse-engineering a
* composite since a complete and accurate definition may not be known.
*/
DISABLED,
/**
* <B>DEFAULT</B> - indicates that components should be placed automatically based upon
* their alignment. This is intended to reflect the default behavior of a compiler
* when a complete definition of a composite is known as well as the alignment of each
* component.
*/
DEFAULT,
/**
* <B>EXPLICIT</B> - indicates an explicit pack value has been specified and that components
* should be placed automatically based upon their alignment, not to exceed the pack value.
*/
EXPLICIT;
}

View file

@ -248,7 +248,7 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl
int otherLength = dtc.getLength();
DataType myParent = getParent();
boolean aligned =
(myParent instanceof Composite) ? ((Composite) myParent).isInternallyAligned() : false;
(myParent instanceof Composite) ? ((Composite) myParent).isPackingEnabled() : false;
// Components don't need to have matching offset when they are aligned, only matching ordinal.
if ((!aligned && (offset != dtc.getOffset())) ||
// Components don't need to have matching length when they are aligned. Is this correct?

View file

@ -23,14 +23,14 @@ import java.util.Comparator;
* NOTE: Structures containing only a flexible array will report a length of 1 which will result in
* improper code unit sizing since we are unable to support a defined data of length 0.
* <p>
* NOTE: The use of zero-length bitfields within unaligned structures is discouraged since they have
* no real affect and are easily misplaced. Their use should be reserved for aligned/packed
* NOTE: The use of zero-length bitfields within non-packed structures is discouraged since they have
* no real affect and are easily misplaced. Their use should be reserved for packed
* structures.
*/
public interface Structure extends Composite {
@Override
Structure clone(DataTypeManager dtm);
public Structure clone(DataTypeManager dtm);
/**
* Returns the component of this structure with the indicated ordinal. If the specified ordinal
@ -39,12 +39,12 @@ public interface Structure extends Composite {
* {@link #getFlexibleArrayComponent()} is preferred for obtaining this special trailing
* component.
*
* @param ordinal the component's ordinal (zero based).
* @param ordinal the ordinal of the component requested.
* @return the data type component.
* @throws ArrayIndexOutOfBoundsException if the ordinal is out of bounds
* @throws IndexOutOfBoundsException if the ordinal is out of bounds
*/
@Override
public abstract DataTypeComponent getComponent(int ordinal);
public abstract DataTypeComponent getComponent(int ordinal) throws IndexOutOfBoundsException;
/**
* Gets the immediate child component that contains the byte at the given offset. If the
@ -68,23 +68,23 @@ public interface Structure extends Composite {
public abstract DataTypeComponent getDataTypeAt(int offset);
/**
* Inserts a new bitfield at the specified ordinal position in this structure. Within aligned
* Inserts a new bitfield at the specified ordinal position in this structure. Within packed
* structures the specified byteWidth and bitOffset will be ignored since packing will occur at
* the specified ordinal position. The resulting component length and bitfield details will
* reflect the use of minimal storage sizing.
* <p>
* For unaligned structures, a component shift will only occur if the bitfield placement
* For structures with packing disabled, a component shift will only occur if the bitfield placement
* conflicts with another component. If no conflict occurs, the bitfield will be placed at the
* specified location consuming any DEFAULT components as needed. When a conflict does occur a
* shift will be performed at the ordinal position based upon the specified byteWidth. When
* located onto existing bitfields they will be packed together provided they do not conflict,
* otherwise the conflict rule above applies.
* <p>
* Supported aligned packing starts with bit-0 (lsb) of the first byte for little-endian, and
* Supported packing starts with bit-0 (lsb) of the first byte for little-endian, and
* with bit-7 (msb) of the first byte for big-endian. This is the default behavior for most
* compilers. Insertion behavior may not work as expected if packing rules differ from this.
*
* @param ordinal the ordinal where the new datatype is to be inserted.
* @param ordinal the ordinal of the component to be inserted.
* @param byteWidth the storage allocation unit width which contains the bitfield. Must be large
* enough to contain the "effective bit size" and corresponding bitOffset. The actual
* component size used will be recomputed during insertion.
@ -99,22 +99,22 @@ public interface Structure extends Composite {
* @return the bitfield component created whose associated data type will be BitFieldDataType.
* @throws InvalidDataTypeException if the specified baseDataType is not a valid base type for
* bitfields.
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the current
* @throws IndexOutOfBoundsException if ordinal is less than 0 or greater than the current
* number of components.
*/
public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException;
throws InvalidDataTypeException, IndexOutOfBoundsException;
/**
* Inserts a new bitfield at the specified location in this composite. This method is intended
* to be used with unaligned structures where the bitfield will be precisely placed. Within an
* aligned structure the specified byteOffset, byteWidth and bitOffset will be used to identify
* to be used with structures with packing disabled where the bitfield will be precisely placed. Within an
* packed structure the specified byteOffset, byteWidth and bitOffset will be used to identify
* the appropriate ordinal but may not be preserved. The component length will be computed based
* upon the specified parameters and will be reduced from byteWidth to its minimal size for the
* new component.
* <p>
* For unaligned mode, a component shift will only occur if the bitfield placement conflicts
* When packing disabled, a component shift will only occur if the bitfield placement conflicts
* with another component. If no conflict occurs, the bitfield will be placed at the specified
* location consuming any DEFAULT components as needed. When a conflict does occur a shift will
* be performed at the point of conflict based upon the specified byteWidth. When located onto
@ -125,8 +125,8 @@ public interface Structure extends Composite {
* Insertion behavior may not work as expected if packing rules differ from this.
* <p>
*
* Zero length bitfields may be inserted although they have no real affect for unaligned
* structures. Only the resulting byte offset within the structure is of significance in
* Zero length bitfields may be inserted although they have no real affect when packing disabled.
* Only the resulting byte offset within the structure is of significance in
* determining its ordinal placement.
* <p>
*
@ -200,67 +200,69 @@ public interface Structure extends Composite {
/**
* Remove all components from this structure (including flex-array), effectively setting the
* length to zero.
* length to zero. Packing and minimum alignment settings are unaffected.
*/
public void deleteAll();
/**
* Clears the defined component at the given component index. Clearing a component causes a
* defined component to be replaced with a number of undefined dataTypes to offset the removal
* of the defined dataType.
* Clears the defined component at the given component ordinal. Clearing a component within
* a non-packed structure causes a defined component to be replaced with a number of undefined
* dataTypes to offset the removal of the defined dataType. In the case of a packed
* structure the component is deleted without backfill.
*
* @param index the index of the component to clear.
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
* @param ordinal the ordinal of the component to clear.
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public void clearComponent(int index) throws ArrayIndexOutOfBoundsException;
public void clearComponent(int ordinal) throws IndexOutOfBoundsException;
/**
* Replaces the component at the given component index with a new component of the indicated
* Replaces the component at the given component ordinal with a new component of the indicated
* data type.
*
* @param index the index where the datatype is to be replaced.
* @param ordinal the ordinal of the component to be replaced.
* @param dataType the datatype to insert.
* @param length the length of the dataType to insert. For fixed length types a length &lt;= 0
* will use the length of the resolved dataType.
* @return the new componentDataType at the index.
* @return the new component
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
* component in this composite data type or an invalid length is specified. 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. In addition, any
* attempt to replace an existing bit-field component or specify a
* {@link BitFieldDataType} will produce this error.
* @throws ArrayIndexOutOfBoundsException if component index is out of bounds
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public DataTypeComponent replace(int index, DataType dataType, int length)
throws ArrayIndexOutOfBoundsException, IllegalArgumentException;
public DataTypeComponent replace(int ordinal, DataType dataType, int length)
throws IndexOutOfBoundsException, IllegalArgumentException;
/**
* Replaces the component at the given component index with a new component of the indicated
* Replaces the component at the given component ordinal with a new component of the indicated
* data type.
*
* @param index the index where the datatype is to be replaced.
* @param ordinal the ordinal of the component to be replaced.
* @param dataType the datatype to insert.
* @param length the length to associate with the dataType. For fixed length types a length
* &lt;= 0 will use the length of the resolved dataType.
* @param name the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the new componentDataType at the index.
* @return the new component.
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
* component in this composite data type or an invalid length is specified. 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. In addition, any
* attempt to replace an existing bit-field component or specify a
* {@link BitFieldDataType} will produce this error.
* @throws ArrayIndexOutOfBoundsException if component index is out of bounds
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public DataTypeComponent replace(int index, DataType dataType, int length, String name,
String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException;
public DataTypeComponent replace(int ordinal, DataType dataType, int length, String name,
String comment) throws IndexOutOfBoundsException, IllegalArgumentException;
/**
* Replaces the component at the specified byte offset with a new component of the indicated
* data type. If the offset corresponds to a bit-field, all bit-fields at that offset will be
* removed and replaced by the specified component. Keep in mind bit-field or any component
* removal must clear sufficient space for an unaligned structure to complete the replacement.
* removal must clear sufficient space in a structure with packing disabled to complete
* the replacement.
*
* @param offset the byte offset into the structure where the datatype is to be replaced.
* @param dataType the datatype to insert.
@ -268,7 +270,7 @@ public interface Structure extends Composite {
* &lt;= 0 will use the length of the resolved dataType.
* @param name the field name to associate with this component.
* @param comment the comment to associate with this component.
* @return the new componentDataType at the index.
* @return the new component.
* @throws IllegalArgumentException if the specified data type is not allowed to replace a
* component in this composite data type or an invalid length is specified. For
* example, suppose dt1 contains dt2. Therefore it is not valid to replace a dt2
@ -288,6 +290,12 @@ public interface Structure extends Composite {
/**
* Get the optional trailing flexible array component associated with this structure.
* <p>
* NOTE: The trailing flexable array may be assigned an incorrect offset
* when packing is enabled and the minimum alignment is specified. In such cases,
* the flex array may be less than the overall structure length. Currently, it is
* assumed the trailing flex array will have an offset equal to the overall
* structure length.
*
* @return optional trailing flexible array component associated with this structure or null if
* not present.
@ -314,25 +322,14 @@ public interface Structure extends Composite {
public void clearFlexibleArrayComponent();
/**
* Increases the size of the structure by the given amount by adding undefined datatypes at the
* end of the structure.
* Increases the size of the structure by the given amount by adding undefined filler at the
* end of the structure. NOTE: This method only has an affect on structures with packing disabled.
*
* @param amount the amount by which to grow the structure.
* @throws IllegalArgumentException if amount &lt; 1
*/
public void growStructure(int amount);
/**
* Sets the current packing value (usually a power of 2). A value of NOT_PACKING should be
* passed if this isn't a packed data type. Otherwise this value indicates a maximum alignment
* for any component within this data type. Calling this method will cause the data type to
* become an internally aligned data type. (Same as {@link Composite#setPackingValue(int)})
*
* @param maxAlignment the new packing value or 0 for NOT_PACKING. A negative value will be
* treated the same as 0.
*/
public void pack(int maxAlignment);
/**
* <code>BitOffsetComparator</code> provides ability to compare an normalized bit offset (see
* {@link #getNormalizedBitfieldOffset(int, int, int, int, boolean)}) with a
@ -365,6 +362,9 @@ public interface Structure extends Composite {
*/
public static class BitOffsetComparator implements Comparator<Object> {
public static final Comparator<Object> INSTANCE_LE = new BitOffsetComparator(false);
public static final Comparator<Object> INSTANCE_BE = new BitOffsetComparator(true);
private boolean bigEndian;
public BitOffsetComparator(boolean bigEndian) {
@ -439,47 +439,4 @@ public interface Structure extends Composite {
}
/**
* <code>OffsetComparator</code> provides ability to compare an Integer offset with a
* DataTypeComponent object. The offset will be consider equal (0) if the component contains the
* offset.
*/
public static 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;
}
}
/**
* <code>OrdinalComparator</code> provides ability to compare an Integer ordinal with a
* DataTypeComponent object. The offset will be consider equal (0) if the component corresponds
* to the specified ordinal.
*/
public static 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

@ -0,0 +1,20 @@
/* ###
* 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.program.model.data;
public interface StructureInternal extends Structure, CompositeInternal {
}

View file

@ -141,6 +141,11 @@ public class TypedefDataType extends GenericDataType implements TypeDef {
return dataType.getDescription();
}
@Override
public boolean isZeroLength() {
return dataType.isZeroLength();
}
@Override
public int getLength() {
return dataType.getLength();
@ -182,6 +187,13 @@ public class TypedefDataType extends GenericDataType implements TypeDef {
}
}
@Override
public void dataTypeAlignmentChanged(DataType dt) {
if (dt == dataType) {
notifyAlignmentChanged();
}
}
@Override
public DataType getBaseDataType() {
if (dataType instanceof TypeDef) {

View file

@ -24,9 +24,12 @@ package ghidra.program.model.data;
*/
public interface Union extends Composite {
@Override
public Union clone(DataTypeManager dtm);
/**
* Inserts a new bitfield at the specified ordinal position in this union.
* For both aligned and unaligned unions the bitfield starts with bit-0 (lsb) of the first byte
* For all Unions, bitfield starts with bit-0 (lsb) of the first byte
* for little-endian, and with bit-7 (msb) of the first byte for big-endian. This is the
* default behavior for most compilers. Insertion behavior may not work as expected if
* packing rules differ from this.
@ -40,10 +43,10 @@ public interface Union extends Composite {
* be BitFieldDataType.
* @throws InvalidDataTypeException if the specified baseDataType is
* not a valid base type for bitfields.
* @throws ArrayIndexOutOfBoundsException if ordinal is less than 0 or greater than the
* @throws IndexOutOfBoundsException if ordinal is less than 0 or greater than the
* current number of components.
*/
public DataTypeComponent insertBitField(int ordinal, DataType baseDataType, int bitSize,
String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException;
throws InvalidDataTypeException, IndexOutOfBoundsException;
}

View file

@ -15,8 +15,7 @@
*/
package ghidra.program.model.data;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.*;
import ghidra.docking.settings.Settings;
import ghidra.program.model.mem.MemBuffer;
@ -24,11 +23,15 @@ import ghidra.util.Msg;
import ghidra.util.UniversalID;
/**
* Basic implementation of the union data type
* Basic implementation of the union data type.
* NOTE: Implementation is not thread safe when being modified.
*/
public class UnionDataType extends CompositeDataTypeImpl implements Union {
private ArrayList<DataTypeComponentImpl> components;
public class UnionDataType extends CompositeDataTypeImpl implements UnionInternal {
private int unionLength;
private int unionAlignment;
private List<DataTypeComponentImpl> components;
/**
* Construct a new empty union with the given name within the
@ -96,7 +99,7 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
@Override
public boolean isNotYetDefined() {
return components.size() == 0;
return unionLength == 0 && isDefaultAligned() && !isPackingEnabled();
}
@Override
@ -124,17 +127,30 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
return components.size();
}
@Override
protected int getPreferredComponentLength(DataType dataType, int length) {
if (!(dataType instanceof Dynamic)) {
length = -1;
}
return super.getPreferredComponentLength(dataType, length);
}
@Override
public DataTypeComponent add(DataType dataType, int length, String componentName,
String comment) throws IllegalArgumentException {
int oldAlignment = getAlignment();
DataTypeComponent dtc = doAdd(dataType, length, componentName, comment);
adjustLength(true);
if (!repack(true) && isPackingEnabled() && oldAlignment != getAlignment()) {
notifyAlignmentChanged();
}
return dtc;
}
private int getBitFieldAllocation(BitFieldDataType bitfieldDt) {
BitFieldPacking bitFieldPacking = getBitFieldPacking();
BitFieldPacking bitFieldPacking = getDataOrganization().getBitFieldPacking();
if (bitFieldPacking.useMSConvention()) {
return bitfieldDt.getBaseTypeSize();
}
@ -144,10 +160,9 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
}
int length = bitfieldDt.getBaseTypeSize();
int packValue = getPackingValue();
if (packValue != NOT_PACKING && length > packValue) {
if (packing > 0 && length > packing) {
length =
DataOrganizationImpl.getLeastCommonMultiple(bitfieldDt.getStorageSize(), packValue);
DataOrganizationImpl.getLeastCommonMultiple(bitfieldDt.getStorageSize(), packing);
}
return length;
}
@ -177,6 +192,8 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
String componentName, String comment) throws IllegalArgumentException {
validateDataType(dataType);
int oldAlignment = getAlignment();
dataType = adjustBitField(dataType);
dataType = dataType.clone(dataMgr);
@ -190,7 +207,9 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
shiftOrdinals(ordinal, 1);
components.add(ordinal, dtc);
adjustLength(true);
if (!repack(true) && isPackingEnabled() && oldAlignment != getAlignment()) {
notifyAlignmentChanged();
}
return dtc;
}
@ -203,10 +222,10 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
@Override
public DataTypeComponent insertBitField(int ordinal, DataType baseDataType, int bitSize,
String componentName, String comment)
throws InvalidDataTypeException, ArrayIndexOutOfBoundsException {
throws InvalidDataTypeException, IndexOutOfBoundsException {
if (ordinal < 0 || ordinal > components.size()) {
throw new ArrayIndexOutOfBoundsException(ordinal);
throw new IndexOutOfBoundsException(ordinal);
}
BitFieldDataType.checkBaseDataType(baseDataType);
@ -216,16 +235,21 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
return insert(ordinal, bitFieldDt, bitFieldDt.getStorageSize(), componentName, comment);
}
@Override
public boolean isZeroLength() {
return unionLength == 0;
}
@Override
public int getLength() {
if (unionLength == 0) {
return 1;
return 1; // 0-length datatype not supported
}
return unionLength;
}
@Override
public DataType clone(DataTypeManager dtm) {
public Union clone(DataTypeManager dtm) {
if (dataMgr == dtm) {
return this;
}
@ -246,16 +270,54 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
@Override
public void delete(int ordinal) {
int oldAlignment = getAlignment();
DataTypeComponent dtc = components.remove(ordinal);
dtc.getDataType().removeParent(this);
shiftOrdinals(ordinal, -1);
adjustLength(true);
if (!repack(true) && isPackingEnabled() && oldAlignment != getAlignment()) {
notifyAlignmentChanged();
}
}
@Override
public void delete(int[] ordinals) {
for (int ordinal : ordinals) {
delete(ordinal);
public void delete(Set<Integer> ordinals) {
if (ordinals.isEmpty()) {
return;
}
int oldAlignment = getAlignment();
List<DataTypeComponentImpl> newComponents = new ArrayList<>();
int newLength = 0;
int ordinalAdjustment = 0;
for (DataTypeComponentImpl dtc : components) {
int ordinal = dtc.getOrdinal();
if (ordinals.contains(ordinal)) {
// component removed
--ordinalAdjustment;
}
else {
if (ordinalAdjustment != 0) {
dtc.setOrdinal(dtc.getOrdinal() + ordinalAdjustment);
}
newComponents.add(dtc);
newLength = Math.max(newLength, dtc.getLength());
}
}
components = newComponents;
if (isPackingEnabled()) {
if (!repack(true) && oldAlignment != getAlignment()) {
notifyAlignmentChanged();
}
}
else {
unionLength = newLength;
notifySizeChanged();
}
}
@ -270,8 +332,8 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
DataType baseDataType = bitfieldDt.getBaseDataType();
baseDataType = baseDataType.clone(dataMgr);
// Both aligned and unaligned bitfields use same adjustment
// unaligned must force bitfield placement at byte offset 0
// Both aligned and non-packed bitfields use same adjustment
// non-packed must force bitfield placement at byte offset 0
int bitSize = bitfieldDt.getDeclaredBitSize();
int effectiveBitSize =
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
@ -303,22 +365,55 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
return bitfieldDt;
}
private void adjustLength(boolean notify) {
@Override
public int getAlignment() {
if (unionAlignment > 0) {
return unionAlignment;
}
if (isPackingEnabled()) {
unionAlignment = CompositeAlignmentHelper.getAlignment(getDataOrganization(), this);
}
else {
unionAlignment = getNonPackedAlignment();
}
return unionAlignment;
}
@Override
public boolean repack(boolean notify) {
int oldLength = unionLength;
int oldAlignment = getAlignment();
unionLength = 0;
for (DataTypeComponent dtc : components) {
// TODO: compute alignment in this loop
int length = dtc.getLength();
if (isInternallyAligned() && dtc.isBitFieldComponent()) {
if (isPackingEnabled() && dtc.isBitFieldComponent()) {
// revise length to reflect compiler bitfield allocation rules
length = getBitFieldAllocation((BitFieldDataType) dtc.getDataType());
}
unionLength = Math.max(length, unionLength);
}
if (notify && oldLength != unionLength) {
notifySizeChanged();
unionAlignment = -1; // force recompute of unionAlignment
getAlignment();
if (isPackingEnabled()) {
unionLength = DataOrganizationImpl.getAlignedOffset(unionAlignment, unionLength);
}
boolean changed = (oldLength != unionLength) || (oldAlignment != unionAlignment);
if (changed && notify) {
if (oldLength != unionLength) {
notifySizeChanged();
}
else if (oldAlignment != unionAlignment) {
notifyAlignmentChanged();
}
}
return changed;
}
@Override
@ -330,13 +425,10 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
return false;
}
if (dt instanceof Union) {
Union union = (Union) dt;
if (isInternallyAligned() != union.isInternallyAligned() ||
isDefaultAligned() != union.isDefaultAligned() ||
isMachineAligned() != union.isMachineAligned() ||
getMinimumAlignment() != union.getMinimumAlignment() ||
getPackingValue() != union.getPackingValue()) {
if (dt instanceof UnionInternal) {
UnionInternal union = (UnionInternal) dt;
if (packing != union.getStoredPackingValue() ||
minimumAlignment != union.getStoredMinimumAlignment()) {
// rely on component match instead of checking length
// since dynamic component sizes could affect length
return false;
@ -365,8 +457,23 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
@Override
public void dataTypeAlignmentChanged(DataType dt) {
if (isInternallyAligned()) {
adjustInternalAlignment();
if (!isPackingEnabled()) {
return;
}
if (dt instanceof BitFieldDataType) {
return; // unsupported
}
boolean hasPossibleChange = false;
for (DataTypeComponentImpl dtc : components) {
if (dtc.getDataType() == dt) {
hasPossibleChange = true;
break;
}
}
if (hasPossibleChange && !repack(true) && isPackingEnabled()) {
// NOTE: Must assume alignment change since we are unable to determine
// without stored alignment
notifyAlignmentChanged();
}
}
@ -386,8 +493,10 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
changed = true;
}
}
if (changed) {
adjustLength(true);
if (changed && !repack(true) && isPackingEnabled()) {
// NOTE: Must assume alignment change since we are unable to determine
// without stored alignment
notifyAlignmentChanged();
}
}
@ -451,13 +560,14 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
}
}
if (changed) {
adjustLength(true);
repack(false);
notifySizeChanged();
}
}
@Override
public void dataTypeDeleted(DataType dt) {
boolean didDelete = false;
boolean changed = false;
for (int i = components.size() - 1; i >= 0; i--) { // reverse order
DataTypeComponentImpl dtc = components.get(i);
boolean removeBitFieldComponent = false;
@ -469,21 +579,23 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
dt.removeParent(this);
components.remove(i);
shiftOrdinals(i, -1);
didDelete = true;
changed = true;
}
}
if (didDelete) {
adjustLength(true);
if (changed && !repack(true) && isPackingEnabled()) {
// NOTE: Must assume alignment change since we are unable to determine
// without stored alignment
notifyAlignmentChanged();
}
}
@Override
public void replaceWith(DataType dataType) throws IllegalArgumentException {
if (!(dataType instanceof Union)) {
if (!(dataType instanceof UnionInternal)) {
throw new IllegalArgumentException();
}
Union union = (Union) dataType;
UnionInternal union = (UnionInternal) dataType;
Iterator<DataTypeComponentImpl> it = components.iterator();
while (it.hasNext()) {
@ -491,8 +603,10 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
dtc.getDataType().removeParent(this);
}
components.clear();
unionAlignment = -1;
setAlignment(union);
this.packing = union.getStoredPackingValue();
this.minimumAlignment = union.getStoredMinimumAlignment();
DataTypeComponent[] compArray = union.getComponents();
for (DataTypeComponent dtc : compArray) {
@ -500,12 +614,8 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
doAdd(dt, dtc.getLength(), dtc.getFieldName(), dtc.getComment());
}
adjustLength(true);
}
@Override
public void dataTypeNameChanged(DataType dt, String oldName) {
// ignored
repack(false);
notifySizeChanged(); // assume size and/or alignment changed
}
@Override
@ -522,16 +632,4 @@ public class UnionDataType extends CompositeDataTypeImpl implements Union {
return "UNION_" + getName();
}
@Override
public void realign() {
if (isInternallyAligned()) {
adjustInternalAlignment();
}
}
@Override
public void adjustInternalAlignment() {
adjustLength(true);
}
}

View file

@ -0,0 +1,20 @@
/* ###
* 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.program.model.data;
public interface UnionInternal extends Union, CompositeInternal {
}

View file

@ -22,6 +22,8 @@ import static org.junit.Assert.*;
import org.junit.*;
import com.google.common.collect.Sets;
import generic.test.AbstractGTest;
import ghidra.program.model.data.*;
import ghidra.util.InvalidNameException;
@ -89,10 +91,12 @@ public class StructureDBTest extends AbstractGTest {
public void testEmpty() throws Exception {
Structure s = new StructureDataType("foo", 0);
assertTrue(s.isNotYetDefined());
assertTrue(s.isZeroLength());
assertEquals(0, s.getNumComponents());
assertEquals(0, s.getNumDefinedComponents());
Structure s2 = (Structure) dataMgr.resolve(s, null);
assertTrue(s2.isNotYetDefined());
assertTrue(s2.isZeroLength());
assertEquals(0, s2.getNumComponents());
assertEquals(0, s.getNumDefinedComponents());
}
@ -101,10 +105,12 @@ public class StructureDBTest extends AbstractGTest {
public void testSizeOne() throws Exception {
Structure s = new StructureDataType("foo", 1);
assertFalse(s.isNotYetDefined());
assertFalse(s.isZeroLength());
assertEquals(1, s.getNumComponents());
assertEquals(0, s.getNumDefinedComponents());
Structure s2 = (Structure) dataMgr.resolve(s, null);
assertFalse(s2.isNotYetDefined());
assertFalse(s2.isZeroLength());
assertEquals(1, s2.getNumComponents());
assertEquals(0, s2.getNumDefinedComponents());
}
@ -442,7 +448,7 @@ public class StructureDBTest extends AbstractGTest {
@Test
public void testSetFlexArray() throws Exception {
struct.setInternallyAligned(true);
struct.setPackingEnabled(true);
struct.delete(2); // remove dword to verify flex array alignment below
@ -450,7 +456,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Aligned\n" +
"pack()\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 2 word 2 null \"Comment2\"\n" +
@ -464,7 +470,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Aligned\n" +
"pack()\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 2 word 2 null \"Comment2\"\n" +
@ -478,7 +484,7 @@ public class StructureDBTest extends AbstractGTest {
@Test
public void testZeroBitFields() throws Exception {
struct.setInternallyAligned(true);
struct.setPackingEnabled(true);
struct.delete(2); // remove dword to verify flex array alignment below
@ -488,7 +494,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Aligned\n" +
"pack()\n" +
"Structure Test {\n" +
" 0 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 4 int:0(0) 1 \"zero bitfield 1\"\n" +
@ -504,7 +510,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Aligned\n" +
"pack()\n" +
"Structure Test {\n" +
" 0 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 4 int:0(0) 1 \"zero bitfield 1\"\n" +
@ -525,7 +531,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -543,7 +549,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -562,7 +568,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -585,7 +591,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -605,7 +611,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -630,7 +636,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -649,7 +655,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -669,7 +675,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -697,7 +703,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -716,7 +722,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -751,7 +757,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -770,7 +776,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -790,7 +796,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -809,7 +815,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -828,7 +834,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -859,7 +865,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -1084,7 +1090,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -1101,7 +1107,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -1118,7 +1124,7 @@ public class StructureDBTest extends AbstractGTest {
@Test
public void testReplaceFlexArrayDependency() throws DataTypeDependencyException {
struct.setInternallyAligned(true);
struct.setPackingEnabled(true);
struct.delete(2); // remove dword to verify flex array alignment below
@ -1129,7 +1135,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Aligned\n" +
"pack()\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 2 word 2 null \"Comment2\"\n" +
@ -1146,7 +1152,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Aligned\n" +
"pack()\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 2 word 2 null \"Comment2\"\n" +
@ -1161,7 +1167,7 @@ public class StructureDBTest extends AbstractGTest {
public void testReplaceBitFieldDependency()
throws InvalidDataTypeException, DataTypeDependencyException {
struct.setInternallyAligned(true);
struct.setPackingEnabled(true);
TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType);
td = (TypeDef) dataMgr.resolve(td, null);
@ -1172,7 +1178,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Aligned\n" +
"pack()\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 2 word 2 null \"Comment2\"\n" +
@ -1189,7 +1195,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Aligned\n" +
"pack()\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 2 word 2 null \"Comment2\"\n" +
@ -1206,7 +1212,7 @@ public class StructureDBTest extends AbstractGTest {
@Test
public void testReplaceWith() throws InvalidDataTypeException {
// NOTE: unaligned bitfields should remain unchanged when
// NOTE: non-packed bitfields should remain unchanged when
// transitioning endianess even though it makes little sense.
// Unaligned structures are not intended to be portable!
@ -1222,7 +1228,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -1246,7 +1252,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/bigStruct\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure bigStruct {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 1 word 2 null \"Comment2\"\n" +
@ -1263,6 +1269,129 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:on
}
@Test
public void testReplaceWithConflict() {
// Test case where structure BAR contains a not-yet-defined structure FOO (impl)
// which has previously been resolved as fully defined. Test verifies that resolving
// BAR properly handles the conflict replacement of the FOO component with a larger
// fully defined datatype.
Structure fooNotYetDefined = new StructureDataType("FOO", 0);
Structure fooDefined = new StructureDataType("FOO", 0);
fooDefined.add(new ArrayDataType(ByteDataType.dataType, 20, 1));
fooDefined = (Structure) dataMgr.resolve(fooDefined, null);
Structure barStruct = new StructureDataType("BAR", 40);
barStruct.replaceAtOffset(0, fooNotYetDefined, -1, "f1", null);
barStruct.replaceAtOffset(10, ByteDataType.dataType, 1, "f2", null);
barStruct = (Structure) dataMgr.resolve(barStruct,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/BAR\n" +
"pack(disabled)\n" +
"Structure BAR {\n" +
" 0 FOO 10 f1 \"\"\n" +
" 10 byte 1 f2 \"\"\n" +
"}\n" +
"Size = 40 Actual Alignment = 1", barStruct);
//@formatter:on
DataTypeComponent dtc1 = barStruct.getDefinedComponents()[1];
assertEquals(1, dtc1.getOrdinal());
assertEquals(dtc1, barStruct.getComponent(1));
}
@Test
public void testReplaceWithConflict2() {
// Test case where structure BAR contains a not-yet-defined structure FOO (impl)
// which has previously been resolved as fully defined. Test verifies that resolving
// BAR properly handles the conflict replacement of the FOO component with a larger
// fully defined datatype.
Structure fooNotYetDefined = new StructureDataType("FOO", 0);
Structure fooDefined = new StructureDataType("FOO", 0);
fooDefined.add(new ArrayDataType(ByteDataType.dataType, 5, 1));
fooDefined = (Structure) dataMgr.resolve(fooDefined, null);
Structure barStruct = new StructureDataType("BAR", 40);
barStruct.replaceAtOffset(0, fooNotYetDefined, -1, "f1", null);
barStruct.replaceAtOffset(10, ByteDataType.dataType, 1, "f2", null);
barStruct = (Structure) dataMgr.resolve(barStruct,
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/BAR\n" +
"pack(disabled)\n" +
"Structure BAR {\n" +
" 0 FOO 5 f1 \"\"\n" +
" 10 byte 1 f2 \"\"\n" +
"}\n" +
"Size = 40 Actual Alignment = 1", barStruct);
//@formatter:on
DataTypeComponent dtc1 = barStruct.getDefinedComponents()[1];
assertEquals(6, dtc1.getOrdinal());
assertEquals(dtc1, barStruct.getComponent(6));
}
@Test
public void testDeleteMany() throws InvalidDataTypeException {
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
struct.insertBitFieldAt(2, 4, 6, IntegerDataType.dataType, 15, "bf3", "bf3Comment");
struct.insertBitFieldAt(2, 4, 21, IntegerDataType.dataType, 11, "bf4", "bf4Comment");
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
" 2 int:3(0) 1 bf1 \"bf1Comment\"\n" +
" 2 int:3(3) 1 bf2 \"bf2Comment\"\n" +
" 2 int:15(6) 3 bf3 \"bf3Comment\"\n" +
" 4 int:11(5) 2 bf4 \"bf4Comment\"\n" +
" 6 word 2 null \"Comment2\"\n" +
" 8 dword 4 field3 \"\"\n" +
" 12 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 13 Actual Alignment = 1", struct);
//@formatter:on
struct.delete(Sets.newHashSet(1, 2, 3, 4, 5, 6));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
// " 2 undefined 1 null \"\"\n" +
// " 3 undefined 1 null \"\"\n" +
// " 4 undefined 1 null \"\"\n" +
" 5 dword 4 field3 \"\"\n" +
" 9 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 10 Actual Alignment = 1", struct);
//@formatter:on
assertEquals(10, struct.getLength());
assertEquals(7, struct.getNumComponents());
assertEquals(3, struct.getNumDefinedComponents());
DataTypeComponent[] comps = struct.getDefinedComponents();
assertEquals(DWordDataType.class, comps[1].getDataType().getClass());
assertEquals(5, comps[1].getOffset());
}
@Test
public void testDelete() throws InvalidDataTypeException {
@ -1273,7 +1402,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -1292,7 +1421,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -1310,7 +1439,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -1327,7 +1456,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -1344,7 +1473,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -1362,7 +1491,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -1380,7 +1509,7 @@ public class StructureDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Structure Test {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
// " 1 undefined 1 null \"\"\n" +
@ -1446,6 +1575,7 @@ public class StructureDBTest extends AbstractGTest {
s.deleteAll();
assertEquals(1, s.getLength());
assertTrue(s.isNotYetDefined());
assertTrue(s.isZeroLength());
assertEquals(0, s.getNumComponents());
}

View file

@ -22,6 +22,8 @@ import static org.junit.Assert.*;
import org.junit.*;
import com.google.common.collect.Sets;
import generic.test.AbstractGTest;
import ghidra.program.model.data.*;
import ghidra.util.task.TaskMonitor;
@ -169,7 +171,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 short 2 null \"\"\n" +
" 0 int:2(0) 1 bf1 \"bf1Comment\"\n" +
@ -179,7 +181,7 @@ public class UnionDBTest extends AbstractGTest {
}
@Test
public void testAlignedBitFieldUnion() throws Exception {
public void testPackedBitFieldUnion() throws Exception {
int cnt = union.getNumComponents();
for (int i = 0; i < cnt; i++) {
@ -187,11 +189,11 @@ public class UnionDBTest extends AbstractGTest {
}
union.insertBitField(0, IntegerDataType.dataType, 2, "bf1", "bf1Comment");
union.insert(0, ShortDataType.dataType);
union.setInternallyAligned(true);
union.setPackingEnabled(true);
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Aligned\n" +
"pack()\n" +
"Union TestUnion {\n" +
" 0 short 2 null \"\"\n" +
" 0 int:2(0) 1 bf1 \"bf1Comment\"\n" +
@ -208,7 +210,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
@ -231,7 +233,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
@ -255,7 +257,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
@ -271,7 +273,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
@ -294,7 +296,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
@ -310,7 +312,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
@ -357,6 +359,42 @@ public class UnionDBTest extends AbstractGTest {
assertEquals(2, union.getLength());
}
@Test
public void testDeleteMany() throws Exception {
Structure struct = createStructure("struct_1", 0);
struct.add(new ByteDataType());
struct.add(new StringDataType(), 10);
union.add(struct);
assertEquals(11, union.getLength());
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
" 0 dword 4 field3 \"\"\n" +
" 0 byte 1 field4 \"Comment4\"\n" +
" 0 struct_1 11 null \"\"\n" +
"}\n" +
"Size = 11 Actual Alignment = 1", union);
//@formatter:on
union.delete(Sets.newHashSet(2, 4));
assertEquals(2, union.getLength());
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field1 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
" 0 byte 1 field4 \"Comment4\"\n" +
"}\n" +
"Size = 2 Actual Alignment = 1", union);
//@formatter:on
}
@Test
public void testIsPartOf() {
Structure struct = createStructure("struct_1", 0);
@ -388,7 +426,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Replaced\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union Replaced {\n" +
" 0 byte 1 field0 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +
@ -403,7 +441,7 @@ public class UnionDBTest extends AbstractGTest {
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestUnion\n" +
"Unaligned\n" +
"pack(disabled)\n" +
"Union TestUnion {\n" +
" 0 byte 1 field0 \"Comment1\"\n" +
" 0 word 2 null \"Comment2\"\n" +

View file

@ -128,6 +128,11 @@ public class TestDoubleDataType implements DataType {
throw new UnsupportedOperationException();
}
@Override
public boolean isZeroLength() {
throw new UnsupportedOperationException();
}
@Override
public String getDescription() {
throw new UnsupportedOperationException();
@ -195,6 +200,11 @@ public class TestDoubleDataType implements DataType {
throw new UnsupportedOperationException();
}
@Override
public void dataTypeAlignmentChanged(DataType dt) {
throw new UnsupportedOperationException();
}
@Override
public void dataTypeDeleted(DataType dt) {
throw new UnsupportedOperationException();

View file

@ -142,7 +142,7 @@ public class TestDoubleDataTypeManager implements DataTypeManager {
}
@Override
public void dataTypeChanged(DataType dataType) {
public void dataTypeChanged(DataType dataType, boolean isAutoChange) {
throw new UnsupportedOperationException();
}

View file

@ -156,7 +156,7 @@ public class TestDummyDataTypeManager implements DataTypeManager {
}
@Override
public void dataTypeChanged(DataType dataType) {
public void dataTypeChanged(DataType dataType, boolean isAutoChange) {
// stub
}