From da800b6e41d47528fadfe33aa1e69fcc566ed5d8 Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Tue, 2 Feb 2021 20:43:39 -0500 Subject: [PATCH] GP-862 Refactor of Composite interface and internals. Changes made to packing and alignment methods (see WhatsNew.html for API changes). --- .../src/global/docs/ChangeHistory.html | 4 +- .../src/global/docs/WhatsNew.html | 147 ++- .../database/data/DBTraceDataTypeManager.java | 4 +- Ghidra/Features/Base/certification.manifest | 4 +- .../DataTypeEditors/StructureEditor.htm | 344 +++---- .../images/StructureEditor.png | Bin 46358 -> 47126 bytes .../images/StructureEditorAligned.png | Bin 52104 -> 0 bytes .../images/StructureEditorPacked.png | Bin 0 -> 45444 bytes .../images/StructureEditorWithFlexArray.png | Bin 61659 -> 52870 bytes .../DataTypeEditors/images/UnionEditor.png | Bin 31172 -> 33023 bytes .../images/UnionEditorAligned.png | Bin 34764 -> 0 bytes .../images/UnionEditorPacked.png | Bin 0 -> 32009 bytes .../data_type_manager_description.htm | 20 +- .../cmd/data/CreateArrayInStructureCmd.java | 3 +- .../merge/datatypes/DataTypeMergeManager.java | 60 +- .../app/merge/datatypes/DataTypePanel.java | 48 +- .../compositeeditor/AddBitFieldAction.java | 4 +- .../compositeeditor/BitFieldEditorDialog.java | 14 +- .../compositeeditor/BitFieldEditorPanel.java | 8 +- .../BitFieldPlacementComponent.java | 6 +- .../core/compositeeditor/CompEditorModel.java | 167 ++-- .../core/compositeeditor/CompEditorPanel.java | 717 +++++++-------- .../compositeeditor/CompositeEditorModel.java | 7 +- .../compositeeditor/CompositeEditorPanel.java | 2 +- .../compositeeditor/CompositeViewerModel.java | 8 +- .../core/compositeeditor/DeleteAction.java | 2 +- .../compositeeditor/EditBitFieldAction.java | 2 +- .../core/compositeeditor/EditorModel.java | 6 +- .../InsertUndefinedAction.java | 4 +- .../compositeeditor/StructureEditorModel.java | 81 +- .../compositeeditor/UnionEditorModel.java | 13 +- .../core/datamgr/DataTypeSynchronizer.java | 1 - .../core/datamgr/DataTypesProvider.java | 5 +- ...peAction.java => Pack1DataTypeAction.java} | 88 +- ...ction.java => PackAllDataTypesAction.java} | 40 +- .../datamgr/actions/PackDataTypeAction.java | 86 +- .../actions/PackSizeDataTypeAction.java | 17 +- .../datamgr/editor/DataTypeEditorManager.java | 8 +- .../datapreview/DataTypePreviewPlugin.java | 54 +- .../core/stackeditor/BiDirectionDataType.java | 248 ++---- .../core/stackeditor/StackFrameDataType.java | 8 +- .../next/DWARFDataTypeConflictHandler.java | 3 +- .../datatype/microsoft/MSDataTypeUtils.java | 6 +- .../CompositeDataTypeHTMLRepresentation.java | 40 +- .../TypeDefDataTypeHTMLRepresentation.java | 4 +- .../util/viewer/field/BytesFieldFactory.java | 2 +- .../ghidra/app/util/xml/DataTypesXmlMgr.java | 6 +- .../javacc/ghidra/app/util/cparser/C/C.jj | 18 +- .../merge/datatypes/DataTypeMerge3Test.java | 4 +- .../merge/datatypes/DataTypeMerge4Test.java | 32 +- .../merge/datatypes/DataTypeMerge6Test.java | 353 +++----- .../datatypes/DataTypeMergeFixupTest.java | 68 +- .../codebrowser/CodeBrowserOptionsTest.java | 6 +- .../compositeeditor/AbstractEditorTest.java | 39 +- ...tractStructureEditorLockedActionsTest.java | 2 +- .../AbstractStructureEditorTest.java | 3 +- .../StructureEditorAlignmentTest.java | 836 ++++++++--------- .../StructureEditorFlexAlignmentTest.java | 76 +- .../StructureEditorLockedActions2Test.java | 10 +- .../StructureEditorLockedActions3Test.java | 12 +- .../StructureEditorLockedActions4Test.java | 16 +- .../StructureEditorLockedCellEditTest.java | 2 +- .../StructureEditorUnlockedActions1Test.java | 4 +- .../StructureEditorUnlockedActions2Test.java | 4 +- .../StructureEditorUnlockedActions3Test.java | 4 +- .../StructureEditorUnlockedActions4Test.java | 7 +- .../StructureEditorUnlockedActions5Test.java | 5 +- .../StructureEditorUnlockedActions6Test.java | 7 +- ...StructureEditorUnlockedEnablementTest.java | 7 +- .../UnionEditorActions1Test.java | 26 +- .../UnionEditorActions2Test.java | 8 +- .../UnionEditorActions3Test.java | 8 +- .../UnionEditorActions4Test.java | 8 +- .../UnionEditorAlignmentTest.java | 103 ++- .../compositeeditor/UnionEditorDnDTest.java | 4 +- .../UnionEditorNotifiedTest.java | 26 +- .../ZeroSizeStructureTest.java | 137 ++- .../compositeeditor/ZeroSizeUnionTest.java | 133 +-- .../core/datamgr/editor/EnumEditor1Test.java | 16 +- .../DataTypePreviewPluginTest.java | 2 +- .../PositiveStackEditorEnablementTest.java | 185 ++-- .../PositiveStackEditorProviderTest.java | 7 +- .../stackeditor/StackEditorActions1Test.java | 13 +- .../stackeditor/StackEditorActions2Test.java | 21 +- .../stackeditor/StackEditorActions4Test.java | 5 +- .../stackeditor/StackEditorCellEditTest.java | 4 - .../core/stackeditor/StackEditorDnDTest.java | 4 - .../StackEditorEnablementTest.java | 184 ++-- .../parser/FunctionSignatureParserTest.java | 4 +- .../data/BitFieldListingDisplayTest.java | 2 +- .../database/data/ConflictHandlerTest.java | 118 +-- .../program/model/data/DataTypeTest.java | 2 +- .../ghidra/util/data/DataTypeParserTest.java | 2 +- .../database/data/StructureDataTypeTest.java | 284 +++++- .../database/data/UnionDataTypeTest.java | 48 +- .../AbstractCompositeImplBitFieldTest.java | 4 +- .../data/MSVCStructureImplBitFieldTest.java | 94 +- .../model/data/MSVCUnionImplBitFieldTest.java | 10 +- .../StructureImplBigEndianBitFieldTest.java | 94 +- ...StructureImplLittleEndianBitFieldTest.java | 94 +- .../data/UnionImplBigEndianBitFieldTest.java | 10 +- .../UnionImplLittleEndianBitFieldTest.java | 10 +- .../actions/FillOutStructureCmd.java | 18 +- .../actions/RenameStructureFieldTask.java | 12 +- .../format/pdb/DefaultCompositeMember.java | 41 +- .../app/util/bin/format/pdb/PdbUtil.java | 26 - .../bin/format/pdb/CompositeMemberTest.java | 84 +- .../pdbapplicator/CppCompositeTypeTest.java | 606 ++++++------- .../src/main/java/generic/test/TestUtils.java | 1 + .../program/database/DataTypeArchiveDB.java | 12 +- .../ghidra/program/database/ProgramDB.java | 10 +- .../ghidra/program/database/data/ArrayDB.java | 26 +- .../program/database/data/CompositeDB.java | 481 ++++------ .../database/data/CompositeDBAdapter.java | 77 +- .../database/data/CompositeDBAdapterV0.java | 14 +- .../database/data/CompositeDBAdapterV1.java | 31 +- ...rV2V3.java => CompositeDBAdapterV2V4.java} | 139 +-- .../database/data/CompositeDBAdapterV5.java | 176 ++++ .../database/data/DataTypeComponentDB.java | 15 +- .../program/database/data/DataTypeDB.java | 75 +- .../database/data/DataTypeManagerDB.java | 103 ++- .../ghidra/program/database/data/EnumDB.java | 20 +- .../database/data/FunctionDefinitionDB.java | 20 +- .../database/data/ParameterDefinitionDB.java | 6 +- .../database/data/ProgramDataTypeManager.java | 16 +- .../database/data/ProjectDataTypeManager.java | 15 +- .../database/data/SettingsDBManager.java | 30 +- .../program/database/data/StructureDB.java | 837 +++++++++--------- .../program/database/data/TypedefDB.java | 32 +- .../ghidra/program/database/data/UnionDB.java | 355 +++++--- .../program/model/data/AbstractDataType.java | 10 + .../model/data/AbstractFloatDataType.java | 8 +- .../model/data/AlignedComponentPacker.java | 53 +- .../model/data/AlignedStructureInspector.java | 6 +- .../model/data/AlignedStructurePacker.java | 35 +- .../program/model/data/AlignmentType.java | 41 + .../program/model/data/ArrayDataType.java | 14 +- .../ghidra/program/model/data/Composite.java | 386 ++++---- .../model/data/CompositeAlignmentHelper.java | 56 +- .../model/data/CompositeDataTypeImpl.java | 404 +++++---- .../program/model/data/CompositeInternal.java | 154 ++++ .../program/model/data/DataOrganization.java | 35 +- .../model/data/DataOrganizationImpl.java | 210 +---- .../ghidra/program/model/data/DataType.java | 72 +- .../model/data/DataTypeComponentImpl.java | 6 +- .../program/model/data/DataTypeImpl.java | 92 +- .../program/model/data/DataTypeManager.java | 4 +- .../program/model/data/EnumDataType.java | 29 +- .../program/model/data/GenericDataType.java | 5 +- .../program/model/data/PackingType.java | 42 + .../model/data/ReadOnlyDataTypeComponent.java | 2 +- .../ghidra/program/model/data/Structure.java | 145 ++- .../program/model/data/StructureDataType.java | 577 ++++++------ .../program/model/data/StructureInternal.java | 20 + .../program/model/data/TypedefDataType.java | 12 + .../java/ghidra/program/model/data/Union.java | 9 +- .../program/model/data/UnionDataType.java | 228 +++-- .../program/model/data/UnionInternal.java | 20 + .../database/data/StructureDBTest.java | 210 ++++- .../program/database/data/UnionDBTest.java | 62 +- .../model/data/TestDoubleDataType.java | 10 + .../model/data/TestDoubleDataTypeManager.java | 2 +- .../model/data/TestDummyDataTypeManager.java | 2 +- Ghidra/Framework/Utility/hs_err_pid16061.log | 33 + .../core/analysis/Pic24DInitAnalyzer.java | 2 +- .../DataTypeEditorsScreenShots.java | 52 +- 166 files changed, 6316 insertions(+), 5486 deletions(-) delete mode 100644 Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorAligned.png create mode 100644 Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorPacked.png delete mode 100644 Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorAligned.png create mode 100644 Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/{AlignDataTypeAction.java => Pack1DataTypeAction.java} (51%) rename Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/{AlignAllDataTypesAction.java => PackAllDataTypesAction.java} (72%) rename Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/{CompositeDBAdapterV2V3.java => CompositeDBAdapterV2V4.java} (54%) create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignmentType.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeInternal.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PackingType.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureInternal.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionInternal.java create mode 100644 Ghidra/Framework/Utility/hs_err_pid16061.log diff --git a/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html b/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html index 21f8caaf81..0bd9779ae0 100644 --- a/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html +++ b/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html @@ -344,7 +344,7 @@ -

Note:While packing of bitfields within aligned +

Note:While packing of bitfields within packed structures is controlled by the compiler specification (e.g., data organization), bit-packing order is currently fixed based upon endianess. Little-endian packs starting with bit-0 (lsb) while big-endian packs starting with bit-7 (msb).

@@ -401,9 +414,10 @@ Insert Undefined Byte
-

Undefined Bytes can only be inserted into a structure that is not aligned. A single - Undefined byte is inserted before the current selection by clicking the Insert Undefined Byte button.

+

Within a non-packed structure undefined components are inserted before the current selection by clicking the Insert Undefined Byte button. Within a packed structure an undefined1 datatype component + is inserted in a similar fashion, although in packed structures it is more appropriate to use a properly sized datatype (e.g., modify datatype + on inserted component).

Move @@ -442,9 +456,9 @@
  • A single copy of the component is created immediately following the selected one.
  • -

    If the structure is not aligned, then there must be enough Undefined bytes following the - component to accommodate the duplicate. In an unaligned structure, Undefined bytes get - replaced by the new copy of the component.

    +

    If the structure is not packed (i.e., pack disabled), then there must be enough undefined components following the + component to accommodate the duplicate. In such a such a structure, undefined bytes get + consumed by the new copy of the component.

    button.
  • The number of duplicates - dialog appears. In an unaligned structure, you can only indicate as many duplicates as will - fit in place of Undefined bytes.
  • + dialog appears. In a non-packed structure, you can only indicate as many duplicates as will + fit in place of undefined bytes. @@ -502,10 +516,10 @@ Clear Component(s)

    -

    Clearing a component changes it into Undefined byte components that take up the same - amount of space as the component being cleared. Components can be cleared in unaligned - structures, but not in unions or aligned structures. This is because the resulting Undefined - bytes are not valid components in a union or aligned structure.

    +

    Clearing a component changes it into an undefined components that take up the same + amount of space as the component being cleared. Components can only be cleared in non-packed + structures (i.e., pack disabled), but not in unions or packed structures. This is because the resulting undefined + components are not valid in a union or packed structure.

    To clear components in a structure:

    @@ -514,7 +528,7 @@
  • Press the Clear Component(s) button.
  • -
  • Each selected component is replaced by Undefined Bytes. The number of Undefined Bytes +
  • Each selected component is replaced by undefined components. The number of undefined components will be equal to the length of the component being cleared.
  • @@ -542,8 +556,8 @@
      -
    1. Specify the number of elements. For an unaligned structure, the maximum size of the - array is limited by the number of Undefined bytes following the selected component.
    2. +
    3. Specify the number of elements. For a non-packed structure (i.e., pack disabled), the maximum size of the + array is limited by the number of undefined components following the selected component.
    4. Press the OK button.
    5. @@ -568,7 +582,7 @@ the data type of the array. The array's dimension is determined by how many of that data type will fit in the space that was occupied by the selection. The size of the array will be the array that can fit in the selected number of bytes. Any left over (unused) bytes at - the end of the selection will become Undefined bytes. + the end of the selection will become undefined components.
    @@ -621,12 +635,12 @@

    -

    Bitfield Actions (unaligned structures)

    +

    Bitfield Actions (Non-Packed Structures)

    -

    The following bitfield actions are available when modifying unaligned structures only and are available +

    The following bitfield actions are available when modifying non-packed structures only (i.e., pack disabled) and are available via the popup menu based upon a selected component or table row. When working with unions and - aligned structures, bitfields may only be specified via datatype text entry specification within the + packed structures, bitfields may only be specified via datatype text entry specification within the table view (e.g., char:1).

    Add Bitfield

    @@ -671,10 +685,10 @@

    Name - The field name associated with this component in the structure or union. When specified, the field names must be unique for the components in a structure or - union. This field is editable, except on components that are Undefined bytes.

    + union. This field is editable, except on undefined components.

    Comment - A comment associated with this component. This field is editable, - except on components that are Undefined bytes.

    + except on undefined components.

    To rearrange the order of the component @@ -687,8 +701,7 @@

    Editing Component Fields

    The DataType, Name and Comment fields are editable. However, the - Name and Comment are not editable when the component's DataType is an - Undefined byte.

    + Name and Comment are not editable on undefined components.

    To place an editable field into edit mode:

    @@ -792,28 +805,29 @@

    Effect of Changing a Component's Size

    -

    An unaligned union's size will always be the size of its largest component. If you +

    A non-packed union's size will always be the size of its largest component. If you change a data type for a component and the component size changes, the union size will - change if necessary. An aligned union is padded to make its size a multiple of the union's + change if necessary. A packed union is padded to make its size a multiple of the union's alignment.

    How a structure is affected by changing a component's data type depends on whether the - structure size is aligned or unaligned.

    + structure size is packed or non-packed.

    -

    Unaligned - If the structure is unaligned, then the new component must - be less than or equal to the original component's size plus any Undefined byte components - that immediately follow it in the structure. In an unaligned structure, decreasing the - component size will create Undefined byte components following it to maintain the - structure size. Increasing the component size replaces Undefined bytes immediately - following the component. The last component of a structure can be always be changed which +

    Non-Packed - If the structure has pack disabled, then the new component must + be less than or equal to the original component's size plus any undefined components + that immediately follow it in the structure. Decreasing the + component size will create undefined components following it to maintain the + structure size and placement of other components. Increasing the component size replaces + undefined components immediately + following the component. The last component of a structure can always be changed which can cause the structure to grow larger.

    -

    Aligned - If the structure is aligned, the component can change size, - which affects the structure's overall size and the alignment of individual components - that follow it.

    +

    Packed - If the structure is packed, a component can change size or alignment, + which can affect the placement of subsequent components within a structure and/or the + overall size and alignment of both structures and unions.

    @@ -859,7 +873,7 @@
    -

    Union or Aligned Structure
    +

    Union or Packed Structure

    The first data type in a cycle group can be added to the end of a structure as a new @@ -897,13 +911,13 @@ cycle group. The data type can then be cycled to any other data type in the group.

    -

    Unaligned Structure

    +

    Non-Packed Structure

    -

    Cycling is implemented similar to how it is implemented in an aligned structure. The +

    Cycling is implemented similar to how it is implemented in a packed structure. The only exception is that the user can only cycle to data types that will fit within the data - boundary of the current component. If Undefined bytes follow the selected component, the - Undefined bytes can be replaced by cycling the data type to a larger sized data type. - Likewise, cycling to a smaller data type will add Undefined bytes after the component being + boundary of the current component. If undefined components follow the selected component, the + component can be replaced by cycling the data type to a larger sized data type. + Likewise, cycling to a smaller data type will add undefined components after the component being cycled. However, the last component is not restricted to a particular size.

    @@ -957,8 +971,8 @@

    Adding a Data Type

    -

    A data type can be added as a component by replacing Undefined bytes. When editing an - unaligned structure, there must be enough Undefined bytes for the data type to replace. A +

    A data type can be added as a component by replacing undefined components. When editing a + non-packed structure, there must be enough undefined components for the new data type. A data type can also be added to the end of a structure or union.

    @@ -997,7 +1011,7 @@

    Inserting a Data Type

    -

    In an aligned structure or a union, a data type can be inserted as a new component.

    +

    In a packed structure or a union, a data type can be inserted as a new component.

    Drag and Drop

    @@ -1025,9 +1039,9 @@

    Replacing a Data Type

    -

    A component can have its data type replaced with a different data type. If an unaligned - structure is being edited then Undefined bytes are created or consumed as necessary to - maintain the position of other components within the structure. For unions and aligned +

    A component can have its data type replaced with a different data type. If a non-packed + structure is being edited then undefined components are created or consumed as necessary to + maintain the position of other components within the structure. For unions and packed structures, the data type simply changes for the component and the overall size is adjusted accordingly.

    @@ -1041,22 +1055,22 @@ component in the editor. If the mouse pointer is a 'circle with a slash' then the data type cannot be dropped to replace the component. This is probably because the data type being dropped - won't fit in the structure in place of the original component. If editing a union or an - aligned structure the data type should always fit and the drop is allowed. If editing an - unaligned structure, the component is replaced only if the new component will fit. (see + won't fit in the structure in place of the original component. If editing a union or + packed structure the data type will always fit and the drop is allowed (provided the datatype is allowed). If editing a + non-packed structure, the component is replaced only if the new component will fit. (see Effect of Changing a Component's Size)

    Contiguous Selection of Multiple Components

    Drag a data type to a block of selected components. - Whether the structure is aligned or unaligned doesn't matter when dropping a data type on + Whether the structure is packed or non-packed doesn't matter when dropping a data type on a block of selected components. This is because the new component (s) will occupy the same space as the currently selected components.
    In a union, all selected components will be replaced with a single component containing the data type dropped.
    In a structure, as many components of the dropped data type as will fit in the - selection are created to replace the selection. In an unaligned structure any left over - bytes in the selection will become Undefined bytes.

    + selection are created to replace the selection. In a non-packed structure any left over + bytes in the selection will become undefined components.

    For information about drag and drop with pointers, see Drag and Drop Basics.

    @@ -1085,9 +1099,9 @@ In a union, all selected components will be replaced with a single component containing the favorite data type.
    In a structure
    , only favorites that will fit in place of the selection will be - enabled. Just like with drag and drop, whether the structure is aligned or unaligned + enabled. Just like with drag and drop, whether the structure is packed or non-packed doesn't matter. The selection becomes as many of the data type as will fit and left over - bytes become Undefined bytes for an unaligned structure.

    + bytes become undefined components for a non-packed structure.

    For information about Pointers as a Favorite, see Favorites Basics.

    @@ -1248,14 +1262,14 @@

    -

    Bitfield Editor (unaligned structures only)

    +

    Bitfield Editor (Non-Packed Structures only)

    The Bitfield Editor is used by the Structure Editor when adding or modifying bitfield components - within unaligned structures to facilitate precise placement at the bit level. The Bitfield Editor - is not supported for unions and aligned structures since automated packing is performed (i.e., + within non-packed structures to facilitate precise placement at the bit level. The Bitfield Editor + is not supported for unions and packed structures since automated packing is performed (i.e., bitfields are specified via datatype text entry within the structure/union table view). - While editing an unaligned structure, the Structure Editor popup menu actions Add Bitfield + While editing a non-packed structure, the Structure Editor popup menu actions Add Bitfield and Edit Bitfield are used to launch the Bitfield Editor.

    The Bitfield Editor includes a visual depiction of the storage allocation bytes and associated diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditor.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditor.png index 5b5902d8978c2091b3f17ee01a0327473a9a719d..194fbb1ffd3e0260c1fec02f41904aba13d2b6db 100644 GIT binary patch literal 47126 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 m^@cDiA{H+0b6RJp)j!Hn0-U-c6LI>%cD82Vy zrAzO<-NRGf_kMSbZ~Q-JgaJv;Is2@&_gr($wf0v0ss{Pf4l_&BOmL3`mM{gnspI8kgR>o}Ig{Ypoz{k{KsN~{%BMFkS^nDwG+|FY9Bbz{Q%MF zapK@_cyT5t@Z0MinaJjV0GSYrAR+xYBv673GTkj9C$RcsNcsKYG$XC;t3`o8kpl1J zK;j)2S}A&+TCS$>cdPk2GXm>n_X5Sw1H$9p!HAd>4o!Z_Fh^`|c%D>IzT0kLjC(ID zl9n*a``u-)KhE(@saKx@=d?ffjNnfD#mzVeHS>UivHbFDxUbzRL`Rdw{y z^T4b;4y;MXJ@bL(yYD)9Gs^czgRYqm1*XZz-#gz)3?j&_gK49Cy+qI+&vGA|NNZ^$=AAEap&M3i7*O!_{JJw+%Y1u;!np-J7$hvkfW2_DCV;UYV5 zqAoFEk>BNuGtTEn&!1ky@CPlTAIN^#Y6!W?SsjkK|4i~mktLNvmKtM`wW!c{L~MW6 z>2|c2x-JB3HTGe&m?k!Se{FWDTVMM{Qy^p2ac0+{mwdeJhqdAlLQV;HHjh^r?U$fj zj5bwDwXrtxezQ5HvVTT-+vfx-9N4Du7tf~GO4I7MI)z)D4<<5fs}6LDHt>3|gZ0Fw ztzU#KN1Jsh+uc7(wNmsEn_EqN$lgLzqH33pTYn~m6U5wW+e!Y6@h2#VL9lvw&8uf8 zP26vPuBMRCKJ5Db((c+#a*pXf1^0!UBvi_^-PkP>n*G;5O%qd14mX{zK9vlA{@zF= zS+axtp{NkAVI%vkhr%V^wzD%-;vV|SOz_qlJpSyTmV` zfv7&&XtA@b@b+k>+V&W^YbG6jru(bU

    tqznZR(->W$^(EM5DoDn1L>HPE4jV*R< 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 diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorAligned.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorAligned.png deleted file mode 100644 index 8c445232049798e7e78da067216f8d01271cb338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52104 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%0DL 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

    uZu_+#LKdyK1G??N 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 diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorPacked.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorPacked.png new file mode 100644 index 0000000000000000000000000000000000000000..ddaf0e678554148e0b1d027a9ced2ac0778ec378 GIT binary patch literal 45444 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 literal 0 HcmV?d00001 diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png index 8bed9e4d6e49acd3e3fe258dc7971c70b9bf8a51..3ab2ea0a878639c2aedee858eafd99220d886363 100644 GIT binary patch literal 52870 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%0A{&b8XHx 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%0A5g5X2s>MS+dOZZ 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

    em~?e;FT*THu&ilAA8BBGzlUDxW$mr;3; z_@Z$|#+Qvlkm;4?&BFx$fvr~ZssX)wSowAs@E(? zg{?j2W{C-Lk6ZMw5f~Zx>gTc$*^zG=(tx2Tk;8>*31t(GrM)L?(DK6J&x$6w5880z zBJ~}uj zOP_$?LpFzeFVz_Lq$68t7%uN!J-1;Kbrn|M`LYNp;A|coC&q=+cBL3|_23@nYs|{c zvK0;7StszuSZU6;!s0Wl{iM^uQ$v2Aubn%d5|NqUTunr>KBRB zvL}e3NnaWTyIvOZS8XKsE{T`NbQ!U5G|0=G$SEN9xi>1=o;4#I5NWvyMkm8gg{Ayo z{&0p7NjhRhm9P^xH&nXO%kwJNiNrsLm&o`J7J~U8Pw&W`Y+)nCl>9p^@1%prY4ThR zC8ewQPvh+l1_kt(7eDLaFt&`C6j35|j#E#n5c%yo=E5zE@~q>fiaT4D3-b$SlmFYF z}MyNnQyV_kczpYdCjF@b#Jz9o<6;o%bFI)4sfLID0v=$#{+v_GO`$7b+<(mPrgr$ zny*NS(#|HJTQL}ckcN?MBuq?B4_29*M>{t_<*BlMyoP-#ED#(< z-8NdORX^J8eP-;!r>oaDFW&!M2#OA_lQletauo6ZRy6*|g?ZVgv4EK&i_rRT_kKs> zAQ+DJZ`Mk()F!D#XS4ip(gB|PlHv;v@%Jvs#aa9}FHd;suqukhJP1&NI2h@ew<7Z~ zxwmlSj%_dufXn*`%tm-fuW8-57}#-lAYoL+^a&uLUX-VF@_k(dCO%B)yPL95u1-;B9C7p5 zI+@3(<;1$9K+?Vzcc?}A=dq1&p(+cL9P->JSu!qLxSc(Mb1dZpEOy*AxIx^@t=}3(&Jh&W<4{C*4^N2C3*pXlP-LSoHZ@u-7t0bFl8@T;YrX z)Z9}|iOJH~&2_*)G!?FwJ<~X+VEb&`7`>TEZuh;!Y$NWUXI38`YGz)ulW6pzvuW1! zyzOu;VXWknW4dUl=6;*TkcNi$7KVWl83W0~z5IT?$u6#9%Gu$KQaIi`SFuK?D@bUM zfzbYIylQcZ*_9SyLjT(v5({~$Xp*||C>|RW(brqJl&r%NQD{qK32NW?Vg8;V-hso| z*b?$F_QH9tUcpM_)So+k^5+e#_W-i9b>l>#e_79-pYy?Wq2rKEPu1J3zqiydnNXq{ zo*Ym>w9M#{#w9N;i71`Ld5xHce356dwe4_J_n4FpfxFBP47oa|IHvZ1kXCCEB_a6d zfGOL0)Sfh~>G=Pcj|q=$Ioi(c)T6)Mb*9_pxmK^FEDI8+hJUmOQ>kPEDI=$Um2uZ#(i-p3dJ~Sxb3gfdP7? zoD+GqBFaDS>$WL9k4to0_WTuB|4M|3eSw2Z!x@Y$5wKY{)7keeu3lD!KQp=WP%%^P zh}5x>#C7DU*;IzXoz3FO&$KpYo|#_s_q_c(x3#yF@CL>!A?jy_ZE9IAjney>5gb8( z`p5|vY2jKeCxb3^dtpem3u=F6+FEns^uI^YkN1h@-;Ws;XuPV^@Lul;pT*B4qi{?^ zY;&Vs+RS^AQTQL_Y0ptpU;jtB&hIz-zfPNf4>nI;ErY(bQJ&|wbVk5#gf+T?GX=E4PAQv_Z57dr|mL;n7Dq#TTtVt=W6;B$Iw zkwWT!q%u$-9)0}(9^rpD3bn4c!MF&?_CR|PJe=fJ^!K#&PTA#uJUIoPK2O4Z$XUjv zP#FoUEqV`y)0O~MvHawU0|ht%*xq<{4ki5DGUi$AP$}-;pFl|d3|ZfJi_FGaa0%|5 z=hCg)VEz#i+GC1eFb}B56NZm}beFEi>1rOYQ#6YrA-UdGE{NbOjZ**F%q3$`C6Y0H`gd zz~bd8zAyPRKwICzDd?4z8}vA{0~FvkNO_)<2CJ))I!eEuO-?tr5jp74*>_~9*CAD` zoN;YA2QZE1#N-p>p;zE$MHXzIo$V`rJYoUoaHI;W4|o`*a61|*S{sE+){9^dd!(A5 z*Z6k7OO?#T@HZ=R34g_m-0fO=#S(xqn)sl%>J$vjQ+$NPKPOe9zdqW>HXd%^sDzk8 zZAkjL{1_evpYr)ecmSKis)1H}JqZ`q@se_=4=42wu8oS?0S0tS$fD`-g68~~IBfq1 z|DnHJJPynj&tTyyqa<0RWG)IG;G#D_&=oiGmtP>+cAP`IL9Nw$@bafgaC||^%sF@g z2pm&D%5f2>Udv~Zyz`m}#av1m;Aka?!-3R7uNfyM(7t-<%d_&%s}jwXP}M_xTo!k? zLUpFU66kzWNO>1*U0nMXQYK~iq#M_@3|+dlo?KG#dA%~86(Jdjvx=8cJ6$JT?{0t? za!h(aS41y{JHVxJ1xzBx*m&xYsv{1(ZKisv*Na|CdzE=WvZ2?VwSIjWfqW2GD=S0x zR{K)d0pOuwp5fX;>Z!U2A5f-(D(TE|qW2=s{TYDscxMq4P;ip=DpLlUmpK7nyw3w^=j z@|C3Qg1{g*sFUjFG!8GT*jTD2z1;Mz0uZbWK?WwOCLB||#wZ5b?)%IWI-y#o{dAv* zKK7JsO%=vX@$fa~jj7w!P6#1%6^z_i!*;TJ&8yw+R1xj_l5e#H&Zw`w8UcTtLO7sS z%K*^-vQnG;0M_56vE$TSi8=Ma4Xa;SHmVC?Su{KID-t-L0|t|@AIZR}d1R`3!@D|L z>_(OfGtcR?WW8Q3>v_u}P$^fh6PnM~vr2E!+g~>*_e!aa3n*@MI}spI^7+1%?%Xh5 z=!KPv2Y+Y7g9Qh#mG5cNiCHInO3lu@oqqq_t;nU0B(*kEL(!mpN=q82tcBZH&fML; zZ{~Ph7@Z7oIUA=M`6GzMO{=BYdDe#WY0Att>~t=!Yg>@E<9P@_^zs*phC3ZP`;$pu z+fEEPtixQFAr)$lHQlyaQs%V3P|NGA%fWRXlSL8bU!L-K@3m62?a*;zK-)zi=p+T! z!s1-{j#R)WPf4_fEc8Un~lKkm)8J%yJ~ zaQ9`hsunedUD+HJcLR4;U_|-G%gL>F#ndrJ>3=`e7Sjz}0hdjFp4P0$l_9zn67+SP z6AcGnun|wT8$^irVT25^fT9fqWu;=j^9B(%<}l!0ja^GWRhbX8x}=P${v&SQai>LW zZ2sEQeW|Jg-;~T^FX&^6G&N45to4+-;YJvM?~XmGt%v4BY_cM*T)Yud$EGBS&bE)A zKS!o9We`U@n~o@XO#CAECw%A%MunH1{RB&C*q$|Y&42dD&_8A(c@`0271#4suQRgx zto_)mY9L8=K(UfO=OQX-0-9wg6T%9Atq#`>*GfZdp-jJ(N+~-ze&xq}=QaDVOBz^3 zw*>15o~Q+b)G#V?32NJIUnZ@YAOs;NdvR=r`74p1i+he-8hMI3RmhN*IM8P7zK@EA z`72Vqh>y|It&ATmbme}LT|W<>shG!HlF{CJ^a*CsJc4IZVtTBs!-;EqtRUL6OHC1) z6C>@p(?6XI5T?9^Lgu&XsE!ag!GI3ze6+)zW^2#ZeBe2)*q?LmSp42upHng0u&zY& z0~j1nuALiy>BppPkk>tbNc_$|v9HgAT?xrQ5UO=ZDzg?x+mqDYMe@+chqAaxelPYt zIb_Y?)Ye!oaUEeM#S)L3tS?dQBDRs$z5k$6-d~lN{Ce#%U`>s*1;}xy|DHsA2}W%_ zw5@|yNdaqXXSc>CAcYv2x7xbqOdB+6=LZGflkg5e8Dt(o^iJilE;ygkTCIm8tQ;M- zCGmR?362o=SK?1u^~bNzpYo9dXTFmJt-&qm_^n>bDdo1~Uy{^5S^Hfg{#x%_)-L5B zkV}$pdlFJ+@2S&B@{Xz)3D*w~jksPKQpl3SBs-!Z7mTv+jB6~MHM#WMnl3H)&5yS+ zZ!g!s0q(3;=WzDooPI{ETlAQd;dimndsm}Sc+aYaPdulWpj5cz_!55QM(SM~cvG;} zY8Pcurz&O0MJ%K|An2`y?k?kb#eW%WbPxwUnWIKZmYipVQHIxftj>Aqq}`bO;dcAJ z2s-;h+EiWoq;GKjWhBD%YBvtk-cAF<42Is{F9c&K_8h+8@QV+o=R8E*On&? z_a-}ZC=2e8S(|wvpskPc@n=}O{K-bc1=uor z0LhXc1+Q-{y7t6JC3z#rXRYR1D1XPoZ#{ubM|iSBPMix~cYR&4VQ~UBHmK@HZG`U^ z>4jqI+v2FOHGKyX0n3G|hhSE}H`us({3W12AOM`cV@q}WADmltbNc>vuDWC01N?%^Hp5*fRmgx72>^gAcJ(M{H z+Gm?Z2Pe|r(yfSGB9h+a*oUHW3kgYMa&tXavB5=K^^4n7whEW&AHN4d?R~l=dVE*W z2;7M?z}DuE1C1Gj?CurN|-(S!+6ka zSa)+B!NS=99$E6W66;saFHu@wT;AN>Y8N1kp9wFwu`FFya2DjE&rqvme!=PqngV6Q z0nyX2OtQv1zG*i%L;Wtd@G|or6*Kx#Y5Zf0fXIDZ%ULfzFz@7b5moHI4IalA&W^xl za?4p-s(2@oXH%@5Z!ouOx5~xDN#RMq6^SLNpDpciPbu3z&e6|k4_%|l$M0TLm+Qe@ z3{__KTR7bwf8pQn^yH4(4B#WP9tqertg%fP{CTu?*_%za_E9=Yk+-*)V3 z)D}Ier3!4uC$O2=Dd{oi_^D}q5=*)3du+dE%`$n)XX_e^-0-X@R$!QrHx-#m%Vy5O zY;!YfoSui*IWcd1FAoZTomNxf6QT@;)2Maf z*toeqpC;2sr$r3Vmu6+&6L4QkYX}7)mZb}>(I;OhRxAu z>0ZB9{F~~dD1M*e6+tW;DGz^bl*ihpl^GFG?rNj#Gtn|GmbO? zOp>7a7{E_s@Yzvb=Uy&kxMh}R9TD-A6y!(YLpYgS$8XpkM#hSqCVWii?G&6#793X=;X2)`*6Yv+1BCCXS#su|e^UxBuF()J2V1U7 znRVa9OYSV@K0YL8)-9-Y)I+M`wcFh2LQDy5ep}6Y7uL+-cp}A&Ufej-FS|<9X8_`j zrkt6Si7M&)T1Km8P@mL1zct6Ncb&1{70@_XQsr+`UuG69?Hp=o74wW+`!l;;TKn4$ zD@h2 z3u@m;3jfq6sCzh!hBCUU-K|jzNo`y8` zs^0bMt2MZ~*_dhLD?WV^Gz}A*ixN)lt|8^P=ZG}VMbt-cF7N^+jtFGNWvvWOz(f5$ z44z(e@vVz*)wgi>Hz4QIIXn=XXr~#Ssb9}9%2Jst^baTZ)JF>;J7hRHNVyf|_3a3X zKNy5$`+oCsVmn*U-Hbldka2e=^JrG{QOd^QBkap}3Zv7^3waYwClB{TI83T_)Tf|f z4(R>>PNuS{yOUy=b))&N%*$WQ8MddgsYB)}C|H_brE@;@v| zzYGex{Kl?a<3W^F+nX5Zb7s*JBbLz>V*RFs>_F zg@Lm9uLG5Aj60vCqUf2&y(^Q|US%Vr;gW`S)tkY%x%8w!L1My@73fei`kTLXi zy1xt++!zqJnKeOUxEw_euR<}%5Y=;YT?7Ea5V9YT>@2o7?tFFenZFs=K_njZM@aJ< z{x{>EwC%n2$#uFNnt?CkHzJ)9dVyhQQU)fYsY)Ry7sybwX$Jc2&7k&X4sgQ6yp(iv z#FI~5i8P6T{L&J0XnCr9IvR+!U033zrH0QnyhSE?F(pX3XLBw6h7)V=U)itlF}-7F zELBr0mtouT4>z$ee$^v)AdTGF86>q3iH1JhUO9r>sduEQygbkpFKGT0!y@f@XPm5G zY=Ff8XBzkcu^=A!yPz7Bh^>zm4ro|qqPdxdPjsL#!wQf|_vSQ2nEf_?14;f%Yy zAr%&(V-ZF#O4`Vuxnr!X|abSZ%5KG`JY?s7~I zHZ}@)v9U%aw-WoqeOO0K{=Ga7euM=@^$TOmYx!=;3cF-v;;dtsSn`IA>6j0GRNocQlWS}bf;`LW!^dV?!(!B^NMMTEf+PZvm5Jt|s_|@sSBQZk)&j!zyl+bpk zEkSnl9u@m*#2b4Qc)_CJ@orw{STq8jB_;@!$$!BhI}nq6mX*00?t&I@ceGfZ9g%%$ z2CXJ75FF2`=oSDVh>>)^J(fx1k|*%ly`4UOiRbYtE1N0SZmD*0(Mg-nYAx4v8=^7QDe;ELTrKU*wKS)bMY8r z-{_dK3g#g4CJC$%$^X6zs9Je`IO-P(Pumj%j5U!cY)`Ez5 z@*bXa@CMY+0=kLs#2h!SNpwJB9ufxPKwOZZlC4+NU<(ZcEkNv>(P3oBD0D~+lm^XU z_C76I|LWLRB)uCnuvr>wh{y7;O^&+9WrmDXvrrc4x+B(CTNH33ofA+1A4CBvq!=dR zQ+Wbr#U-{{E~91mvAAVli#-3*m<$nD(A*jkP2fjf=e*c?D}(jFrsZ?n5-0(%9j+h& z?%Seq+>$b)+>H3|8n|?89-b^ppdjckEHmwE_y=?iT-aiEg1fQF!ZQY~9z(DE8X@xB{!gd{~mO0Hc1pilI6TFEn!2=4$cK8umaMF1-Sv7Cs9u8!P|Ys;-Z zB8WOF!??Pt5B>>;F3Sz++t>%j^^;{Q8ASud`ll*r*d7oHy@AZo1oRW6>A`il_UiU0 zDbjkZQQrl*FjJHEDnvf^D-x8pB@iyZ9-0Ah3|pbiJT6d*b(3D|iYk;#fB;_)%1ep2 z6|l_P=1D!RNW;!ls4$E|!c{c34t}vkW0lazZDDM1U+kg`<@>c!5LOEz=T-GOITw?$ zR}trx!TWLAXTw8c1AORFpm2DB9;~X2wIdW}fhE6!@4sCh(FFj>lO+#&sNw8*BX1vI|=qo4^&7i^_{@vgIawSU@H#&3Cg zW8HpL?CG%x%F-D6$Cvk`2r_3Z$O@VMD5T@49Q|T!zGsKW6>&?j`}PXMkfiAjiXeIN zf}Z2I#t5NSNukJfz{i__A!lanHz;plT($`FC*3Dq-nXy}413~YhR0ok!R|vFo~L33 zs&n&Ws|~P0$*q@g(k-s6(n_n;`){|b&C%U%nfKN&vNn$cI+=-X*kHR?OSi#7s}Iim zYWXx*IIpfLKR8l%CM)e`yCEyp|ac4b_zl4n3^B^*u;0tF+HU{EO;W)btN(H zz9D5oe@u}}5)#;0OzgK?R@q+0faHsy?f^%r2@J03ny$CCr>h-z0!M9gNs&|fLg!s2G*IbMU3Ht)dUcM68b`A=xckemvjmk6O1r)qrq*8ZVIx}J!>K;kK) zr6^Hab7cf%SH(TK1aCm5{mB-> z6YI{cc#5v@SGIMW<9Ev!h4vhLSps{ZS?P^cQxF-xt*{+C2TxN; zcOW71InTqo5qsnsVb*_Eg+tu>1PXuY?9NzfQ{=wZsL92s`7QJA`JrIBD|}f;MnEv% zn$9T|XPY;M<0b`smY=a{rp}GX4MT@nq8BZjc-NI! z@owoem&eq{bh&ieRvo~{tJ!rvW)B~@&kM8ko-3>fci^gRUtsc1>x;NQdxpBpP$!H= zW}>}N!9ijj0(caZucRUu8uxs4^6XonSD^cKW>25^>)ga3TBJqnM9o3MkBz?myTk{sEiR`m(B` zO}^vdM%-Ezqf$NF#SY7!=2qM_3gP@>+?gSAa7#qBZS-73Bbol>xDdl_YzM0%!+Mn` zwUnjY;!Uq87noOK&y6+j#+3NZ3F1(AK$dZ=bD-CnQ>qKa;~E1RA7s5}|A4~L#pB2n z-9`OAF^u+-8`ZDiWh@1M&D6CU!q!^3nos3?Vo0avF+?0qS8K4a@r}IoBgmbu3F%1U zzvMzX9Wz@jK)HJ^vpGJTIVUBU$vu`22LLN&eIU zOwLwBf?tD&)lHXkC@nu5q}h#~BE1AGd3lLJS_4KoKhgA1 z_r}U{<;pCy5!tIyc5x_o`Wi|3pMW#YONqwP^<%}$tExVvf{fOpweDPRrlz?ba+;=5 zZLCc|vJZ^N12^gADMe0>`M868dW=nr+hu(yGf3bqYstq*G{Hr*86>49#ToP!E1yVZ zJ#K9d<8!2$ppVnaLjuE#0YTLjx8nQLm(^CA4%|H%E8x?I8WV^h9gk?ev}BMXwl6-} zYnjo#r50LsKj=7bSD0VpW4~Vbm^6l3buBR`kKANj_og5*b*?AiSPwag5R7}#NZHJ$ zk7#E~ptKImE)eaB#!)tJ9(CCiq2sD0@fFB9ZnU?il=-Im2bXc=ESHJWHpy1YkH%XG zP#m!F9V;Y1&4dic{meI5jIPn*=OV(qby&Jbua&=uUfHhK*$1e`T4fz%H6Nd18-8lZ)|<0XT_RS{9+VL_E`H806KX z0URqMPM+TScQ{r)7Qd^2eomcNv!<-opATQly3hdgVJo9g{q4)Pw{i zks_$8;+8A<3iZw?}Jt4oc3GUaM> zNefYYTU5l2k5$<6j|mXTFG!XrJh6^q`(9v-rG4Xs5;yG60cqcMDQng1&_?BQrxGQ` zVsKw^Ha4DXjdH*-cjlv|S2Z6|q+=imS=(D?6Q7^q&h|c?(IoHo;vbK1hqbv?xXF4L z1S!a_md3{*BOKQpy<~c~?Oc=`_PSz`Zyj}j^HCmERjX_@7dx@;9r*1Y2D_=d`PrIu|y(TAzbH(~B1SlqNKbx}>!!SQn8j%4)SAgr6|3yBI7r{>67L97aaQ{+IC$JIIr2P6>#@@M&P+MG6gMz(%{o% zQ;swUYPH`pEl+#;`mQ8DR_;6HP*G*5(ecsyCL17LO|3G_m3<{cjW67v!~MsVnJ_&1zn)sIK5(&f1r9ziP$-L1S+evfoU8 z`Fuv+ta9V}HL316WwvtmcVlt~Wppm>XNDuG)B@@WxUut;S4Zk!G*(>zCQNF}jg{eJ+%{g2*@|KC zolu;8PaTjg|x>$*nBp>YaK9)VwTkBy03wy&7Sb?8V1b` zJ<={8{n7=ePgO~6H3~&lSY5BEbo#j=Dxs6H!^uvgxhxm4B*h|5jC^f>(EP@Ga2~J^ z6G8UkT?4KY{*IZ2aocK8)lWMGvDvSxV@+#+^-o0bl``CJajprp&fWcLMUW3IQx0c`(3I!$O$nb;0}fwL1!Q!N zu0B0JDW-gE(7VNaaAjj>z|EF0xmMjo(z9gDS4Wv`$UA@Vm3zmPF{Xg8eRxn8aH~+t1oiREqOv2Y7El`gpz=mjh(DCd@jiC`&a_6OsVFBq% zU^rvO@hbJj_Xs2R3^ocoyW8$9s`@|A^7JdzyOWaN$!;8UK(~qO^$Z$e^c!@qyKDlY zRYgDEZz~!b!ddru5py zEeY>3a#rb)*U>oX`%q2YeAj$dsX;sP`El1r2=g)7GV4EraC_t8sFH(|xQ}Pfx^R1> zbT0*gYn07?TcGx~DrE7@AI|fuqygLUy@+C<1ZM!L7*7 ztynH;1`!pZWVf12-ykpv{GHH&TX=IXj)Hj4f9id&a$W~;v>WD(_Z2*+`GK)e8;Ug z7oVd~e9_(l8bynVVv&H&X^CDjJ;IylAo%bPYg5M=z&iCCcmzx^;pWpj#lI`W``k-L z<}CaVa7VIuwD+)S+Unvo$HO{s%_^3~DsZf>J|MppNfK^ zQ1;h1Jcrj2^H{K5eO)lp7(q3pAySzAbJjP%s2VypilE{kMc~`no}zqk^@w<}E!5C# zF*gpHaNl$_=U<~RGB7K%B`qM1XA+gShUA2N*4IqD&mv%+>ma<;n?b7D{kPfwD>^-* zk&yD-nX*EbJN2iTE)}qC z0G$?VOJgc^M!;Ied)GwkVj5+HNpG{#C5W$C90$Ey*#&u(pBq(}{QkU--0;mW*ogm% zSc}2^^(L%OvzpJzCTd8%1UU|=T>uJwyUR3SCTwV#`V0WQFY}C4N=)8vljy+P3q0C9 zCe2km&wjS&`0_*|;=dLysI%yF-i9kgaPw;3$)ZYBs?>TpijIoj<$rcy6Bo@-tv+-F zb@kDIiX_kMqR((&4j9#Zld|&mxPt%d@ghP;IQN@z;mzVr_D6w#b_IkS{lnj_ef9FB z|GTmqNRYQII9c`Id>l1t^GS{N?neGcY|Xe*%x^ZZi;B!JGV$3g$TIgh+jYtOcdoK4 z;MrFME^^+Cxeyc18=iN26z}i>b%PO4gDj?>rAMR?wflD+Dgg_>{)OHld-gBD7k*va zT;kahzi{Mi<-{LIngCmsRf1+S_H5Y_CTqAWfI`3?%JrGrJiUJAxtEWji~#p=0~KyF zfQq&Vw&DZ@l1S)2NI>LMha1D6K|OOdM#{sr?I@taLZFm71CfY^z}T^m^<3NfZ4gyj z03-|9HIQ(4EMT*H7c7Idngw7eBch6qzrb55S8*EP^gMHex_T2RxX@Vez60EXL+H+o z$y^PsOz*Pz)k^!yYelV5LZ@^@(MD&Hu3v{m?P3Ohl>t>3x_sM~z)ON0$o#6!ZMHRb zb02KJxnjG#+0F=rFoIM=O5q%921ULGJ~9zQkeRa&A|N;5TdnPA*ymguFt|-`cY0p56l1BaE*svIQa%^d49ekS4v zFCRrA5-K!QtC70Xny%}P{5FRHz_DakLEvnERs4H$_MA0Kzz+MignPPqroJ-2N*f6i z8Q8Wv>ubxS03{SX$VcfQ{!tbH6KZYly~BK@-1rlT4&soVC3?=Rie$z=V`k23y1T6d z$Zto?jCuzuJzl~Df0_VD)Ky~^DuA}DFk}`uVE+~XUK|ZcRwH{kf$9A1iQaacsQo}i z!(;RQJ6EO18U^qseeGG?PaJ06Qc=wb&z9QsU{k#*D5;z=Ntj|85vRqce3FmJnz`bL=4y{Fxs7irT zQPX4k;}awVtd*Q^li!%g_EA{`)OLQNuM>qKbg??r(vJeoaNlSeRIR@u1kr^GtI%SW zt-Nc`d*kt!OxY9xYgGYu(!{~7?m==JnjBdb7Z|+HWD9JY_s(ygd*Z=?aDdU>nPG0@ z8EN;;6$4q(SD}d`%Z^hyEDD3p)@(l)#of!v;&Q`Cov3<)GQBWuN2$a*#_(^aM{3rc zf_hq{CE}BiwsUNykLq*|i$7&%u7+vGF+Ql0njQ1i>^Jt!0+ZAL8g#2TiI<3Fse@Xo zCFuwc7v8goM@Q?i*k2V+6MaQCmryhm)GE&~9?Yx@OUl5!)f1SU4 z!c@K!F^ZapR2%ZFeQ&EY{d8p)pmcgOZ4k8hLN`A=_h0-fHw*|fVZ@Jy#lrq6^|KnhSO7HY$E7gvs>9?iV-8c<}~Q=uczQoYEIxF&tBR_+SXg+#R-1F%YqB z6=^;7-Kzqak_!gjaFr7IrlrK_Wfo;&SP$Q*%~|-1K$CtV^Ek>IvK!k2h3;f)ds39u zVZJPvwaHo~tC)RqX8s*&U2QsvDcdgk#;#YK1LBXge#<+R)sfRD0SGKc0Aj)$^~3~) z?yK?A{YHDE(D3@+O)-V@we1o7u=U(jd(s^ldI2gM-rM` zDIDO9QE>OLj6HagxJoD9iqsaGzo*698IR>-+salvE~8nV`GP}uw8VY_F>e7&Yh$C|(;%(~`UM#m1jILI&=eQ|wd zE;gMw4DtM=JSRI!z%yjGU!7h5%o-5wdvQZGenhly9W$9(x@wRzb=%cB-w38+z%|tR zj(6x#+Y+d-3)s3dscKKRF)^#GeBGwU_)^<&5o?Ua>SDh}rkW+5e{>o(io$nqD5j<1vr8m}`RtXrMeQaqBj#d}k?&$Z8`hHxHe)!Hp zyW=aIZ4GEc-;ZiZLBF^j+`e-1W|Y{R>RhLbq?@ZLWyt<|Sa&ETDIJ2ULndnt0(e_A zIb~|pz}@gb=p9wD$H{Fv$1gCgXXunw^X&|T#MV5_k)FA90-%y@u1PBWYfwk+!-O4Jvx?EFGP`eNzV;tm!2)!%d>}5qYd9#c*t*sUi`(=q zXkA9Z+4gk=&UVkjoj@4+KBju94s@g_PYS9f=6!GdxVC-E)q0%d_My+F$oG@7amZ9Gkc-kZn*Z$hCE7`nV(oObm%?pP|KDm7#ZE zzBw)k)j?#+^8pUwmcB@4J8 zbA(2;RY2)ES}R8{3T!$Cru0c=X6@2U3_P{{uy`gMhn{iqqrl0fn8r<0QS1|(j<-9Z zOyoaOlbF{g2u?G(_t8?E8s?1iCb%=O^goEA@ZY{6Kn;%MDPr4uW|s!VYpK(HSE8J$ z6x}U|FT%iVi)1rr9er{t?)6Xw`-LGMw!6#{)uCb3%H}>}f+=K@pUrF?A1WReadl;D0fa{dx z9I9buz%l^m7*M{kXORo&L4#ZO*qT|eH)4XIh8>IUNPfcSHq`p8KttZ_@hDj46pf&M zMK}@Cu2@yMnz6 z?Vj<9SLe1!o=W^JMClTSC$nsg!$61l8tzuQhDVUiAh^Y!#lv)vIpXQVpG|JJY*Foj zRTfUo1OB!*#YWrr|-(VG7w%as=IjoVyV!RqP^>mI_+luD`}#y z-$Z}$KZ^IC`n7leMZv<#CdrxjhI30Ky1#dCgGfor{&__x-BB0(D}McpDmIv)=JJ%u zs0!-R7GRD8w>NzPb6wT%iT*orO<_d+9{j&~Y2oT^=`R{0uWo$$s6MWo?M3mA{7;%S z7o3pinJmDgFLV6le{!jc6IQ$&-y0k7t&eyA5kQ_5*v;IixRRew7Bc-$`ro8{zi#$L zRkkhiu_fWL;x62#ed9g@KgTGqg7dFWvgreLi}<%4cOB*8F&lG%zTf?3A9p8ih;4RU z;Jq-Sq$5pT9h4{N{LKm24?A`TBpN`X=3Q!h-lfLnDUz&p^M!JFbC{WL()J#nSH;pP zXjXp`NGKZ;zqsj-Tg!z^QZU`FhOp>eNpL7j6x{q6?7wjN#p+HCQDKKzgy(0fnI@oP z;a*wP5VKUPS1|a1rD^@Fb=*&W(Y|A*nDO*)Z91}D8|%{>(O^9E?h`1xEWmp5Z3S2s zy{&*_`Y=f&!h2VtUBCDo!bqP7p6nV_7&S+~p9AAK+x4Y-nP@PyAk6Gd(D*3o9}T)O zx6k*engF>F9A$IqQX2*F7V-0fVWknzBLVmMfs*Unia(SVc)TIHZ*Xb~0#+ad zMXuLy{#M2 zsy^~;D{TNgVzv@!?Dh!3j`d$SF)TL>6o@F0c16P7aiw4j!^g$V!LOaSo`3z$MCQak z;=+(HzJ|llG6I1QUfpb?Iff4nd`c4_Lz%x`@bTo!Nbp%TZ_1*nIobx-K~t?^obOr;h7c0@vuIf3!xDgO@9A%tIn$c^CpmFkMWtjoBM=R*l0zZB$3f`kyL z`MDUM12N#FlziisuX55or;$k>O>MY(BH)ac&RSPVw?)Gj@H{qxxkfd=6cx>_TCsi|`#V7~ zTU9%%YG5_t1PnW3+wRS$b%$%L3II5p$zkq21V(M}+~ANJB3&ORl9A(JqOkMo=$q}d zD5!K5u$6KcRe`o}!Tr-;R7=6ptQ}V%D?w;=%?Noxagn44W_y*B#dCJc$u`nx<3BVQ zcLH{**gB~E+PS2ugDbwZ5O5W(_PV`j{kdOC;Zo_XI??svi?WdcdIKY})VX0JZ&qZ( z<|9!RhWhpoy^XQr`$u)NNZtOe10d8*a0AjckMjx9y!3UAWV~Ume4&K-1Cpbc&b-^x zT;Vo%Ww6Ri<=Fwg`((t_YcrAXQuIBKmK)x^|70YRIRFKPYMu9P@R9g4nIOE{jP1Eh z3*66XS{HNc)4#h;68enkxXw!>^|muCak)MkF4F`3{_Ii&yK2xbXpVwd^$j?f@{PiR z7J}liFl6~@h?7$jV1`X#(rBMeNZIgvC9gA4~{^*xg8Y)4~) z+78VgjVCc8A<0PyFkutVCNr5R^s}C5P9A&|zH+P=SD5IKN7aAC)QyDf{C&)*505S) z3L)fX{lInOB&kb9HxaY3PMIu}Gw4rt>0}4dVb1_mQGu?&bj+Arj^@MARhFc*Izs)bv^`nwVq$E z)&teWhe6(JgGJBCgX1^#_B0y9l1YUIxWEB%MleytU6X0^ZN7NH2S}`(v5|Td% z`**nDYW7L=`aJ&t*S$d_DumGW+TTa&Q5wSAh@kB`6+|6SL&}=C0Jj&ju`KCGh5hB| zu+&?L4mg{E!SL!_Ir-t1SP7!w!TazAoPWQ;6H9?U6Qfnr0I~VEVX2KmJWY?{ea`0k z8=Edvc{qfredv=LRzKwY;PFV#gSI8(@j8}uT|b|Y?$$QM*U1U5YZ;ZI9P!U2g8!1h zCi$G%OQlf;W&ypz5C7C5mU3aPchyfsoEe9lqxjx9XwGglN}kg!)-Sr>@3=WJTgYQ07s0E54dTk~RKV6Fxa&V{XUD^xMwf~i|{>t6xKnZsv-yyLuN zsX-cYNJYjEwcYu=I@ssqN8Y%VvV03~UEn<8?)0_N=|MgtTK@O*Cz(P5lf72dVNGp=?j)jQF|6=7bF5 zUM55*sb#MJ>Lu4?g_b)Ot?C1NJ`E0UjQ!9n=X zT#e{QP>whLBcx|E_)V-;Vp>wdxaf-J^-D7J2*Aa6Nd}Z964wUTmdU<1&!LQptivj0 z?IF;)$^g<C5%SPd8377G%&$1qU{^iDW%<@&C z?>1w!&Qu&+LY#V&Hum`v=Z!J0mFUzy3SR86`DxqNYa5$KXiSPAgz83p#fyG*?@|0a zwxyF8kNcMa42SS!%%Fjc34h7wZ3O@-y#zl&3tnyQ5$~q?{F z`r&`d>Fd>g-*m33=<6fzVE5-X1s|&JH46t^uiyD^FK40ShJaaA8Smnrbi1cNuTEOg zodP&ZL;(rl-(o4W~&rrj)i!w)P!DQ&y5Y!lk_{DbslBplbiE&3la& zc0?LpVGn>31}8LI8S@OtGSfPd59?I>e_vKjLjlXVP|MImJ3^l&zv1+&5yCgi+W*{g zA3tWwj;G)MM|N@SnAU4=g)3HnWD<{c-mV-z`ybWAG1Hx-|Mvm^zl?$9pUereHdVI^ zkA5do$gfcvy90#pA<^*XpRgRr5@mk>2LGMi*vgc*qsMZMI-(-dJ4hFz^dh~3fP^L>y@QGbmEJpu^j;Er?5Jwv&-6R%{ABD`TSBvmY9H=00#$$SYGbAIu6cN z7!J-A!y9NIP%Y>G(C(rQt@lZIl0Ywt8|BPMlr zLrKs9+&r7|=6RH1G(O)q_i*U*&kv!XEpj_hW6DBeHy=eu!6l*mVDJ zR!DmDL?^<3mSO1}|AW|eOV52$JkK)y2%9k(`O2UVVw&P|2SEX1r`wiN^fz~VGNtH! zH=KjJ=}C8w$fVLd%v>Uv1H_I;tvuIOc^Jjclp-wChW8xXs^^< z3cEwfkA4x2yRK_#UyUQ`nDY+DNAEkR##YbW+{K8F+}&#M{%~>4^YGe5zNENZz^eL1 z<9*_}ND(z2~)maZ}2Sr7B~1Swl8j)0kup2$I$ zo$oSO>tC~;^jz}Cy2 zO}QK9{rb9kH_LqG#TON$4=&>_Mvsi@IJ8~R^a|DVig#zd@m4cdE1!E|j0=8^F~`jL z#ix%+pFV^^+c51+;3CjKKho+J}7+rqMIe@xZJEuvTN9HzRA~!XI(aUGVHUS zwL@hranh0OS(mMNnbLMyB;6GE9`>`Fh}9z+^1 z6@Vw7F5!#Y?dSv8gIGo=+4FCy|xcrQx_ zWf%SATD0^?jb=)>M;Su#Eu-HUBx*lHp;x)~Qh`!mjz<4UHPsY)I=t^&r}v*Zcu1+A zz(hGE;fsATLh#D!VgCRLjiAyPotQ@%ffRT1OzNl3tfz?sy8XGIHKzThL%@%<7A*w@ z)X+AsH0xbf3#Qh_iw!>Ick=pW?XOR$*5UOzwT*W1YFG5hGpA;8?`3iv+F9{v6iQ7= znA_gzMM|qcD87s%r9bn+BJUPw1i~@`o!XGnsiLJsd@$wY0UZlt2!#_b>?I6Bp%9Z% zeAadM_-D}kRj^|cFASg5kAd4{j{`)Qi5*dG#y4ixlc~}w40O8xk`(;AS)lbk63~hD z#hBC4w&U?)c=J-vJ;|Q3pX(eT+k10c%l#(u({91+Alo>HNvu}O6V3rQ&~)Pp@A2`< ze743)CqL}+B7sowK&Oyxu&X1;DN9RJD^nbNBFYGE(E97A%BR#XOEG6vmO^O>FYG2t zQj@e?dDHIek0j=~35a`P)X%01b+`_iOkdq56?Z>36QGD7E#nv!m^hU7<~!MO=?nfi zUw(%aX6njYY;Qq54;d%@%n$pVk`cHd03t-iWXv|5CCo5z7rTM#{m$YG*hPu#9#~oh z5zpV>Tiz;{CXrxhln3`uSx`;Iaoo%EwWH!LpF$2KLJef_*N#(x`Rqfw{i=CRb@)DD zq9=%9Ad-YG?izMm?o-AXAB@9bAo`dP(=*pN524f-2c7KrufW@0yx;Ym2iIr3=xK!z z%f24pp!?4}Z~_u-y>_fWCYqGeGB8S~;= z%*saxP@%c?0ef+Bg_%{x;e0x?sJq|8=F{DvB7?(h%~FXeM+l~F9sio&qHykfBQy*wS2?BI7Zp@`#(xyTG)Z1os8s<6~As`(-=ff${@V{rAP3c(B5 zBS%EOhI?K`ZaRG-<|5IuTMts@)l&9gt9GTUPz12lwHb>*+Nax%r`YxM3cG0@&y`fF zFg_S}j>gsLbIi2KQvH@+Z2pMblYjzZj?qj@NRss#x?0krJIwsbh@j}Vj9J)CzDPw5 z(8;DhxWHHT(-k6gj~dNb$YWzuu&JS%adRu8j7tC4$hqw(`;#ib?FbDwbo!V;s#Iq4 zc|H3C$8r;H8daUX~S&*g*BRhvhA#pDW`s=y+X}zos{7LnvIA zxxUl=n*QOnj=NlGcM`}5aohi;Gzndyq1L~=NI`_onX8k~+_tpbRwCnz^D{EA75XWa zs^~Iy=vKZFG>yh>@g{Dbx$#3PbvuQAH>EpcC3KWpl)8wXtBB#YmaBGe&3T|L=%;nD z)-zpSW?$8rmfYh2iw;$-Oe2$?F{kc31lFc#fkeGA^!fU(A9;(6>WiZUY@6Ql!y3Ix zBQ4-0`E~B|;?mkBVzfO5G-g&S1G8+TV%!Q;18I%pPXcHQGtd(bsZ%nQHtE-uFe?>a z2e&K3tN~|-B%y{$X%~yoa{HNI?Q#ey(mM(4gVIcODw5@huDd^^eqLw+I0&(I+n_M`=E+$~hiY;d?TnO~mn92xdD$f0MFp@(Qkq)?<|^iX%o>M0^IFs9;TVzeLhK z?j$}7i>}spJTqu(E6afymg%b}2Wr8dU+CVW2U2R=fwe zA?QA&`&xyjgf;Y8e*vdKlPVPu2y}XMWc_2b8SpJlZ=WMGC!8#-EC(m^5jNpz@TB3_Va9eZ-fwhyVjt5^cUO_K(mF@>Nj02Ip)%ae4!Uup}=kAa!VeIEF zV$)khXgHUuI<#f1&_9O=J!&mF7E;0hIvIwO6~7IzOrt9VU!)MB@0pxG*=i6SArQ`+ ze--Dm(;3bPsd8)C0Q!=|rTo(F1uxA1{J6TnTnH8rR1}YTe;90m@VUr6S2`ddt9L?4 zY0>v0n+*IRQGzg&YY3y@xV_?t07$51;iNtn=;UBR@_NYP1GXic zozk8W2n7_uz@M~gV^w)8v(5spGhEW>OvKSwZ7)jA4T&_j3dbX|o!EpIMc~2$^>pZ* z${6=PA(ARVe>fHqEM3J(d`mcBYqFzwDBHh-j?a=QY>PMG+QAcuiVh8tGX3g@qg zL==UUcZ7m!XOS{({F!bVHdW?kRTfprV;BMMV(wDA>8e;&iR0(*QGijl@TdAAa_^PH zRxfv|A2B-J|2#V+e-?1U3-D=stA0xTo?hUGwof*o;KVT#TyW2yhyBR9eq@^!Bc3+R zl<4M-B%~EF$9So}YmJ4KsZ6A$5X@c6F+8IZ-j5V~^He9$?p>GiauB6{na7Tw^@y8w zOGlWd%vpK23>%2B{YJEn5G-6$LjNFj7Ma8Yi-^t$l=7+SdJ0EM9G-f_0uzC&1d%@Q+Qz51>!t+3!X<5$tmT-?89T$w?4a1433dM4jQnlP5e3`9N5 zq_z0K?^*4dN80ceI#4Sxfr1Ytm+8%pYLK=_i9>%IY4qkI4d*A9Hp)Y@~Eef?Bd zxp=zVnQi2`sJ0nTnsI`i%yg9lh$ftBs?obS9-rY>tNcJ882)If$SPVwjGI| z?OzV`BL@65DdE7vfwPF6Dh+7TJJ_afK>UOTy!GWl3T|ej-&-0Bv>_a?R6k-Q!u&;3%HtI z^`=~pdHq)p>M1VVtx{`fZIc7RUQ)&jA^LyO#Bb^~;+bwc5VR9pL^C@?Qt?&8 z>kfi0gh1XKx$mqEm%TJ7&3{AhMq&=7)U$B%nP+e8SgG})#c~~W#J5VRG|5JgBDa-K zJic8aVrwJb4=M87k54OqLAAZ-@RqvTd__BFhfn*PK+4dM*RN7{Q?~7_ldsx^~;}#sP0gMXjDPA5`7QyevF3+x`dn0feMKq!$@RRnNo^aa+{k6^yu7n9-?0Lcu;m1Ge%$)GHoe zN1EpI!stXfG**opb(lskql0{vy*Mj}0Y`-Ux+*CpwDY|=HZV6MRRI1eV|J&qkI=iX z^{c>e$07@UIk1uJt#Xy;bL`L8#?5QL)S{=kK!GeRKm@ChNkqpLf5?WOjWAi(eINGjb&?m+s?gKXx#{77wrq*#E zyBuvRuieDQfeTTq3UAw5rS3Q9(h}WFDF2~B!fig8qm1MirqZ85;DN4MgM#1UiZNZ; zNqwdCwb*;}rf;8=8}$?g$G!YS;e#LoW+HTZxDP#dXrfIHMf}K)h**Rl`zw8O9@q_` z1VOu8ueK`*627?vqMyc)ZIp{OO$Tn7AYdrLll<7%eGnIO0M)s7e;- zLux(n?Q%7SDD@HJb?-%L$mkC=`jIj=e8zSGMFU@D+Io>m&qvOOQH90HE!;GOe1-5= zHjzQh3|~pslGeR5e3Q;}MNlsuTrNNZQK3$)1IYFpfYF~sWhi(16DXpZo6vI)LE;N+ zW9KYyim^U2Y6Z=4$rW3}s_bA&4a99Q$7)0(gs^8@KyV zUzwP6id<*8G)VDZS27_H2jeQyzo?DhGzTM#@^g^lZ?aIX;1<{_QtHn=E0FI0b6;V8 zSZWFx4^h2;NiZ@#O5{FcBj)(SHnJ#PP!A#9R)s%Z{_0~y7UN@(?VP;KpH@_EK{L)4 z!zJYi-l+)g$!&f#e~%M}`|YRh{nh6P2qxadkC6pOMHfupoBEdhR*T~^ep2Q-+$)ctH8}gi=a?~ zZ`*kHaKe=9-Oz2jLBuq172OZN^?>HYQCEX12XI4g^!V z*Q1UW1OeQK(eL)*3(vxj?7B)R;*w|R*hwD6tV&;dx(m;C-D-Y9zk1%U9vJ6C_W2^k;Q%>v)2>2b~o#{6o$UNVDdjd-91_ihvz6KO9OsC zBf!w9mnn+NM51;%$#yhIA>LuSN;%CJqbGbg1=D@QAuW(TzlnD|M%7k zOBK4q=!zRuCC@bJ%q6?Ill)$IW=IDveIVqFX!1Q#IKv)jNg8|n4CJ*LeNxDYtpWEZ z70|2X=*k$wPR!=FQbpYrQbk;|&HEna z`-z@xKv8Mln~?&xn@q=&4GlInR~A(dlyYpgw~}ugZeprVe2o&q2wH zhBxy~vjyt{^q)_jwyfm8?u}yX9#u`p9ydTsozmpYf0|ri<6&Jd$0<+oH$;US6>r6e*}<9yoCA#%vQ8kp>{8bRQwTFzTZaQtFkjT#9du(Nf)%He(B@-Tx<`#eSGN;Eth-n}w+){~y zJx@y$lvrd}+lzZfSp%)$rpHOnuCT=xRijd##TC&HSqwZh`i79VS8=-#xY>&F+VR}3 z)Fv_s;?U9{QizH<_ey{Agu*>GZKWKgM6+yW)il})j$E`?Wg^Yxp2o#`Xq6c1iMVeT zaq`W1trs&|cuk@yWkyQj@mxCa^@&na#dOCmDs7{E9;{X_vr0-Hc;VaZy%Rrk@uJd^ z4sZ7Ft>xkeqZ;kI0#sB~taIbV#AME?a_a+00*d{J7JkOFQ4Ll36AZez4#UH1J)9N6 zvjAAa;ZtPy8&qE`?*KkRI8psCOELFn~;GAtZ1@!Yv2%#Aj|0z5UCu(L-e zH5{Hm%mF?~X?e%9hl5Anbib7)H{}6yx=)3dtHEGeP|JQOjgY>z&*UV$aoioQXnE;S z-bs+);*-QUq5ATF_}SeRhRS z+-*ZwfG_~=lShhNvA6RmUaZ<|Je(`!q+8$x=c{Lrz6mZ_7nEt}didF%_6I>XoU55O zZfh(TY*Lp!sUxhKRsN1@Dv!|IkK>jNIon@{1R*88|=U*5wJHL!q0($c~7 z2@(5g1|j9v6r;SmM%hVR{<0K*#!4d_o6Djd88u15^!8Kfuq zuEv3Xo8DD3D}zcK^0?9+?-Dn~`q)J4QXcaX+hyU@TsbTdR%V<1n;%k&f$3ZD@f}NR z@^PS?HJYDv?^Pdcc-FM842W)N#@FC1Sff!Z>HbYNqQh8w&sCzZ`b7Mhuz*HHewz58 zLvvmBP`8zB=NEDUI)+uv36{a8kmMwLV86M_69|zpK870#RsR&Acq@%m6>cG>8p%_=REwM$0_b}R0fRmagU_O z_SZVwJAChNV7oR5Io+oxoNW}j7D=k81#I#ZjgFW;z4yhg<{F@slLfWHsVhns$H4YD z-Mt80`|j33p8Y>dc=dEA`({~)SZBefJ*Osowf&=&m6kZ;ngu*gYcONX!f$ZQ z-nyEX4=n&jxb}BFswm=n) z@uAd*a_-2ohT=x)KBbToxed(__dE5d+5_BQ9^9~|V`m|ls}H~>RN6rwKaj;G$~rOU z^HRLhcU|9lgBC#>xeB04rI0 zfa$AvGnP~4^|(BQ>7**9$=%GFPgHLFOhISCw_$e5na%m(_<`ILM5Vb9T$Z=#2dKPb9NVqnFhiJmdicvbU5-Er4o?m;7iK{fOb0 z?7q=Y!r&fB0`fT1%r3=faPg?{!nbdouZQ=KOU%ut)419PPEMMia(eDAyBcwgm%p}$ zcQ_arZ_TP2h@TKe!8y5@j|apwN}4l1up5`RvO0T}_Clt;ae|u9UlP<}bTN}C1ODVb#@mDtZ)3wIvqUVkKexUeai@X~oZ099m4lP#rzJS)(}qW`?Prgf^Nv zt$?v|y@5NHdM+ zJJ7V(N}l+~DkbpBlK37(ZwT)fuO+V-dvvmP$G-bgJ;zumxDml2N(xsj9VM`)DQ%Zb ze{!AB_&!KB4BFR#9bWey?m+u?c%MeUwX$+%IjXGXH_wdiDz)@ZH=Q(YdTo)T-~A>i zOTykaG@1o0&Yk?ur9a*zz6lp%u%2jVBW#bN_=}CWvwuTZR4qqn>onO^v9>(5_1c$gZH(n1>g;2wG9aJGU8ts@?#UA6VRRzG4Ot@%L~qL8bx ziCKAOPPU<2@@5c3F#ZGlT~PS>o9yy8epCr4-QRsT_BATTnh)n*a35ojOq_O@;`8UXfgBb+YBbVAYRI6S1&98bUSQO~4x|yt716>{waoa%oO*<)`*ufFb ztg3=3n4z?l{WDWZOON7ki%^yNN+HWu1=j#Z&c^kFx2LYPwUWzjrY-YchXG~0rA_?P z0?o|I4Rf+$|8&zN6jL9*qc|+~^E-T}a0GpiPu{2j>otCgazeWb^}!~CQLC0FuwBOw z0@H2s)bUjPK2fI$D{rb@vUp5O0^t3a#9F!aPTn|#UE+86SrsY{GaZ+>vTqzsTzYt` zpQv~m``CEpKKtP>@t6Hc{s*%RYj+wN8;3189aA%2)Zl;}lPgaj{WF38?ipoXpVsEh z&j;m)RB>pO`<$!0TnOO!zpdJKNSWo>oi0sk(=QAmWO;vr2}78c7I`0X?#!N)+L%s1 z>J9Q7z$l1@-t?wJNSKNlx*iCm{qEx#PkB5rbsXqw4!*(F#>X_qTR&5=q0(knJl&-U5du+dl zFeCRqL14zSaPO_Ku>ZL;sEfGIpc-AY(EQeYPqMVpc$UZB9`IqywNq?p{>K|Dz6ivE z%-8FR6J90v;JUocmi#IwG|C1TUzb0oaOs&wnkM(&vYd@gtmiS);;>J1ReBdYeBiBi zM)nLv$5H|G>WCICYglumct>{ASvthVket;g11sS?#fjL=j*pEcrS{JnHuhaT5Vu}9 zka*V6C3%y16NoEOem|ZY9~R@89zQy~vz`F>@TjJm#15B$MM=lY0A$!|?$l@KlVA4{ z92YqwUu6CO2oqk>21Fd){$?2&Odp?LGvTj+j-P*ZimLzYs?-5Q9e<~Q4AhaT7(eU} zSC9=vIQ2i`B~9K>khn|H(vvR=)a>NLYXUVC*rPNCy-0zwaAhU|KYC*x@Y23{${pLi-SST1``tA$-lTD2NE(0cHR3x)*k)gI$^<0LJmaB1Bd=30h>3cz+hOL zjq2T;x~MGWfYeEg`uNFwwL`iBvzd}-D3|xKiT_#loI`IE)0mPI@Gq!t27jXbbyu-d zn1ILjD;AxSloZe9MD+~m>%9mBtZ(B{zzpWzmz=GcaiKu$Y`uH@aJC}+cqN_7pe`=icMuF9EU8JM`? zdUpplqsv_P&*Uw_uP$CUe-R!}DtU^u!8c#Y2%@i0`4KTqnHi<}i^j*=@8axmxYR^a zc)bWZ8d9KQ;y&wIK)%?|@9Y8I4Cw-GfUi_H|fQZ0Dm=vS7b{xPsk_>4^kk zo^Haf2#T%&fUYX$|7%=$d}X)5AqbZ=G0#!nrc)lW9CLG1eOW-36vxAd0rKyBR4`|q z9A<02)Hnl3nTN4$L3em^$~HbW8m{lcxvIgVU%8r&q!nVGm}d7MI^-Pey!&QND5JOz zlxU-Tj&}oNn4=poLP<@|2J?5_hZ0O0E2{)sJ8X8K^!^JTPL<|y!2geriM&M8~ zLp+&JRFC=W8+K%3-P?(UdH_AtShktn4aVG^$T1#^O(=`EFwuB^Zd$=pRE)+b%BVqP z>_Fj9XdjdpMu&?J<})Cg_$-=CE+cz@5 z_&kIKfQS|G?VlE-JRT+5)n=bnSuC&W_P&msIPV&ESMk(;0zG7#s;Uk`0fMu9hUY*P zIhTO6tUBmOvzkiZFsGZ{K3ULC8i0L(=_M}I@>`Mv!87450%1Q{(aHWTO77EQ>*Cl3 zk0ORt4p7LAg%H?pyql`BD;zeeb17eYxtd{xtpfyZjE*MUPXK16sxMWg#MnGUfiux} zF}6fAKWfJB1kGzXkP>kU5M6n7*gI^Toex4klhgy2l`>uqHx8o-D3b`e3LbhZTnB85 zFxSRmziHmRThrD;?7|$SXQA0!DZ8+j;4K|WX>UUo`>)ghK|yfvz0x-R4<4xdiw8c* z2fptN_0=~%TNap80^AN1h&`QBZVN#Se%yuX!cS5tZ=FCC9~^alhguFskoEv#IV~@M z@nmhZpB0In0~krIAO+GFBNH_pfywl5bvRop&h8|P^p4qv0J{+C&@fz6i{}_pqcOvo zvcAJqGI;FK$)wAt$pW~K)48GV@XE4zZt72&9jQk^3B1uO>b-_hDQvz9A*OUK)qArx z?7&G{<-~L__|J$SN47K00ZoC7mGj5pG{mMd04wVvU!EG@4XjoMU$51<`n_4{qN&TS#pdvK<|Gi^(Xt2&o2It`RreW03Zp9 znvqe&o(4curL3)|OmWIPoQAMTy2|g8ZJqB=r_Ez`9^_UfM?V@x&Z+NI8G63dATMp& zWo2FksT{bf*u{6??=w6_!$z{U17BoeAFB48GT9dGR-pRe8^>%#`;q3&eYGoT%qBj2 z$+1GtbG$6>ow25Nme)^ehEJ>(G=&l`vthRd-WM?tvx3s9^m4 z=P}RH22766_tWOfS4l#~3dM5&r}yx{E^|5{i-dm>FaYEi%GbI1c6SRaQ4MlaV5;v#da-(y5v*B`#%WG$H5p5pBFd%Hm+Xk7x<9tv-l3Dv>7d*-Y*WX5Za z^_i@_uob>I{ZQe%r8DluUD`8UwW8R4ZVRN5PV;A^`6$<9Oy158s=B$*Wl!4<6~Z?V z0i>oH)=QZ_XUmPOsh=ECuy7!gk!`-vpo?DUR4hnbva}hE!8JL*r9QEqaBN*@nJ(ot zi9$V^Pqgei5Ymk3WE0f2a6|Zp3E7`q{k96wcgioDm4ocnD_&c(y!_$gc|7ls=QoOC zo3M|?XYkUboA^%#PT5g&Jm{?)v(+Mz!QnmaMI~8;)M4-+Rs*cM!xj6l zT4Y}s721Et$nD+>xjGwznxhA1Px=xq z)3U*euGlr(5{-k1i5hD~+s2{AT=?waj6sgh08X`R?h z^zB%B9k_!Sex;@wg{NP3b$UOtI4>b}Bzx09 z^9AOoGlT$i2$gulNbb-G3a$LVPuu@HeI2vNh2h&Ox<{x07kjb1d>p?{j{fgf**oi# zb2|2@%r2!!Sq6W74xQQ)iTo37+}f2nd`)T|=k(B2>MHT5hK|fq9;jK>dpB2Vw5c2C z6gp}Hznh+~Ot&i}B5@#^Ib6RhUJ~5qGQ&n>J%6RWSPAJcFN zAl6aO7c04j_Je_JHP6App|=kafl=BHxWX4B5;{33$Jb(2uY6Ks10JVuCT{qb)d0hu zd%M_Q;d20HOQj*TVSQ&3^f&L{VykxYk@^V=GMTGUf}Z>kT2seY(XVj{aB}v3aOeOd!;IWqgqNI zS>Q<3hNnvR_YGj@lot(IS7Vmhby=$CjP+6j_Pw5l)y>Q>IM+W7YCPD)H?=VKHLJ1> zVs1w z;eO+xIHfw*?ZrN8I#G;Mf~%pR(E8KASZHJ?YV!R=Q^znDz(n(Ur)SuCd+Tlvd4I*0 z?~S?p=WXM7dw-I2P0r}F@IWjG+8)cxCF-ODL>5`;W$)vF-bc@PywteTv8q|(q$-Dq zY6Lv=#dJ<|l5(puo!mn(_3F5tXpmj-BcYvN>+Wkp1fNen2zypbnHSEiiv6fGifT;g zo?|lt7&7(pVKlGb?naqNjXXXd6pP1#=$e{9Pr8>C6mmfqoY;rY@WO87$6q?QFgkBx zd-^skH(`Q&JJY)Ng;sxgiO!UR0hk_Ki_T$+1ok5P*+PsZ7xa*dC|%tCu8t!$FBb#7X|f{A%P0fC*SsXV!zu}>!S~|fMM~C z+i9!@!pHOXKV;i#QHQVbRKJKy_ShHTbo`lvKNCw>_NMvTvJ3sCgTGvz*sgwoPv)bC zioZl~;@=XIH@)n{TXQtGytvmrc2C&Yl{((E|7|wlZW_ZMtJp0xw!XPPe#d?NVsC1F zESe}~&FeS#`-%5h>}r3$af9O&M4T(m&#@00&sbIW{Ihz&{3|)MJ7)2*bi&Yxs-#h; z!{XDaX^`0A^o4a^*TXJ4?2L6icYVCEwzh=TX(T|0exA_f-6)o}j;4dzFH$|B0+*uT0u>iB}|I-_AXz^>1cM8 z$^{FP4S)g%v;a}~^d8R!#19Rx$3@CAH9IZhzf<6JB+6-eteVEIW9S%| z0@NngTo*b%YwI2FqAY-lqBa&N&PMMIh^hj)qdS`e@Yo4BH>AJn(IZg;q&yn4uhlCy zbt>5Ax;f_1ObYoG={uS;#OQvZdG=6hfha)KpuN+I&yXj^dK^n5iKFTC*_^ww5dDccZw7%B)Kg0tT9><~KOF~w7u$=6m0F-cwUBs5U%e-P&VgObn z%4kQz9$+=p6k^XpasgIjP;5_0*zdF^wo9Ja6QCdX9{%t2gQTp1N_O}R77LU?;))Hb z3s3?0qy)P5(O97}9-mkyX%{ssI0>lHLZ#TcxEC_q;=T*{!bs`VPbWD25 zRK*wpnxMLpb-36-D3%mGTdxxeu<1vj?CDC|T(Hx0wL|G}kL)oZEzlGJoQiG8Bpe_@ zxLlPK;@I=SQMThnEWn?e%NdoLFXtAw2H-2!I-y40!GQ$ikNEgTSfSBYwP?b-%FQ*! z4g?20_6qTw9GBI>hMg{&0*1;eyXnW_64*SElH&*rv77748 zF^a$5@*dX-AOIjfI1PRI`;i7F=7PmKrN%Z?%)JesdkR}~jm0jrb*^K)O-e|-@d>*h zVqXI>^j^Oh*RPS(+Qs+z{rtYDcsu$QkYhwc2SgP6L`0D4Ey`qZn+ z|DS17O%>5f^TS$K`T*Jxc|a_pX(;wluiAPfC*J~5lwkMOhyxZhDO^8OrD)Ooc7q3( z&{U>JX{9$>mSYjlz-KD5Ne*%8_;lNb%5?)OAK`0s18S)%eh{PI^y^!2%+F4?{iSGC zeG}}<2VJq}_Ppiswnl!(sPQ7|{my$Pd0YG(dz6XRCnsS!90@$d>3&%GT@?JDdYaMh zRJ;|o)6Q4p5@{@Ug)*WO+eq;cFKtXQe8WWCQ(anD5s0b9YQ|ED9csm#nYBH`B#%x2 zU+G+304NMt50I%JxgG(veO+tzX-OPolqR;PxRmGN2QqfB-dlN+bZf)$wtIKMi!bT( ziI=Q18k;Z9rxidd>u*C)DVHI+ORM^4dr+Z7<3W%8L>pAQ#xs+%p>!-pVNosyyfDKBwaSq^9a4QZp4_8%{lfVG?~5&qYn=)^H4 zvfCwW{&lN5GY~)*r0;ZuVMGYm=2f#(b?i6QiBX4-4 zffrDe?+fA7t9Wkgv-5&Y5(t(H4CuD+@1fEdLrTi|GzV@tiU4A$OPq0r`$G%xk`358 z=am7QWg?;NX8@O}waRH!r|Y`?n#_CV9^&QzuD*(Sw{5*9rcVrGybAb$UUc<5r}HwR ze3u*k<>`wKHZxvao^rW%oN0mAJ-5cvnh;;$<#OS)!oW!x!1REc&?MY(@78#bUW*bn zr=Cus_J4pzSmrG3k-G889mVFSDC-QwoF%sprbZ319Zn;p66pAY=j+k?*AApSv!60v z=w(srf0SrzS;!IR_+xnumzJjsSf2fSlhGN06!VqS-Dh6~V-4FrUnzH}S$c2sQQR9t zbd2K>tFij9w5iLIdi7v^qL81y|KolbooJEB|4M0m-}$?0I@CfF_rq?xf0+&xJ#%yy z8f7~0Dh(oM+K z@baKGu95zdY_iDq+*@Wv|8FbG>I;O{`?Xpdwtb36!=sG2ksn28(R^liQCTh$^I3b| zih@8{LM@m9G4l{3fO;$@vO28Dr4=tU`%$sQLk11(t^NmJDq;p!l3n~9dLL8kv)b$g zBzku=CdI=K&{^*e& zprHRfPdDUclr9whnJR*jW(bvj!*8L)t*D}e^RXf1LhPS@zi#IQl<_L)zrT!5`1ZFk zmOz$&W?23I;34Uh`!BgmaYmGSg5Tp=?iZkSsX^mLribR_e-;jB%M6?7(tek?7{5Iz zJOYrt{HDCJu5EMXq{|B_4I#a_eX&F*JAVsbHm&sueqEpKfwQszqN0^b)RUx;@fDOY zUA83G;D^y8|J21|6@VkCZ8aqHV!Aw5$oo`)a&LcY-sC+mw8;dFNXiZ2X{pdlBfo4O zz`~aw4WP-|8xEX_d;Mu2J*|V?UwsH{;TXUpb#x+2bSvj<)&ZEV1@GZo@BESv6NE9R zx~S`>nf!&!3QWg5u!PhxNEHA`9C=`g$?gi_?}Rr2O(`$b0{~*xeW1&pDs_$_ISnh+ z3&i)!?y`HlKb^lgOV_Na{pKU`%1XC!12{qa`%?*Ka&&>i6P98`=hNe2?b0q9$_%k64Z=tfstCU;waZzjunF9KUU?GgbbDh~i% zL3H7TXQ)!b#!PSI*FU)iYb14Lvnh$AkFdPrUTPT-tSJ13-df zUYM77l=_rlpp=0s>KaMnc+vbFk4&1SN$NqkdAOu?Z2MR3rosY*32dgX@Nm4|X)%=iD!x|} zfv3mXDmCN5R)JGBBB@@EYDH+h31Az%edTIv!yog*ephF+qaIM{zk7>=NLr)P^sc#r zD4~-yy((f)AwvP7SicGj7j#MXGSCan6(H9E1-}enPWRIWfPWEKm%(E@x-Ek0_;EGWQ;$M9C!p(^LudSY;JB@oEnh z((~j7K0V+sY%#_47(=CpCgz#h-wwlp-h8PO|FF?|eKP{R1Yo~$`|s3MzDANsVnY8h z`dTXeR}j2kvToK9#1rKdE7kSVn$m`_EoM;;I!S@z#Hui0R#cVuD}tef4#+%O8@J{K28R{ z$N>4PIC99yV84NiQ1a@^bX(7wTY0grgCHNz<*ZI>iA}zG(dFgJH&J*|k`27<7(Y}Z zCfOnXNp$Xjdi;eXjkwfiks+YHQ8qHFKa6utoPOQ4aeWPs2RMs2K7Dbs3yHTT+KEg$ zXq@89_>VD$W?UCh!hx2%@dcxbKH|3IwIPA2bt@+VCC7HZpZxu9%YlWQ(#x!Z;O>*J zM5`&DibYOhsIO{^VAePaRk|l8=Y1`1SbbC@c3%ftlbh<6A|Vvu@steZo1o;de>=OYCnlpGJWgK} z3&Jo*dI|c7^I_!jt^Zi(DW(3`MQbUb1^X7*f;E244&iqPyv&S##{aD5u>kDVsHOQ` zupfI1<+Q_TMF20II7?nqr z^Ld3;TE`^?rWQlD;7wf>Di3O*PUMx64x^rIMeK73gBnAukI3`7!!-u-ykJ*^n;iDl z-wyL)Q`A2#C5k(gi{M$Ny2gn-278LFPI|vnN)%KK zW4KT|KU!4Pd5P~@9yUoL&u|&UlZjzl$VdSD;Ow0ykURDL5U|;eoU*SBq9%$-H{mf{I(ThB?2`WxuB0e6=i{i;67&0>~!?p59AZ zkrkRd`C874o15bFra=`M+(LFtBX4|+e({jT5luJ!xpTkHGJ zg>YSapVyA_Joa%MJ1fO=o=+>eSU%Z#IlhRW|3q;|vfp%zXN^Da%@XH5zqPRJXKyck z{*nHO%y5m{xjsCY6f!6?)ky4SkKe8X0NZ?|)=u7Tf7U?jxFUHTZS=&|C#W-2QUDtE;(>eDY_$4i z30RqxzoEH0-^H`)rN5V1c{-$JfA7OU&yov%Y#z6nLwF0KsFEl_ML|eyKvcJsl5}B* zqC#iMlxYZ^T~IwxGV!9N1Ri)gW zYs(ewB-<>r+}c-z+pem!YxUDlrr4-?V6%E>ifHWK3^fZFk|1YJe}Ya)t6fY_Uv96L zL@Go-)Gc#ViHqNqSs~LFT3Oe-*J6g^q+$p6Jd0(Er@)EDn&^2tx;myvFrF5|->u zdW9rnb$y!*><+ba;w#s<{BgJ&Sgc5#FOkP&qadMS!s$ky-P9_!@PW9^n-zATLeo5^ zn`6}|tT|A-Rsxujp4e1dKd(%Ok_1O|y9N2i7pd9Jr)G52o^!=T{GNS>40wbTkh0C& zUjfqlWXx%O4?Eft?Wy$EOgIkKA2Y@lD_OP zY4JeEX?5$LHjWWkf##*hr>za-)RVXWW`YmeT+5d{&WHv!5@Gn}Q^j!2IxXHDe-jue zN?ox~0dx~+XXO7j4?8tb$E2fT7y3Tf(^;=_v>z*j%y59@l%<`yon_JMdQ*LNyy?dL z4)0{l#8K$}pP_B0(%d~-cxl*XIB;I(ZD^YKFzySE=X}YoRTAHMih?PcUqf}HKksQ8 zQqwu2dePnTae~m3^hV4jw>rIKe&fP%k1mg84gU3K-wd=%+~uTn34VGVXME)EdC0n4 z;Iy27_yH8Zop%?jNZ;oM>02#)iqxm_+_u%KwN@GS90@Ri0iaBm4AchEynv$Q0=RN& zBgMw8myVPbL#c!;qR#-j%=jxGS*Psq$)u}w^XFHete2H`%lcMW{ZV{;e5k{J0N)$# z&iLnm?cGTq};y#zMvg5qv9UM ztDbByl+ua^vM=vfS>_f1E4+K*#ph`Tyt6&8vmB#}F%9-b@1D3Lw+7Fe7PPUGu+1`+PZ$^IYzT*ake1yX-3m z&(URAxEm03N_yiwSX8ouqjxQWz&a({Lq0lUe^2&Eej-!XkF~d0mS}0bEcVS3i4e#! z=|}usjwhb!rz`YSEE;d`nE7!SY%ZmCVX`!)fQOnJAEV+u?DCzKCTE4}{n34CyhnQr zqtUb}LcygZWQH4iXZu3ANKn`PZfo;nbZgZGCq5dgmbHuH4V~RswN_!}JbC5rC>&=e z+b>G=AF5U7jGp6&=_Z(d`RJ?B(8OeaQo5WU5_AP z3H^+&p1pbJqEg!}R;@2@YCArqJmi`$c!TTk?=JOr(_~&KMt#}pP1gUL@ed%Fk{{;S z5WKc%VhVZHHm&8mYsWTDz@l1isrBK8P*H4wX7Te6G|c~imuJ|+6ab*Y(7)S*pY6w_@<$ihMnCyS&;Iy9Mj^{Bh>_{k;O6pta(7z4&!qIvU zU+U`o#zuqSsdXw_PtKwa&|{H%gx2x4;ykp5Wo4z|rl8iI5DD%pN(ozUH=5phPt*}t z`)uL?Nq*W%YLDM?{2OP%R#1HnciG^vX{qNaKdL<09Ww>GEOkAga>NYk z*hB;J)#nk_8XBvxO2`<;)@nOAq`DrER`9a2gxh+2xVTV<3P28Ypn!Vmx<26Kolu*| z?-7d49!c2r3+NxH7owx0^LfR(5?(|kfuM){X9PhG?|V>N?O%i{M^J1zSKoqPz1}UO z0}W~i9H8#xu!VX+x)JSpWzZ6B%K4dU^Dq-C$$jTbmIu?R>pL(9gXmJ4Fx4opmAR!?0(h>tskIHsj zC?DToRW65pxMIiTw0=D5;NcS9|7c#Cp_I&ZLn<5IlAG_+#i5BI-z;FZ($oryi<7n0 z=TET%Pic}2R@!^|vz?I3X|y8&P`Fd&Sk8w|D=GVmIcY>hvdN>%;z7PV6afQR_$e!L z{5+q^d;I-pm(A}-Ij=T5T%4jbT)*~U|C}aM zDIh6i<1eL>CPEbXJUgFeq&Q|mgiz&P3J8h&`_v2n&61kBLz5ra&$B3TICGB=Nbk-#30~-{@5M^H+H8$Dn2obL@JR>D zOH3Dp4>lI`DMiVaQwu9SxqlK_chpF|S)0_kPRzOrr{tX(kmsBkYS^cw{+bxXX*!!t zSz_OHam4MX|E2ctP_pH?mGDnr|J|#72s0VHLMo)H(`2xhFrsqn_q7{sSMbbOb&<

    U_6)6<4Df6K8l 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 z zg4b5RaQq-GuDjUj%jnwN5I6ko$G^|j?1_GRc(N1lnFAeBsqC8-8L7U^kR)W@5j|M&U)xF79SghSZFj1WXKKoC%hqsY#F9Agc0Pe z2M#T*EuggR&Wq3U?6giHbe6NB*3evkrUfIh)7DLQsn4~*-`Rl)-*l+Y!@X``7xnwh zttG#==%3J72!j;bx|IA3qaYcyI|YZl^4Jox6Q#C@j&7O7L`P0@+7)C%oc7(&6x>hC z0AXJNFcyzk9ZQxy=Qd&$6Nlt@z8dgapOqbqzXnJO-1q&pd%95;t{2{KmrA z*lXj!6?M2<1Z-7662$yn3hlU$rWGc17RsR2j@MSa+;SYsOAy2~xPFNOjoeWu&(O9k z-;0#v+K(49oj`ea0x-8xkzCi(1hK^JBU8+blLoU?o3nY*>{Iex3Vq89tAs{Ceh(EyGBE%7M1ZbM7WHg^FS@c zd1JjB`lCk{kf?7LAkBp}*n`g8g>W1?*I2n}BCSq*-}ii~E5i?2>GJP(zbU$NaaJ03x5-}*dSwLw&%8O)R3W@vgP)4ymP6r30ZDWDS!s&Ys}fJA2+mzqi~t=x!Ioe=BB^+dMO6mfI1k0H)GQO^K;JuZ zEHqQnl0gv`VENd{&q!tC%y!>h5OHO?^YW}p>gbYnL~;979LWi+vOyK=3#Lu?tG!bWWB(HvbT3X=zG$Y$0RD` z6C5{Aq8r&Ldy|8AaM-v=`PZBp!i=OrECV_uNfPnrck#P$f^5uFV^OKz-;Ai86L1n?du eM83LyXN&ICKnkHZP0Tp*y@IUD%{*z-$NvYyQ=fhS diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorAligned.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorAligned.png deleted file mode 100644 index 2562fb7eb2fff47100b6e383df8c2310048a649b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34764 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 Zd2gI literal 0 HcmV?d00001 diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm index a877246ee1..01daa93f42 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm @@ -487,14 +487,13 @@ Archive action.

    -

    Aligning All Data Types In a Program or Archive

    +

    Pack All Data Types In a Program or Archive

    -

    Right-click on the program or data type archive where data types are to be aligned, - and select the Align All... action. A confirmation dialog will appear to - make sure you want to align all the structures and unions in the program or data type - archive. If you continue, all structures and unions that are unaligned will be changed to - aligned data types with no minimum alignment (the default) and no packing.

    +

    Right-click on the program or data type archive where structures and unions are to be packed, + and select the Pack All... action. A confirmation dialog will appear to + make sure you want to pack all composites in the program or data type + archive. If you continue, all non-packed composites will have default packing enabled.

    Updating an Archive From a Source @@ -926,15 +925,14 @@

    - +

    Committing Changes To Source Archive

    diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayInStructureCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayInStructureCmd.java index df6c6e079d..db26d3d1dc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayInStructureCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/data/CreateArrayInStructureCmd.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -103,7 +102,7 @@ public class CreateArrayInStructureCmd implements Command { try { ArrayDataType adt = new ArrayDataType(dataType, numElements, dataType.getLength()); clearStruct(struct, compData.getParentOffset(), length); - if (struct.isInternallyAligned()) { + if (struct.isPackingEnabled()) { struct.insert(index, adt, adt.getLength()); } else { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java index 3fdbd5ab11..8eedd74e53 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java @@ -1164,7 +1164,7 @@ public class DataTypeMergeManager implements MergeResolver { updateAlignment(sourceDt, destStruct); DataTypeManager sourceDTM = sourceDt.getDataTypeManager(); - boolean aligned = sourceDt.isInternallyAligned(); + boolean aligned = sourceDt.isPackingEnabled(); // Add each of the defined components back in. DataTypeComponent[] comps = sourceDt.getDefinedComponents(); @@ -1480,17 +1480,25 @@ public class DataTypeMergeManager implements MergeResolver { private void updateAlignment(Composite sourceDt, Composite destinationDt) { if (sourceDt.isDefaultAligned()) { - destinationDt.setToDefaultAlignment(); + destinationDt.setToDefaultAligned(); } else if (sourceDt.isMachineAligned()) { - destinationDt.setToMachineAlignment(); + destinationDt.setToMachineAligned(); } else { - destinationDt.setMinimumAlignment(sourceDt.getMinimumAlignment()); + destinationDt.setExplicitMinimumAlignment(sourceDt.getExplicitMinimumAlignment()); + } + if (sourceDt.isPackingEnabled()) { + if (sourceDt.hasExplicitPackingValue()) { + destinationDt.setExplicitPackingValue(sourceDt.getExplicitPackingValue()); + } + else { + destinationDt.setToDefaultPacking(); + } + } + else { + destinationDt.setPackingEnabled(false); } - destinationDt.setPackingValue(sourceDt.getPackingValue()); - boolean aligned = sourceDt.isInternallyAligned(); - destinationDt.setInternallyAligned(aligned); } private void updateComposite(long sourceDtID, Composite sourceDt, Composite destDt, @@ -1860,11 +1868,15 @@ public class DataTypeMergeManager implements MergeResolver { private boolean compositeDataTypeWasChanged(Composite c1, Composite c2) { DataTypeManager dtm1 = c1.getDataTypeManager(); DataTypeManager dtm2 = c2.getDataTypeManager(); - if (c1.isInternallyAligned() != c2.isInternallyAligned() || - c1.isDefaultAligned() != c2.isDefaultAligned() || - c1.isMachineAligned() != c2.isMachineAligned() || - c1.getMinimumAlignment() != c2.getMinimumAlignment() || - c1.getPackingValue() != c2.getPackingValue()) { + + PackingType packingType = c1.getPackingType(); + AlignmentType alignmentType = c1.getAlignmentType(); + + if ((packingType != c2.getPackingType()) || (alignmentType != c2.getAlignmentType()) || + (packingType == PackingType.EXPLICIT && + c1.getExplicitPackingValue() != c2.getExplicitPackingValue()) || + (alignmentType == AlignmentType.EXPLICIT && + c1.getExplicitMinimumAlignment() != c2.getExplicitMinimumAlignment())) { return true; } @@ -1877,7 +1889,7 @@ public class DataTypeMergeManager implements MergeResolver { boolean checkOffsets = false; if (c1 instanceof Structure) { - if (!((Structure) c1).isInternallyAligned()) { + if (!((Structure) c1).isPackingEnabled()) { if (c1.getNumComponents() != c2.getNumComponents()) { return true; } @@ -2444,13 +2456,13 @@ public class DataTypeMergeManager implements MergeResolver { } /** - * Process fixup for unaligned structure component + * Process fixup for non-packed structure component * @param info fixup info * @param struct result structure * @param dt component datatype * @return false if component not found, else true */ - private boolean fixUpUnalignedStructureComponent(FixUpInfo info, Structure struct, + private boolean fixUpNonPackedStructureComponent(FixUpInfo info, Structure struct, DataType dt) { int offset = info.index; DataTypeComponent dtc = struct.getComponentAt(offset); @@ -2533,7 +2545,7 @@ public class DataTypeMergeManager implements MergeResolver { boolean isFlexArrayFixup = (info.index == Integer.MAX_VALUE); if (compDt != null) { - if (struct.isInternallyAligned() || isFlexArrayFixup) { + if (struct.isPackingEnabled() || isFlexArrayFixup) { if (!fixUpAlignedStructureComponent(info, struct, compDt)) { String msg = isFlexArrayFixup ? "flex-array component" : ("component " + info.index); @@ -2544,7 +2556,7 @@ public class DataTypeMergeManager implements MergeResolver { return true; } - if (!fixUpUnalignedStructureComponent(info, struct, compDt)) { + if (!fixUpNonPackedStructureComponent(info, struct, compDt)) { Msg.warn(this, "Structure Merge: Couldn't get component at offset " + info.index + " in " + struct.getPathName()); return false; @@ -2556,7 +2568,7 @@ public class DataTypeMergeManager implements MergeResolver { else if (isFlexArrayFixup) { struct.clearFlexibleArrayComponent(); } - else if (struct.isInternallyAligned()) { + else if (struct.isPackingEnabled()) { int ordinal = info.index; int numComponents = struct.getNumComponents(); if (ordinal >= 0 && ordinal < numComponents) { @@ -2595,7 +2607,7 @@ public class DataTypeMergeManager implements MergeResolver { * @return the number of contiguous undefined bytes or 0. */ private int getNumUndefinedBytes(Structure struct, int ordinal) { - if (struct.isInternallyAligned()) { + if (struct.isPackingEnabled()) { return 0; } int numComponents = struct.getNumComponents(); @@ -3276,7 +3288,7 @@ public class DataTypeMergeManager implements MergeResolver { private static int getComponentFixupIndex(DataTypeComponent dtc) { Composite composite = (Composite) dtc.getParent(); - if (composite.isInternallyAligned() || (composite instanceof Union)) { + if (composite.isPackingEnabled() || (composite instanceof Union)) { return dtc.getOrdinal(); } return dtc.getOffset(); @@ -3301,7 +3313,7 @@ public class DataTypeMergeManager implements MergeResolver { * or components were resolved. * @param id id of data type needed to be fixed up * @param compID id of either component or base type - * @param index offset into unaligned structure, or ordinal into union or aligned + * @param index offset into non-packed structure, or ordinal into union or packed * structure; or parameter/return ordinal; for other data types index is not used (specify -1). * For structure trailing flex-array specify {@link Integer#MAX_VALUE}. * @param resolvedDataTypes hashtable used for resolving the data type @@ -3333,7 +3345,7 @@ public class DataTypeMergeManager implements MergeResolver { } /** - * Find unaligned structure bitfield component at or after specified component + * Find non-packed structure bitfield component at or after specified component * which matches this info's bitfield data. * @param struct structure * @param dtc structure component contained within struct @@ -3412,7 +3424,7 @@ public class DataTypeMergeManager implements MergeResolver { /** * - * @param index offset into unaligned structure, or ordinal into union or aligned + * @param index offset into non-packed structure, or ordinal into union or packed * structure; for other data types, offset is not used (specify -1) * @param resolvedDataTypes hashtable used for resolving the data type */ @@ -3466,7 +3478,7 @@ public class DataTypeMergeManager implements MergeResolver { } private void cleanUpStructure(int[] indexArray, Structure dt) { - boolean aligned = dt.isInternallyAligned(); + boolean aligned = dt.isPackingEnabled(); Arrays.sort(indexArray); for (int i = indexArray.length - 1; i >= 0; i--) { if (aligned) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java index f0ff3dee2b..ab746965c9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java @@ -149,28 +149,8 @@ class DataTypePanel extends JPanel { } private void formatAlignment(Composite composite) { - StringBuffer alignmentBuffer = new StringBuffer(); - if (!composite.isInternallyAligned()) { - alignmentBuffer.append("Unaligned"); - } - else if (composite.isDefaultAligned()) { - alignmentBuffer.append("Aligned"); - } - else if (composite.isMachineAligned()) { - alignmentBuffer.append("Machine aligned"); - } - else { - long alignment = composite.getMinimumAlignment(); - alignmentBuffer.append("align(" + alignment + ")"); - } - if (composite.isInternallyAligned()) { - long packingValue = composite.getPackingValue(); - if (packingValue != Composite.NOT_PACKING) { - alignmentBuffer.append(" pack(" + packingValue + ")"); - } - } - - insertString(alignmentBuffer.toString() + "\n\n", sourceAttrSet); + String str = CompositeDataTypeImpl.getAlignmentAndPackingString(composite); + insertString(str + "\n\n", sourceAttrSet); } private void insertAlignment(Composite composite) { @@ -202,7 +182,7 @@ class DataTypePanel extends JPanel { buffer.append("[0]"); } else if (dt instanceof BitFieldDataType && - !((Composite) dtc.getParent()).isInternallyAligned()) { + !((Composite) dtc.getParent()).isPackingEnabled()) { BitFieldDataType bfDt = (BitFieldDataType) dt; buffer.append("("); buffer.append(Integer.toString(bfDt.getBitOffset())); @@ -255,24 +235,24 @@ class DataTypePanel extends JPanel { DataTypeComponent[] components = comp.getDefinedComponents(); DataTypeComponent flexDtc = null; if (comp instanceof Structure) { - showComponentOffset = !comp.isInternallyAligned(); + showComponentOffset = !comp.isPackingEnabled(); flexDtc = ((Structure) comp).getFlexibleArrayComponent(); } int offsetLength = showComponentOffset ? Integer.toHexString(comp.getLength()).length() : 0; int maxDtNameLength = 10; int maxFieldNameLength = 1; - for (int i = 0; i < components.length; i++) { - maxDtNameLength = max(getDataTypeName(components[i]), maxDtNameLength); - maxFieldNameLength = max(components[i].getFieldName(), maxFieldNameLength); + for (DataTypeComponent component : components) { + maxDtNameLength = max(getDataTypeName(component), maxDtNameLength); + maxFieldNameLength = max(component.getFieldName(), maxFieldNameLength); } if (flexDtc != null) { maxDtNameLength = max(getDataTypeName(flexDtc), maxDtNameLength); maxFieldNameLength = max(flexDtc.getFieldName(), maxFieldNameLength); } - for (int i = 0; i < components.length; i++) { - renderComponent(components[i], maxDtNameLength, maxFieldNameLength, offsetLength); + for (DataTypeComponent component : components) { + renderComponent(component, maxDtNameLength, maxFieldNameLength, offsetLength); } if (flexDtc != null) { renderComponent(flexDtc, maxDtNameLength, maxFieldNameLength, offsetLength); @@ -293,9 +273,9 @@ class DataTypePanel extends JPanel { String[] names = enuum.getNames(); int maxLength = 0; - for (int i = 0; i < names.length; i++) { - if (names[i].length() > maxLength) { - maxLength = names[i].length(); + for (String name : names) { + if (name.length() > maxLength) { + maxLength = name.length(); } } long[] values = enuum.getValues(); @@ -336,8 +316,8 @@ class DataTypePanel extends JPanel { return; } int maxLength = 0; - for (int i = 0; i < vars.length; i++) { - String typeName = vars[i].getDataType().getDisplayName(); + for (ParameterDefinition var : vars) { + String typeName = var.getDataType().getDisplayName(); if (typeName.length() > maxLength) { maxLength = typeName.length(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java index 442bd8fe62..c6188c5ed9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/AddBitFieldAction.java @@ -69,8 +69,8 @@ public class AddBitFieldAction extends CompositeEditorTableAction { public void adjustEnablement() { boolean enabled = true; CompEditorModel editorModel = (CompEditorModel) model; - // Union do not support unaligned placement of bitfields - if (!(editorModel.viewComposite instanceof Structure) || editorModel.isAligned() || + // Union do not support non-packed placement of bitfields + if (!(editorModel.viewComposite instanceof Structure) || editorModel.isPackingEnabled() || editorModel.getNumSelectedRows() != 1 || editorModel.isFlexibleArraySelection()) { enabled = false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorDialog.java index 47036afab9..adf37c72ae 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorDialog.java @@ -38,7 +38,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider { private Composite composite; private CompositeChangeListener listener; - private BitFieldEditorPanel bitFieldEditorPanel; // for unaligned use case + private BitFieldEditorPanel bitFieldEditorPanel; // for non-packed use case BitFieldEditorDialog(Composite composite, DataTypeManagerService dtmService, int editOrdinal, boolean showOffsetsInHex, CompositeChangeListener listener) { @@ -290,11 +290,11 @@ public class BitFieldEditorDialog extends DialogComponentProvider { } private static String getCompositeType(Composite composite) { - // currently supports unaligned case only! - if (composite.isInternallyAligned()) { - throw new IllegalArgumentException("Aligned use not supported"); + // currently supports non-packed case only! + if (composite.isPackingEnabled()) { + throw new IllegalArgumentException("Packed use not supported"); } - String alignmentMode = composite.isInternallyAligned() ? "Aligned" : "Unaligned"; + String alignmentMode = composite.isPackingEnabled() ? "Packed" : "Non-Packed"; String type = (composite instanceof Union) ? "Union" : "Structure"; return alignmentMode + " " + type; } @@ -309,7 +309,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider { baseDataType = ((BitFieldDataType) dtc.getDataType()).getBaseDataType(); } } - else if (!composite.isNotYetDefined()) { + else if (!composite.isZeroLength()) { offset = composite.getLength(); } @@ -346,7 +346,7 @@ public class BitFieldEditorDialog extends DialogComponentProvider { // Assume a reasonable alignment in identifying aligned offset int alignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization, - Composite.NOT_PACKING, bitfieldDt.getBaseDataType(), bitfieldDt.getBaseTypeSize()); + CompositeInternal.NO_PACKING, bitfieldDtc); int adjustedOffset = offset - (offset % alignment); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java index c438f2344b..a70cec5916 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java @@ -41,7 +41,7 @@ import resources.ResourceManager; /** * BitFieldEditorPanel provides the ability to add or modify bitfields - * within unaligned structures. + * within non-packed structures. */ public class BitFieldEditorPanel extends JPanel { @@ -81,9 +81,9 @@ public class BitFieldEditorPanel extends JPanel { super(); this.composite = composite; - if (composite.isInternallyAligned()) { + if (composite.isPackingEnabled()) { // A different bitfield editor should be used for aligned composites - throw new IllegalArgumentException("composite must be unaligned"); + throw new IllegalArgumentException("composite must be non-packed"); } setLayout(new VerticalLayout(5)); @@ -169,7 +169,7 @@ public class BitFieldEditorPanel extends JPanel { int offset = placementComponent.getAllocationOffset(); decrementButton.setEnabled(offset > 0); - int length = composite.isNotYetDefined() ? 0 : composite.getLength(); + int length = composite.isZeroLength() ? 0 : composite.getLength(); incrementButton.setEnabled(offset < length); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java index 882c9ff970..18d028a304 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldPlacementComponent.java @@ -157,7 +157,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { } allocationByteOffset = 0; allocationByteSize = 1; - if (!editUseEnabled) { + if (!editUseEnabled && composite != null) { allocationByteSize = composite.getLength(); } init(null); @@ -456,7 +456,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { composite.delete(editOrdinal); int sizeChange = initialLength - composite.getLength(); - if (!composite.isInternallyAligned() && editOrdinal < composite.getNumComponents()) { + if (!composite.isPackingEnabled() && editOrdinal < composite.getNumComponents()) { // deletions cause shift which is bad - pad with defaults for (int i = 0; i < sizeChange; i++) { composite.insert(editOrdinal, DataType.DEFAULT); @@ -851,7 +851,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable { BitFieldDataType bitfield = (BitFieldDataType) component.getDataType(); int storageSize = 8 * bitfield.getStorageSize(); rightBit = leftAdj + storageSize - bitfield.getBitOffset() - 1; - // Use effective bit-size since unaligned uses are only concerned with actual + // Use effective bit-size since non-packed uses are only concerned with actual // bits stored (NOTE: this may cause a transition from declared to effective // bit-size when editing a bitfield where the these bit-sizes differ). int bitSize = bitfield.getBitSize(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java index ea8d0360ba..392e0ba492 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorModel.java @@ -21,7 +21,6 @@ import docking.widgets.OptionDialog; import docking.widgets.fieldpanel.support.*; import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.data.*; -import ghidra.program.model.data.Composite.AlignmentType; import ghidra.program.model.lang.InsufficientBytesException; import ghidra.util.*; import ghidra.util.exception.*; @@ -232,7 +231,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { int numComps = getNumComponents(); // Make sure we don't have a selection with rows outside the table. - // This can happen due to switching between aligned and unaligned. + // This can happen due to switching between packed and non-packed. FieldSelection allRows = new FieldSelection(); allRows.addRange(0, numComps + 1); selection.intersect(allRows); @@ -346,30 +345,31 @@ public abstract class CompEditorModel extends CompositeEditorModel { * and does not perform any edit notification. * * @param rows array with each row (component) index to delete - * @param monitor the task monitor * @throws CancelledException if cancelled */ - private void delete(int[] rows, TaskMonitor monitor) throws CancelledException { + private void delete(int[] rows) throws CancelledException { int n = rows.length; - monitor.initialize(n); - - int[] selectedRows = getSelectedRows(); Arrays.sort(rows); + + Set rowSet = new HashSet<>(); + for (int i = n - 1; i >= 0; i--) { - monitor.checkCanceled(); - monitor.setMessage("Deleting " + (n - i + 1) + " of " + n); int rowIndex = rows[i]; int componentOrdinal = convertRowToOrdinal(rowIndex); - doDelete(componentOrdinal); - monitor.incrementProgress(1); + if (componentOrdinal < row) { + row--; + } + rowSet.add(componentOrdinal); } + viewComposite.delete(rowSet); + // Not sure if this is the right behavior. Assuming the deleted rows were selected, // restore the selection to be the first row that was deleted so that the UI leaves the // user's selection close to where it was. - if (selectedRows.length > 0) { - setSelection(new int[] { selectedRows[0] }); + if (rows.length > 0) { + setSelection(new int[] { rows[0] }); } notifyCompositeChanged(); @@ -409,14 +409,13 @@ public abstract class CompEditorModel extends CompositeEditorModel { } final int entries = endRowIndex - startRowIndex + 1; - int[] ordinals = new int[entries]; + Set ordinals = new HashSet<>(); monitor.initialize(entries); - int i = 0; - for (int rowIndex = endRowIndex; rowIndex >= startRowIndex; rowIndex--, i++) { + for (int rowIndex = endRowIndex; rowIndex >= startRowIndex; rowIndex--) { monitor.checkCanceled(); int componentOrdinal = convertRowToOrdinal(rowIndex); - ordinals[i] = componentOrdinal; + ordinals.add(componentOrdinal); if (componentOrdinal < row) { row--; } @@ -432,7 +431,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { } @Override - public void deleteSelectedComponents(TaskMonitor monitor) throws UsrException { + public void deleteSelectedComponents() throws UsrException { if (!isDeleteAllowed()) { throw new UsrException("Deleting is not allowed."); } @@ -443,7 +442,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { int[] selectedComponents = getSelectedComponentRows(); int firstRowIndex = !selection.isEmpty() ? selectedComponents[0] : getRowCount(); try { - delete(selectedComponents, monitor); + delete(selectedComponents); } finally { componentEdited(); @@ -1531,7 +1530,7 @@ public abstract class CompEditorModel extends CompositeEditorModel { } DataType dt = getComponent(rowIndex).getDataType(); int maxDups = Integer.MAX_VALUE; - // If editModel is showing undefined bytes (unaligned) + // If editModel is showing undefined bytes (non-packed) // then constrain by number of undefined bytes that follow. if (isShowingUndefinedBytes() && (dt != DataType.DEFAULT)) { int numBytes = getNumUndefinedBytesAt(rowIndex + 1); @@ -1609,23 +1608,6 @@ public abstract class CompEditorModel extends CompositeEditorModel { return rowIndex; } - public boolean isAligned() { - return viewComposite.isInternallyAligned(); - } - - public void setAligned(boolean aligned) { - boolean currentViewIsAligned = viewComposite.isInternallyAligned(); - if (currentViewIsAligned == aligned) { - return; - } - viewComposite.setInternallyAligned(aligned); - notifyCompositeChanged(); - } - - public int getPackingValue() { - return viewComposite.getPackingValue(); - } - protected boolean isSizeEditable() { return false; } @@ -1644,69 +1626,102 @@ public abstract class CompEditorModel extends CompositeEditorModel { hadChanges = false; return hadChanges; } - hadChanges = !(viewComposite.isInternallyAligned() == oldComposite.isInternallyAligned() && - viewComposite.getPackingValue() == oldComposite.getPackingValue() && - viewComposite.isDefaultAligned() == oldComposite.isDefaultAligned() && - viewComposite.isMachineAligned() == oldComposite.isMachineAligned() && - viewComposite.getMinimumAlignment() == oldComposite.getMinimumAlignment()); + + PackingType packingType = getPackingType(); + AlignmentType alignmentType = getAlignmentType(); + + hadChanges = (packingType != oldComposite.getPackingType()) || + (alignmentType != oldComposite.getAlignmentType()) || + (packingType == PackingType.EXPLICIT && + getExplicitPackingValue() != oldComposite.getExplicitPackingValue()) || + (alignmentType == AlignmentType.EXPLICIT && + getExplicitMinimumAlignment() != oldComposite.getExplicitMinimumAlignment()); return hadChanges; } /** - * Return the external (minimum) alignment type for the structure or union being viewed + * Return the (minimum) alignment type for the structure or union being viewed * @return the alignment type */ - public AlignmentType getMinimumAlignmentType() { - if (viewComposite.isDefaultAligned()) { - return AlignmentType.DEFAULT_ALIGNED; - } - if (viewComposite.isMachineAligned()) { - return AlignmentType.MACHINE_ALIGNED; - } - return AlignmentType.ALIGNED_BY_VALUE; + public AlignmentType getAlignmentType() { + return viewComposite.getAlignmentType(); } - /** - * Return the external (minimum) alignment value for the structure or union being viewed. - * @return the alignment - */ - public int getMinimumAlignment() { - if (viewComposite != null) { - return viewComposite.getMinimumAlignment(); - } - return 0; + public int getExplicitMinimumAlignment() { + return viewComposite.getExplicitMinimumAlignment(); } - public void setAlignmentType(AlignmentType alignmentType) { - if (alignmentType == AlignmentType.DEFAULT_ALIGNED) { - viewComposite.setToDefaultAlignment(); + public void setAlignmentType(AlignmentType alignmentType, int explicitValue) { + AlignmentType currentAlignType = getAlignmentType(); + if (alignmentType == AlignmentType.DEFAULT) { + if (currentAlignType == AlignmentType.DEFAULT) { + return; + } + viewComposite.setToDefaultAligned(); } - else if (alignmentType == AlignmentType.MACHINE_ALIGNED) { - viewComposite.setToMachineAlignment(); + else if (alignmentType == AlignmentType.MACHINE) { + if (currentAlignType == AlignmentType.MACHINE) { + return; + } + viewComposite.setToMachineAligned(); } else { - int alignment = viewComposite.getMinimumAlignment(); - if (alignment == 0) { - alignment = viewDTM.getDataOrganization().getAlignment(viewComposite, getLength()); + if (currentAlignType == AlignmentType.EXPLICIT && + explicitValue == viewComposite.getExplicitMinimumAlignment()) { + return; } - viewComposite.setMinimumAlignment(alignment); // Causes the type to change, but not the value. + viewComposite.setExplicitMinimumAlignment(explicitValue); + } + if (fixSelection()) { + selectionChanged(); } notifyCompositeChanged(); } - public abstract void setAlignment(int alignmentValue) throws InvalidInputException; + public boolean isPackingEnabled() { + return viewComposite.isPackingEnabled(); + } - public void setPackingValue(int packingValue) { - int currentViewPackingValue = viewComposite.getPackingValue(); - if (currentViewPackingValue == packingValue) { - return; + public PackingType getPackingType() { + return viewComposite.getPackingType(); + } + + public int getExplicitPackingValue() { + return viewComposite.getExplicitPackingValue(); + } + + public void setPackingType(PackingType packingType, int explicitValue) { + PackingType currentPacktype = getPackingType(); + if (packingType == PackingType.DISABLED) { + if (currentPacktype == PackingType.DISABLED) { + return; + } + viewComposite.setPackingEnabled(false); + } + else if (packingType == PackingType.DEFAULT) { + if (currentPacktype == PackingType.DEFAULT) { + return; + } + viewComposite.setToDefaultPacking(); + } + else { + if (currentPacktype == PackingType.EXPLICIT && + explicitValue == viewComposite.getExplicitPackingValue()) { + return; + } + viewComposite.setExplicitPackingValue(explicitValue); + } + if (fixSelection()) { + selectionChanged(); } - viewComposite.setPackingValue(packingValue); notifyCompositeChanged(); } public int getActualAlignment() { - return viewDTM.getDataOrganization().getAlignment(viewComposite, getLength()); + return viewComposite.getAlignment(); +// return viewDTM.getDataOrganization().getAlignment(viewComposite, getLength()); } + + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java index 6e0f4c1ca7..e16c706737 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompEditorPanel.java @@ -21,23 +21,21 @@ import java.awt.dnd.DropTargetDragEvent; import java.awt.event.*; import javax.swing.*; +import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.Document; import docking.widgets.OptionDialog; import docking.widgets.button.GRadioButton; -import docking.widgets.checkbox.GCheckBox; import docking.widgets.fieldpanel.support.FieldSelection; import docking.widgets.label.GDLabel; import ghidra.app.plugin.core.compositeeditor.BitFieldPlacementComponent.BitAttributes; import ghidra.program.model.data.*; import ghidra.program.model.data.Composite; -import ghidra.program.model.data.Composite.AlignmentType; import ghidra.util.HelpLocation; import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.InvalidInputException; import ghidra.util.layout.PairLayout; import ghidra.util.layout.VerticalLayout; @@ -47,37 +45,35 @@ import ghidra.util.layout.VerticalLayout; */ public class CompEditorPanel extends CompositeEditorPanel { - private final static String NO_PACKING_STRING = ""; protected final static Insets LEFT_INSETS = new Insets(2, 3, 1, 0); protected final static Insets VERTICAL_INSETS = new Insets(2, 0, 1, 0); // GUI components for displaying composite data type information. - protected GridBagLayout gridBagLayout; - protected JPanel infoPanel; - protected JLabel nameLabel; + private GridBagLayout gridBagLayout; + private JPanel infoPanel; + private JLabel nameLabel; protected JTextField nameTextField; - protected JLabel descriptionLabel; - protected JTextField descriptionTextField; - protected JLabel categoryLabel; - protected JTextField categoryStatusTextField; - protected JLabel sizeLabel; - protected JTextField sizeStatusTextField; + private JLabel descriptionLabel; + private JTextField descriptionTextField; + private JLabel categoryLabel; + private JTextField categoryStatusTextField; + private JLabel sizeLabel; + private JTextField sizeTextField; - protected JCheckBox internalAlignmentCheckBox; + private JPanel alignPanel; + private JRadioButton defaultAlignButton; + private JRadioButton machineAlignButton; + private JRadioButton explicitAlignButton; + private JTextField explicitAlignTextField; - protected JPanel minimumAlignmentPanel; - protected JRadioButton defaultMinAlignButton; - protected JRadioButton machineMinAlignButton; - protected JRadioButton byValueMinAlignButton; - protected JTextField minAlignValueTextField; + private JPanel packingPanel; + private JCheckBox packingEnablementButton; + private JRadioButton defaultPackingButton; + private JRadioButton explicitPackingButton; + private JTextField explicitPackingTextField; - protected JPanel packingPanel; - protected JRadioButton noPackingButton; - protected JRadioButton byValuePackingButton; - protected JTextField packingValueTextField; - - protected JLabel actualAlignmentLabel; - protected JTextField actualAlignmentValueTextField; + private JLabel actualAlignmentLabel; + private JTextField actualAlignmentValueTextField; private BitFieldPlacementComponent bitViewComponent; @@ -100,7 +96,6 @@ public class CompEditorPanel extends CompositeEditorPanel { */ public CompEditorPanel(CompEditorModel model, CompositeEditorProvider provider) { super(model, provider); - setInternallyAligned(model.viewComposite.isInternallyAligned()); } @Override @@ -111,6 +106,8 @@ public class CompEditorPanel extends CompositeEditorPanel { @Override public void componentDataChanged() { + refreshGUIPackingValue(); + refreshGUIMinimumAlignmentValue(); refreshGUIActualAlignmentValue(); setCompositeSize(model.getLength()); } @@ -135,25 +132,7 @@ public class CompEditorPanel extends CompositeEditorPanel { if (c != null) { setCategoryName(c.toString()); } - setInternallyAligned(((CompEditorModel) model).isAligned()); - refreshGUIMinimumAlignmentType(); - refreshGUIMinimumAlignmentValue(); - refreshGUIActualAlignmentValue(); - setCompositeSize(model.getLength()); - refreshGUIPackingValue(); - } - - private void refreshGUIMinimumAlignmentType() { - AlignmentType minimumAlignmentType = ((CompEditorModel) model).getMinimumAlignmentType(); - if (minimumAlignmentType == AlignmentType.DEFAULT_ALIGNED) { - defaultMinAlignButton.setSelected(true); - } - else if (minimumAlignmentType == AlignmentType.MACHINE_ALIGNED) { - machineMinAlignButton.setSelected(true); - } - else { - byValueMinAlignButton.setSelected(true); - } + componentDataChanged(); } @Override @@ -267,7 +246,6 @@ public class CompEditorPanel extends CompositeEditorPanel { setupCategory(); setupSize(); setupActualAlignment(); - setupInternallyAligned(); setupMinimumAlignment(); setupPacking(); @@ -363,56 +341,16 @@ public class CompEditorPanel extends CompositeEditorPanel { infoPanel.add(categoryStatusTextField, gridBagConstraints); } - private void setupInternallyAligned() { - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - - internalAlignmentCheckBox = new GCheckBox("Align"); - gridBagConstraints.anchor = GridBagConstraints.LINE_END; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.gridx = 4; - gridBagConstraints.gridy = 3; - internalAlignmentCheckBox.setSelected(model.viewComposite.isInternallyAligned()); - internalAlignmentCheckBox.setToolTipText( - "Whether or not the internal components of this structure are aligned."); - internalAlignmentCheckBox.setEnabled(true); - if (helpManager != null) { - helpManager.registerHelp(internalAlignmentCheckBox, new HelpLocation( - provider.getHelpTopic(), provider.getHelpName() + "_" + "InternallyAligned")); - } - internalAlignmentCheckBox.setName("Internally Aligned"); - - internalAlignmentCheckBox.addItemListener(e -> adjustInternalAlignment()); - infoPanel.add(internalAlignmentCheckBox, gridBagConstraints); - } - - protected void adjustInternalAlignment() { - boolean aligned = internalAlignmentCheckBox.isSelected(); - ((CompEditorModel) model).setAligned(aligned); - if (aligned) { - showMinimumAlignment(); - } - else { - hideMinimumAlignment(); - } - packingValueTextField.setEditable(aligned); - if (aligned) { - showPacking(); - } - else { - hidePacking(); - } - } - @Override public Dimension getPreferredSize() { // make sure our preferred size accounts for that of our components that the user // may choose to show Dimension preferredSize = super.getPreferredSize(); - if (minimumAlignmentPanel.isShowing()) { + if (alignPanel.isShowing()) { return preferredSize; } - Dimension alignmentPanelPreferredSize = minimumAlignmentPanel.getPreferredSize(); + Dimension alignmentPanelPreferredSize = alignPanel.getPreferredSize(); preferredSize.width += alignmentPanelPreferredSize.width; Dimension packingPanelPreferredSize = packingPanel.getPreferredSize(); @@ -422,34 +360,50 @@ public class CompEditorPanel extends CompositeEditorPanel { } private void setupMinimumAlignment() { - defaultMinAlignButton = new GRadioButton("none "); - machineMinAlignButton = new GRadioButton("machine "); - byValueMinAlignButton = new GRadioButton(); - minAlignValueTextField = new JTextField(); - setupDefaultMinAlignButton(); - setupMachineMinAlignButton(); - setupByValueMinAlignButton(); - ButtonGroup minAlignGroup = new ButtonGroup(); - minAlignGroup.add(defaultMinAlignButton); - minAlignGroup.add(machineMinAlignButton); - minAlignGroup.add(byValueMinAlignButton); - refreshGUIMinimumAlignmentType(); - minimumAlignmentPanel = new JPanel(new GridBagLayout()); - minimumAlignmentPanel.setBorder(BorderFactory.createTitledBorder("align( minimum )")); + DataOrganization dataOrganization = + ((CompEditorModel) model).viewComposite.getDataOrganization(); + int machineAlignment = dataOrganization.getMachineAlignment(); + + defaultAlignButton = new GRadioButton("default "); + explicitAlignButton = new GRadioButton(); + explicitAlignTextField = new JTextField(); + machineAlignButton = new GRadioButton("machine: " + machineAlignment); + setupDefaultMinAlignButton(); + setupExplicitAlignButton(); + setupMachineMinAlignButton(); + ButtonGroup minAlignGroup = new ButtonGroup(); + minAlignGroup.add(defaultAlignButton); + minAlignGroup.add(explicitAlignButton); + minAlignGroup.add(machineAlignButton); + + alignPanel = new JPanel(new GridBagLayout()); + TitledBorder border = BorderFactory.createTitledBorder("align (minimum)"); +// border.setTitlePosition(TitledBorder.ABOVE_TOP); + alignPanel.setBorder(border); if (helpManager != null) { - helpManager.registerHelp(minimumAlignmentPanel, new HelpLocation( - provider.getHelpTopic(), provider.getHelpName() + "_" + "AlignMinimum")); + helpManager.registerHelp(alignPanel, + new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Align")); } - String alignmentToolTip = "" + "The minimum alignment to be used when
    " + - "aligning this data type inside another data type.

    " + - "Align this data type
    " + - "... in the default way based only on its components with no minimum,
    " + - "... to a multiple of the machine alignment,
    " + - "... to a multiple of the specified value in the text field." + ""; - minimumAlignmentPanel.setToolTipText(alignmentToolTip); + String alignmentToolTip = + "The align control allows the overall minimum alignment of this
    " + + "data type to be specified. The actual computed alignment
    " + + "may be any multiple of this value. (<F1> for help)"; + alignPanel.setToolTipText(alignmentToolTip); addMinimumAlignmentComponents(); + + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; + gridBagConstraints.fill = GridBagConstraints.NONE; + gridBagConstraints.weightx = 0; + gridBagConstraints.gridx = 5; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridheight = 4; + infoPanel.add(alignPanel, gridBagConstraints); + infoPanel.invalidate(); + + refreshGUIActualAlignmentValue(); } private void addMinimumAlignmentComponents() { @@ -460,121 +414,143 @@ public class CompEditorPanel extends CompositeEditorPanel { gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.gridwidth = 2; - minimumAlignmentPanel.add(defaultMinAlignButton, gridBagConstraints); + alignPanel.add(defaultAlignButton, gridBagConstraints); gridBagConstraints.anchor = GridBagConstraints.WEST; gridBagConstraints.fill = GridBagConstraints.NONE; gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; - gridBagConstraints.gridwidth = 2; - minimumAlignmentPanel.add(machineMinAlignButton, gridBagConstraints); + gridBagConstraints.gridwidth = 1; + alignPanel.add(explicitAlignButton, gridBagConstraints); + + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 1; + alignPanel.add(explicitAlignTextField, gridBagConstraints); gridBagConstraints.anchor = GridBagConstraints.WEST; gridBagConstraints.fill = GridBagConstraints.NONE; gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = 1; - minimumAlignmentPanel.add(byValueMinAlignButton, gridBagConstraints); - - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 2; - gridBagConstraints.gridwidth = 1; - minimumAlignmentPanel.add(minAlignValueTextField, gridBagConstraints); + gridBagConstraints.gridwidth = 2; + alignPanel.add(machineAlignButton, gridBagConstraints); } private void setupDefaultMinAlignButton() { - defaultMinAlignButton.setName("Default Minimum Alignment"); + defaultAlignButton.setName("Default Alignment"); String alignmentToolTip = - "" + "Sets this data type to have no minimum alignment
    " + - "when aligning this data type inside another data type.
    " + - "Align this data type based only on its components." + ""; - defaultMinAlignButton.setToolTipText(alignmentToolTip); + "Sets this data type to use default alignment.
    " + + "If packing is disabled, the default will be 1 byte. If packing
    " + + "is enabled, the alignment is computed based upon the pack
    " + + "setting and the alignment of each component data type."; + + defaultAlignButton.addActionListener(e -> { + ((CompEditorModel) model).setAlignmentType(AlignmentType.DEFAULT, -1); + }); + + defaultAlignButton.setToolTipText(alignmentToolTip); if (helpManager != null) { - helpManager.registerHelp(defaultMinAlignButton, new HelpLocation( - provider.getHelpTopic(), provider.getHelpName() + "_" + "AlignMinimum")); + helpManager.registerHelp(defaultAlignButton, new HelpLocation( + provider.getHelpTopic(), provider.getHelpName() + "_" + "Align")); } } private void setupMachineMinAlignButton() { - machineMinAlignButton.setName("Machine Minimum Alignment"); - String alignmentToolTip = "" + "Sets this data type to have a minimum alignment
    " + - "that is a multiple of the machine alignment
    " + - "when aligning this data type inside another data type." + ""; - machineMinAlignButton.setToolTipText(alignmentToolTip); + machineAlignButton.setName("Machine Alignment"); + String alignmentToolTip = + "Sets this data type to use the machine alignment
    " + + "as specified by the compiler specification. If packing is
    " + + "enabled, the computed alignment of this composite should be
    " + + "the machine alignment value."; + machineAlignButton.setToolTipText(alignmentToolTip); + + machineAlignButton.addActionListener(e -> { + ((CompEditorModel) model).setAlignmentType(AlignmentType.MACHINE, -1); + }); + if (helpManager != null) { - helpManager.registerHelp(machineMinAlignButton, new HelpLocation( - provider.getHelpTopic(), provider.getHelpName() + "_" + "AlignMinimum")); + helpManager.registerHelp(machineAlignButton, new HelpLocation( + provider.getHelpTopic(), provider.getHelpName() + "_" + "Align")); } } - private void setupByValueMinAlignButton() { - byValueMinAlignButton.setName("By Value Minimum Alignment"); - String alignmentToolTip = "" + "Sets this data type to have a minimum alignment
    " + - "that is a multiple of the specified value
    " + - "when aligning this data type inside another data type." + ""; - byValueMinAlignButton.setToolTipText(alignmentToolTip); + private void setupExplicitAlignButton() { + explicitAlignButton.setName("Explicit Alignment"); + String alignmentToolTip = + "Sets this data type to use the explicit alignment value
    " + + "specified. If packing is enabled, the computed alignment of
    " + + "this composite may be any multiple of this value."; + explicitAlignButton.setToolTipText(alignmentToolTip); + + explicitAlignButton.addActionListener(e -> { + chooseExplicitAlign(); + }); + if (helpManager != null) { - helpManager.registerHelp(byValueMinAlignButton, new HelpLocation( - provider.getHelpTopic(), provider.getHelpName() + "_" + "AlignMinimum")); + helpManager.registerHelp(explicitAlignButton, new HelpLocation( + provider.getHelpTopic(), provider.getHelpName() + "_" + "Align")); } - minAlignValueTextField.setName("Minimum Alignment Value"); - minAlignValueTextField.setEditable(true); - minAlignValueTextField.setToolTipText(alignmentToolTip); + explicitAlignTextField.setName("Explicit Alignment Value"); + explicitAlignTextField.setEditable(true); + explicitAlignTextField.addActionListener(e -> adjustExplicitMinimumAlignmentValue()); + + explicitAlignTextField.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + if (explicitAlignButton.isSelected()) { + return; + } + explicitAlignButton.setSelected(true); + chooseExplicitAlign(); + } + + @Override + public void focusLost(FocusEvent e) { + adjustExplicitMinimumAlignmentValue(); + } + }); + + explicitAlignTextField.setToolTipText(alignmentToolTip); if (helpManager != null) { - helpManager.registerHelp(minAlignValueTextField, new HelpLocation( - provider.getHelpTopic(), provider.getHelpName() + "_" + "AlignMinimum")); + helpManager.registerHelp(explicitAlignTextField, new HelpLocation( + provider.getHelpTopic(), provider.getHelpName() + "_" + "Align")); } refreshGUIMinimumAlignmentValue(); // Display the initial value. } - private void showMinimumAlignment() { - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0; - gridBagConstraints.gridx = 5; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridheight = 4; - infoPanel.add(minimumAlignmentPanel, gridBagConstraints); - infoPanel.invalidate(); - validate(); - } - - private void hideMinimumAlignment() { - infoPanel.remove(minimumAlignmentPanel); - infoPanel.invalidate(); - validate(); - } - - protected void adjustMinimumAlignmentValue() { - String value = minAlignValueTextField.getText(); + private void adjustExplicitMinimumAlignmentValue() { + setStatus(null); + String value = explicitAlignTextField.getText(); try { - int minAlignment = Integer.decode(value); + int minAlignment = Integer.decode(value.trim()); try { - ((CompEditorModel) model).setAlignment(minAlignment); + ((CompEditorModel) model).setAlignmentType(AlignmentType.EXPLICIT, minAlignment); adjustCompositeInfo(); } - catch (InvalidInputException e1) { + catch (IllegalArgumentException e1) { refreshGUIMinimumAlignmentValue(); - String message = "\"" + value + "\" is not a valid minimum alignment value."; + String message = "\"" + value + "\" is not a valid alignment value."; setStatus(message); } } catch (NumberFormatException e1) { refreshGUIMinimumAlignmentValue(); - String message = "\"" + value + "\" is not a valid minimum alignment value."; + String message = "\"" + value + "\" is not a valid alignment value."; setStatus(message); } } private void setupActualAlignment() { GridBagConstraints gridBagConstraints = new GridBagConstraints(); - String actualAlignmentToolTip = "" + "The actual alignment to be used when
    " + - "aligning this data type inside another data type." + ""; + String actualAlignmentToolTip = + "The actual alignment to be used for this data type.
    " + + "A combination of the pack and alignment settings made to this datatype
    " + + "combined with alignments of the individual components are used to
    " + + "to compute the actual alignment of this datatype."; JPanel actualAlignmentPanel = new JPanel(new BorderLayout()); actualAlignmentLabel = new GDLabel("Alignment:"); @@ -588,7 +564,7 @@ public class CompEditorPanel extends CompositeEditorPanel { infoPanel.add(actualAlignmentPanel, gridBagConstraints); actualAlignmentValueTextField = new JTextField(8); - actualAlignmentValueTextField.setText("" + ((CompEditorModel) model).getMinimumAlignment()); + actualAlignmentValueTextField.setText("" + ((CompEditorModel) model).getActualAlignment()); actualAlignmentValueTextField.setToolTipText(actualAlignmentToolTip); actualAlignmentValueTextField.setEditable(false); if (helpManager != null) { @@ -604,36 +580,55 @@ public class CompEditorPanel extends CompositeEditorPanel { gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 3; infoPanel.add(actualAlignmentValueTextField, gridBagConstraints); + actualAlignmentValueTextField.setBackground(new Color(getBackground().getRGB())); } private void setupPacking() { - noPackingButton = new GRadioButton("none "); - byValuePackingButton = new GRadioButton(); - packingValueTextField = new JTextField(); - setupNoPackingButton(); - setupByValuePackingButton(); + + packingPanel = new JPanel(new VerticalLayout(0)); + + packingEnablementButton = new JCheckBox("pack"); + packingEnablementButton.setEnabled(true); + packingEnablementButton.setFont(UIManager.getFont("TitledBorder.font")); + packingEnablementButton.setForeground(UIManager.getColor("TitledBorder.titleColor")); + packingPanel.add(packingEnablementButton); + + JPanel innerPanel = new JPanel(new GridBagLayout()); + innerPanel.setBorder(UIManager.getBorder("TitledBorder.border")); + packingPanel.add(innerPanel); + + defaultPackingButton = new GRadioButton("default "); + explicitPackingButton = new GRadioButton(); + explicitPackingTextField = new JTextField(); + + setupDefaultPackingButton(); + setupExplicitPackingButton(); + setupPackingEnablementButton(); ButtonGroup packingGroup = new ButtonGroup(); - packingGroup.add(noPackingButton); - packingGroup.add(byValuePackingButton); + packingGroup.add(defaultPackingButton); + packingGroup.add(explicitPackingButton); - packingPanel = new JPanel(new GridBagLayout()); - packingPanel.setBorder(BorderFactory.createTitledBorder("pack( maximum )")); if (helpManager != null) { helpManager.registerHelp(packingPanel, new HelpLocation(provider.getHelpTopic(), - provider.getHelpName() + "_" + "PackMaximum")); + provider.getHelpName() + "_" + "Pack")); } - String packingToolTipText = - "\"none\" indicates components are not being packed.
    " + - "Otherwise, the value indicates the maximum alignment to use when packing any component.
    " + - "Note: An individual data type's alignment may override this value."; - packingPanel.setToolTipText(packingToolTipText); - addPackingComponents(); + addPackingComponents(innerPanel); + + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; + gridBagConstraints.fill = GridBagConstraints.NONE; + gridBagConstraints.weightx = 0; + gridBagConstraints.gridx = 7; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridheight = 4; + infoPanel.add(packingPanel, gridBagConstraints); + refreshGUIPackingValue(); } - private void addPackingComponents() { + private void addPackingComponents(JPanel gridPanel) { GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.anchor = GridBagConstraints.WEST; @@ -641,67 +636,87 @@ public class CompEditorPanel extends CompositeEditorPanel { gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.gridwidth = 2; - packingPanel.add(noPackingButton, gridBagConstraints); + gridPanel.add(defaultPackingButton, gridBagConstraints); gridBagConstraints.anchor = GridBagConstraints.WEST; gridBagConstraints.fill = GridBagConstraints.NONE; gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = 1; - packingPanel.add(byValuePackingButton, gridBagConstraints); + gridPanel.add(explicitPackingButton, gridBagConstraints); gridBagConstraints.anchor = GridBagConstraints.WEST; gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = 1; - packingPanel.add(packingValueTextField, gridBagConstraints); + gridPanel.add(explicitPackingTextField, gridBagConstraints); + + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.fill = GridBagConstraints.NONE; + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 2; +// gridPanel.add(disabledPackingButton, gridBagConstraints); } - private void setupNoPackingButton() { - noPackingButton.setName("No Packing"); + private void setupPackingEnablementButton() { + packingEnablementButton.setName("Packing Enablement"); String packingToolTipText = - "\"none\" indicates components are not being packed.
    " + - "Otherwise, the value indicates the maximum alignment to use when packing any component.
    " + - "Note: An individual data type's alignment may override this value."; - - noPackingButton.addActionListener(e -> { - ((CompEditorModel) model).setPackingValue(Composite.NOT_PACKING); + "Enable packing when details of all components are known (including sizing and alignment).
    " + + "Disable packing when Reverse Engineering composite. (<F1> for help)"; + packingEnablementButton.addActionListener(e -> { + ((CompEditorModel) model).setPackingType( + packingEnablementButton.isSelected() ? PackingType.DEFAULT : PackingType.DISABLED, + -1); }); - noPackingButton.setToolTipText(packingToolTipText); + packingEnablementButton.setToolTipText(packingToolTipText); if (helpManager != null) { - helpManager.registerHelp(noPackingButton, new HelpLocation(provider.getHelpTopic(), - provider.getHelpName() + "_" + "PackMaximum")); + helpManager.registerHelp(packingEnablementButton, + new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Pack")); } } - private void setupByValuePackingButton() { - byValuePackingButton.setName("By Value Packing"); + private void setupDefaultPackingButton() { + defaultPackingButton.setName("Default Packing"); String packingToolTipText = - "\"none\" indicates components are not being packed.
    " + - "Otherwise, the value indicates the maximum alignment to use when packing any component.
    " + - "Note: An individual data type's alignment may override this value."; + "Indicates default compiler packing rules should be applied."; - byValuePackingButton.addActionListener(e -> chooseByValuePacking()); - byValuePackingButton.setToolTipText(packingToolTipText); + defaultPackingButton.addActionListener(e -> { + ((CompEditorModel) model).setPackingType(PackingType.DEFAULT, -1); + }); + + defaultPackingButton.setToolTipText(packingToolTipText); if (helpManager != null) { - helpManager.registerHelp(byValuePackingButton, new HelpLocation(provider.getHelpTopic(), - provider.getHelpName() + "_" + "PackMaximum")); + helpManager.registerHelp(defaultPackingButton, new HelpLocation(provider.getHelpTopic(), + provider.getHelpName() + "_" + "Pack")); + } + } + + private void setupExplicitPackingButton() { + explicitPackingButton.setName("Explicit Packing"); + String packingToolTipText = + "Indicates an explicit pack size should be applied."; + + explicitPackingButton.addActionListener(e -> chooseByValuePacking()); + explicitPackingButton.setToolTipText(packingToolTipText); + if (helpManager != null) { + helpManager.registerHelp(explicitPackingButton, new HelpLocation(provider.getHelpTopic(), + provider.getHelpName() + "_" + "Pack")); } - packingValueTextField.setName("Packing Value"); - packingValueTextField.setEditable(true); + explicitPackingTextField.setName("Packing Value"); + explicitPackingTextField.setEditable(true); + explicitPackingTextField.addActionListener(e -> adjustPackingValue()); - packingValueTextField.addActionListener(e -> adjustPackingValue()); - - packingValueTextField.addFocusListener(new FocusListener() { + explicitPackingTextField.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { - if (byValuePackingButton.isSelected()) { + if (explicitPackingButton.isSelected()) { return; } - byValuePackingButton.setSelected(true); + explicitPackingButton.setSelected(true); chooseByValuePacking(); } @@ -711,31 +726,25 @@ public class CompEditorPanel extends CompositeEditorPanel { } }); - packingValueTextField.setToolTipText(packingToolTipText); + explicitPackingTextField.setToolTipText(packingToolTipText); if (helpManager != null) { - helpManager.registerHelp(packingValueTextField, new HelpLocation( - provider.getHelpTopic(), provider.getHelpName() + "_" + "PackMaximum")); + helpManager.registerHelp(explicitPackingTextField, new HelpLocation( + provider.getHelpTopic(), provider.getHelpName() + "_" + "Pack")); } } - protected void chooseByValuePacking() { - ((CompEditorModel) model).setPackingValue(1); - packingValueTextField.selectAll(); - packingValueTextField.requestFocus(); + private void chooseByValuePacking() { + ((CompEditorModel) model).setPackingType(PackingType.EXPLICIT, 1); + explicitPackingTextField.selectAll(); + explicitPackingTextField.requestFocus(); } - protected void adjustPackingValue() { - if (!packingValueTextField.isShowing()) { - return; - } - String value = packingValueTextField.getText(); + private void adjustPackingValue() { + setStatus(null); + String value = explicitPackingTextField.getText(); try { - int packingAlignment = 0; - if (!value.toLowerCase().equals(NO_PACKING_STRING)) { - packingAlignment = Integer.decode(value); - } - - ((CompEditorModel) model).setPackingValue(packingAlignment); + int explicitPacking = Integer.decode(value.trim()); + ((CompEditorModel) model).setPackingType(PackingType.EXPLICIT, explicitPacking); adjustCompositeInfo(); } catch (NumberFormatException e1) { @@ -744,47 +753,31 @@ public class CompEditorPanel extends CompositeEditorPanel { } } - private void showPacking() { - GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.fill = GridBagConstraints.NONE; - gridBagConstraints.weightx = 0; - gridBagConstraints.gridx = 7; - gridBagConstraints.gridy = 0; - gridBagConstraints.gridheight = 4; - infoPanel.add(packingPanel, gridBagConstraints); - infoPanel.invalidate(); - validate(); - } - - private void hidePacking() { - infoPanel.remove(packingPanel); - infoPanel.invalidate(); - validate(); - } - /** * Sets the currently displayed structure packing value (maximum component alignment) */ public void refreshGUIPackingValue() { - int packingValue = ((CompEditorModel) model).getPackingValue(); - boolean isPacking = (packingValue != Composite.NOT_PACKING); - if (isPacking) { - byValuePackingButton.setSelected(true); + PackingType packingType = ((CompEditorModel) model).getPackingType(); + String packingString = ""; + + boolean packingEnabled = packingType != PackingType.DISABLED; + packingEnablementButton.setSelected(packingEnabled); + + defaultPackingButton.setEnabled(packingEnabled); + explicitPackingButton.setEnabled(packingEnabled); + explicitPackingTextField.setEnabled(packingEnabled); + + if (packingType == PackingType.DEFAULT) { + defaultPackingButton.setSelected(true); } - else { - noPackingButton.setSelected(true); - } - String packingString; - if (isPacking) { + else if (packingType == PackingType.EXPLICIT) { + int packValue = ((CompEditorModel) model).getExplicitPackingValue(); packingString = - model.showHexNumbers ? CompositeViewerModel.getHexString(packingValue, true) - : Integer.toString(packingValue); + model.showHexNumbers ? CompositeViewerModel.getHexString(packValue, true) + : Integer.toString(packValue); + explicitPackingButton.setSelected(true); } - else { - packingString = NO_PACKING_STRING; - } - packingValueTextField.setText(packingString); + explicitPackingTextField.setText(packingString); } protected void setupSize() { @@ -798,18 +791,18 @@ public class CompEditorPanel extends CompositeEditorPanel { gridBagConstraints.gridy = 3; infoPanel.add(sizeLabel, gridBagConstraints); - sizeStatusTextField = new JTextField(10); - sizeStatusTextField.setName("Total Length"); - sizeStatusTextField.setEditable(false); - sizeStatusTextField.setToolTipText("The current size in bytes."); + sizeTextField = new JTextField(10); + sizeTextField.setName("Total Length"); + sizeTextField.setToolTipText("The current size in bytes."); + setSizeEditable(false); gridBagConstraints.ipadx = 60; gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 3; - infoPanel.add(sizeStatusTextField, gridBagConstraints); + infoPanel.add(sizeTextField, gridBagConstraints); - sizeStatusTextField.addActionListener(e -> updatedStructureSize()); - sizeStatusTextField.addFocusListener(new FocusListener() { + sizeTextField.addActionListener(e -> updatedStructureSize()); + sizeTextField.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { // don't care @@ -817,7 +810,7 @@ public class CompEditorPanel extends CompositeEditorPanel { @Override public void focusLost(FocusEvent e) { - if (sizeStatusTextField.isEditable()) { + if (sizeTextField.isEditable()) { updatedStructureSize(); } } @@ -825,7 +818,15 @@ public class CompEditorPanel extends CompositeEditorPanel { } protected void setSizeEditable(boolean editable) { - sizeStatusTextField.setEditable(editable); + sizeTextField.setEditable(editable); + if (editable) { + // editable - use same background as category field + sizeTextField.setBackground(categoryStatusTextField.getBackground()); + } + else { + // not editable - use same background as panel + sizeTextField.setBackground(new Color(getBackground().getRGB())); + } } private void updatedStructureSize() { @@ -833,7 +834,7 @@ public class CompEditorPanel extends CompositeEditorPanel { if (updatingSize) { return; } - if (!sizeStatusTextField.isShowing()) { + if (!sizeTextField.isShowing()) { return; } @@ -841,7 +842,7 @@ public class CompEditorPanel extends CompositeEditorPanel { return; } - String valueStr = sizeStatusTextField.getText(); + String valueStr = sizeTextField.getText(); Integer value; try { updatingSize = true; @@ -866,6 +867,7 @@ public class CompEditorPanel extends CompositeEditorPanel { } } ((StructureEditorModel) model).setStructureSize(structureSize); + model.setStatus(null); } } catch (NumberFormatException e1) { @@ -931,37 +933,14 @@ public class CompEditorPanel extends CompositeEditorPanel { else if (source == descriptionTextField) { updatedDescription(); } - else if (source == defaultMinAlignButton) { - chooseDefaultMinAlign(); - } - else if (source == machineMinAlignButton) { - chooseMachineMinAlign(); - } - else if (source == byValueMinAlignButton) { - chooseByValueMinAlign(); - } - else if (source == minAlignValueTextField) { - updatedMinAlignValue(); - } }; nameTextField.addActionListener(fieldActionListener); descriptionTextField.addActionListener(fieldActionListener); - defaultMinAlignButton.addActionListener(fieldActionListener); - machineMinAlignButton.addActionListener(fieldActionListener); - byValueMinAlignButton.addActionListener(fieldActionListener); - minAlignValueTextField.addActionListener(fieldActionListener); fieldFocusListener = new FocusListener() { @Override public void focusGained(FocusEvent e) { - Object source = e.getSource(); - if (source == minAlignValueTextField) { - if (byValueMinAlignButton.isSelected()) { - return; - } - byValueMinAlignButton.setSelected(true); - chooseByValueMinAlign(); - } + // ignore } @Override @@ -973,33 +952,24 @@ public class CompEditorPanel extends CompositeEditorPanel { else if (source == descriptionTextField) { updatedDescription(); } - else if (source == minAlignValueTextField) { - updatedMinAlignValue(); - } } }; nameTextField.addFocusListener(fieldFocusListener); descriptionTextField.addFocusListener(fieldFocusListener); - minAlignValueTextField.addFocusListener(fieldFocusListener); - } - protected void chooseDefaultMinAlign() { - ((CompEditorModel) model).setAlignmentType(AlignmentType.DEFAULT_ALIGNED); - } - - protected void chooseMachineMinAlign() { - ((CompEditorModel) model).setAlignmentType(AlignmentType.MACHINE_ALIGNED); - } - - protected void chooseByValueMinAlign() { - ((CompEditorModel) model).setAlignmentType(AlignmentType.ALIGNED_BY_VALUE); - minAlignValueTextField.selectAll(); - minAlignValueTextField.requestFocus(); - } - - protected void updatedMinAlignValue() { - adjustMinimumAlignmentValue(); + private void chooseExplicitAlign() { + if (((CompEditorModel) model).getAlignmentType() != AlignmentType.EXPLICIT) { + Composite viewComposite = ((CompEditorModel) model).viewComposite; + int defaultValue = 1; + if (viewComposite.isPackingEnabled()) { + defaultValue = viewComposite.getDataOrganization().getMachineAlignment(); + } + ((CompEditorModel) model).setAlignmentType(AlignmentType.EXPLICIT, + defaultValue); + } + explicitAlignTextField.selectAll(); + explicitAlignTextField.requestFocus(); } private void removeFieldListeners() { @@ -1011,14 +981,14 @@ public class CompEditorPanel extends CompositeEditorPanel { descriptionTextField.removeActionListener(fieldActionListener); descriptionTextField.removeFocusListener(fieldFocusListener); - defaultMinAlignButton.addActionListener(fieldActionListener); + defaultAlignButton.addActionListener(fieldActionListener); - machineMinAlignButton.addActionListener(fieldActionListener); + machineAlignButton.addActionListener(fieldActionListener); - byValueMinAlignButton.addActionListener(fieldActionListener); + explicitAlignButton.addActionListener(fieldActionListener); - minAlignValueTextField.addActionListener(fieldActionListener); - minAlignValueTextField.removeFocusListener(fieldFocusListener); + explicitAlignTextField.addActionListener(fieldActionListener); + explicitAlignTextField.removeFocusListener(fieldFocusListener); } /** @@ -1139,56 +1109,31 @@ public class CompEditorPanel extends CompositeEditorPanel { descriptionTextField.setText(description); } - /** - * Checks the GUI to determine if this composite is internally aligned - * @return true if interanlly aligned - */ - public boolean isInternallyAlignedInGui() { - return internalAlignmentCheckBox.isSelected(); - } - - /** - * Sets the currently displayed structure minimum alignment type - * - * @param aligned true if aligned - */ - public void setInternallyAligned(boolean aligned) { - boolean alignedInGui = internalAlignmentCheckBox.isSelected(); - if (alignedInGui != aligned) { - internalAlignmentCheckBox.setSelected(aligned); - } - adjustInternalAlignment(); - } - - /** - * Updates the GUI display of the minimum alignment value. - */ public void refreshGUIMinimumAlignmentValue() { - int minimumAlignment = ((CompEditorModel) model).getMinimumAlignment(); - String value = minAlignValueTextField.getText(); - boolean emptyValue = (value.length() == 0); - boolean notByValue = ((CompEditorModel) model).viewComposite.isDefaultAligned() || - ((CompEditorModel) model).viewComposite.isMachineAligned(); - if (notByValue) { - if (!emptyValue) { - minAlignValueTextField.setText(""); - } - return; // No value displayed since default or machine. - } - // Change the display to the correct value. - String minimumAlignmentStr = - model.showHexNumbers ? CompositeViewerModel.getHexString(minimumAlignment, true) - : Integer.toString(minimumAlignment); - minAlignValueTextField.setText(minimumAlignmentStr); + AlignmentType alignmentType = ((CompEditorModel) model).getAlignmentType(); + String minimumAlignmentStr = ""; + if (alignmentType == AlignmentType.DEFAULT) { + defaultAlignButton.setSelected(true); + } + else if (alignmentType == AlignmentType.MACHINE) { + machineAlignButton.setSelected(true); + } + else { + explicitAlignButton.setSelected(true); + int minimumAlignment = ((CompEditorModel) model).getExplicitMinimumAlignment(); + minimumAlignmentStr = + model.showHexNumbers ? CompositeViewerModel.getHexString(minimumAlignment, true) + : Integer.toString(minimumAlignment); + } + explicitAlignTextField.setText(minimumAlignmentStr); } /** * Updates the GUI display of the actual alignment value. */ public void refreshGUIActualAlignmentValue() { - int actualAlignment = ((CompEditorModel) model).viewDTM.getDataOrganization().getAlignment( - ((CompEditorModel) model).viewComposite, ((CompEditorModel) model).getLength()); + int actualAlignment = ((CompEditorModel) model).getActualAlignment(); String alignmentStr = model.showHexNumbers ? CompositeViewerModel.getHexString(actualAlignment, true) : Integer.toString(actualAlignment); @@ -1200,7 +1145,7 @@ public class CompEditorPanel extends CompositeEditorPanel { * @return the size */ public int getCompositeSize() { - return Integer.decode(sizeStatusTextField.getText()); + return Integer.decode(sizeTextField.getText()); } /** @@ -1210,12 +1155,12 @@ public class CompEditorPanel extends CompositeEditorPanel { */ public void setCompositeSize(int size) { boolean sizeIsEditable = ((CompEditorModel) model).isSizeEditable(); - if (sizeStatusTextField.isEditable() != sizeIsEditable) { - sizeStatusTextField.setEditable(sizeIsEditable); + if (sizeTextField.isEditable() != sizeIsEditable) { + setSizeEditable(sizeIsEditable); } String sizeStr = model.showHexNumbers ? CompositeViewerModel.getHexString(size, true) : Integer.toString(size); - sizeStatusTextField.setText(sizeStr); + sizeTextField.setText(sizeStr); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java index 334ce10939..f925f51541 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorModel.java @@ -35,7 +35,6 @@ import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.util.*; import ghidra.util.exception.*; -import ghidra.util.task.TaskMonitor; /** * Model for editing a composite data type. Specific composite data type editors @@ -546,7 +545,7 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen } @Override - public void deleteSelectedComponents(TaskMonitor monitor) throws UsrException { + public void deleteSelectedComponents() throws UsrException { if (!isDeleteAllowed()) { throw new UsrException("Deleting is not allowed."); } @@ -610,8 +609,8 @@ public abstract class CompositeEditorModel extends CompositeViewerModel implemen private boolean hasCompPathNameChanges(Composite currentViewComposite, Composite oldComposite) { // Check component data type pathnames. - DataTypeComponent[] comps = currentViewComposite.getComponents(); - DataTypeComponent[] oldComps = oldComposite.getComponents(); + DataTypeComponent[] comps = currentViewComposite.getDefinedComponents(); + DataTypeComponent[] oldComps = oldComposite.getDefinedComponents(); if (comps.length != oldComps.length) { return true; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java index d7f748cd5f..4cbf429fc0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java @@ -153,7 +153,7 @@ public abstract class CompositeEditorPanel extends JPanel private boolean launchBitFieldEditor(int modelColumn, int editingRow) { if (model.viewComposite instanceof Structure && - !model.viewComposite.isInternallyAligned() && + !model.viewComposite.isPackingEnabled() && model.getDataTypeColumn() == modelColumn && editingRow < model.getNumComponents()) { // check if we are attempting to edit a bitfield DataTypeComponent dtComponent = model.getComponent(editingRow); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java index f475d58d4f..8e966a9f24 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeViewerModel.java @@ -363,7 +363,7 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager * @return this size */ public int getLength() { - if (viewComposite != null && !viewComposite.isNotYetDefined()) { + if (viewComposite != null && !viewComposite.isZeroLength()) { return viewComposite.getLength(); } return 0; @@ -1386,8 +1386,7 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager protected void selectionChanged() { updatingSelection(() -> { - for (int i = 0; i < modelListeners.size(); i++) { - CompositeViewerModelListener listener = modelListeners.get(i); + for (CompositeViewerModelListener listener : modelListeners) { listener.selectionChanged(); } }); @@ -1411,8 +1410,7 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager */ protected void notify(List listeners, Consumer method) { swing(() -> { - for (int i = 0; i < listeners.size(); i++) { - T listener = listeners.get(i); + for (T listener : listeners) { method.accept(listener); } }); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java index 95252ee9fa..cd236c29df 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/DeleteAction.java @@ -52,7 +52,7 @@ public class DeleteAction extends CompositeEditorTableAction { private void doDelete(TaskMonitor monitor) { try { - model.deleteSelectedComponents(monitor); + model.deleteSelectedComponents(); } catch (CancelledException e) { // user cancelled diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java index 6b3c7125b7..605b6530e6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditBitFieldAction.java @@ -48,7 +48,7 @@ public class EditBitFieldAction extends CompositeEditorTableAction { private DataTypeComponent getUnalignedBitFieldComponent() { CompEditorModel editorModel = (CompEditorModel) model; if ((editorModel.viewComposite instanceof Structure) && - !editorModel.viewComposite.isInternallyAligned() && + !editorModel.viewComposite.isPackingEnabled() && editorModel.getNumSelectedRows() == 1) { int rowIndex = model.getSelectedRows()[0]; if (rowIndex < model.getNumComponents()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java index ce3456616a..e915024ec2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorModel.java @@ -322,8 +322,7 @@ public interface EditorModel { public void cycleDataType(CycleGroup cycleGroup); /** - * - * @param parent + * Create array component * @throws UsrException */ public void createArray() throws UsrException; @@ -331,10 +330,9 @@ public interface EditorModel { /** * Delete the selected components. * - * @param monitor the task monitor * @throws UsrException if the data type isn't allowed to be deleted. */ - public void deleteSelectedComponents(TaskMonitor monitor) throws UsrException; + public void deleteSelectedComponents() throws UsrException; /** * Creates multiple duplicates of the indicated component. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java index 8915ea41eb..72a8e6e557 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/InsertUndefinedAction.java @@ -58,7 +58,7 @@ public class InsertUndefinedAction extends CompositeEditorTableAction { int index = model.getMinIndexSelected(); if (index >= 0) { DataType undefinedDt = - model.viewComposite.isInternallyAligned() ? Undefined1DataType.dataType + model.viewComposite.isPackingEnabled() ? Undefined1DataType.dataType : DataType.DEFAULT; DataTypeInstance dti = DataTypeInstance.getDataTypeInstance(undefinedDt, -1); model.insert(index, dti.getDataType(), dti.getLength()); @@ -77,7 +77,7 @@ public class InsertUndefinedAction extends CompositeEditorTableAction { if (model.viewComposite instanceof Structure) { boolean isContiguousSelection = model.getSelection().getNumRanges() == 1; DataType undefinedDt = - model.viewComposite.isInternallyAligned() ? Undefined1DataType.dataType + model.viewComposite.isPackingEnabled() ? Undefined1DataType.dataType : DataType.DEFAULT; enabled = isContiguousSelection && model.isInsertAllowed(model.getMinIndexSelected(), undefinedDt); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java index 34e188ba43..f5436d210d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorModel.java @@ -25,7 +25,6 @@ import docking.widgets.dialogs.InputDialogListener; import docking.widgets.fieldpanel.support.FieldRange; import docking.widgets.fieldpanel.support.FieldSelection; import ghidra.docking.settings.SettingsImpl; -import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; import ghidra.program.model.lang.InsufficientBytesException; import ghidra.util.Msg; @@ -201,17 +200,7 @@ class StructureEditorModel extends CompEditorModel { @Override public int getNumComponents() { - if (viewComposite == null) { - return 0; - } - if (isShowingUndefinedBytes()) { - if (viewComposite.isNotYetDefined()) { - return 0; - } - return viewComposite.getNumComponents(); - } - DataTypeComponent[] definedComponents = ((Structure) viewComposite).getDefinedComponents(); - return definedComponents.length; + return viewComposite == null ? 0 : viewComposite.getNumComponents(); } @Override @@ -252,14 +241,14 @@ class StructureEditorModel extends CompEditorModel { @Override protected boolean isSizeEditable() { - return !isAligned(); + return !isPackingEnabled(); } void setStructureSize(int size) { if (viewComposite == null) { return; } - int currentLength = viewComposite.isNotYetDefined() ? 0 : viewComposite.getLength(); + int currentLength = viewComposite.isZeroLength() ? 0 : viewComposite.getLength(); if (currentLength == size) { return; } @@ -449,8 +438,8 @@ class StructureEditorModel extends CompEditorModel { DataTypeComponent comp = deleteComponentAndResidual(startIndex - 1); try { - if (!isAligned() && comp.isBitFieldComponent()) { - // insert residual undefined bytes before inserting unaligned bitfield + if (!isPackingEnabled() && comp.isBitFieldComponent()) { + // insert residual undefined bytes before inserting non-packed bitfield int lenChange = len - getLength(); insert(endIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY); } @@ -485,8 +474,8 @@ class StructureEditorModel extends CompEditorModel { DataTypeComponent comp = deleteComponentAndResidual(endIndex + 1); try { - if (!isAligned() && comp.isBitFieldComponent()) { - // insert residual undefined bytes before inserting unaligned bitfield + if (!isPackingEnabled() && comp.isBitFieldComponent()) { + // insert residual undefined bytes before inserting non-packed bitfield int lenChange = len - getLength(); insert(startIndex, DataType.DEFAULT, 1, lenChange, TaskMonitor.DUMMY); } @@ -507,7 +496,7 @@ class StructureEditorModel extends CompEditorModel { DataTypeComponent comp = getComponent(index); deleteComponent(index); - if (isAligned() || !comp.isBitFieldComponent() || index >= getNumComponents()) { + if (isPackingEnabled() || !comp.isBitFieldComponent() || index >= getNumComponents()) { return comp; } @@ -627,7 +616,7 @@ class StructureEditorModel extends CompEditorModel { } @Override - public void deleteSelectedComponents(TaskMonitor monitor) throws UsrException { + public void deleteSelectedComponents() throws UsrException { if (!isDeleteAllowed()) { throw new UsrException("Deleting is not allowed."); } @@ -646,7 +635,7 @@ class StructureEditorModel extends CompEditorModel { selectionChanged(); return; } - super.deleteSelectedComponents(monitor); + super.deleteSelectedComponents(); } @Override @@ -660,14 +649,14 @@ class StructureEditorModel extends CompEditorModel { int rowIndex = getRow(); DataTypeComponent comp = getComponent(rowIndex); DataType dt = comp.getDataType(); - if (viewComposite.isInternallyAligned()) { + if (viewComposite.isPackingEnabled()) { return true; } if (dt.equals(DataType.DEFAULT)) { return true; // Insert an undefined and push everything down. } if (comp.isBitFieldComponent()) { - return false; // unable to place unaligned bitfield in a reasonable fashion + return false; // unable to place non-packed bitfield in a reasonable fashion } // Can always duplicate at the end. if (isAtEnd(rowIndex) || onlyUndefinedsUntilEnd(rowIndex + 1)) { @@ -956,7 +945,7 @@ class StructureEditorModel extends CompEditorModel { dtc.getComment()); } else { - if (isAligned() || !(dataType instanceof BitFieldDataType)) { + if (isPackingEnabled() || !(dataType instanceof BitFieldDataType)) { dtc = ((Structure) viewComposite).insert(rowIndex, dataType, length, name, comment); } @@ -1174,41 +1163,9 @@ class StructureEditorModel extends CompEditorModel { } } - @Override - public void setAligned(boolean aligned) { - boolean currentViewIsAligned = viewComposite.isInternallyAligned(); - if (currentViewIsAligned == aligned) { - return; - } - viewComposite.setInternallyAligned(aligned); - if (fixSelection()) { - selectionChanged(); - } - notifyCompositeChanged(); - } - - public void adjustAlignment(PluginTool tool, int minAlignment) { - int currentViewAlignment = viewComposite.getMinimumAlignment(); - if (currentViewAlignment == minAlignment) { - return; - } - viewComposite.setMinimumAlignment(minAlignment); - notifyCompositeChanged(); - } - - @Override - public void setAlignment(int minAlignment) throws InvalidInputException { - int currentViewAlignment = viewComposite.getMinimumAlignment(); - if (currentViewAlignment == minAlignment) { - return; - } - viewComposite.setMinimumAlignment(minAlignment); - notifyCompositeChanged(); - } - @Override public boolean isShowingUndefinedBytes() { - return !viewComposite.isInternallyAligned(); + return !viewComposite.isPackingEnabled(); } public void createInternalStructure(TaskMonitor monitor) @@ -1259,7 +1216,7 @@ class StructureEditorModel extends CompEditorModel { final StructureDataType structureDataType = new StructureDataType(originalCategoryPath, uniqueName, length, originalDTM); -// if (isAligned()) { +// if (isPackingEnabled()) { // structureDataType.setPackingValue(getPackingValue()); // } @@ -1281,7 +1238,7 @@ class StructureEditorModel extends CompEditorModel { length += compLength; - if (!structureDataType.isInternallyAligned() && component.isBitFieldComponent()) { + if (!structureDataType.isPackingEnabled() && component.isBitFieldComponent()) { BitFieldDataType bitfield = (BitFieldDataType) dt; structureDataType.insertBitFieldAt(component.getOffset() - firstDtc.getOffset(), compLength, bitfield.getBitOffset(), bitfield.getBaseDataType(), @@ -1296,8 +1253,8 @@ class StructureEditorModel extends CompEditorModel { lastDtc = component; } DataType addedDataType = createDataTypeInOriginalDTM(structureDataType); - if (viewComposite.isInternallyAligned()) { - deleteSelectedComponents(monitor); + if (viewComposite.isPackingEnabled()) { + deleteSelectedComponents(); insert(minRow, addedDataType, addedDataType.getLength()); } else { @@ -1434,7 +1391,7 @@ class StructureEditorModel extends CompEditorModel { DataTypeComponent dtc = struct.getComponent(i); DataType dt = dtc.getDataType(); int compLength = dtc.getLength(); - if (!isAligned()) { + if (!isPackingEnabled()) { if (dtc.isBitFieldComponent()) { BitFieldDataType bitfield = (BitFieldDataType) dt; viewStruct.insertBitFieldAt(currentOffset + dtc.getOffset(), compLength, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java index 93366c7c03..3e5a8420e1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorModel.java @@ -40,7 +40,8 @@ import docking.widgets.fieldpanel.support.FieldSelection; import ghidra.program.model.data.*; import ghidra.program.model.lang.InsufficientBytesException; -import ghidra.util.exception.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.UsrException; import ghidra.util.task.TaskMonitor; class UnionEditorModel extends CompEditorModel { @@ -502,16 +503,6 @@ class UnionEditorModel extends CompEditorModel { } } - @Override - public void setAlignment(int minAlignment) throws InvalidInputException { - long currentViewAlignment = viewComposite.getMinimumAlignment(); - if (currentViewAlignment == minAlignment) { - return; - } - viewComposite.setMinimumAlignment(minAlignment); - notifyCompositeChanged(); - } - /** * Returns the number of undefined bytes that are available in the structure * beginning at the specified row index. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java index 7971464994..30bf283847 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java @@ -494,7 +494,6 @@ public class DataTypeSynchronizer { dataType.setLastChangeTimeInSourceArchive(0); // Set timestamp so user must re-sync. fixedSync = true; - dataTypeManager.dataTypeChanged(dataType); } } if (fixedSync) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java index 8919c69140..d526bbb203 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java @@ -152,10 +152,11 @@ public class DataTypesProvider extends ComponentProviderAdapter { addLocalAction(new DeleteArchiveAction(plugin)); addLocalAction(new RenameAction(plugin)); addLocalAction(new EditAction(plugin)); - addLocalAction(new AlignDataTypeAction(plugin)); + // NOTE: it make very little sense to blindly enable packing +// addLocalAction(new PackDataTypeAction(plugin)); // addLocalAction( new PackDataTypeAction( plugin )); // addLocalAction( new PackSizeDataTypeAction( plugin )); - addLocalAction(new AlignAllDataTypesAction(plugin)); +// addLocalAction(new PackAllDataTypesAction(plugin)); // addLocalAction( new DefineDataTypeAlignmentAction( plugin )); addLocalAction(new CreateEnumFromSelectionAction(plugin)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AlignDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/Pack1DataTypeAction.java similarity index 51% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AlignDataTypeAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/Pack1DataTypeAction.java index 88578c0dcd..def536c39c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AlignDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/Pack1DataTypeAction.java @@ -25,97 +25,62 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode; import ghidra.program.model.data.*; -import ghidra.util.HelpLocation; import ghidra.util.Msg; -public class AlignDataTypeAction extends DockingAction { +public class Pack1DataTypeAction extends DockingAction { + private DataTypeManagerPlugin plugin; - public AlignDataTypeAction(DataTypeManagerPlugin plugin) { - super("Align Data Type", plugin.getName()); - setPopupMenuData(new MenuData(new String[] { "Align" }, "Edit")); - setHelpLocation(new HelpLocation(plugin.getName(), getName())); - } - - @Override - public boolean isAddToPopup(ActionContext context) { - DataTypeNode node = getSelectedDataTypeNode(context); - if (node == null) { - return false; - } - DataType dataType = node.getDataType(); - if (dataType instanceof BuiltInDataType || dataType instanceof Pointer || - dataType instanceof MissingBuiltInDataType) { - return false; - } - if (!node.isModifiable()) { - return false; - } - return true; + public Pack1DataTypeAction(DataTypeManagerPlugin plugin) { + super("Pack1 Data Type", plugin.getName()); + this.plugin = plugin; + setPopupMenuData(new MenuData(new String[] { "Pack (1)" }, "Edit")); } @Override public boolean isEnabledForContext(ActionContext context) { - DataTypeNode node = getSelectedDataTypeNode(context); - if (node == null) { - return false; - } - DataType dataType = node.getDataType(); - if (dataType instanceof Composite) { - return !((Composite) dataType).isInternallyAligned(); - } - return false; - } - - private DataTypeNode getSelectedDataTypeNode(ActionContext context) { Object contextObject = context.getContextObject(); if (!(contextObject instanceof GTree)) { - return null; + return false; } GTree gTree = (GTree) contextObject; TreePath[] selectionPaths = gTree.getSelectionPaths(); if (selectionPaths.length != 1) { - return null; + return false; } DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent(); if (!(node instanceof DataTypeNode)) { - return null; + return false; } - return (DataTypeNode) node; + setEnabled(node.isModifiable()); + return true; } @Override public void actionPerformed(ActionContext context) { GTree gTree = (GTree) context.getContextObject(); TreePath[] selectionPaths = gTree.getSelectionPaths(); - for (TreePath treePath : selectionPaths) { - final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent(); - DataType dataType = dataTypeNode.getDataType(); - DataTypeManager dataTypeManager = dataType.getDataTypeManager(); - DataOrganization dataOrganization = dataTypeManager.getDataOrganization(); - alignDataType(dataType, dataOrganization); + if (selectionPaths.length != 1) { + Msg.error(this, "Pack is only allowed on an individual data type."); + return; } - } - - private void alignDataType(DataType dataType, DataOrganization dataOrganization) { + TreePath treePath = selectionPaths[0]; + final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent(); + DataType dataType = dataTypeNode.getDataType(); DataTypeManager dataTypeManager = dataType.getDataTypeManager(); if (dataTypeManager == null) { - Msg.error(this, "Can't align data type " + dataType.getName() + - " without a data type manager."); - return; - } - if (!(dataType instanceof Structure)) { - Msg.error(this, "Can't align data type " + dataType.getName() + - ". It's not a structure."); + Msg.error(this, + "Can't pack data type " + dataType.getName() + " without a data type manager."); return; } + int transactionID = -1; boolean commit = false; try { // start a transaction - transactionID = dataTypeManager.startTransaction("align " + dataType.getName()); - ((Structure) dataType).setInternallyAligned(true); + transactionID = dataTypeManager.startTransaction("pack of " + dataType.getName()); + packDataType(dataType); commit = true; } finally { @@ -124,4 +89,13 @@ public class AlignDataTypeAction extends DockingAction { } } + private void packDataType(DataType dataType) { + if (!(dataType instanceof Composite)) { + Msg.error(this, + "Can't pack data type " + dataType.getName() + ". It's not a composite."); + return; + } + ((Composite) dataType).pack(1); + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AlignAllDataTypesAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackAllDataTypesAction.java similarity index 72% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AlignAllDataTypesAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackAllDataTypesAction.java index 8abbd3163f..de63eb7293 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AlignAllDataTypesAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackAllDataTypesAction.java @@ -29,19 +29,18 @@ import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.archive.Archive; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.program.model.data.*; -import ghidra.util.HelpLocation; import ghidra.util.Msg; -public class AlignAllDataTypesAction extends DockingAction { +public class PackAllDataTypesAction extends DockingAction { private DataTypeManagerPlugin plugin; - public AlignAllDataTypesAction(DataTypeManagerPlugin plugin) { - super("Align All Data Types", plugin.getName()); + public PackAllDataTypesAction(DataTypeManagerPlugin plugin) { + super("Pack All Composites", plugin.getName()); this.plugin = plugin; - setPopupMenuData(new MenuData(new String[] { "Align All..." }, "Edit")); - setHelpLocation(new HelpLocation(plugin.getName(), getName())); + setPopupMenuData(new MenuData(new String[] { "Pack All..." }, "Edit")); +// setHelpLocation(new HelpLocation(plugin.getName(), getName())); } @Override @@ -85,27 +84,27 @@ public class AlignAllDataTypesAction extends DockingAction { int result = OptionDialog.showOptionDialog( plugin.getTool().getToolFrame(), - "Align Data Types", - "Are you sure you want to align all of the data types in " + + "Pack All Composites", + "Are you sure you want to enable packing of all non-packed composites in " + dataTypeManager.getName() + - "?\nBoth structures and unions that are currently unaligned will become aligned.\n" + - "This could cause component offsets to change and datatype sizes to change.\n" + + "?\nAll structures and unions that are not currently packed will default packing enabled.\n" + + "This could cause component offsets to change as well as size and alignment of these data types to change.\n" + "Do you want to continue?", "Continue", OptionDialog.WARNING_MESSAGE); if (result == OptionDialog.CANCEL_OPTION) { return; } - alignDataTypes(dataTypeManager, dataOrganization); + packDataTypes(dataTypeManager, dataOrganization); } else { - Msg.showWarn(this, gTree, "Alignment Not Allowed", - "The archive must be modifiable to align data types."); + Msg.showWarn(this, gTree, "Modification Not Allowed", + "The archive must be modifiable to pack data types."); } } } - private void alignDataTypes(DataTypeManager dataTypeManager, DataOrganization dataOrganization) { + private void packDataTypes(DataTypeManager dataTypeManager, DataOrganization dataOrganization) { if (dataTypeManager == null) { - Msg.error(this, "Can't align data types without a data type manager."); + Msg.error(this, "Can't pack data types without a data type manager."); return; } int transactionID = -1; @@ -113,9 +112,8 @@ public class AlignAllDataTypesAction extends DockingAction { try { // start a transaction transactionID = - dataTypeManager.startTransaction("Align all data types in " + - dataTypeManager.getName()); - alignEachStructure(dataTypeManager, dataOrganization); + dataTypeManager.startTransaction("Pack Composite Types"); + packEachStructure(dataTypeManager, dataOrganization); commit = true; } finally { @@ -124,12 +122,14 @@ public class AlignAllDataTypesAction extends DockingAction { } } - private void alignEachStructure(DataTypeManager dataTypeManager, + private void packEachStructure(DataTypeManager dataTypeManager, DataOrganization dataOrganization) { Iterator allComposites = dataTypeManager.getAllComposites(); while (allComposites.hasNext()) { Composite composite = allComposites.next(); - composite.setInternallyAligned(true); + if (!composite.isPackingEnabled()) { + composite.setPackingEnabled(true); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackDataTypeAction.java index 9c08a29697..ec93f82c93 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackDataTypeAction.java @@ -26,81 +26,101 @@ import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode; import ghidra.program.model.data.*; import ghidra.util.Msg; -import ghidra.util.exception.InvalidInputException; public class PackDataTypeAction extends DockingAction { - private DataTypeManagerPlugin plugin; public PackDataTypeAction(DataTypeManagerPlugin plugin) { super("Pack Data Type", plugin.getName()); - this.plugin = plugin; - setPopupMenuData(new MenuData(new String[] { "Pack" }, "Edit")); + setPopupMenuData(new MenuData(new String[] { "Pack (default)" }, "Edit")); +// setHelpLocation(new HelpLocation(plugin.getName(), getName())); + } + + @Override + public boolean isAddToPopup(ActionContext context) { + DataTypeNode node = getSelectedDataTypeNode(context); + if (node == null) { + return false; + } + DataType dataType = node.getDataType(); + if (dataType instanceof BuiltInDataType || dataType instanceof Pointer || + dataType instanceof MissingBuiltInDataType) { + return false; + } + if (!node.isModifiable()) { + return false; + } + return true; } @Override public boolean isEnabledForContext(ActionContext context) { + DataTypeNode node = getSelectedDataTypeNode(context); + if (node == null) { + return false; + } + DataType dataType = node.getDataType(); + if (dataType instanceof Composite) { + return !((Composite) dataType).isPackingEnabled(); + } + return false; + } + + private DataTypeNode getSelectedDataTypeNode(ActionContext context) { Object contextObject = context.getContextObject(); if (!(contextObject instanceof GTree)) { - return false; + return null; } GTree gTree = (GTree) contextObject; TreePath[] selectionPaths = gTree.getSelectionPaths(); if (selectionPaths.length != 1) { - return false; + return null; } DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent(); if (!(node instanceof DataTypeNode)) { - return false; + return null; } - setEnabled(node.isModifiable()); - return true; + return (DataTypeNode) node; } @Override public void actionPerformed(ActionContext context) { GTree gTree = (GTree) context.getContextObject(); TreePath[] selectionPaths = gTree.getSelectionPaths(); - if (selectionPaths.length != 1) { - Msg.error(this, "Pack is only allowed on an individual data type."); - return; + for (TreePath treePath : selectionPaths) { + final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent(); + DataType dataType = dataTypeNode.getDataType(); + DataTypeManager dataTypeManager = dataType.getDataTypeManager(); + DataOrganization dataOrganization = dataTypeManager.getDataOrganization(); + alignDataType(dataType, dataOrganization); } - TreePath treePath = selectionPaths[0]; - final DataTypeNode dataTypeNode = (DataTypeNode) treePath.getLastPathComponent(); - DataType dataType = dataTypeNode.getDataType(); + } + + private void alignDataType(DataType dataType, DataOrganization dataOrganization) { DataTypeManager dataTypeManager = dataType.getDataTypeManager(); if (dataTypeManager == null) { - Msg.error(this, - "Can't pack data type " + dataType.getName() + " without a data type manager."); + Msg.error(this, "Can't align data type " + dataType.getName() + + " without a data type manager."); + return; + } + if (!(dataType instanceof Structure)) { + Msg.error(this, "Can't align data type " + dataType.getName() + + ". It's not a structure."); return; } - int transactionID = -1; boolean commit = false; try { // start a transaction - transactionID = dataTypeManager.startTransaction("pack of " + dataType.getName()); - packDataType(dataType); + transactionID = dataTypeManager.startTransaction("align " + dataType.getName()); + ((Structure) dataType).setPackingEnabled(true); commit = true; } - catch (InvalidInputException iie) { - // TODO Auto-generated catch block - iie.printStackTrace(); - } finally { // commit the changes dataTypeManager.endTransaction(transactionID, commit); } } - private void packDataType(DataType dataType) throws InvalidInputException { - if (!(dataType instanceof Structure)) { - Msg.error(this, - "Can't pack data type " + dataType.getName() + ". It's not a structure."); - return; - } - ((Structure) dataType).pack(1); - } - } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackSizeDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackSizeDataTypeAction.java index 78a0a0c99a..ee538ddb98 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackSizeDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PackSizeDataTypeAction.java @@ -27,7 +27,6 @@ import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeTreeNode; import ghidra.program.model.data.*; import ghidra.util.Msg; -import ghidra.util.exception.InvalidInputException; public class PackSizeDataTypeAction extends DockingAction { @@ -73,7 +72,8 @@ public class PackSizeDataTypeAction extends DockingAction { return; } - NumberInputDialog numberInputDialog = new NumberInputDialog("pack alignment", 0, 0, 16); + NumberInputDialog numberInputDialog = + new NumberInputDialog("explicit pack value", 0, 0, 16); if (!numberInputDialog.show()) { return; } @@ -88,9 +88,8 @@ public class PackSizeDataTypeAction extends DockingAction { packDataType(dataType, packSize); commit = true; } - catch (InvalidInputException iie) { - // TODO Auto-generated catch block - iie.printStackTrace(); + catch (IllegalArgumentException iie) { + Msg.showError(this, null, "Invalid Pack Value", iie.getMessage()); } finally { // commit the changes @@ -98,13 +97,13 @@ public class PackSizeDataTypeAction extends DockingAction { } } - private void packDataType(DataType dataType, int packSize) throws InvalidInputException { - if (!(dataType instanceof Structure)) { + private void packDataType(DataType dataType, int packSize) throws IllegalArgumentException { + if (!(dataType instanceof Composite)) { Msg.error(this, - "Can't pack data type " + dataType.getName() + ". It's not a structure."); + "Can't pack data type " + dataType.getName() + ". It's not a composite."); return; } - ((Structure) dataType).pack(packSize); + ((Composite) dataType).pack(packSize); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java index e52ef5cd58..308657c440 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/DataTypeEditorManager.java @@ -463,25 +463,25 @@ public class DataTypeEditorManager return editorOptionMgr.showUnionNumbersInHex(); } - public void createNewStructure(Category category, boolean isInternallyAligned) { + public void createNewStructure(Category category, boolean isPacked) { String newName = getUniqueName(category, "struct"); DataTypeManager dataTypeManager = category.getDataTypeManager(); SourceArchive sourceArchive = dataTypeManager.getLocalSourceArchive(); StructureDataType structureDataType = new StructureDataType(category.getCategoryPath(), newName, 0, dataTypeManager); structureDataType.setSourceArchive(sourceArchive); - structureDataType.setInternallyAligned(isInternallyAligned); + structureDataType.setPackingEnabled(isPacked); edit(structureDataType); } - public void createNewUnion(Category category, boolean isInternallyAligned) { + public void createNewUnion(Category category, boolean isPacked) { String newName = getUniqueName(category, "union"); DataTypeManager dataTypeManager = category.getDataTypeManager(); SourceArchive sourceArchive = dataTypeManager.getLocalSourceArchive(); UnionDataType unionDataType = new UnionDataType(category.getCategoryPath(), newName, dataTypeManager); unionDataType.setSourceArchive(sourceArchive); - unionDataType.setInternallyAligned(isInternallyAligned); + unionDataType.setPackingEnabled(isPacked); edit(unionDataType); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java index 2c51239153..a9f2a5df0f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPlugin.java @@ -101,7 +101,7 @@ public class DataTypePreviewPlugin extends ProgramPlugin { model = new DTPPTableModel(); table = new DTPPTable(model); component = new DTPPScrollPane(table); - dataTypeManager = new LayeredDataTypeManager(); + dataTypeManager = new LayeredDataTypeManager(activeProgram); addDataType(new ByteDataType()); addDataType(new WordDataType()); @@ -177,27 +177,29 @@ public class DataTypePreviewPlugin extends ProgramPlugin { private void updateModel() { + LayeredDataTypeManager newDtm = new LayeredDataTypeManager(activeProgram); + + int transactionId = newDtm.startTransaction("add datatypes"); + try { + Iterator allDataTypes = dataTypeManager.getAllDataTypes(); + while (allDataTypes.hasNext()) { + newDtm.resolve(allDataTypes.next(), null); + } + } + finally { + newDtm.endTransaction(transactionId, true); + } + // NOTE: data types do not respond to switching the data organization object // since this is cached internal to the data type at time of construction. // We must purge old datatypes and have them re-instantiated by the // datatype manager List dtPaths = getModelDataTypePaths(); model.removeAll(); - dataTypeManager.invalidate(); - int transactionId = dataTypeManager.startTransaction("realign"); - try { - Iterator allComposites = dataTypeManager.getAllComposites(); - while (allComposites.hasNext()) { - Composite composite = allComposites.next(); - if (composite.isInternallyAligned()) { - composite.realign(); - } - } - } - finally { - dataTypeManager.endTransaction(transactionId, true); - } + LayeredDataTypeManager oldDtm = dataTypeManager; + dataTypeManager = newDtm; + oldDtm.close(); for (DataTypePath dtPath : dtPaths) { DataType dataType = dataTypeManager.getDataType(dtPath); @@ -538,14 +540,11 @@ public class DataTypePreviewPlugin extends ProgramPlugin { } private void add(Composite c, DataTypeComponentPreview parent) { - DataTypeComponent[] comps = c.getComponents(); + DataTypeComponent[] comps = c.getDefinedComponents(); for (DataTypeComponent element : comps) { DataTypeComponentPreview preview = new DataTypeComponentPreview(c, element); preview.setParent(parent); DataType dataType = element.getDataType(); - if (dataType == DataType.DEFAULT) { - continue; - } if (dataType instanceof Composite) { add((Composite) element.getDataType(), preview); } @@ -714,21 +713,22 @@ public class DataTypePreviewPlugin extends ProgramPlugin { private class LayeredDataTypeManager extends StandAloneDataTypeManager { - public LayeredDataTypeManager() { + DataOrganization layeredDataOrganization1; + + public LayeredDataTypeManager(Program program) { super("DataTypePreviewer"); + this.layeredDataOrganization1 = + program != null ? program.getDataTypeManager().getDataOrganization() : null; } @Override public DataOrganization getDataOrganization() { - if (currentProgram != null) { - return currentProgram.getDataTypeManager().getDataOrganization(); + if (layeredDataOrganization1 == null) { + return super.getDataOrganization(); } - return super.getDataOrganization(); - } - - void invalidate() { - invalidateCache(); + return layeredDataOrganization1; } } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java index 171bf11bb0..54e5b525c3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/BiDirectionDataType.java @@ -15,8 +15,9 @@ */ package ghidra.app.plugin.core.stackeditor; -import java.util.Collections; -import java.util.Comparator; +import java.util.*; + +import javax.help.UnsupportedOperationException; import ghidra.program.model.data.*; import ghidra.util.exception.AssertException; @@ -54,6 +55,49 @@ public abstract class BiDirectionDataType extends StructureDataType this.splitOffset = splitOffset; } + @Override + public int getAlignment() { + throw new UnsupportedOperationException( + "BiDirectionDataType.getAlignment() not implemented."); + } + + @Override + public boolean repack(boolean notify) { + throw new AssertException(); + } + + @Override + public void setToDefaultAligned() { + // ignore + } + + @Override + public void setToMachineAligned() { + // ignore + } + + @Override + public void setPackingEnabled(boolean aligned) { + // ignore + } + + @Override + public void setExplicitPackingValue(int packingValue) { + // ignore + } + + @Override + public void setExplicitMinimumAlignment(int minimumAlignment) { + // ignore + } + + @Override + public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name, + String comment) { + throw new UnsupportedOperationException( + "BiDirectionDataType.setFlexibleArrayComponent() not implemented."); + } + protected DataTypeComponent getDefinedComponentAt(int offset) { if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) { return null; @@ -108,20 +152,15 @@ public abstract class BiDirectionDataType extends StructureDataType return positiveLength; } - @Override - public int getLength() { - return structLength; - } - @Override public void delete(int index) { if (index < 0 || index >= numComponents) { - throw new ArrayIndexOutOfBoundsException(index); + throw new IndexOutOfBoundsException(index); } DataTypeComponent comp = getComponent(index); int offset = comp.getOffset(); int length = comp.getLength(); - int idx = Collections.binarySearch(components, new Integer(index), ordinalComparator); + int idx = Collections.binarySearch(components, index, ordinalComparator); if (idx >= 0) { DataTypeComponent dtc = components.remove(idx); dtc.getDataType().removeParent(this); @@ -137,7 +176,7 @@ public abstract class BiDirectionDataType extends StructureDataType } @Override - public void delete(int[] ordinals) { + public void delete(Set ordinals) { for (int ordinal : ordinals) { delete(ordinal); } @@ -173,6 +212,7 @@ public abstract class BiDirectionDataType extends StructureDataType } } structLength += deltaLength; +// nonpackedAlignedStructLength = -1; } /* @@ -198,7 +238,7 @@ public abstract class BiDirectionDataType extends StructureDataType protected DataTypeComponent getDefinedComponent(int ordinal) { if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } int idx = Collections.binarySearch(components, new Integer(ordinal), ordinalComparator); if (idx >= 0) { @@ -210,7 +250,7 @@ public abstract class BiDirectionDataType extends StructureDataType @Override public DataTypeComponent getComponent(int ordinal) { if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } int idx = Collections.binarySearch(components, new Integer(ordinal), ordinalComparator); if (idx >= 0) { @@ -255,12 +295,14 @@ public abstract class BiDirectionDataType extends StructureDataType numComponents += deltaLength; positiveLength += deltaLength; structLength += deltaLength; +// nonpackedAlignedStructLength = -1; } if (nextOffset < splitOffset - negativeLength) { int deltaLength = splitOffset - nextOffset - negativeLength; numComponents += deltaLength; negativeLength += deltaLength; structLength += deltaLength; +// nonpackedAlignedStructLength = -1; } checkAncestry(dataType); dataType = dataType.clone(getDataTypeManager()); @@ -319,6 +361,7 @@ public abstract class BiDirectionDataType extends StructureDataType numComponents++; positiveLength += length; structLength += length; +// nonpackedAlignedStructLength = -1; notifySizeChanged(); return dtc; } @@ -341,6 +384,7 @@ public abstract class BiDirectionDataType extends StructureDataType numComponents++; negativeLength += length; structLength += length; +// nonpackedAlignedStructLength = -1; notifySizeChanged(); return dtc; } @@ -367,39 +411,14 @@ public abstract class BiDirectionDataType extends StructureDataType } numComponents += absAmount; structLength += absAmount; +// nonpackedAlignedStructLength = -1; notifySizeChanged(); } @Override public DataTypeComponent insert(int index, DataType dataType, int length, String newName, String comment) { - throw new AssertException("BiDirectionDataType.insert() not implemented."); -// if (index < 0 || index > numComponents) { -// throw new ArrayIndexOutOfBoundsException(index); -// } -// if (index == numComponents) { -// return add(dataType, length, newName, comment); -// } -// validateDataType(dataType); -// -// dataType = resolve(dataType); -// checkAncestry(dataType); -// -// int idx = Collections.binarySearch(components, new Integer(index), ordinalComparator); -// if (idx < 0) { -// idx = -idx -1; -// } -// if (dataType == DataType.DEFAULT) { -// shiftOffsets(idx, 1, 1); -// return getComponent(index); -// } -// int offset = ((DataTypeComponent)getComponent(index)).getOffset(); -// DataTypeComponent dtc = new DataTypeComponentImpl(dataType,this, length,index, -// offset, newName, comment); -// shiftOffsets(idx, 1, dtc.getLength()); -// components.add(idx, dtc); -// sizeChanged(); -// return dtc; + throw new UnsupportedOperationException("BiDirectionDataType.insert() not implemented."); } protected void insertAtOffset(int offset, int numBytes) { @@ -441,6 +460,7 @@ public abstract class BiDirectionDataType extends StructureDataType } numComponents += numBytes; structLength += numBytes; +// nonpackedAlignedStructLength = -1; notifySizeChanged(); } @@ -479,7 +499,7 @@ public abstract class BiDirectionDataType extends StructureDataType if ((splitOffset != biDir.getSplitOffset()) || (negativeLength != biDir.getNegativeLength()) || (positiveLength != biDir.getPositiveLength()) || - (structLength != biDir.getLength())) { + (getLength() != biDir.getLength())) { return false; } DataTypeComponent[] myComps = getDefinedComponents(); @@ -499,72 +519,13 @@ public abstract class BiDirectionDataType extends StructureDataType @Override public void dataTypeSizeChanged(DataType dt) { - throw new AssertException("BiDirectionDataType.dataTypeSizeChanged() not implemented."); -// int n = components.size(); -// boolean didChange = false; -// for(int i=0;i dtcLen) { -// int consumed = consumeBytesAfter(i, dtLen-dtcLen); -// if (consumed > 0) { -// shiftOffsets(i+1, 0-consumed, 0); -// didChange = true; -// } -// } -// } -// } -// if (didChange & dtMgr != null) { -// dtMgr.dataTypeChanged(this); -// } + // ignore } - /** - * - * @param index the index of the defined component that is consuming the bytes. - * @param numBytes the number of undefined bytes to consume - * @return the number of bytes actually consumed - */ -// private int consumeBytesAfter(int index, int numBytes) { -// throw new AssertException("BiDirectionDataType.consumeBytesAfter() not implemented."); -// DataTypeComponentImpl thisDtc = (DataTypeComponentImpl)components.get(index); -// int thisLen = thisDtc.getLength(); -// int nextOffset = thisDtc.getOffset()+thisLen; -// int available = structLength-nextOffset; -// if (index+1 < components.size()) { -// DataTypeComponent nextDtc = (DataTypeComponent)components.get(index+1); -// available = nextDtc.getOffset() - nextOffset; -// } -// else { -// available = structLength-nextOffset; -// } -// if (numBytes <= available) { -// thisDtc.setLength(thisLen + numBytes); -// return numBytes; -// } -// else { -// thisDtc.setLength(thisLen + available); -// return available; -// } -// } - -// private boolean hasRoom(int index, int offset, int length) { -// if (offset+length > this.positiveLength) { -// return false; -// } -// if (index+1 < components.size()) { -// DataTypeComponent nextDtc = (DataTypeComponent)components.get(index+1); -// return offset+length <= nextDtc.getOffset(); -// } -// return true; -// } + @Override + public void dataTypeAlignmentChanged(DataType dt) { + // ignore + } @Override public abstract BiDirectionDataType clone(DataTypeManager dtm); @@ -572,7 +533,7 @@ public abstract class BiDirectionDataType extends StructureDataType @Override public void clearComponent(int index) { if (index < 0 || index >= numComponents) { - throw new ArrayIndexOutOfBoundsException(index); + throw new IndexOutOfBoundsException(index); } int idx = Collections.binarySearch(components, new Integer(index), ordinalComparator); if (idx >= 0) { @@ -589,81 +550,20 @@ public abstract class BiDirectionDataType extends StructureDataType } public void replaceWith(Structure struct) { - throw new AssertException("BiDirectionDataType.replaceWith() not implemented."); -// int oldLength = structLength; -// doReplaceWith(struct); -// if (oldLength != structLength) { -// sizeChanged(); -// } -// else if (dtMgr != null) { -// dtMgr.dataTypeChanged(this); -// } + throw new UnsupportedOperationException( + "BiDirectionDataType.replaceWith() not implemented."); } -// private void doReplaceWith(Structure struct) { -// throw new AssertException("BiDirectionDataType.doReplaceWith() not implemented."); -// components.clear(); -// structLength = struct.getLength(); -// numComponents = structLength; -// -// DataTypeComponent[] components = struct.getDefinedComponents(); -// for(int i=0;i=0;i--) { -// DataTypeComponentImpl dtc = (DataTypeComponentImpl)components.get(i); -// if (dtc.getDataType() == dt) { -// components.remove(i); -// shiftOffsets(i, dtc.getLength()-1, 0); -// didChange = true; -// } -// } -// if (didChange && dtMgr != null) { -// dtMgr.dataTypeChanged(this); -// } - + throw new UnsupportedOperationException( + "BiDirectionDataType.dataTypeDeleted() not implemented."); } @Override public void dataTypeReplaced(DataType oldDt, DataType newDt) { - throw new AssertException("BiDirectionDataType.dataTypeReplaced() not implemented."); -// int n = components.size(); -// boolean didChange = false; -// for(int i=0;i dtcLen) { -// int consumed = consumeBytesAfter(i, dtLen-dtcLen); -// if (consumed > 0) { -// shiftOffsets(i+1, 0-consumed, 0); -// didChange = true; -// } -// } -// } -// } -// if (didChange & dtMgr != null) { -// dtMgr.dataTypeChanged(this); -// } + throw new UnsupportedOperationException( + "BiDirectionDataType.dataTypeReplaced() not implemented."); } @Override @@ -682,9 +582,9 @@ public abstract class BiDirectionDataType extends StructureDataType @Override public DataTypeComponent replace(int index, DataType dataType, int length, String newName, - String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException { + String comment) throws IndexOutOfBoundsException, IllegalArgumentException { if (index < 0 || index >= numComponents) { - throw new ArrayIndexOutOfBoundsException(index); + throw new IndexOutOfBoundsException(index); } validateDataType(dataType); checkAncestry(dataType); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackFrameDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackFrameDataType.java index 6b102cfcdf..3b9eec09e1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackFrameDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/stackeditor/StackFrameDataType.java @@ -68,8 +68,7 @@ public class StackFrameDataType extends BiDirectionDataType { this.returnAddressOffset = stackDt.returnAddressOffset; this.stack = stackDt.stack; this.defaultSettings = stackDt.defaultSettings; - for (int i = 0; i < stackDt.components.size(); i++) { - DataTypeComponent dtc = stackDt.components.get(i); + for (DataTypeComponentImpl dtc : stackDt.components) { replaceAtOffset(dtc.getOffset(), dtc.getDataType(), dtc.getLength(), dtc.getFieldName(), dtc.getComment()); } @@ -216,7 +215,7 @@ public class StackFrameDataType extends BiDirectionDataType { * @see ghidra.program.model.listing.StackFrame#getFrameSize() */ public int getFrameSize() { - return structLength; + return getLength(); } /* (non-Javadoc) @@ -261,8 +260,9 @@ public class StackFrameDataType extends BiDirectionDataType { } private boolean adjustStackFrameSize(int newSize, int oldSize, boolean isNegative) { - if (newSize < 0) + if (newSize < 0) { return false; + } int delta = newSize - oldSize; if (delta == 0) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java index b3e783f8d8..753a78ecf4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java @@ -82,8 +82,7 @@ class DWARFDataTypeConflictHandler extends DataTypeConflictHandler { * @return true if empty or default and false otherwise */ private boolean isCompositeDefault(Composite composite) { - return composite.isNotYetDefined() - || ((composite instanceof Structure) && ((Structure) composite).getNumDefinedComponents() == 0); + return composite.isNotYetDefined() || (composite.getNumDefinedComponents() == 0); } private boolean isCompositePart(Composite full, Composite part, Set visitedDataTypes) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MSDataTypeUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MSDataTypeUtils.java index c125ccbfb6..7b56c34661 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MSDataTypeUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MSDataTypeUtils.java @@ -85,8 +85,10 @@ public class MSDataTypeUtils { CategoryPath categoryPath, String structureName, int packValue) { StructureDataType struct = new StructureDataType(categoryPath, structureName, 0, dataTypeManager); - struct.setInternallyAligned(true); - struct.setPackingValue(packValue); + struct.setPackingEnabled(true); + if (packValue > 0) { + struct.setExplicitPackingValue(packValue); + } return struct; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/CompositeDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/CompositeDataTypeHTMLRepresentation.java index 5ae0045eba..b0edc058d9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/CompositeDataTypeHTMLRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/CompositeDataTypeHTMLRepresentation.java @@ -15,7 +15,7 @@ */ package ghidra.app.util.html; -import static ghidra.util.HTMLUtilities.friendlyEncodeHTML; +import static ghidra.util.HTMLUtilities.*; import java.awt.Color; import java.util.*; @@ -77,7 +77,7 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat } protected List buildWarnings(Composite comp) { - if (!comp.isNotYetDefined()) { + if (!comp.isZeroLength()) { return Collections.emptyList(); } List list = new ArrayList<>(); @@ -87,7 +87,7 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat @Override protected TextLine buildFooterText(DataType dataType) { - if (dataType.isNotYetDefined()) { + if (dataType.isZeroLength()) { return new TextLine("0"); } return super.buildFooterText(dataType); @@ -95,44 +95,24 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat protected List buildAlignmentText(Composite dataType) { List list = new ArrayList<>(); - if (!dataType.isInternallyAligned()) { - list.add(new TextLine("unaligned")); + String alignStr = CompositeDataTypeImpl.getMinAlignmentString(dataType); + if (alignStr != null && alignStr.length() != 0) { + list.add(new TextLine(alignStr)); } - else if (dataType.isDefaultAligned()) { - list.add(new TextLine("align()")); - } - else if (dataType.isMachineAligned()) { - list.add(new TextLine("align(machine)")); - } - else { - long alignment = dataType.getMinimumAlignment(); - list.add(new TextLine("align(" + alignment + ")")); - } - TextLine packingText = buildPackingText(dataType); - if (packingText != null) { - list.add(packingText); + String packStr = CompositeDataTypeImpl.getPackingString(dataType); + if (packStr != null && packStr.length() != 0) { + list.add(new TextLine(packStr)); } return list; } - protected TextLine buildPackingText(Composite dataType) { - if (!dataType.isInternallyAligned()) { - return null; - } - long packingValue = dataType.getPackingValue(); - if (packingValue == Composite.NOT_PACKING) { - return null; - } - return new TextLine(" pack(" + packingValue + ")"); - } - protected TextLine buildAlignmentValueText(Composite composite) { return new TextLine("" + composite.getAlignment()); } private List buildContent(Composite comp) { List list = new ArrayList<>(); - if (comp.isNotYetDefined()) { + if (comp.isZeroLength()) { return list; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java index 45232d0c92..da591abaf1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java @@ -70,7 +70,7 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio protected List buildWarnings() { DataType baseType = typeDef.getBaseDataType(); - if (!(baseType instanceof Composite) || !baseType.isNotYetDefined()) { + if (!(baseType instanceof Composite) || !baseType.isZeroLength()) { return Collections.emptyList(); } List list = new ArrayList<>(); @@ -81,7 +81,7 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio @Override protected TextLine buildFooterText(DataType dataType) { - if (dataType.isNotYetDefined()) { + if (dataType.isZeroLength()) { return new TextLine("0"); } return super.buildFooterText(dataType); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BytesFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BytesFieldFactory.java index 54736f7baa..7792221b03 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BytesFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/BytesFieldFactory.java @@ -292,7 +292,7 @@ public class BytesFieldFactory extends FieldFactory { return null; // e.g., union } Structure struct = (Structure) baseDataType; - if (!struct.isInternallyAligned()) { + if (!struct.isPackingEnabled()) { return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java index 6e158a6c16..f541e4bf45 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/DataTypesXmlMgr.java @@ -623,7 +623,7 @@ public class DataTypesXmlMgr { XmlAttributes attrs = new XmlAttributes(); attrs.addAttribute("NAME", struct.getDisplayName()); attrs.addAttribute("NAMESPACE", struct.getCategoryPath().getPath()); - attrs.addAttribute("SIZE", struct.isNotYetDefined() ? 0 : struct.getLength(), true); + attrs.addAttribute("SIZE", struct.isZeroLength() ? 0 : struct.getLength(), true); writer.startElement("STRUCTURE", attrs); writeRegularComment(writer, struct.getDescription()); DataTypeComponent[] members = struct.getComponents(); @@ -637,7 +637,7 @@ public class DataTypesXmlMgr { XmlAttributes attrs = new XmlAttributes(); attrs.addAttribute("NAME", union.getDisplayName()); attrs.addAttribute("NAMESPACE", union.getCategoryPath().getPath()); - attrs.addAttribute("SIZE", union.isNotYetDefined() ? 0 : union.getLength(), true); + attrs.addAttribute("SIZE", union.isZeroLength() ? 0 : union.getLength(), true); writer.startElement("UNION", attrs); writeRegularComment(writer, union.getDescription()); DataTypeComponent[] members = union.getComponents(); @@ -649,7 +649,7 @@ public class DataTypesXmlMgr { private void writerMember(XmlWriter writer, DataTypeComponent member) { XmlAttributes attrs = new XmlAttributes(); - // TODO: how should we output bitfields (aligned/unaligned) and flex array + // TODO: how should we output bitfields (packed/non-packed) and flex array attrs.addAttribute("OFFSET", member.getOffset(), true); attrs.addAttribute("DATATYPE", member.getDataType().getDisplayName()); attrs.addAttribute("DATATYPE_NAMESPACE", member.getDataType().getCategoryPath().getPath()); diff --git a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj index d11b7a3c90..dd583c69e0 100644 --- a/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj +++ b/Ghidra/Features/Base/src/main/javacc/ghidra/app/util/cparser/C/C.jj @@ -1675,17 +1675,25 @@ Composite StructOrUnion() : {Composite comp;} ( ( DeclSpec() )* { comp = new StructureDataType(ANONYMOUS_STRUCT_PREFIX + cnt++, 0); - // Always set the packing, because by default structures should be aligned - // setting 0 turns off packing, but sets structures to be aligned - comp.setPackingValue(this.packSize); + // Always set the packing, because by default structures should be aligned + if (packSize > 0) { + comp.setExplicitPackingValue(packSize); + } + else { + comp.setPackingEnabled(true); // ensure default packing enabled + } } | ( DeclSpec() )* { comp = new UnionDataType(ANONYMOUS_UNION_PREFIX + cnt++); // Always set the packing, because by default structures should be aligned - // setting 0 turns off packing, but sets structures to be aligned. - comp.setPackingValue(this.packSize); + if (packSize > 0) { + comp.setExplicitPackingValue(packSize); + } + else { + comp.setPackingEnabled(true); // ensure default packing enabled + } } ) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge3Test.java index 78ea69269c..d97a996841 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge3Test.java @@ -604,7 +604,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { executeMerge(); - chooseOption(DataTypeMergeManager.OPTION_MY);// choose My Bar + chooseOption(DataTypeMergeManager.OPTION_MY);// choose My Bar // TODO: I see no reason for a conflict ! setErrorsExpected(true); @@ -2201,7 +2201,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest { (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); //@formatter:off assertEquals("/Category1/Category2/CoolUnion\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Union CoolUnion {\n" + " 0 qword 8 null \"\"\n" + " 0 byte:4(4) 1 BF1 \"my bf1\"\n" + diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge4Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge4Test.java index 3241043768..5c63251269 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge4Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge4Test.java @@ -27,7 +27,7 @@ import ghidra.program.database.ProgramModifierListener; import ghidra.program.model.data.*; import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; /** * More data type merge tests. @@ -502,7 +502,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { int transactionID = program.startTransaction("test"); Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); try { - dtm.remove(foo, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(foo, TaskMonitor.DUMMY); commit = true; } finally { @@ -709,7 +709,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { // delete Bar from Foo fs.delete(3); // add Foo to Bar - dtm.remove(bs, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bs, TaskMonitor.DUMMY); // Add s1, s2, s3 Structure s1 = new StructureDataType(new CategoryPath("/MISC"), "S1", 0); @@ -809,7 +809,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { // delete Bar from Foo foo.delete(3); // remove Bar from the data type manager - dtm.remove(bar, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bar, TaskMonitor.DUMMY); // Add s1, s2, s3 Structure s1 = new StructureDataType(new CategoryPath("/MISC"), "S1", 0); @@ -920,7 +920,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { // delete Bar from Foo fs.delete(3); // remove Bar from the data type manager - dtm.remove(bs, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bs, TaskMonitor.DUMMY); // Add s1, s2, s3 Structure s1 = new StructureDataType(new CategoryPath("/MISC"), "S1", 0); @@ -1034,7 +1034,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure fs = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); try { // delete Foo from the data type manager - dtm.remove(fs, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(fs, TaskMonitor.DUMMY); commit = true; } finally { @@ -1051,7 +1051,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // delete Foo from the data type manager - dtm.remove(foo, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(foo, TaskMonitor.DUMMY); // Add s1, s2, s3 Structure s1 = new StructureDataType(new CategoryPath("/MISC"), "S1", 0); @@ -1129,7 +1129,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure fs = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); try { // delete Foo from the data type manager - dtm.remove(fs, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(fs, TaskMonitor.DUMMY); commit = true; } finally { @@ -1146,7 +1146,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // delete Foo from the data type manager - dtm.remove(foo, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(foo, TaskMonitor.DUMMY); // Add s1, s2, s3 Structure s1 = new StructureDataType(new CategoryPath("/MISC"), "S1", 0); @@ -1318,7 +1318,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // remove Bar from the data type manager - dtm.remove(bar, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bar, TaskMonitor.DUMMY); commit = true; } finally { @@ -1441,7 +1441,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // remove Bar from the data type manager - dtm.remove(bar, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bar, TaskMonitor.DUMMY); commit = true; } finally { @@ -1555,7 +1555,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // remove Bar from the data type manager - dtm.remove(bar, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bar, TaskMonitor.DUMMY); commit = true; } finally { @@ -1643,7 +1643,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // remove Bar from the data type manager - dtm.remove(bar, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bar, TaskMonitor.DUMMY); commit = true; } finally { @@ -1750,7 +1750,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // remove Bar from the data type manager - dtm.remove(bar, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bar, TaskMonitor.DUMMY); commit = true; } finally { @@ -1844,7 +1844,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // remove Bar from the data type manager - dtm.remove(bar, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bar, TaskMonitor.DUMMY); commit = true; } finally { @@ -1943,7 +1943,7 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest { Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar"); try { // remove Bar from the data type manager - dtm.remove(bar, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(bar, TaskMonitor.DUMMY); commit = true; } finally { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge6Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge6Test.java index 5182291665..88636dc157 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge6Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge6Test.java @@ -21,7 +21,7 @@ import org.junit.Test; import ghidra.program.database.*; import ghidra.program.model.data.*; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; /** * Data type merge tests for aligned data types. @@ -31,9 +31,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { private void setupStructureMachineAlignedVsValue() throws Exception { mtf.initialize("notepad", new OriginalProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.OriginalProgramModifierListener#modifyOriginal(ghidra.program.database.ProgramDB) - */ + @Override public void modifyOriginal(ProgramDB program) throws Exception { boolean commit = false; @@ -45,7 +43,8 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - s.setInternallyAligned(true); + s.setToDefaultPacking(); + // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); @@ -60,9 +59,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -72,7 +68,8 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - s.setToMachineAlignment(); + s.setToMachineAligned(); + // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); @@ -87,9 +84,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -99,7 +93,8 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - s.setMinimumAlignment(4); + s.setExplicitMinimumAlignment(4); + // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); @@ -129,11 +124,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); Structure s = (Structure) c.getDataType("IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(false, s.isDefaultAligned()); - assertEquals(true, s.isMachineAligned()); - assertEquals(8, s.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + assertTrue(s.hasDefaultPacking()); + assertTrue(s.isMachineAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals(4, s.getComponent(2).getOffset()); @@ -155,11 +148,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); Structure s = (Structure) c.getDataType("IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(false, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(4, s.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + assertTrue(s.hasDefaultPacking()); + assertTrue(s.hasExplicitMinimumAlignment()); + assertEquals(4, s.getExplicitMinimumAlignment()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals(4, s.getComponent(2).getOffset()); @@ -181,11 +173,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); Structure s = (Structure) c.getDataType("IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + assertTrue(s.hasDefaultPacking()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals(4, s.getComponent(2).getOffset()); @@ -197,9 +187,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { private void setupStructurePack1VsPack2() throws Exception { mtf.initialize("notepad", new OriginalProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.OriginalProgramModifierListener#modifyOriginal(ghidra.program.database.ProgramDB) - */ + @Override public void modifyOriginal(ProgramDB program) throws Exception { boolean commit = false; @@ -211,7 +199,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - s.setInternallyAligned(true); + s.setToDefaultPacking(); // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); @@ -220,7 +208,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(8, s.getComponent(3).getOffset()); assertEquals(16, s.getLength()); assertEquals(4, s.getAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + commit = true; } finally { @@ -228,9 +216,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -240,7 +225,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - s.setPackingValue(1); + s.pack(1); // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); @@ -249,7 +234,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(7, s.getComponent(3).getOffset()); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - assertEquals(1, s.getPackingValue()); + commit = true; } finally { @@ -257,9 +242,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -269,7 +251,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - s.setPackingValue(2); + s.pack(2); // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); @@ -278,7 +260,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(8, s.getComponent(3).getOffset()); assertEquals(16, s.getLength()); assertEquals(2, s.getAlignment()); - assertEquals(2, s.getPackingValue()); + commit = true; } finally { @@ -302,11 +284,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(1, s.getPackingValue()); + assertTrue(s.hasExplicitPackingValue()); + assertEquals(1, s.getExplicitPackingValue()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); assertEquals(3, s.getComponent(2).getOffset()); @@ -329,11 +310,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(2, s.getPackingValue()); + assertTrue(s.hasExplicitPackingValue()); + assertEquals(2, s.getExplicitPackingValue()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals(4, s.getComponent(2).getOffset()); @@ -355,11 +335,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2/Category3")); Structure s = (Structure) c.getDataType("IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + assertTrue(s.hasDefaultPacking()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals(4, s.getComponent(2).getOffset()); @@ -371,9 +349,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { private void setupStructureMinAlignVsPack() throws Exception { mtf.initialize("notepad", new OriginalProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.OriginalProgramModifierListener#modifyOriginal(ghidra.program.database.ProgramDB) - */ + @Override public void modifyOriginal(ProgramDB program) throws Exception { boolean commit = false; @@ -385,7 +361,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - s.setInternallyAligned(true); + s.setToDefaultPacking(); // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); @@ -394,7 +370,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(8, s.getComponent(3).getOffset()); assertEquals(16, s.getLength()); assertEquals(4, s.getAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + commit = true; } finally { @@ -402,9 +378,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -414,7 +387,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - s.setToMachineAlignment(); + s.setToMachineAligned(); // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); @@ -430,9 +403,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -442,7 +412,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - s.setPackingValue(1); + s.pack(1); assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); @@ -450,7 +420,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(7, s.getComponent(3).getOffset()); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - assertEquals(1, s.getPackingValue()); + commit = true; } finally { @@ -474,11 +444,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(false, s.isDefaultAligned()); - assertEquals(true, s.isMachineAligned()); - assertEquals(8, s.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + assertTrue(s.hasDefaultPacking()); + assertTrue(s.isMachineAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals(4, s.getComponent(2).getOffset()); @@ -501,11 +469,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(1, s.getPackingValue()); + assertTrue(s.hasExplicitPackingValue()); + assertEquals(1, s.getExplicitPackingValue()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); assertEquals(3, s.getComponent(2).getOffset()); @@ -517,9 +484,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { private void setupStructureAddVsAlign() throws Exception { mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -534,6 +499,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { s.add(new IntegerDataType()); // Offsets change to 0,2,4,8. + assertFalse(s.isPackingEnabled()); assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); assertEquals(3, s.getComponent(2).getOffset()); @@ -541,7 +507,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(15, s.getComponent(4).getOffset()); assertEquals(19, s.getLength()); assertEquals(1, s.getAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); commit = true; } finally { @@ -549,9 +514,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -563,7 +525,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - s.setInternallyAligned(true); + s.setToDefaultPacking(); // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); @@ -572,7 +534,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(8, s.getComponent(3).getOffset()); assertEquals(16, s.getLength()); assertEquals(4, s.getAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + commit = true; } finally { @@ -596,11 +558,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(false, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + assertFalse(s.isPackingEnabled()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); assertEquals(3, s.getComponent(2).getOffset()); @@ -624,11 +584,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + assertTrue(s.hasDefaultPacking()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals(4, s.getComponent(2).getOffset()); @@ -640,9 +598,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { private void setupStructureFieldNameVsPack() throws Exception { mtf.initialize("notepad", new OriginalProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.OriginalProgramModifierListener#modifyOriginal(ghidra.program.database.ProgramDB) - */ + @Override public void modifyOriginal(ProgramDB program) throws Exception { boolean commit = false; @@ -654,7 +610,8 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - s.setInternallyAligned(true); + s.setToDefaultPacking(); + // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); @@ -669,9 +626,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -685,6 +639,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(4, s.getAlignment()); s.getComponent(1).setFieldName("MyComponentOne"); + assertTrue(s.hasDefaultPacking()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals("MyComponentOne", s.getComponent(1).getFieldName()); @@ -692,7 +649,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(8, s.getComponent(3).getOffset()); assertEquals(16, s.getLength()); assertEquals(4, s.getAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + commit = true; } finally { @@ -700,9 +657,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -714,7 +668,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); assertEquals(16, s.getLength()); assertEquals(4, s.getAlignment()); - s.setPackingValue(1); + s.pack(1); assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); @@ -722,7 +676,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(7, s.getComponent(3).getOffset()); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - assertEquals(1, s.getPackingValue()); + commit = true; } finally { @@ -746,11 +700,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + assertTrue(s.hasDefaultPacking()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(2, s.getComponent(1).getOffset()); assertEquals(4, s.getComponent(2).getOffset()); @@ -773,11 +725,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(1, s.getPackingValue()); + assertTrue(s.hasExplicitPackingValue()); + assertEquals(1, s.getExplicitPackingValue()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); assertEquals(3, s.getComponent(2).getOffset()); @@ -789,9 +740,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { private void setupStructureRemoveVsPack() throws Exception { mtf.initialize("notepad", new OriginalProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.OriginalProgramModifierListener#modifyOriginal(ghidra.program.database.ProgramDB) - */ + @Override public void modifyOriginal(ProgramDB program) throws Exception { boolean commit = false; @@ -803,7 +752,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - s.setInternallyAligned(true); + s.setToDefaultPacking(); // Offsets change to 0,2,4,8. assertEquals(0, s.getComponent(0).getOffset()); @@ -812,7 +761,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(8, s.getComponent(3).getOffset()); assertEquals(16, s.getLength()); assertEquals(4, s.getAlignment()); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + commit = true; } finally { @@ -820,9 +769,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -832,7 +778,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); + dtm.remove(s, TaskMonitor.DUMMY); // Offsets change to 0,2,4,8. Structure intStruct = (Structure) dtm.getDataType( @@ -845,9 +791,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -857,7 +800,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - s.setPackingValue(1); + s.pack(1); assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); @@ -865,7 +808,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(7, s.getComponent(3).getOffset()); assertEquals(15, s.getLength()); assertEquals(1, s.getAlignment()); - assertEquals(1, s.getPackingValue()); + commit = true; } finally { @@ -906,11 +849,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure s = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, s.isInternallyAligned()); - assertEquals(true, s.isDefaultAligned()); - assertEquals(false, s.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, s.getMinimumAlignment()); - assertEquals(1, s.getPackingValue()); + assertTrue(s.hasExplicitPackingValue()); + assertEquals(1, s.getExplicitPackingValue()); + assertTrue(s.isDefaultAligned()); + assertEquals(0, s.getComponent(0).getOffset()); assertEquals(1, s.getComponent(1).getOffset()); assertEquals(3, s.getComponent(2).getOffset()); @@ -922,9 +864,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { private void setupStructureInUnionAndViceVersa() throws Exception { mtf.initialize("notepad", new OriginalProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.OriginalProgramModifierListener#modifyOriginal(ghidra.program.database.ProgramDB) - */ + @Override public void modifyOriginal(ProgramDB program) throws Exception { boolean commit = false; @@ -934,11 +874,11 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Structure s = (Structure) dtm.getDataType( new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - s.setInternallyAligned(true); + s.setPackingEnabled(true); Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - union.setInternallyAligned(true); + union.setPackingEnabled(true); commit = true; } @@ -947,9 +887,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -975,9 +912,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1019,11 +953,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Structure intStruct = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"), "IntStruct"); - assertEquals(true, intStruct.isInternallyAligned()); - assertEquals(true, intStruct.isDefaultAligned()); - assertEquals(false, intStruct.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, intStruct.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, intStruct.getPackingValue()); + assertTrue(intStruct.hasDefaultPacking()); + assertTrue(intStruct.isDefaultAligned()); + assertEquals(5, intStruct.getNumComponents()); assertEquals(0, intStruct.getComponent(0).getOffset()); assertEquals(2, intStruct.getComponent(1).getOffset()); @@ -1036,11 +968,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { Union coolUnion = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - assertEquals(true, coolUnion.isInternallyAligned()); - assertEquals(true, coolUnion.isDefaultAligned()); - assertEquals(false, coolUnion.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, coolUnion.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, coolUnion.getPackingValue()); + assertTrue(coolUnion.hasDefaultPacking()); + assertTrue(coolUnion.isDefaultAligned()); + assertEquals(6, coolUnion.getNumComponents()); assertEquals("qword", coolUnion.getComponent(0).getDataType().getDisplayName()); assertEquals("word", coolUnion.getComponent(1).getDataType().getDisplayName()); @@ -1061,9 +991,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { public void setupUnionMachineAlignedVsValue() throws Exception { mtf.initialize("notepad", new OriginalProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.OriginalProgramModifierListener#modifyOriginal(ghidra.program.database.ProgramDB) - */ + @Override public void modifyOriginal(ProgramDB program) throws Exception { boolean commit = false; @@ -1075,7 +1003,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { "CoolUnion"); assertEquals(96, union.getLength()); assertEquals(1, union.getAlignment()); - union.setInternallyAligned(true); + union.setPackingEnabled(true); assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); @@ -1091,9 +1019,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1103,7 +1028,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - union.setToMachineAlignment(); + union.setToMachineAligned(); assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); @@ -1119,9 +1044,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1131,7 +1053,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - union.setMinimumAlignment(4); + union.setExplicitMinimumAlignment(4); assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); @@ -1162,11 +1084,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Union union = (Union) c.getDataType("CoolUnion"); - assertEquals(true, union.isInternallyAligned()); - assertEquals(false, union.isDefaultAligned()); - assertEquals(true, union.isMachineAligned()); - assertEquals(8, union.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, union.getPackingValue()); + assertTrue(union.hasDefaultPacking()); + assertTrue(union.isMachineAligned()); + assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); assertEquals(4, union.getComponent(2).getLength()); @@ -1189,11 +1109,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Union union = (Union) c.getDataType("CoolUnion"); - assertEquals(true, union.isInternallyAligned()); - assertEquals(false, union.isDefaultAligned()); - assertEquals(false, union.isMachineAligned()); - assertEquals(4, union.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, union.getPackingValue()); + assertTrue(union.hasDefaultPacking()); + assertTrue(union.hasExplicitMinimumAlignment()); + assertEquals(4, union.getExplicitMinimumAlignment()); + assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); assertEquals(4, union.getComponent(2).getLength()); @@ -1216,11 +1135,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Union union = (Union) c.getDataType("CoolUnion"); - assertEquals(true, union.isInternallyAligned()); - assertEquals(true, union.isDefaultAligned()); - assertEquals(false, union.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, union.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, union.getPackingValue()); + assertTrue(union.hasDefaultPacking()); + assertTrue(union.isDefaultAligned()); + assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); assertEquals(4, union.getComponent(2).getLength()); @@ -1233,9 +1150,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { public void setupUnionPack1VsPack2() throws Exception { mtf.initialize("notepad", new OriginalProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.OriginalProgramModifierListener#modifyOriginal(ghidra.program.database.ProgramDB) - */ + @Override public void modifyOriginal(ProgramDB program) throws Exception { boolean commit = false; @@ -1247,7 +1162,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { "CoolUnion"); assertEquals(96, union.getLength()); assertEquals(1, union.getAlignment()); - union.setInternallyAligned(true); + union.setPackingEnabled(true); assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); @@ -1263,9 +1178,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1275,7 +1187,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - union.setPackingValue(1); + union.pack(1); assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); @@ -1291,9 +1203,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1303,7 +1212,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { try { Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); - union.setPackingValue(2); + union.pack(2); assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); @@ -1334,11 +1243,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Union union = (Union) c.getDataType("CoolUnion"); - assertEquals(true, union.isInternallyAligned()); - assertEquals(true, union.isDefaultAligned()); - assertEquals(false, union.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, union.getMinimumAlignment()); - assertEquals(1, union.getPackingValue()); + assertTrue(union.hasExplicitPackingValue()); + assertEquals(1, union.getExplicitPackingValue()); + assertTrue(union.isDefaultAligned()); + assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); assertEquals(4, union.getComponent(2).getLength()); @@ -1361,11 +1269,10 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Union union = (Union) c.getDataType("CoolUnion"); - assertEquals(true, union.isInternallyAligned()); - assertEquals(true, union.isDefaultAligned()); - assertEquals(false, union.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, union.getMinimumAlignment()); - assertEquals(2, union.getPackingValue()); + assertTrue(union.hasExplicitPackingValue()); + assertEquals(2, union.getExplicitPackingValue()); + assertTrue(union.isDefaultAligned()); + assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); assertEquals(4, union.getComponent(2).getLength()); @@ -1388,11 +1295,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); Category c = dtm.getCategory(new CategoryPath("/Category1/Category2")); Union union = (Union) c.getDataType("CoolUnion"); - assertEquals(true, union.isInternallyAligned()); - assertEquals(true, union.isDefaultAligned()); - assertEquals(false, union.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, union.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, union.getPackingValue()); + assertTrue(union.hasDefaultPacking()); + assertTrue(union.isDefaultAligned()); + assertEquals(8, union.getComponent(0).getLength()); assertEquals(2, union.getComponent(1).getLength()); assertEquals(4, union.getComponent(2).getLength()); @@ -1416,9 +1321,7 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { struct2.add(new StringDataType(), 4); mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + @Override public void modifyLatest(ProgramDB program) throws Exception { boolean commit = false; @@ -1444,12 +1347,9 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertTrue(new PointerDataType(new FloatDataType()).isEquivalent( s.getComponent(0).getDataType())); assertTrue(new FloatDataType().isEquivalent(s.getComponent(1).getDataType())); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); + } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) throws Exception { boolean commit = false; @@ -1474,7 +1374,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(4, s.getComponent(1).getLength()); assertTrue(new CharDataType().isEquivalent(s.getComponent(0).getDataType())); assertTrue(new StringDataType().isEquivalent(s.getComponent(1).getDataType())); - assertEquals(Composite.NOT_PACKING, s.getPackingValue()); } }); @@ -1495,7 +1394,6 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertTrue(new PointerDataType(new FloatDataType()).isEquivalent( s1.getComponent(0).getDataType())); assertTrue(new FloatDataType().isEquivalent(s1.getComponent(1).getDataType())); - assertEquals(Composite.NOT_PACKING, s1.getPackingValue()); Structure s2 = (Structure) dtm.getDataType(new CategoryPath("/Category1"), "ABCStructure.conflict"); @@ -1508,6 +1406,5 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest { assertEquals(4, s2.getComponent(1).getLength()); assertTrue(new CharDataType().isEquivalent(s2.getComponent(0).getDataType())); assertTrue(new StringDataType().isEquivalent(s2.getComponent(1).getDataType())); - assertEquals(Composite.NOT_PACKING, s2.getPackingValue()); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMergeFixupTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMergeFixupTest.java index a646d4d8cb..04ab41dc12 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMergeFixupTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMergeFixupTest.java @@ -60,7 +60,7 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { Structure inner = new StructureDataType("inner", 0); inner.add(new ByteDataType()); inner.add(new WordDataType()); - inner.setInternallyAligned(true); + inner.setPackingEnabled(true); try { Category rootCategory = dtm.getCategory(rootPath); @@ -103,7 +103,7 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { Structure outer = new StructureDataType("outer", 0); outer.add(new ByteDataType()); outer.add(inner); - outer.setInternallyAligned(true); + outer.setPackingEnabled(true); int transactionID = program.startTransaction("create outer struct, modify inner"); try { @@ -134,14 +134,14 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { waitForCompletion(); DataTypeManager dtm = resultProgram.getDataTypeManager(); - Structure inner = (Structure) dtm.getDataType(rootPath, "inner"); + StructureInternal inner = (StructureInternal) dtm.getDataType(rootPath, "inner"); assertNull(inner); - Structure outer = (Structure) dtm.getDataType(rootPath, "outer"); + StructureInternal outer = (StructureInternal) dtm.getDataType(rootPath, "outer"); assertNotNull(outer); - assertEquals(true, outer.isInternallyAligned()); + assertEquals(true, outer.isPackingEnabled()); assertEquals(true, outer.isDefaultAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, outer.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, outer.getPackingValue()); + assertEquals(CompositeInternal.DEFAULT_ALIGNMENT, outer.getStoredMinimumAlignment()); + assertEquals(CompositeInternal.DEFAULT_PACKING, outer.getStoredPackingValue()); assertEquals(1, outer.getNumComponents()); assertTrue(new ByteDataType().isEquivalent(outer.getComponent(0).getDataType())); assertEquals(1, outer.getLength()); @@ -162,24 +162,24 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { waitForCompletion(); DataTypeManager dtm = resultProgram.getDataTypeManager(); - Structure inner = (Structure) dtm.getDataType(rootPath, "inner"); + StructureInternal inner = (StructureInternal) dtm.getDataType(rootPath, "inner"); assertNotNull(inner); - assertEquals(true, inner.isInternallyAligned()); + assertEquals(true, inner.isPackingEnabled()); assertEquals(true, inner.isDefaultAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, inner.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, inner.getPackingValue()); + assertEquals(CompositeInternal.DEFAULT_ALIGNMENT, inner.getStoredMinimumAlignment()); + assertEquals(CompositeInternal.DEFAULT_PACKING, inner.getStoredPackingValue()); assertEquals(2, inner.getNumComponents()); assertTrue(new CharDataType().isEquivalent(inner.getComponent(0).getDataType())); assertTrue(new WordDataType().isEquivalent(inner.getComponent(1).getDataType())); assertEquals(4, inner.getLength()); assertEquals(2, inner.getAlignment()); - Structure outer = (Structure) dtm.getDataType(rootPath, "outer"); + StructureInternal outer = (StructureInternal) dtm.getDataType(rootPath, "outer"); assertNotNull(outer); - assertEquals(true, outer.isInternallyAligned()); + assertEquals(true, outer.isPackingEnabled()); assertEquals(true, outer.isDefaultAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, outer.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, outer.getPackingValue()); + assertEquals(CompositeInternal.DEFAULT_ALIGNMENT, outer.getStoredMinimumAlignment()); + assertEquals(CompositeInternal.DEFAULT_PACKING, outer.getStoredPackingValue()); assertEquals(2, outer.getNumComponents()); assertTrue(new ByteDataType().isEquivalent(outer.getComponent(0).getDataType())); assertEquals(inner, outer.getComponent(1).getDataType()); @@ -220,7 +220,7 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { Structure inner = new StructureDataType("inner", 0); inner.add(new ByteDataType()); inner.add(new WordDataType()); - inner.setInternallyAligned(true); + inner.setPackingEnabled(true); try { Category rootCategory = dtm.getCategory(rootPath); @@ -263,7 +263,7 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { Structure outer = new StructureDataType("outer", 0); outer.add(new ByteDataType()); outer.add(inner); - outer.setInternallyAligned(true); + outer.setPackingEnabled(true); int transactionID = program.startTransaction("create outer struct, don't modify inner"); @@ -283,14 +283,14 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { waitForCompletion(); DataTypeManager dtm = resultProgram.getDataTypeManager(); - Structure inner = (Structure) dtm.getDataType(rootPath, "inner"); + StructureInternal inner = (StructureInternal) dtm.getDataType(rootPath, "inner"); assertNull(inner); - Structure outer = (Structure) dtm.getDataType(rootPath, "outer"); + StructureInternal outer = (StructureInternal) dtm.getDataType(rootPath, "outer"); assertNotNull(outer); - assertEquals(true, outer.isInternallyAligned()); + assertEquals(true, outer.isPackingEnabled()); assertEquals(true, outer.isDefaultAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, outer.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, outer.getPackingValue()); + assertEquals(CompositeInternal.DEFAULT_ALIGNMENT, outer.getStoredMinimumAlignment()); + assertEquals(CompositeInternal.DEFAULT_PACKING, outer.getStoredPackingValue()); assertEquals(1, outer.getNumComponents()); assertTrue(new ByteDataType().isEquivalent(outer.getComponent(0).getDataType())); assertEquals(1, outer.getLength()); @@ -335,7 +335,7 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { Structure inner = new StructureDataType("inner", 0); inner.add(new ByteDataType()); inner.add(new WordDataType()); - inner.setInternallyAligned(true); + inner.setPackingEnabled(true); try { Category rootCategory = dtm.getCategory(rootPath); @@ -378,7 +378,7 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { Structure other = new StructureDataType("other", 0); other.add(new ByteDataType()); other.add(new PointerDataType(new VoidDataType())); - other.setInternallyAligned(true); + other.setPackingEnabled(true); Structure outer = new StructureDataType("outer", 0); outer.add(new ByteDataType()); @@ -386,7 +386,7 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { outer.add(new FloatDataType()); outer.add(other); outer.add(new ByteDataType()); - outer.setInternallyAligned(true); + outer.setPackingEnabled(true); int transactionID = program.startTransaction( "create outer struct with other structure after inner"); @@ -407,15 +407,15 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { DataTypeManager dtm = resultProgram.getDataTypeManager(); - Structure inner = (Structure) dtm.getDataType(rootPath, "inner"); + StructureInternal inner = (StructureInternal) dtm.getDataType(rootPath, "inner"); assertNull(inner); - Structure other = (Structure) dtm.getDataType(rootPath, "other"); + StructureInternal other = (StructureInternal) dtm.getDataType(rootPath, "other"); assertNotNull(other); - assertEquals(true, other.isInternallyAligned()); + assertEquals(true, other.isPackingEnabled()); assertEquals(true, other.isDefaultAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, other.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, other.getPackingValue()); + assertEquals(CompositeInternal.DEFAULT_ALIGNMENT, other.getStoredMinimumAlignment()); + assertEquals(CompositeInternal.DEFAULT_PACKING, other.getStoredPackingValue()); assertEquals(2, other.getNumComponents()); assertTrue(new ByteDataType().isEquivalent(other.getComponent(0).getDataType())); assertTrue(new PointerDataType(new VoidDataType()).isEquivalent( @@ -423,12 +423,12 @@ public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest { assertEquals(8, other.getLength()); assertEquals(4, other.getAlignment()); - Structure outer = (Structure) dtm.getDataType(rootPath, "outer"); + StructureInternal outer = (StructureInternal) dtm.getDataType(rootPath, "outer"); assertNotNull(outer); - assertEquals(true, outer.isInternallyAligned()); + assertEquals(true, outer.isPackingEnabled()); assertEquals(true, outer.isDefaultAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, outer.getMinimumAlignment()); - assertEquals(Composite.NOT_PACKING, outer.getPackingValue()); + assertEquals(CompositeInternal.DEFAULT_ALIGNMENT, outer.getStoredMinimumAlignment()); + assertEquals(CompositeInternal.DEFAULT_PACKING, outer.getStoredPackingValue()); assertEquals(4, outer.getNumComponents()); assertTrue(new ByteDataType().isEquivalent(outer.getComponent(0).getDataType())); assertTrue(new FloatDataType().isEquivalent(outer.getComponent(1).getDataType())); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java index 8a64ddfddd..fd0791da01 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserOptionsTest.java @@ -90,12 +90,12 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest struct.add(CharDataType.dataType); struct.add(IntegerDataType.dataType); struct.add(CharDataType.dataType); - struct.setInternallyAligned(true); + struct.setPackingEnabled(true); builder.applyDataType("0x1001100", struct); builder.setBytes("0x1001200", "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); struct = new StructureDataType("struct2", 12); - struct.setInternallyAligned(false); + struct.setPackingEnabled(false); struct.insertAtOffset(0, CharDataType.dataType, -1); struct.insertAtOffset(4, IntegerDataType.dataType, -1); struct.insertAtOffset(8, CharDataType.dataType, -1); @@ -486,7 +486,7 @@ public class CodeBrowserOptionsTest extends AbstractGhidraHeadedIntegrationTest showTool(tool); loadProgram(); - // turn alignment bytes option on but it has no impact on displayed bytes for unaligned structure + // turn alignment bytes option on but it has no impact on displayed bytes for non-packed structure Options options = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS); options.setBoolean("Bytes Field.Display Structure Alignment Bytes", true); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractEditorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractEditorTest.java index a8f1c39ea0..8223a73e07 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractEditorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractEditorTest.java @@ -47,7 +47,6 @@ import ghidra.framework.plugintool.util.PluginException; import ghidra.program.database.ProgramBuilder; import ghidra.program.model.data.*; import ghidra.program.model.data.Composite; -import ghidra.program.model.data.Composite.AlignmentType; import ghidra.program.model.data.Enum; import ghidra.program.model.listing.Program; import ghidra.test.AbstractGhidraHeadedIntegrationTest; @@ -213,8 +212,7 @@ public abstract class AbstractEditorTest extends AbstractGhidraHeadedIntegration } protected CycleGroupAction getCycleGroup(DataType dt) { - for (int cycleIndex = 0; cycleIndex < cycles.size(); cycleIndex++) { - CycleGroupAction action = cycles.get(cycleIndex); + for (CycleGroupAction action : cycles) { CycleGroup group = action.getCycleGroup(); DataType[] types = group.getDataTypes(); for (DataType type : types) { @@ -227,8 +225,7 @@ public abstract class AbstractEditorTest extends AbstractGhidraHeadedIntegration } protected FavoritesAction getFavorite(String name) { - for (int favIndex = 0; favIndex < favorites.size(); favIndex++) { - FavoritesAction action = favorites.get(favIndex); + for (FavoritesAction action : favorites) { if (action.getDataType().getDisplayName().equals(name)) { return action; } @@ -332,13 +329,17 @@ public abstract class AbstractEditorTest extends AbstractGhidraHeadedIntegration } protected void invoke(final DockingActionIf action) { + invoke(action, true); + } + + protected void invoke(final DockingActionIf action, boolean wait) { assertNotNull(action); boolean isEnabled = runSwing(() -> action.isEnabled()); if (!isEnabled) { Msg.debug(this, "Calling actionPerformed() on a disabled action: " + action.getName(), ReflectionUtilities.createJavaFilteredThrowable()); } - runSwing(() -> action.actionPerformed(new ActionContext()), false); + runSwing(() -> action.actionPerformed(new ActionContext()), wait); waitForSwing(); } @@ -811,20 +812,30 @@ public abstract class AbstractEditorTest extends AbstractGhidraHeadedIntegration actionEnablement); } - protected void assertIsInternallyAligned(boolean aligned) { - assertEquals(aligned, ((CompEditorModel) model).isAligned()); + protected void assertIsPackingEnabled(boolean aligned) { + assertEquals(aligned, ((CompEditorModel) model).isPackingEnabled()); } - protected void assertPackingValue(int value) { - assertEquals(value, ((CompEditorModel) model).getPackingValue()); + protected void assertDefaultPacked() { + assertEquals(PackingType.DEFAULT, ((CompEditorModel) model).getPackingType()); } - protected void assertMinimumAlignmentType(AlignmentType alignmentType) { - assertEquals(alignmentType, ((CompEditorModel) model).getMinimumAlignmentType()); + protected void assertPacked(int pack) { + assertEquals(PackingType.EXPLICIT, ((CompEditorModel) model).getPackingType()); + assertEquals(pack, ((CompEditorModel) model).getExplicitPackingValue()); } - protected void assertMinimumAlignmentValue(int value) { - assertEquals(value, ((CompEditorModel) model).getMinimumAlignment()); + protected void assertIsDefaultAligned() { + assertEquals(AlignmentType.DEFAULT, ((CompEditorModel) model).getAlignmentType()); + } + + protected void assertIsMachineAligned() { + assertEquals(AlignmentType.MACHINE, ((CompEditorModel) model).getAlignmentType()); + } + + protected void assertExplicitAlignment(int alignment) { + assertEquals(AlignmentType.EXPLICIT, ((CompEditorModel) model).getAlignmentType()); + assertEquals(alignment, ((CompEditorModel) model).getExplicitMinimumAlignment()); } protected void assertActualAlignment(int value) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorLockedActionsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorLockedActionsTest.java index c342ae7fc6..582ef0a782 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorLockedActionsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorLockedActionsTest.java @@ -28,7 +28,7 @@ public abstract class AbstractStructureEditorLockedActionsTest extends AbstractS try { DataTypeManager dataTypeManager = cat.getDataTypeManager(); if (dt.getDataTypeManager() != dataTypeManager) { - dt = (Structure) dt.clone(dataTypeManager); + dt = dt.clone(dataTypeManager); } CategoryPath categoryPath = cat.getCategoryPath(); if (!dt.getCategoryPath().equals(categoryPath)) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorTest.java index fb3236d068..7f321ef597 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/AbstractStructureEditorTest.java @@ -60,7 +60,7 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest { try { DataTypeManager dataTypeManager = cat.getDataTypeManager(); if (dt.getDataTypeManager() != dataTypeManager) { - dt = (Structure) dt.clone(dataTypeManager); + dt = dt.clone(dataTypeManager); } CategoryPath categoryPath = cat.getCategoryPath(); if (!dt.getCategoryPath().equals(categoryPath)) { @@ -81,6 +81,7 @@ public abstract class AbstractStructureEditorTest extends AbstractEditorTest { installProvider(new StructureEditorProvider(plugin, structDt, showInHex)); model = provider.getModel(); }); + waitForSwing(); getActions(); structureModel = (StructureEditorModel) model; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorAlignmentTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorAlignmentTest.java index e1fa6feff2..b81a6df05c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorAlignmentTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorAlignmentTest.java @@ -23,7 +23,6 @@ import javax.swing.JTextField; import org.junit.Test; import ghidra.program.model.data.*; -import ghidra.program.model.data.Composite.AlignmentType; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Library; import ghidra.program.model.symbol.ExternalLocation; @@ -32,7 +31,7 @@ import ghidra.program.model.symbol.SourceType; public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { @Test - public void testUnalignedStructure() { + public void testNonPackedStructure() { init(emptyStructure, pgmRootCat, false); assertTrue(structureModel.hasChanges());// initial unsaved empty structure @@ -44,10 +43,8 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { structureModel.getOriginalCategoryPath().getPath()); assertEquals(0, structureModel.getNumComponents());// no components assertEquals(1, structureModel.getRowCount());// blank row - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertLength(0); assertActualAlignment(1); assertEquals(0, structureModel.getNumSelectedComponentRows()); @@ -102,7 +99,9 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { assertLength(10); assertActualAlignment(1); - pressButtonByName(getPanel(), "Internally Aligned"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -115,7 +114,7 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { @Test public void testEnablementDefaultAlignedStructure() throws Exception { - emptyStructure.setInternallyAligned(true); + emptyStructure.setPackingEnabled(true); init(emptyStructure, pgmRootCat, false); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); @@ -159,8 +158,12 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { waitForSwing(); - pressButtonByName(getPanel(), "Internally Aligned"); - pressButtonByName(getPanel(), "Machine Minimum Alignment"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); + + pressButtonByName(getPanel(), "Machine Alignment"); + assertIsMachineAligned(); assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -184,19 +187,24 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { waitForSwing(); - pressButtonByName(editorPanel, "Internally Aligned"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); + JTextField minAlignField = - (JTextField) getInstanceField("minAlignValueTextField", editorPanel); + (JTextField) getInstanceField("explicitAlignTextField", editorPanel); assertNotNull(minAlignField); - JRadioButton byValueMinAlignButton = - (JRadioButton) getInstanceField("byValueMinAlignButton", editorPanel); - assertNotNull(byValueMinAlignButton); - pressButton(byValueMinAlignButton); - assertEquals("4", minAlignField.getText()); + JRadioButton explicitAlignButton = + (JRadioButton) getInstanceField("explicitAlignButton", editorPanel); + assertNotNull(explicitAlignButton); + pressButton(explicitAlignButton); + assertEquals("8", minAlignField.getText()); // toy.cspec machine alignment is default value assertEquals(false, structureModel.viewComposite.isDefaultAligned()); assertEquals(false, structureModel.viewComposite.isMachineAligned()); - assertEquals(4, structureModel.getMinimumAlignment()); + assertEquals(8, structureModel.getExplicitMinimumAlignment()); + + assertActualAlignment(8); assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -204,7 +212,7 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); assertLength(16); - assertActualAlignment(4); + assertActualAlignment(8); } @Test @@ -232,10 +240,10 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { checkByValueAlignedStructure(16, 16, 16); } - public void checkByValueAlignedStructure(int value, int alignment, int length) + public void checkByValueAlignedStructure(int minAlignment, int alignment, int length) throws Exception { - emptyStructure.setInternallyAligned(true); - emptyStructure.setMinimumAlignment(value); + emptyStructure.setPackingEnabled(true); + emptyStructure.setExplicitMinimumAlignment(minAlignment); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyStructure.add(new ByteDataType()); @@ -245,19 +253,20 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { init(emptyStructure, pgmRootCat, false); CompEditorPanel editorPanel = (CompEditorPanel) getPanel(); - JRadioButton byValueMinAlignButton = - (JRadioButton) getInstanceField("byValueMinAlignButton", editorPanel); - assertNotNull(byValueMinAlignButton); - assertEquals(true, byValueMinAlignButton.isSelected()); + JRadioButton explicitAlignButton = + (JRadioButton) getInstanceField("explicitAlignButton", editorPanel); + assertNotNull(explicitAlignButton); + assertEquals(true, explicitAlignButton.isSelected()); JTextField minAlignField = - (JTextField) getInstanceField("minAlignValueTextField", editorPanel); + (JTextField) getInstanceField("explicitAlignTextField", editorPanel); assertNotNull(minAlignField); - assertEquals("" + value, minAlignField.getText()); + assertEquals("" + minAlignment, minAlignField.getText()); assertEquals(false, structureModel.viewComposite.isDefaultAligned()); assertEquals(false, structureModel.viewComposite.isMachineAligned()); - assertEquals(value, structureModel.getMinimumAlignment()); + + assertExplicitAlignment(minAlignment); assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -270,9 +279,9 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { @Test public void testDefaultAlignedPacked1Structure() throws Exception { - int value = 1; - emptyStructure.setInternallyAligned(true); - emptyStructure.setPackingValue(value); + int pack = 1; + emptyStructure.setPackingEnabled(true); + emptyStructure.pack(pack); init(emptyStructure, pgmRootCat, false); CompEditorPanel editorPanel = (CompEditorPanel) getPanel(); @@ -283,17 +292,18 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { addDataType(arrayDt); JRadioButton byValuePackingButton = - (JRadioButton) findComponentByName(editorPanel, "By Value Packing"); + (JRadioButton) findComponentByName(editorPanel, "Explicit Packing"); assertNotNull(byValuePackingButton); JTextField packingValueField = (JTextField) findComponentByName(editorPanel, "Packing Value"); assertNotNull(packingValueField); assertEquals(true, byValuePackingButton.isSelected()); - assertEquals("" + value, packingValueField.getText()); + assertEquals(Integer.toString(pack), packingValueField.getText()); assertEquals(true, structureModel.viewComposite.isDefaultAligned()); assertEquals(false, structureModel.viewComposite.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, structureModel.getMinimumAlignment()); + + assertIsDefaultAligned(); assertEquals(3, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -306,7 +316,6 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { @Test public void testAlignedEditToFunctionDefinitionDataType() throws Exception { - int value = 1; startTransaction("addExternal"); ExternalLocation extLoc = program.getExternalManager().addExtFunction(Library.UNKNOWN, @@ -321,8 +330,8 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { boolean commit = false; txId = program.startTransaction("Modify Program"); try { - simpleStructure.setInternallyAligned(true); - simpleStructure.setPackingValue(value); + simpleStructure.setPackingEnabled(true); + simpleStructure.pack(1); programDTM = program.getListing().getDataTypeManager(); functionDefinition = @@ -358,7 +367,7 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { } @Test - public void testSelectionOnGoFromUnalignedToAlignedStructure() throws Exception { + public void testSelectionOnGoFromNonPackedToDefaultPackedStructure() throws Exception { init(emptyStructure, pgmRootCat, false); CompEditorPanel editorPanel = (CompEditorPanel) getPanel(); @@ -372,10 +381,8 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { structureModel.getOriginalCategoryPath().getPath()); assertEquals(0, structureModel.getNumComponents());// no components assertEquals(1, structureModel.getRowCount());// blank row - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertLength(0); assertActualAlignment(1); assertEquals(0, structureModel.getNumSelectedComponentRows()); @@ -388,7 +395,9 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { checkSelection(new int[] { 3 }); - pressButtonByName(editorPanel, "Internally Aligned"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); assertEquals(0, structureModel.getNumComponents()); assertEquals(1, structureModel.getRowCount()); @@ -397,368 +406,383 @@ public class StructureEditorAlignmentTest extends AbstractStructureEditorTest { checkSelection(new int[] { 0 }); } -// public void testTurnOffAlignmentInStructure() throws Exception { -// emptyStructure.setInternallyAligned(true); -// emptyStructure.setMinimumAlignment(8); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// JRadioButton byValueButton = (JRadioButton)findComponentByName(getPanel(), "By Value Minimum Alignment"); -// assertEquals(true, byValueButton.isSelected()); -// JTextField minAlignField = (JTextField)findComponentByName(getPanel(), "Minimum Alignment Value"); -// assertEquals("8", minAlignField.getText()); -// -// assertEquals(false, structureModel.viewComposite.isDefaultAligned()); -// assertEquals(false, structureModel.viewComposite.isMachineAligned()); -// assertEquals(8, structureModel.getMinimumAlignment()); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(8); -// assertActualAlignment(8); -// assertEquals(true, structureModel.isAligned()); -// -// pressButtonByName(getPanel(), "Internally Aligned"); -// -// assertEquals(false, structureModel.isAligned()); -// assertEquals(true, structureModel.viewComposite.isDefaultAligned()); -// assertEquals(false, structureModel.viewComposite.isMachineAligned()); -// assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, structureModel.getMinimumAlignment()); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(5); -// assertActualAlignment(1); -// } -// -// public void testInsertUnaligned1() throws Exception { -// emptyStructure.setInternallyAligned(false); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(5); -// assertActualAlignment(1); -// -// DataType asciiDt = model.getOriginalDataTypeManager().findDataType("/char"); -// assertNotNull(asciiDt); -// insertAtPoint(asciiDt,0,0); -// -// assertEquals(4, structureModel.getNumComponents()); -// assertEquals(5, structureModel.getRowCount()); -// checkRow(0, 1, "ch", asciiDt, "", ""); -// checkRow(1, 1, "db", new ByteDataType(), "", ""); -// checkRow(2, 4, "float", new FloatDataType(), "", ""); -// checkRow(3, 5, "char[5]", arrayDt, "", ""); -// assertLength(5); -// assertActualAlignment(1); -// } -// -// public void testInsertUnaligned2() throws Exception { -// emptyStructure.setInternallyAligned(false); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(5); -// assertActualAlignment(1); -// -// DataType asciiDt = model.getOriginalDataTypeManager().findDataType("/char"); -// assertNotNull(asciiDt); -// insertAtPoint(asciiDt,2,3); -// -// assertEquals(4, structureModel.getNumComponents()); -// assertEquals(5, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 1, "ch", asciiDt, "", ""); -// checkRow(3, 5, "char[5]", arrayDt, "", ""); -// assertLength(5); -// assertActualAlignment(1); -// } -// -// public void testInsertUnaligned3() throws Exception { -// emptyStructure.setInternallyAligned(false); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(5); -// assertActualAlignment(1); -// -// DataType doubleDt = model.getOriginalDataTypeManager().findDataType("/double"); -// assertNotNull(doubleDt); -// insertAtPoint(doubleDt,3,3); -// -// assertEquals(4, structureModel.getNumComponents()); -// assertEquals(5, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// checkRow(3, 8, "double", doubleDt, "", ""); -// assertLength(8); -// assertActualAlignment(1); -// } -// -// public void testReplaceUnaligned1() throws Exception { -// emptyStructure.setInternallyAligned(false); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(5); -// assertActualAlignment(1); -// -// DataType asciiDt = model.getOriginalDataTypeManager().findDataType("/char"); -// assertNotNull(asciiDt); -// addAtPoint(asciiDt,2,3); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 1, "ch", asciiDt, "", ""); -// assertLength(4); -// assertActualAlignment(1); -// } -// -// public void testReplaceUnaligned2() throws Exception { -// emptyStructure.setInternallyAligned(false); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(5); -// assertActualAlignment(1); -// -// DataType doubleDt = model.getOriginalDataTypeManager().findDataType("/double"); -// assertNotNull(doubleDt); -// addAtPoint(doubleDt,3,3); -// -// assertEquals(4, structureModel.getNumComponents()); -// assertEquals(5, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// checkRow(3, 8, "double", doubleDt, "", ""); -// assertLength(8); -// assertActualAlignment(1); -// } -// -// public void testInsertAligned1() throws Exception { -// emptyStructure.setInternallyAligned(true); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(8); -// assertActualAlignment(4); -// -// DataType asciiDt = model.getOriginalDataTypeManager().findDataType("/char"); -// assertNotNull(asciiDt); -// insertAtPoint(asciiDt,0,0); -// -// assertEquals(4, structureModel.getNumComponents()); -// assertEquals(5, structureModel.getRowCount()); -// checkRow(0, 1, "ch", asciiDt, "", ""); -// checkRow(1, 1, "db", new ByteDataType(), "", ""); -// checkRow(2, 4, "float", new FloatDataType(), "", ""); -// checkRow(3, 5, "char[5]", arrayDt, "", ""); -// assertLength(8); -// assertActualAlignment(4); -// } -// -// public void testInsertAligned2() throws Exception { -// emptyStructure.setInternallyAligned(true); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(8); -// assertActualAlignment(4); -// -// DataType asciiDt = model.getOriginalDataTypeManager().findDataType("/char"); -// assertNotNull(asciiDt); -// insertAtPoint(asciiDt,2,3); -// -// assertEquals(4, structureModel.getNumComponents()); -// assertEquals(5, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 1, "ch", asciiDt, "", ""); -// checkRow(3, 5, "char[5]", arrayDt, "", ""); -// assertLength(8); -// assertActualAlignment(4); -// } -// -// public void testInsertAligned3() throws Exception { -// emptyStructure.setInternallyAligned(true); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(8); -// assertActualAlignment(4); -// -// DataType doubleDt = model.getOriginalDataTypeManager().findDataType("/double"); -// assertNotNull(doubleDt); -// insertAtPoint(doubleDt,3,3); -// -// assertEquals(4, structureModel.getNumComponents()); -// assertEquals(5, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// checkRow(3, 8, "double", doubleDt, "", ""); -// assertLength(8); -// assertActualAlignment(8); -// } -// -// public void testReplaceAligned1() throws Exception { -// emptyStructure.setInternallyAligned(true); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(8); -// assertActualAlignment(4); -// -// DataType asciiDt = model.getOriginalDataTypeManager().findDataType("/char"); -// assertNotNull(asciiDt); -// addAtPoint(asciiDt,2,3); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 1, "ch", asciiDt, "", ""); -// assertLength(4); -// assertActualAlignment(4); -// } -// -// public void testReplaceAligned2() throws Exception { -// emptyStructure.setInternallyAligned(true); -// -// DataType arrayDt = new ArrayDataType(new AsciiDataType(), 5, 1); -// emptyStructure.add(new ByteDataType()); -// emptyStructure.add(new FloatDataType()); -// emptyStructure.add(arrayDt); -// -// init(emptyStructure, pgmRootCat, false); -// -// assertEquals(3, structureModel.getNumComponents()); -// assertEquals(4, structureModel.getRowCount()); -// checkRow(0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, "char[5]", arrayDt, "", ""); -// assertLength(8); -// assertActualAlignment(4); -// -// DataType doubleDt = model.getOriginalDataTypeManager().findDataType("/double"); -// assertNotNull(doubleDt); -// addAtPoint(doubleDt,3,3); -// -// assertEquals(4, structureModel.getNumComponents()); -// assertEquals(5, structureModel.getRowCount()); -// checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); -// checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); -// checkRow(2, 5, 5, "char[5]", arrayDt, "", ""); -// checkRow(3, 10, 8, "double", doubleDt, "", ""); -// assertLength(18); -// assertActualAlignment(8); -// } + @Test + public void testTurnOffAlignmentInStructure() throws Exception { + emptyStructure.setPackingEnabled(true); + emptyStructure.setExplicitMinimumAlignment(8); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + CompEditorPanel editorPanel = (CompEditorPanel) getPanel(); + + JRadioButton byValueButton = + (JRadioButton) findComponentByName(getPanel(), "Explicit Alignment"); + assertEquals(true, byValueButton.isSelected()); + JTextField minAlignField = + (JTextField) findComponentByName(getPanel(), "Explicit Alignment Value"); + assertEquals("8", minAlignField.getText()); + + assertEquals(false, structureModel.viewComposite.isDefaultAligned()); + assertEquals(false, structureModel.viewComposite.isMachineAligned()); + assertEquals(8, structureModel.getExplicitMinimumAlignment()); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(8); + assertEquals(true, structureModel.isPackingEnabled()); + + pressButtonByName(editorPanel, "Packing Enablement"); // toggle -> disable packing + + assertEquals(false, structureModel.isPackingEnabled()); + assertEquals(true, structureModel.viewComposite.isDefaultAligned()); + assertEquals(false, structureModel.viewComposite.isMachineAligned()); + assertEquals(false, structureModel.viewComposite.hasExplicitMinimumAlignment()); + + assertEquals(9, structureModel.getNumComponents()); + assertEquals(10, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(4, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(5, 8, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(1); + } + + @Test + public void testInsertUnaligned1() throws Exception { + emptyStructure.setPackingEnabled(false); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 5, "char[5]", arrayDt, "", ""); + assertLength(10); + assertActualAlignment(1); + + DataType asciiDt = model.getOriginalDataTypeManager().getDataType("/char"); + assertNotNull(asciiDt); + insertAtPoint(asciiDt, 0, 0); + + assertEquals(4, structureModel.getNumComponents()); + assertEquals(5, structureModel.getRowCount()); + checkRow(0, 0, 1, "char", asciiDt, "", ""); + checkRow(1, 1, 1, "db", new ByteDataType(), "", ""); + checkRow(2, 2, 4, "float", new FloatDataType(), "", ""); + checkRow(3, 6, 5, "char[5]", arrayDt, "", ""); + assertLength(11); + assertActualAlignment(1); + } + + @Test + public void testInsertUnaligned2() throws Exception { + emptyStructure.setPackingEnabled(false); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 5, "char[5]", arrayDt, "", ""); + assertLength(10); + assertActualAlignment(1); + + DataType asciiDt = model.getOriginalDataTypeManager().getDataType("/char"); + assertNotNull(asciiDt); + insertAtPoint(asciiDt, 2, 3); + + assertEquals(4, structureModel.getNumComponents()); + assertEquals(5, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 1, "char", asciiDt, "", ""); + checkRow(3, 6, 5, "char[5]", arrayDt, "", ""); + assertLength(11); + assertActualAlignment(1); + } + + @Test + public void testInsertUnaligned3() throws Exception { + emptyStructure.setPackingEnabled(false); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 5, "char[5]", arrayDt, "", ""); + assertLength(10); + assertActualAlignment(1); + + DataType doubleDt = model.getOriginalDataTypeManager().getDataType("/double"); + assertNotNull(doubleDt); + insertAtPoint(doubleDt, 3, 3); + + assertEquals(4, structureModel.getNumComponents()); + assertEquals(5, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 5, "char[5]", arrayDt, "", ""); + checkRow(3, 10, 8, "double", doubleDt, "", ""); + assertLength(18); + assertActualAlignment(1); + } + + @Test + public void testReplaceUnaligned1() throws Exception { + emptyStructure.setPackingEnabled(false); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 5, "char[5]", arrayDt, "", ""); + assertLength(10); + assertActualAlignment(1); + + DataType asciiDt = model.getOriginalDataTypeManager().getDataType("/char"); + assertNotNull(asciiDt); + addAtPoint(asciiDt, 2, 3); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 1, "char", asciiDt, "", ""); + assertLength(6); + assertActualAlignment(1); + } + + @Test + public void testReplaceUnaligned2() throws Exception { + emptyStructure.setPackingEnabled(false); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 5, "char[5]", arrayDt, "", ""); + assertLength(10); + assertActualAlignment(1); + + DataType doubleDt = model.getOriginalDataTypeManager().getDataType("/double"); + assertNotNull(doubleDt); + addAtPoint(doubleDt, 3, 3); + + assertEquals(4, structureModel.getNumComponents()); + assertEquals(5, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 1, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 5, 5, "char[5]", arrayDt, "", ""); + checkRow(3, 10, 8, "double", doubleDt, "", ""); + assertLength(18); + assertActualAlignment(1); + } + + @Test + public void testInsertAligned1() throws Exception { + emptyStructure.setPackingEnabled(true); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(4); + + DataType asciiDt = model.getOriginalDataTypeManager().getDataType("/char"); + assertNotNull(asciiDt); + insertAtPoint(asciiDt, 0, 0); + + assertEquals(4, structureModel.getNumComponents()); + assertEquals(5, structureModel.getRowCount()); + checkRow(0, 0, 1, "char", asciiDt, "", ""); + checkRow(1, 1, 1, "db", new ByteDataType(), "", ""); + checkRow(2, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(3, 8, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(4); + } + + @Test + public void testInsertAligned2() throws Exception { + emptyStructure.setPackingEnabled(true); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(4); + + DataType asciiDt = model.getOriginalDataTypeManager().getDataType("/char"); + assertNotNull(asciiDt); + insertAtPoint(asciiDt, 2, 3); + + assertEquals(4, structureModel.getNumComponents()); + assertEquals(5, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 1, "char", asciiDt, "", ""); + checkRow(3, 9, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(4); + } + + @Test + public void testInsertAligned3() throws Exception { + emptyStructure.setPackingEnabled(true); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(4); + + DataType doubleDt = model.getOriginalDataTypeManager().getDataType("/double"); + assertNotNull(doubleDt); + insertAtPoint(doubleDt, 3, 3); + + assertEquals(4, structureModel.getNumComponents()); + assertEquals(5, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); + checkRow(3, 16, 8, "double", doubleDt, "", ""); // alignment is 4 + assertLength(24); + assertActualAlignment(4); + } + + @Test + public void testReplaceAligned1() throws Exception { + emptyStructure.setPackingEnabled(true); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(4); + + DataType asciiDt = model.getOriginalDataTypeManager().getDataType("/char"); + assertNotNull(asciiDt); + addAtPoint(asciiDt, 2, 3); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 1, "char", asciiDt, "", ""); + assertLength(12); + assertActualAlignment(4); + } + + @Test + public void testReplaceAligned2() throws Exception { + emptyStructure.setPackingEnabled(true); + + DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); + emptyStructure.add(new ByteDataType()); + emptyStructure.add(new FloatDataType()); + emptyStructure.add(arrayDt); + + init(emptyStructure, pgmRootCat, false); + + assertEquals(3, structureModel.getNumComponents()); + assertEquals(4, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); + assertLength(16); + assertActualAlignment(4); + + DataType doubleDt = model.getOriginalDataTypeManager().getDataType("/double"); + assertNotNull(doubleDt); + addAtPoint(doubleDt, 3, 3); // same as insert on last row + + assertEquals(4, structureModel.getNumComponents()); + assertEquals(5, structureModel.getRowCount()); + checkRow(0, 0, 1, "db", new ByteDataType(), "", ""); + checkRow(1, 4, 4, "float", new FloatDataType(), "", ""); + checkRow(2, 8, 5, "char[5]", arrayDt, "", ""); + checkRow(3, 16, 8, "double", doubleDt, "", ""); + assertLength(24); + assertActualAlignment(4); + } //////////////////////////// diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java index c17a597edd..24005deab7 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorFlexAlignmentTest.java @@ -23,10 +23,15 @@ import javax.swing.JTextField; import org.junit.Test; import ghidra.program.model.data.*; -import ghidra.program.model.data.Composite.AlignmentType; public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTest { + // 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. + @Test public void testUnalignedStructure() { init(emptyStructure, pgmRootCat, false); @@ -40,10 +45,8 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes structureModel.getOriginalCategoryPath().getPath()); assertEquals(0, structureModel.getNumComponents());// no components assertEquals(1, structureModel.getRowCount());// blank row - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertLength(0); assertActualAlignment(1); assertEquals(0, structureModel.getNumSelectedComponentRows()); @@ -83,7 +86,9 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes assertLength(2); assertActualAlignment(1); - pressButtonByName(getPanel(), "Internally Aligned"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); assertEquals(2, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -105,8 +110,12 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes waitForSwing(); - pressButtonByName(getPanel(), "Internally Aligned"); - pressButtonByName(getPanel(), "Machine Minimum Alignment"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); + + pressButtonByName(getPanel(), "Machine Alignment"); + assertIsMachineAligned(); assertEquals(2, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -130,19 +139,22 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes waitForSwing(); - pressButtonByName(editorPanel, "Internally Aligned"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); + JTextField minAlignField = - (JTextField) getInstanceField("minAlignValueTextField", editorPanel); + (JTextField) getInstanceField("explicitAlignTextField", editorPanel); assertNotNull(minAlignField); - JRadioButton byValueMinAlignButton = - (JRadioButton) getInstanceField("byValueMinAlignButton", editorPanel); - assertNotNull(byValueMinAlignButton); - pressButton(byValueMinAlignButton); - assertEquals("4", minAlignField.getText()); + JRadioButton explicitAlignButton = + (JRadioButton) getInstanceField("explicitAlignButton", editorPanel); + assertNotNull(explicitAlignButton); + pressButton(explicitAlignButton); + assertEquals("8", minAlignField.getText()); // toy.cspec machine alignment is default value assertEquals(false, structureModel.viewComposite.isDefaultAligned()); assertEquals(false, structureModel.viewComposite.isMachineAligned()); - assertEquals(4, structureModel.getMinimumAlignment()); + assertEquals(8, structureModel.getExplicitMinimumAlignment()); assertEquals(2, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -151,9 +163,9 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes checkRow(0, 0, 1, "db", ByteDataType.dataType, "", ""); checkRow(1, 1, 1, "char", CharDataType.dataType, "", ""); checkBlankRow(2); - checkRow(3, 4, 0, "ddw[0]", DWordDataType.dataType, "", ""); - assertLength(4); - assertActualAlignment(4); + checkRow(3, 8, 0, "ddw[0]", DWordDataType.dataType, "", ""); + assertLength(8); + assertActualAlignment(8); } @Test @@ -183,8 +195,8 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes public void checkByValueAlignedStructure(int value, int alignment, int length) throws Exception { - emptyStructure.setInternallyAligned(true); - emptyStructure.setMinimumAlignment(value); + emptyStructure.setPackingEnabled(true); + emptyStructure.setExplicitMinimumAlignment(value); emptyStructure.add(ByteDataType.dataType); emptyStructure.add(CharDataType.dataType); @@ -193,19 +205,19 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes init(emptyStructure, pgmRootCat, false); CompEditorPanel editorPanel = (CompEditorPanel) getPanel(); - JRadioButton byValueMinAlignButton = - (JRadioButton) getInstanceField("byValueMinAlignButton", editorPanel); - assertNotNull(byValueMinAlignButton); - assertEquals(true, byValueMinAlignButton.isSelected()); + JRadioButton explicitAlignButton = + (JRadioButton) getInstanceField("explicitAlignButton", editorPanel); + assertNotNull(explicitAlignButton); + assertEquals(true, explicitAlignButton.isSelected()); JTextField minAlignField = - (JTextField) getInstanceField("minAlignValueTextField", editorPanel); + (JTextField) getInstanceField("explicitAlignTextField", editorPanel); assertNotNull(minAlignField); assertEquals("" + value, minAlignField.getText()); assertEquals(false, structureModel.viewComposite.isDefaultAligned()); assertEquals(false, structureModel.viewComposite.isMachineAligned()); - assertEquals(value, structureModel.getMinimumAlignment()); + assertEquals(value, structureModel.getExplicitMinimumAlignment()); assertEquals(2, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); @@ -220,8 +232,8 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes @Test public void testDefaultAlignedPacked1Structure() throws Exception { int value = 1; - emptyStructure.setInternallyAligned(true); - emptyStructure.setPackingValue(value); + emptyStructure.setPackingEnabled(true); + emptyStructure.setExplicitPackingValue(value); init(emptyStructure, pgmRootCat, false); CompEditorPanel editorPanel = (CompEditorPanel) getPanel(); @@ -231,17 +243,17 @@ public class StructureEditorFlexAlignmentTest extends AbstractStructureEditorTes addFlexDataType(DWordDataType.dataType, null, null); JRadioButton byValuePackingButton = - (JRadioButton) findComponentByName(editorPanel, "By Value Packing"); + (JRadioButton) findComponentByName(editorPanel, "Explicit Packing"); assertNotNull(byValuePackingButton); JTextField packingValueField = (JTextField) findComponentByName(editorPanel, "Packing Value"); assertNotNull(packingValueField); assertEquals(true, byValuePackingButton.isSelected()); - assertEquals("" + value, packingValueField.getText()); + assertEquals(Integer.toString(value), packingValueField.getText()); assertEquals(true, structureModel.viewComposite.isDefaultAligned()); assertEquals(false, structureModel.viewComposite.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, structureModel.getMinimumAlignment()); + assertEquals(false, structureModel.viewComposite.hasExplicitMinimumAlignment()); assertEquals(2, structureModel.getNumComponents()); assertEquals(4, structureModel.getRowCount()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions2Test.java index 4ecc69eae1..f9398846bc 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions2Test.java @@ -243,7 +243,7 @@ public class StructureEditorLockedActions2Test extends AbstractStructureEditorLo DataType dt2 = getDataType(2); DataType dt7 = getDataType(7); - invoke(duplicateMultipleAction); + invoke(duplicateMultipleAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); badInput(dialog, 3); @@ -270,7 +270,7 @@ public class StructureEditorLockedActions2Test extends AbstractStructureEditorLo boolean commit = false; txId = program.startTransaction("Modify Program"); try { - simpleStructure.setInternallyAligned(true); + simpleStructure.setPackingEnabled(true); commit = true; } finally { @@ -288,7 +288,7 @@ public class StructureEditorLockedActions2Test extends AbstractStructureEditorLo DataType originalDt4 = getDataType(4); // Make selected components into internal structure. - invoke(createInternalStructureAction); + invoke(createInternalStructureAction, false); // Specify name for structure. JDialog inputDialog = waitForJDialog("Specify the Structure's Name"); @@ -331,7 +331,7 @@ public class StructureEditorLockedActions2Test extends AbstractStructureEditorLo DataType originalDt4 = getDataType(4); // Make selected components into internal structure. - invoke(createInternalStructureAction); + invoke(createInternalStructureAction, false); // Specify name for structure. JDialog inputDialog = waitForJDialog("Specify the Structure's Name"); @@ -365,7 +365,7 @@ public class StructureEditorLockedActions2Test extends AbstractStructureEditorLo DataType originalDt4 = getDataType(4); // Make selected components into internal structure. - invoke(createInternalStructureAction); + invoke(createInternalStructureAction, false); // Specify name for structure. JDialog inputDialog = waitForJDialog("Specify the Structure's Name"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions3Test.java index 804272686b..8ae4c1ac90 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions3Test.java @@ -47,7 +47,7 @@ public class StructureEditorLockedActions3Test extends AbstractStructureEditorLo DataType originalDt4 = getDataType(4); // Make selected components into internal structure. - invoke(createInternalStructureAction); + invoke(createInternalStructureAction, false); // Specify name for structure. InputDialog inputDialog = waitForDialogComponent(InputDialog.class); @@ -173,7 +173,7 @@ public class StructureEditorLockedActions3Test extends AbstractStructureEditorLo assertEquals(29, getModel().getLength()); assertEquals(8, getModel().getNumComponents()); setSelection(new int[] { 4 }); - invoke(fav); + invoke(fav, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 8); @@ -213,7 +213,7 @@ public class StructureEditorLockedActions3Test extends AbstractStructureEditorLo // setSelection(new int[] {1}); // DataType dt1 = getDataType(1); // assertTrue(getDataType(1).isEquivalent(new WordDataType())); - // invoke(pointerAction); + // invoke(pointerAction, false); // dialog = (NumberInputDialog)env.waitForDialogComponent(NumberInputDialog.class, 1000); // assertNotNull(dialog); // cancelInput(dialog, 2); @@ -259,7 +259,7 @@ public class StructureEditorLockedActions3Test extends AbstractStructureEditorLo // DataType dt1 = getDataType(1); // assertTrue(getDataType(1).isEquivalent(new StringDataType())); // assertEquals(5, getModel().getComponent(1).getLength()); - // invoke(pointerAction); + // invoke(pointerAction, false); // dialog = (NumberInputDialog)env.waitForDialogComponent(NumberInputDialog.class, 1000); // assertNotNull(dialog); // okInput(dialog, 8); @@ -299,7 +299,7 @@ public class StructureEditorLockedActions3Test extends AbstractStructureEditorLo // // setSelection(new int[] {10}); // DataType dt10 = getDataType(10); - // invoke(pointerAction); + // invoke(pointerAction, false); // dialog = (NumberInputDialog)env.waitForDialogComponent(NumberInputDialog.class, 1000); // assertNotNull(dialog); // okInput(dialog, 2); @@ -321,7 +321,7 @@ public class StructureEditorLockedActions3Test extends AbstractStructureEditorLo // // setSelection(new int[] {15}); // DataType dt15 = getDataType(15); - // invoke(pointerAction); + // invoke(pointerAction, false); // dialog = (NumberInputDialog)env.waitForDialogComponent(NumberInputDialog.class, 1000); // assertNotNull(dialog); // okInput(dialog, 4); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions4Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions4Test.java index 2b1ceeb8ff..6893ef3200 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions4Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedActions4Test.java @@ -36,7 +36,7 @@ public class StructureEditorLockedActions4Test extends AbstractStructureEditorLo DataType dt8 = getDataType(8); // Make array of 7 bytes - invoke(arrayAction); + invoke(arrayAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); assertEquals("Enter Number", dialog.getTitle()); @@ -65,7 +65,7 @@ public class StructureEditorLockedActions4Test extends AbstractStructureEditorLo checkSelection(new int[] { 1 }); // Make array of 5 quadwords - invoke(arrayAction); + invoke(arrayAction, false); waitForPostedSwingRunnables(); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); @@ -109,7 +109,7 @@ public class StructureEditorLockedActions4Test extends AbstractStructureEditorLo assertEquals(getLength(16), dt16Len); assertEquals(getDataType(16), dt16); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); badInput(dialog, 20); @@ -126,7 +126,7 @@ public class StructureEditorLockedActions4Test extends AbstractStructureEditorLo assertEquals(getLength(2), dt16Len); assertEquals(getDataType(2), dt16); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 10); @@ -180,7 +180,7 @@ public class StructureEditorLockedActions4Test extends AbstractStructureEditorLo assertEquals(getLength(8), dt8Len); assertEquals(getDataType(8), dt8); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 7); @@ -194,7 +194,7 @@ public class StructureEditorLockedActions4Test extends AbstractStructureEditorLo assertEquals(getLength(2), dt8Len); assertEquals(getDataType(2), dt8); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 6); @@ -241,7 +241,7 @@ public class StructureEditorLockedActions4Test extends AbstractStructureEditorLo assertEquals(1, getLength(1)); assertEquals(getDataType(1), dt1); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); badInput(dialog, 7); @@ -255,7 +255,7 @@ public class StructureEditorLockedActions4Test extends AbstractStructureEditorLo assertEquals(1, getLength(1)); assertEquals(getDataType(1), dt1); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); badInput(dialog, 10); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedCellEditTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedCellEditTest.java index 6faa392580..030538916b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedCellEditTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedCellEditTest.java @@ -523,7 +523,7 @@ public class StructureEditorLockedCellEditTest extends AbstractStructureEditorTe public void testEditDataTypeNotSelf() throws Exception { Structure testStruct = new StructureDataType("testStruct", 20); - testStruct.setInternallyAligned(false); + testStruct.setPackingEnabled(false); DataTypeManager dtm = program.getDataTypeManager(); txId = program.startTransaction("Add testStruct"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions1Test.java index c9020aa290..92c8f8205f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions1Test.java @@ -46,7 +46,7 @@ public class StructureEditorUnlockedActions1Test assertEquals(getDataType(3), DataType.DEFAULT); assertEquals(getDataType(4), dt3); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); @@ -60,7 +60,7 @@ public class StructureEditorUnlockedActions1Test assertEquals(getDataType(3), dt3); setSelection(new int[] { 2 }); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions2Test.java index ea4dd50efe..482eb80345 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions2Test.java @@ -165,7 +165,7 @@ public class StructureEditorUnlockedActions2Test assertEquals(getDataType(3), DataType.DEFAULT); assertEquals(getDataType(4), dt3); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); @@ -179,7 +179,7 @@ public class StructureEditorUnlockedActions2Test assertEquals(getDataType(3), dt3); setSelection(new int[] { 2 }); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions3Test.java index c91b9d021d..82fa2c4e00 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions3Test.java @@ -44,7 +44,7 @@ public class StructureEditorUnlockedActions3Test DataType dt2 = getDataType(2);// word DataType dt7 = getDataType(7);// SimpleUnion - invoke(duplicateMultipleAction); + invoke(duplicateMultipleAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); @@ -73,7 +73,7 @@ public class StructureEditorUnlockedActions3Test DataType dt0 = getDataType(0); DataType dt1 = getDataType(1); - invoke(duplicateMultipleAction); + invoke(duplicateMultipleAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions4Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions4Test.java index 0eee6ce58e..ab011f5bf1 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions4Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions4Test.java @@ -26,7 +26,6 @@ import org.junit.Test; import docking.widgets.dialogs.NumberInputDialog; import ghidra.program.model.data.*; import ghidra.util.exception.UsrException; -import ghidra.util.task.TaskMonitor; public class StructureEditorUnlockedActions4Test extends AbstractStructureEditorUnlockedActionsTest { @@ -56,7 +55,7 @@ public class StructureEditorUnlockedActions4Test invoke(applyAction); assertTrue(complexStructure.isEquivalent(model.viewComposite)); assertEquals(1, complexStructure.getLength()); - assertTrue(complexStructure.isNotYetDefined()); + assertTrue(complexStructure.isZeroLength()); } @Test @@ -74,7 +73,7 @@ public class StructureEditorUnlockedActions4Test assertEquals(2, model.getComponent(5).getLength()); // Make array of 3 pointers - invoke(arrayAction); + invoke(arrayAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 3); @@ -222,7 +221,7 @@ public class StructureEditorUnlockedActions4Test runSwing(() -> { try { model.clearSelectedComponents(); - model.deleteSelectedComponents(TaskMonitor.DUMMY); + model.deleteSelectedComponents(); } catch (UsrException e) { failWithException("Unexpected error", e); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java index de327790b1..0ca919016d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions5Test.java @@ -87,6 +87,7 @@ public class StructureEditorUnlockedActions5Test invoke(applyAction); assertTrue(simpleStructure.isEquivalent(model.viewComposite)); assertTrue(simpleStructure.isNotYetDefined()); + assertTrue(simpleStructure.isZeroLength()); assertTrue(viewCopy.isEquivalent(model.viewComposite)); // Is now allowed // assertEquals( @@ -132,7 +133,7 @@ public class StructureEditorUnlockedActions5Test assertTrue(dt2 instanceof WordDataType); // Cancel the array dialog - invoke(arrayAction); + invoke(arrayAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); cancelInput(dialog); @@ -163,7 +164,7 @@ public class StructureEditorUnlockedActions5Test assertEquals(getDataType(3), DataType.DEFAULT); assertEquals(getDataType(4), dt3); - invoke(action); + invoke(action, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); cancelInput(dialog); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions6Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions6Test.java index 7f9609cdc4..61989d7a89 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions6Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedActions6Test.java @@ -43,7 +43,7 @@ public class StructureEditorUnlockedActions6Test assertTrue(dt15 instanceof Array); // Make array of 2 arrays - invoke(arrayAction); + invoke(arrayAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); @@ -70,7 +70,7 @@ public class StructureEditorUnlockedActions6Test assertEquals("dword", dt3.getDisplayName()); // Make array of 5 quadwords - invoke(arrayAction); + invoke(arrayAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 5); @@ -88,6 +88,7 @@ public class StructureEditorUnlockedActions6Test init(emptyStructure, pgmRootCat); int originalLength = 0; assertTrue(emptyStructure.isNotYetDefined()); + assertTrue(emptyStructure.isZeroLength()); int newLength = 5; assertEquals(originalLength, model.getLength()); @@ -151,7 +152,7 @@ public class StructureEditorUnlockedActions6Test assertEquals(0, model.getLength()); assertEquals(0, model.getNumComponents()); - invoke(fav); + invoke(fav, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 7); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java index 54556b06c6..f1d07eaf13 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.compositeeditor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import org.junit.Assert; import org.junit.Test; @@ -32,7 +31,7 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit try { DataTypeManager dataTypeManager = cat.getDataTypeManager(); if (dt.getDataTypeManager() != dataTypeManager) { - dt = (Structure) dt.clone(dataTypeManager); + dt = dt.clone(dataTypeManager); } CategoryPath categoryPath = cat.getCategoryPath(); if (!dt.getCategoryPath().equals(categoryPath)) { @@ -336,7 +335,7 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit assertEquals("word", getDataType(3).getDisplayName()); assertTrue(!editBitFieldAction.isEnabled()); - structureModel.setAligned(true); + structureModel.setPackingType(PackingType.DEFAULT, 0); // Edit Bitfield action not enabled for Aligned mode setSelection(new int[] { 1 }); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions1Test.java index f8b6ae5cb2..867c4e1e5b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions1Test.java @@ -26,8 +26,6 @@ import org.junit.Test; import docking.widgets.dialogs.NumberInputDialog; import ghidra.program.model.data.*; -import ghidra.program.model.data.Composite.AlignmentType; -import ghidra.util.task.TaskMonitor; public class UnionEditorActions1Test extends AbstractUnionEditorTest { @@ -44,10 +42,8 @@ public class UnionEditorActions1Test extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(emptyUnion.getName(), model.getCompositeName()); @@ -83,10 +79,8 @@ public class UnionEditorActions1Test extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { model.getNumComponents() }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(8); assertEquals(simpleUnion.getName(), model.getCompositeName()); @@ -283,8 +277,8 @@ public class UnionEditorActions1Test extends AbstractUnionEditorTest { assertEquals(0, model.getLength()); assertEquals(0, model.getNumComponents()); - invoke(fav); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(fav, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 7); dialog = null; @@ -377,8 +371,8 @@ public class UnionEditorActions1Test extends AbstractUnionEditorTest { assertTrue(getDataType(2).isEquivalent(new CharDataType())); assertEquals(getDataType(3), dt3); - invoke(action); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(action, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); cancelInput(dialog); dialog = null; @@ -668,7 +662,7 @@ public class UnionEditorActions1Test extends AbstractUnionEditorTest { DataType dt3 = getDataType(3); // Cancel the array dialog - invoke(arrayAction); + invoke(arrayAction, false); dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); cancelInput(dialog); @@ -766,7 +760,7 @@ public class UnionEditorActions1Test extends AbstractUnionEditorTest { init(complexUnion, pgmTestCat, false); setSelection(new int[] { 3, 4 }); - model.deleteSelectedComponents(TaskMonitor.DUMMY); + model.deleteSelectedComponents(); DataType viewCopy = model.viewComposite.clone(null); assertFalse(complexUnion.isEquivalent(model.viewComposite)); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions2Test.java index c18010746e..0b14238243 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions2Test.java @@ -46,8 +46,8 @@ public class UnionEditorActions2Test extends AbstractUnionEditorTest { assertTrue(getDataType(2).isEquivalent(new CharDataType())); assertEquals(getDataType(3), dt3); - invoke(action); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(action, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 7); dialog = null; @@ -59,8 +59,8 @@ public class UnionEditorActions2Test extends AbstractUnionEditorTest { assertTrue(getDataType(2).isEquivalent(new StringDataType())); assertEquals(getDataType(3), dt3); - invoke(action); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(action, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 10); dialog = null; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions3Test.java index 3f4f14deb5..a7ac6f9a88 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions3Test.java @@ -35,8 +35,8 @@ public class UnionEditorActions3Test extends AbstractUnionEditorTest { DataType dt11 = getDataType(11); // Make array of 2 arrays - invoke(arrayAction); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(arrayAction, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); dialog = null; @@ -58,8 +58,8 @@ public class UnionEditorActions3Test extends AbstractUnionEditorTest { DataType dt3 = getDataType(3); // Make array of 5 quadwords - invoke(arrayAction); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(arrayAction, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 5); dialog = null; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions4Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions4Test.java index a00d4bf516..5af5f6707d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions4Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorActions4Test.java @@ -36,8 +36,8 @@ public class UnionEditorActions4Test extends AbstractUnionEditorTest { assertEquals(2, model.getComponent(4).getLength()); // Make array of 3 pointers - invoke(arrayAction); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(arrayAction, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 3); dialog = null; @@ -59,8 +59,8 @@ public class UnionEditorActions4Test extends AbstractUnionEditorTest { DataType dt2 = getDataType(2); DataType dt3 = getDataType(3); - invoke(duplicateMultipleAction); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(duplicateMultipleAction, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 5); dialog = null; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorAlignmentTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorAlignmentTest.java index 3e99c43f12..db815aad5f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorAlignmentTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorAlignmentTest.java @@ -23,7 +23,6 @@ import javax.swing.JTextField; import org.junit.Test; import ghidra.program.model.data.*; -import ghidra.program.model.data.Composite.AlignmentType; public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @@ -40,10 +39,8 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { unionModel.getOriginalCategoryPath().getPath()); assertEquals(0, unionModel.getNumComponents());// no components assertEquals(1, unionModel.getRowCount());// blank row - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertLength(0); assertActualAlignment(1); assertEquals(0, unionModel.getNumSelectedComponentRows()); @@ -88,7 +85,9 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { addDataType(new FloatDataType()); addDataType(arrayDt); - pressButtonByName(getPanel(), "Internally Aligned"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); assertEquals(true, unionModel.viewComposite.isDefaultAligned()); assertEquals(3, unionModel.getNumComponents()); @@ -102,7 +101,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testEnablementDefaultAlignedUnion() throws Exception { - emptyUnion.setInternallyAligned(true); + emptyUnion.setPackingEnabled(true); init(emptyUnion, pgmRootCat, false); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); @@ -112,16 +111,16 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { // Check enablement. CompositeEditorTableAction[] pActions = provider.getActions(); - for (int i = 0; i < pActions.length; i++) { - if ((pActions[i] instanceof FavoritesAction) || - (pActions[i] instanceof CycleGroupAction) || - (pActions[i] instanceof EditFieldAction) || - (pActions[i] instanceof PointerAction) || - (pActions[i] instanceof HexNumbersAction) || (pActions[i] instanceof ApplyAction)) { - checkEnablement(pActions[i], true); + for (CompositeEditorTableAction pAction : pActions) { + if ((pAction instanceof FavoritesAction) || + (pAction instanceof CycleGroupAction) || + (pAction instanceof EditFieldAction) || + (pAction instanceof PointerAction) || + (pAction instanceof HexNumbersAction) || (pAction instanceof ApplyAction)) { + checkEnablement(pAction, true); } else { - checkEnablement(pActions[i], false); + checkEnablement(pAction, false); } } @@ -144,8 +143,11 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { addDataType(new FloatDataType()); addDataType(arrayDt); - pressButtonByName(getPanel(), "Internally Aligned"); - pressButtonByName(getPanel(), "Machine Minimum Alignment"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); + + pressButtonByName(getPanel(), "Machine Alignment"); assertEquals(true, unionModel.viewComposite.isMachineAligned()); assertEquals(3, unionModel.getNumComponents()); @@ -166,16 +168,19 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { addDataType(new FloatDataType()); addDataType(arrayDt); - pressButtonByName(getPanel(), "Internally Aligned"); - pressButtonByName(getPanel(), "By Value Minimum Alignment"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> enable packing + assertIsPackingEnabled(true); + assertDefaultPacked(); + + pressButtonByName(getPanel(), "Explicit Alignment"); JTextField minAlignField = - (JTextField) findComponentByName(getPanel(), "Minimum Alignment Value"); - assertEquals("4", minAlignField.getText()); + (JTextField) findComponentByName(getPanel(), "Explicit Alignment Value"); + assertEquals("8", minAlignField.getText()); // toy.cspec machine alignment is default value assertEquals(false, unionModel.viewComposite.isDefaultAligned()); assertEquals(false, unionModel.viewComposite.isMachineAligned()); - assertEquals(4, unionModel.getMinimumAlignment()); + assertEquals(8, unionModel.getExplicitMinimumAlignment()); assertEquals(3, unionModel.getNumComponents()); assertEquals(4, unionModel.getRowCount()); @@ -183,7 +188,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { checkRow(1, 4, "float", new FloatDataType(), "", ""); checkRow(2, 5, "char[5]", arrayDt, "", ""); assertLength(8); - assertActualAlignment(4); + assertActualAlignment(8); } @Test @@ -211,9 +216,9 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { checkByValueAlignedUnion(16, 16, 16); } - public void checkByValueAlignedUnion(int value, int alignment, int length) throws Exception { - emptyUnion.setInternallyAligned(true); - emptyUnion.setMinimumAlignment(value); + public void checkByValueAlignedUnion(int minAlignment, int alignment, int length) throws Exception { + emptyUnion.setPackingEnabled(true); + emptyUnion.setExplicitMinimumAlignment(minAlignment); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -223,15 +228,15 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { init(emptyUnion, pgmRootCat, false); JRadioButton byValueButton = - (JRadioButton) findComponentByName(getPanel(), "By Value Minimum Alignment"); + (JRadioButton) findComponentByName(getPanel(), "Explicit Alignment"); assertEquals(true, byValueButton.isSelected()); JTextField minAlignField = - (JTextField) findComponentByName(getPanel(), "Minimum Alignment Value"); - assertEquals("" + value, minAlignField.getText()); + (JTextField) findComponentByName(getPanel(), "Explicit Alignment Value"); + assertEquals("" + minAlignment, minAlignField.getText()); assertEquals(false, unionModel.viewComposite.isDefaultAligned()); assertEquals(false, unionModel.viewComposite.isMachineAligned()); - assertEquals(value, unionModel.getMinimumAlignment()); + assertEquals(minAlignment, unionModel.getExplicitMinimumAlignment()); assertEquals(3, unionModel.getNumComponents()); assertEquals(4, unionModel.getRowCount()); @@ -244,8 +249,8 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testTurnOffAlignmentInUnion() throws Exception { - emptyUnion.setInternallyAligned(true); - emptyUnion.setMinimumAlignment(8); + emptyUnion.setPackingEnabled(true); + emptyUnion.setExplicitMinimumAlignment(8); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -255,15 +260,15 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { init(emptyUnion, pgmRootCat, false); JRadioButton byValueButton = - (JRadioButton) findComponentByName(getPanel(), "By Value Minimum Alignment"); + (JRadioButton) findComponentByName(getPanel(), "Explicit Alignment"); assertEquals(true, byValueButton.isSelected()); JTextField minAlignField = - (JTextField) findComponentByName(getPanel(), "Minimum Alignment Value"); + (JTextField) findComponentByName(getPanel(), "Explicit Alignment Value"); assertEquals("8", minAlignField.getText()); assertEquals(false, unionModel.viewComposite.isDefaultAligned()); assertEquals(false, unionModel.viewComposite.isMachineAligned()); - assertEquals(8, unionModel.getMinimumAlignment()); + assertEquals(8, unionModel.getExplicitMinimumAlignment()); assertEquals(3, unionModel.getNumComponents()); assertEquals(4, unionModel.getRowCount()); @@ -272,14 +277,14 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { checkRow(2, 5, "char[5]", arrayDt, "", ""); assertLength(8); assertActualAlignment(8); - assertEquals(true, unionModel.isAligned()); + assertEquals(true, unionModel.isPackingEnabled()); - pressButtonByName(getPanel(), "Internally Aligned"); + pressButtonByName(getPanel(), "Packing Enablement"); // toggle -> disable packing - assertEquals(false, unionModel.isAligned()); + assertEquals(false, unionModel.isPackingEnabled()); assertEquals(true, unionModel.viewComposite.isDefaultAligned()); assertEquals(false, unionModel.viewComposite.isMachineAligned()); - assertEquals(Composite.DEFAULT_ALIGNMENT_VALUE, unionModel.getMinimumAlignment()); + assertEquals(false, unionModel.viewComposite.hasExplicitMinimumAlignment()); assertEquals(3, unionModel.getNumComponents()); assertEquals(4, unionModel.getRowCount()); @@ -292,7 +297,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testInsertUnaligned1() throws Exception { - emptyUnion.setInternallyAligned(false); + emptyUnion.setPackingEnabled(false); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -325,7 +330,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testInsertUnaligned2() throws Exception { - emptyUnion.setInternallyAligned(false); + emptyUnion.setPackingEnabled(false); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -358,7 +363,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testInsertUnaligned3() throws Exception { - emptyUnion.setInternallyAligned(false); + emptyUnion.setPackingEnabled(false); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -391,7 +396,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testReplaceUnaligned1() throws Exception { - emptyUnion.setInternallyAligned(false); + emptyUnion.setPackingEnabled(false); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -423,7 +428,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testReplaceUnaligned2() throws Exception { - emptyUnion.setInternallyAligned(false); + emptyUnion.setPackingEnabled(false); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -456,7 +461,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testInsertAligned1() throws Exception { - emptyUnion.setInternallyAligned(true); + emptyUnion.setPackingEnabled(true); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -489,7 +494,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testInsertAligned2() throws Exception { - emptyUnion.setInternallyAligned(true); + emptyUnion.setPackingEnabled(true); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -522,7 +527,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testInsertAligned3() throws Exception { - emptyUnion.setInternallyAligned(true); + emptyUnion.setPackingEnabled(true); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -555,7 +560,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testReplaceAligned1() throws Exception { - emptyUnion.setInternallyAligned(true); + emptyUnion.setPackingEnabled(true); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); @@ -587,7 +592,7 @@ public class UnionEditorAlignmentTest extends AbstractUnionEditorTest { @Test public void testReplaceAligned2() throws Exception { - emptyUnion.setInternallyAligned(true); + emptyUnion.setPackingEnabled(true); DataType arrayDt = new ArrayDataType(new CharDataType(), 5, 1); emptyUnion.add(new ByteDataType()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorDnDTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorDnDTest.java index 39ae852454..450e8b9245 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorDnDTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorDnDTest.java @@ -63,7 +63,7 @@ public class UnionEditorDnDTest extends AbstractUnionEditorTest { assertNotNull(dt4); addAtPoint(dt4, 3, 0); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 25); dialog = null; @@ -106,7 +106,7 @@ public class UnionEditorDnDTest extends AbstractUnionEditorTest { DataType dt4 = model.getOriginalDataTypeManager().getDataType("/string"); assertNotNull(dt4); insertAtPoint(dt4, 0, 0); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 25); dialog = null; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java index 006ea5d25e..56759dde85 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/UnionEditorNotifiedTest.java @@ -25,11 +25,9 @@ import org.junit.Assert; import org.junit.Test; import ghidra.program.model.data.*; -import ghidra.program.model.data.Composite.AlignmentType; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.UsrException; import ghidra.util.task.TaskMonitor; -import ghidra.util.task.TaskMonitorAdapter; public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { @@ -40,7 +38,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { try { DataTypeManager dataTypeManager = cat.getDataTypeManager(); if (dt.getDataTypeManager() != dataTypeManager) { - dt = (Union) dt.clone(dataTypeManager); + dt = dt.clone(dataTypeManager); } CategoryPath categoryPath = cat.getCategoryPath(); if (!dt.getCategoryPath().equals(categoryPath)) { @@ -61,10 +59,8 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { installProvider(new UnionEditorProvider(plugin, unionDt, showInHex)); model = provider.getModel(); }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); startTransaction("Modify Program"); } @@ -86,7 +82,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { init(complexUnion, pgmTestCat, false); assertEquals("/aa/bb", getDataType(20).getCategoryPath().getPath()); - pgmTestCat.moveCategory(pgmBbCat, TaskMonitorAdapter.DUMMY_MONITOR); + pgmTestCat.moveCategory(pgmBbCat, TaskMonitor.DUMMY); waitForSwing(); assertEquals("/testCat/bb", getDataType(20).getCategoryPath().getPath()); } @@ -101,7 +97,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { init(simpleUnion, pgmBbCat, false); assertEquals(pgmBbCat.getCategoryPathName(), model.getOriginalCategoryPath().getPath()); - pgmTestCat.moveCategory(pgmBbCat, TaskMonitorAdapter.DUMMY_MONITOR); + pgmTestCat.moveCategory(pgmBbCat, TaskMonitor.DUMMY); waitForSwing(); assertTrue(model.getOriginalCategoryPath().getPath().startsWith( pgmTestCat.getCategoryPathName())); @@ -142,9 +138,9 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { DataType dt18 = getDataType(18).clone(programDTM); DataType dt20 = getDataType(20).clone(programDTM); SwingUtilities.invokeLater(() -> { - programDTM.remove(complexUnion, TaskMonitorAdapter.DUMMY_MONITOR); + programDTM.remove(complexUnion, TaskMonitor.DUMMY); programDTM.getCategory(pgmRootCat.getCategoryPath()).removeCategory("Temp", - TaskMonitorAdapter.DUMMY_MONITOR); + TaskMonitor.DUMMY); }); waitForSwing(); @@ -250,7 +246,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { DataType origCopy = complexUnion.clone(null); // Verify the Reload Union Editor? dialog is displayed. - dialog = env.waitForWindow("Reload Union Editor?", 1000); + dialog = waitForWindow("Reload Union Editor?"); assertNotNull(dialog); pressButtonByText(dialog, "Yes"); dialog.dispose(); @@ -287,7 +283,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { waitForSwing(); // Verify the Reload Union Editor? dialog is displayed. - dialog = env.waitForWindow("Reload Union Editor?", 1000); + dialog = waitForWindow("Reload Union Editor?"); assertNotNull(dialog); pressButtonByText(dialog, "No"); dialog.dispose(); @@ -374,7 +370,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { assertEquals(21, model.getNumComponents()); SwingUtilities.invokeLater(() -> complexUnion.getDataTypeManager().remove( - simpleStructure, TaskMonitorAdapter.DUMMY_MONITOR)); + simpleStructure, TaskMonitor.DUMMY)); waitForSwing(); assertEquals(15, model.getNumComponents()); } @@ -400,7 +396,7 @@ public class UnionEditorNotifiedTest extends AbstractUnionEditorTest { assertTrue(simpleUnion.isEquivalent(getDataType(0))); SwingUtilities.invokeLater(() -> simpleUnion.getDataTypeManager().remove(simpleUnion, - TaskMonitorAdapter.DUMMY_MONITOR)); + TaskMonitor.DUMMY)); waitForSwing(); assertEquals(0, model.getNumComponents()); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeStructureTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeStructureTest.java index de6c7ccd4e..63dc787c74 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeStructureTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeStructureTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.*; import org.junit.Test; import ghidra.program.model.data.*; -import ghidra.program.model.data.Composite.AlignmentType; public class ZeroSizeStructureTest extends AbstractStructureEditorTest { @@ -39,10 +38,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(emptyStructure.getName(), model.getCompositeName()); @@ -55,12 +52,10 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { DataType dt = pgmRootCat.getDataType(emptyStructure.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(emptyStructure.getName(), model.getCompositeName()); @@ -76,7 +71,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { DataType dt = pgmBbCat.getDataType(simpleStructure.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(8, model.getNumComponents()); assertEquals(9, model.getRowCount()); @@ -87,10 +82,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 8 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(29); assertEquals(simpleStructure.getName(), model.getCompositeName()); @@ -110,10 +103,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(simpleStructure.getName(), model.getCompositeName()); @@ -125,16 +116,14 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { invoke(applyAction); assertTrue(simpleStructure.isEquivalent(model.viewComposite)); - assertTrue(simpleStructure.isNotYetDefined()); + assertTrue(simpleStructure.isZeroLength()); dt = pgmBbCat.getDataType(simpleStructure.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(simpleStructure.getName(), model.getCompositeName()); @@ -179,7 +168,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { DataType dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(1, model.getNumComponents()); assertEquals(2, model.getRowCount()); @@ -190,10 +179,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 1 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(1); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -213,10 +200,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -229,12 +214,10 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -280,7 +263,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { DataType dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(1, model.getNumComponents()); assertEquals(2, model.getRowCount()); @@ -291,10 +274,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 1 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(1); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -314,10 +295,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -330,15 +309,13 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); assertTrue(innerStructure.isEquivalent(model.viewComposite)); - assertTrue(innerStructure.isNotYetDefined()); + assertTrue(innerStructure.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -373,13 +350,13 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { } assertNotNull(innerStructure); assertNotNull(innerTypedef); - assertTrue(!innerTypedef.isNotYetDefined()); + assertTrue(!innerTypedef.isZeroLength()); init(innerStructure, pgmTestCat, false); DataType dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(1, model.getNumComponents()); assertEquals(2, model.getRowCount()); @@ -390,10 +367,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 1 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(1); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -413,10 +388,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -429,15 +402,13 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); assertTrue(innerStructure.isEquivalent(model.viewComposite)); - assertTrue(innerStructure.isNotYetDefined()); + assertTrue(innerStructure.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -446,7 +417,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(false, applyAction.isEnabled()); // assertStatus("/testCat/innerStructure is contained in /testCat/innerStructureTypedef and can't be changed to a zero size data type."); - assertTrue(innerTypedef.isNotYetDefined()); + assertTrue(innerTypedef.isZeroLength()); assertEquals(1, innerTypedef.getLength()); } @@ -481,7 +452,7 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { DataType dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(1, model.getNumComponents()); assertEquals(2, model.getRowCount()); @@ -492,10 +463,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 1 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(2); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -515,10 +484,8 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerStructure.getName(), model.getCompositeName()); @@ -531,15 +498,13 @@ public class ZeroSizeStructureTest extends AbstractStructureEditorTest { dt = pgmTestCat.getDataType(innerStructure.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); assertTrue(innerStructure.isEquivalent(model.viewComposite)); - assertTrue(innerStructure.isNotYetDefined()); + assertTrue(innerStructure.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerStructure.getName(), model.getCompositeName()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeUnionTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeUnionTest.java index 15c8e66b58..9e749ea549 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeUnionTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/ZeroSizeUnionTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.*; import org.junit.Test; import ghidra.program.model.data.*; -import ghidra.program.model.data.Composite.AlignmentType; public class ZeroSizeUnionTest extends AbstractUnionEditorTest { @@ -39,10 +38,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(emptyUnion.getName(), model.getCompositeName()); @@ -55,12 +52,10 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { DataType dt = pgmRootCat.getDataType(emptyUnion.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(emptyUnion.getName(), model.getCompositeName()); @@ -76,7 +71,7 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { DataType dt = pgmBbCat.getDataType(simpleUnion.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(7, model.getNumComponents()); assertEquals(8, model.getRowCount()); @@ -87,10 +82,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 7 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(8); assertEquals(simpleUnion.getName(), model.getCompositeName()); @@ -110,10 +103,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(simpleUnion.getName(), model.getCompositeName()); @@ -125,18 +116,16 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { invoke(applyAction); assertTrue(simpleUnion.isEquivalent(model.viewComposite)); - assertTrue(simpleUnion.isNotYetDefined()); + assertTrue(simpleUnion.isZeroLength()); dt = pgmBbCat.getDataType(simpleUnion.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); assertTrue(simpleUnion.isEquivalent(model.viewComposite)); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(simpleUnion.getName(), model.getCompositeName()); @@ -179,7 +168,7 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { DataType dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(1, model.getNumComponents()); assertEquals(2, model.getRowCount()); @@ -190,10 +179,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 1 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(1); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -213,10 +200,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -229,12 +214,10 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -278,7 +261,7 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { DataType dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(1, model.getNumComponents()); assertEquals(2, model.getRowCount()); @@ -289,10 +272,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 1 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(1); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -312,10 +293,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -328,15 +307,13 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); assertTrue(innerUnion.isEquivalent(model.viewComposite)); - assertTrue(innerUnion.isNotYetDefined()); + assertTrue(innerUnion.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -374,7 +351,7 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { DataType dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(1, model.getNumComponents()); assertEquals(2, model.getRowCount()); @@ -385,10 +362,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 1 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(1); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -408,10 +383,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -424,15 +397,13 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); assertTrue(innerUnion.isEquivalent(model.viewComposite)); - assertTrue(innerUnion.isNotYetDefined()); + assertTrue(innerUnion.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -470,7 +441,7 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { DataType dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); - assertFalse(dt.isNotYetDefined()); + assertFalse(dt.isZeroLength()); assertEquals(1, model.getNumComponents()); assertEquals(2, model.getRowCount()); @@ -481,10 +452,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 1 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(1); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -504,10 +473,8 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { assertEquals(0, model.getNumSelectedComponentRows()); assertEquals(1, model.getNumSelectedRows()); checkSelection(new int[] { 0 }); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerUnion.getName(), model.getCompositeName()); @@ -520,15 +487,13 @@ public class ZeroSizeUnionTest extends AbstractUnionEditorTest { dt = pgmTestCat.getDataType(innerUnion.getName()); assertNotNull(dt); - assertTrue(dt.isNotYetDefined()); + assertTrue(dt.isZeroLength()); assertTrue(innerUnion.isEquivalent(model.viewComposite)); - assertTrue(innerUnion.isNotYetDefined()); + assertTrue(innerUnion.isZeroLength()); - assertIsInternallyAligned(false); - assertPackingValue(Composite.NOT_PACKING); - assertMinimumAlignmentType(AlignmentType.DEFAULT_ALIGNED); - assertMinimumAlignmentValue(Composite.DEFAULT_ALIGNMENT_VALUE); + assertIsPackingEnabled(false); + assertIsDefaultAligned(); assertActualAlignment(1); assertLength(0); assertEquals(innerUnion.getName(), model.getCompositeName()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java index 34e4002087..2a2dbce884 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java @@ -774,14 +774,14 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { enumm.add("One", 1); Structure structX = new StructureDataType("StructX", 0); - structX.setInternallyAligned(true); + structX.setPackingEnabled(true); structX.add(new ByteDataType()); structX.add(enumm); structX.add(new ByteDataType()); category.addDataType(structX, DataTypeConflictHandler.DEFAULT_HANDLER); Structure structY = new StructureDataType("StructY", 0); - structY.setInternallyAligned(false); + structY.setPackingEnabled(false); structY.add(new ByteDataType()); structY.add(enumm); category.addDataType(structY, DataTypeConflictHandler.DEFAULT_HANDLER); @@ -815,7 +815,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { assertEquals(intValue, 4); Structure structX = (Structure) category.getDataType("StructX"); - assertTrue(structX.isInternallyAligned()); + assertTrue(structX.isPackingEnabled()); assertEquals(3, structX.getNumComponents()); assertEquals(1, structX.getComponent(0).getLength()); assertEquals(4, structX.getComponent(1).getLength()); @@ -823,7 +823,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { assertEquals(12, structX.getLength()); Structure structY = (Structure) category.getDataType("StructY"); - assertFalse(structY.isInternallyAligned()); + assertFalse(structY.isPackingEnabled()); assertEquals(2, structY.getNumComponents()); assertEquals(1, structY.getComponent(0).getLength()); assertEquals(4, structY.getComponent(1).getLength()); @@ -846,14 +846,14 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { enumm.setDescription("ABCD"); Structure structX = new StructureDataType("StructX", 0); - structX.setInternallyAligned(true); + structX.setPackingEnabled(true); structX.add(new ByteDataType()); structX.add(enumm); structX.add(new ByteDataType()); category.addDataType(structX, DataTypeConflictHandler.DEFAULT_HANDLER); Structure structY = new StructureDataType("StructY", 0); - structY.setInternallyAligned(false); + structY.setPackingEnabled(false); structY.add(new ByteDataType()); structY.add(enumm); category.addDataType(structY, DataTypeConflictHandler.DEFAULT_HANDLER); @@ -926,7 +926,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { assertEquals(1, value1); Structure structX = (Structure) category.getDataType("StructX"); - assertTrue(structX.isInternallyAligned()); + assertTrue(structX.isPackingEnabled()); assertEquals(3, structX.getNumComponents()); assertEquals(1, structX.getComponent(0).getLength()); assertEquals(4, structX.getComponent(1).getLength()); @@ -934,7 +934,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest { assertEquals(12, structX.getLength()); Structure structY = (Structure) category.getDataType("StructY"); - assertFalse(structY.isInternallyAligned()); + assertFalse(structY.isPackingEnabled()); assertEquals(2, structY.getNumComponents()); assertEquals(1, structY.getComponent(0).getLength()); assertEquals(4, structY.getComponent(1).getLength()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPluginTest.java index 5637560c5b..575f6e7370 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datapreview/DataTypePreviewPluginTest.java @@ -180,7 +180,7 @@ public class DataTypePreviewPluginTest extends AbstractGhidraHeadedIntegrationTe plugin.addDataType(ShortDataType.dataType); Structure struct = new StructureDataType("test", 0); - struct.setInternallyAligned(true); + struct.setPackingEnabled(true); struct.add(IntegerDataType.dataType, "intField", ""); struct.add(LongDataType.dataType, "longField", ""); struct.add(ShortDataType.dataType, "shortField", ""); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/PositiveStackEditorEnablementTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/PositiveStackEditorEnablementTest.java index b8131ace29..de41b808dd 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/PositiveStackEditorEnablementTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/PositiveStackEditorEnablementTest.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.stackeditor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import org.junit.Test; @@ -26,10 +25,6 @@ import ghidra.program.model.data.Pointer; public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { - /** - * Constructor for StackEditorLockedActionsTest. - * @param name the testcase name. - */ public PositiveStackEditorEnablementTest() { super(true); } @@ -52,13 +47,13 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { assertEquals(stackModel.getTypeName(), "Stack"); // Check enablement. - for (int i = 0; i < actions.length; i++) { - if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction)) { - checkEnablement(actions[i], true); + for (CompositeEditorTableAction action : actions) { + if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -83,13 +78,13 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { assertEquals(stackModel.getTypeName(), "Stack"); // Check enablement. - for (int i = 0; i < actions.length; i++) { - if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction)) { - checkEnablement(actions[i], true); + for (CompositeEditorTableAction action : actions) { + if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } @@ -97,9 +92,9 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement. int numBytes = getModel().getMaxReplaceLength(0); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); @@ -108,17 +103,17 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { if (offset < 0 && offset > paramOffset) { enabled = false; } - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof DeleteAction) || (actions[i] instanceof PointerAction) || - (actions[i] instanceof HexNumbersAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof DeleteAction) || (action instanceof PointerAction) || + (action instanceof HexNumbersAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -143,49 +138,49 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { setSelection(new int[] { 19 });// undefined (no variable here). int numBytes = getModel().getMaxReplaceLength(19); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } setSelection(new int[] { 14 });// pointer numBytes = getModel().getMaxReplaceLength(14); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = (len <= numBytes) // && !(favDt instanceof TerminatedStringDataType) // && !(favDt instanceof TerminatedUnicodeDataType) ; - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof ArrayAction) || (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof ArrayAction) || (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -197,24 +192,24 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on first component selected. model.setSelection(new int[] { 0 }); int numBytes = getModel().getMaxReplaceLength(0); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof DeleteAction) || (actions[i] instanceof ArrayAction) || - (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof DeleteAction) || (action instanceof ArrayAction) || + (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -226,23 +221,23 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on central component selected. model.setSelection(new int[] { 1 }); int numBytes = getModel().getMaxReplaceLength(1); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof ArrayAction) || (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof ArrayAction) || (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -254,24 +249,24 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on last component selected. model.setSelection(new int[] { model.getNumComponents() - 1 }); int numBytes = getModel().getMaxReplaceLength(model.getNumComponents() - 1); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof DeleteAction) || (actions[i] instanceof ArrayAction) || - (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof DeleteAction) || (action instanceof ArrayAction) || + (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -282,14 +277,14 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on a contiguous multi-component selection. model.setSelection(new int[] { 2, 3, 4 }); - for (int i = 0; i < actions.length; i++) { - if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || (actions[i] instanceof PointerAction) || - (actions[i] instanceof ClearAction)) { - checkEnablement(actions[i], true); + for (CompositeEditorTableAction action : actions) { + if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || (action instanceof PointerAction) || + (action instanceof ClearAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -300,13 +295,13 @@ public class PositiveStackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on a non-contiguous multi-component selection. model.setSelection(new int[] { 2, 3, 6, 7 }); - for (int i = 0; i < actions.length; i++) { - if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || (actions[i] instanceof ClearAction)) { - checkEnablement(actions[i], true); + for (CompositeEditorTableAction action : actions) { + if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || (action instanceof ClearAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/PositiveStackEditorProviderTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/PositiveStackEditorProviderTest.java index 8ce546bf2b..fa3c5166a0 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/PositiveStackEditorProviderTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/PositiveStackEditorProviderTest.java @@ -15,8 +15,7 @@ */ package ghidra.app.plugin.core.stackeditor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import javax.swing.JTextField; @@ -29,10 +28,6 @@ import ghidra.program.model.data.Pointer; public class PositiveStackEditorProviderTest extends AbstractStackEditorTest { - /** - * Constructor for UnionEditorProviderTest. - * @param name the testcase name. - */ public PositiveStackEditorProviderTest() { super(true); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions1Test.java index 075274f84c..c3aca87f3c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions1Test.java @@ -24,10 +24,7 @@ import docking.widgets.dialogs.NumberInputDialog; import ghidra.program.model.data.*; public class StackEditorActions1Test extends AbstractStackEditorTest { - /** - * Constructor for StackEditorActionsTest. - * @param name the testcase name. - */ + public StackEditorActions1Test() { super(false); } @@ -54,15 +51,15 @@ public class StackEditorActions1Test extends AbstractStackEditorTest { // Make array of 2 arrays assertEquals("", model.getStatus()); - invoke(arrayAction); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(arrayAction, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 3); assertEquals("", model.getStatus()); dialog = null; waitUntilDialogProviderGone(NumberInputDialog.class, 2000); - invoke(arrayAction); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(arrayAction, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); okInput(dialog, 2); dialog = null; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions2Test.java index c3f6da101d..7915c5bd48 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions2Test.java @@ -30,10 +30,7 @@ import ghidra.program.model.listing.StackFrame; import ghidra.program.model.listing.Variable; public class StackEditorActions2Test extends AbstractStackEditorTest { - /** - * Constructor for StackEditorActionsTest. - * @param name the testcase name. - */ + public StackEditorActions2Test() { super(false); } @@ -365,12 +362,12 @@ public class StackEditorActions2Test extends AbstractStackEditorTest { assertEquals(1, model.getComponent(1).getLength()); assertEquals("", model.getStatus()); - invoke(arrayAction); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(arrayAction, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); assertEquals("Enter Number", dialog.getTitle()); badInput(dialog, 5); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); assertEquals("Enter Number", dialog.getTitle()); okInput(dialog, 4); @@ -395,13 +392,13 @@ public class StackEditorActions2Test extends AbstractStackEditorTest { checkSelection(new int[] { 0 }); assertEquals("", model.getStatus()); - invoke(arrayAction); + invoke(arrayAction, false); waitForSwing(); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 2000); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); assertEquals("Enter Number", dialog.getTitle()); badInput(dialog, 2); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 2000); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); assertEquals("Value must be between 1 and 1", dialog.getStatusText()); assertEquals("Enter Number", dialog.getTitle()); @@ -428,8 +425,8 @@ public class StackEditorActions2Test extends AbstractStackEditorTest { // Cancel the array dialog assertEquals("", model.getStatus()); - invoke(arrayAction); - dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000); + invoke(arrayAction, false); + dialog = waitForDialogComponent(NumberInputDialog.class); assertNotNull(dialog); cancelInput(dialog); assertEquals("", model.getStatus()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions4Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions4Test.java index a695cb8d14..a6eca4f12f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions4Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorActions4Test.java @@ -31,10 +31,7 @@ import ghidra.app.plugin.core.compositeeditor.FavoritesAction; import ghidra.program.model.data.*; public class StackEditorActions4Test extends AbstractStackEditorTest { - /** - * Constructor for StackEditorActionsTest. - * @param name the testcase name. - */ + public StackEditorActions4Test() { super(false); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorCellEditTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorCellEditTest.java index 49f5a2a2c5..986e78b74b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorCellEditTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorCellEditTest.java @@ -32,10 +32,6 @@ import ghidra.program.model.listing.Variable; public class StackEditorCellEditTest extends AbstractStackEditorTest { - /** - * Constructor for UnionEditorCellEditTest. - * @param name the testcase name. - */ public StackEditorCellEditTest() { super(false); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorDnDTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorDnDTest.java index 24a2796233..2631a85525 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorDnDTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorDnDTest.java @@ -24,10 +24,6 @@ import ghidra.program.model.data.*; public class StackEditorDnDTest extends AbstractStackEditorTest { - /** - * Constructor for StackEditorDnDTest. - * @param name the testcase name. - */ public StackEditorDnDTest() { super(false); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorEnablementTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorEnablementTest.java index 773cec909b..ea93174bab 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorEnablementTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/stackeditor/StackEditorEnablementTest.java @@ -25,10 +25,6 @@ import ghidra.program.model.data.Pointer; public class StackEditorEnablementTest extends AbstractStackEditorTest { - /** - * Constructor for StackEditorLockedActionsTest. - * @param name the testcase name. - */ public StackEditorEnablementTest() { super(false); } @@ -51,13 +47,13 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { assertEquals(stackModel.getTypeName(), "Stack"); // Check enablement. - for (int i = 0; i < actions.length; i++) { - if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction)) { - checkEnablement(actions[i], true); + for (CompositeEditorTableAction action : actions) { + if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -82,13 +78,13 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { assertEquals(stackModel.getTypeName(), "Stack"); // Check enablement. - for (int i = 0; i < actions.length; i++) { - if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction)) { - checkEnablement(actions[i], true); + for (CompositeEditorTableAction action : actions) { + if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } @@ -96,23 +92,23 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement. int numBytes = getModel().getMaxReplaceLength(0); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof DeleteAction) || (actions[i] instanceof PointerAction) || - (actions[i] instanceof HexNumbersAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof DeleteAction) || (action instanceof PointerAction) || + (action instanceof HexNumbersAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -137,45 +133,45 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { setSelection(new int[] { 19 });// Move to a float int numBytes = getModel().getMaxReplaceLength(19); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof DeleteAction) || (actions[i] instanceof ArrayAction) || - (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof DeleteAction) || (action instanceof ArrayAction) || + (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } setSelection(new int[] { 5 });// Move to a pointer numBytes = getModel().getMaxReplaceLength(5); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); - checkEnablement(actions[i], true); + checkEnablement(action, true); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof ArrayAction) || (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof ArrayAction) || (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -187,24 +183,24 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on first component selected. runSwing(() -> model.setSelection(new int[] { 0 })); int numBytes = getModel().getMaxReplaceLength(0); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof DeleteAction) || (actions[i] instanceof ArrayAction) || - (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof DeleteAction) || (action instanceof ArrayAction) || + (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -217,23 +213,23 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { runSwing(() -> model.setSelection(new int[] { 1 })); int numBytes = getModel().getMaxReplaceLength(1); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof ArrayAction) || (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof ArrayAction) || (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -245,24 +241,24 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on last component selected. runSwing(() -> model.setSelection(new int[] { model.getNumComponents() - 1 })); int numBytes = getModel().getMaxReplaceLength(model.getNumComponents() - 1); - for (int i = 0; i < actions.length; i++) { - if (actions[i] instanceof FavoritesAction) { - FavoritesAction fav = (FavoritesAction) actions[i]; + for (CompositeEditorTableAction action : actions) { + if (action instanceof FavoritesAction) { + FavoritesAction fav = (FavoritesAction) action; DataType favDt = fav.getDataType(); int len = favDt.getLength(); boolean enabled = ((len <= numBytes) && ((favDt instanceof Pointer) || (len > 0))); - checkEnablement(actions[i], enabled); + checkEnablement(action, enabled); } - else if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || - (actions[i] instanceof ShowComponentPathAction) || - (actions[i] instanceof EditFieldAction) || (actions[i] instanceof ClearAction) || - (actions[i] instanceof DeleteAction) || (actions[i] instanceof ArrayAction) || - (actions[i] instanceof PointerAction)) { - checkEnablement(actions[i], true); + else if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || + (action instanceof ShowComponentPathAction) || + (action instanceof EditFieldAction) || (action instanceof ClearAction) || + (action instanceof DeleteAction) || (action instanceof ArrayAction) || + (action instanceof PointerAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -273,14 +269,14 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on a contiguous multi-component selection. runSwing(() -> model.setSelection(new int[] { 2, 3, 4 })); - for (int i = 0; i < actions.length; i++) { - if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || (actions[i] instanceof PointerAction) || - (actions[i] instanceof ClearAction)) { - checkEnablement(actions[i], true); + for (CompositeEditorTableAction action : actions) { + if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || (action instanceof PointerAction) || + (action instanceof ClearAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } @@ -291,13 +287,13 @@ public class StackEditorEnablementTest extends AbstractStackEditorTest { // Check enablement on a non-contiguous multi-component selection. runSwing(() -> model.setSelection(new int[] { 2, 3, 6, 7 })); - for (int i = 0; i < actions.length; i++) { - if ((actions[i] instanceof CycleGroupAction) || - (actions[i] instanceof HexNumbersAction) || (actions[i] instanceof ClearAction)) { - checkEnablement(actions[i], true); + for (CompositeEditorTableAction action : actions) { + if ((action instanceof CycleGroupAction) || + (action instanceof HexNumbersAction) || (action instanceof ClearAction)) { + checkEnablement(action, true); } else { - checkEnablement(actions[i], false); + checkEnablement(action, false); } } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/parser/FunctionSignatureParserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/parser/FunctionSignatureParserTest.java index 159f2c3801..719a79b6b3 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/parser/FunctionSignatureParserTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/parser/FunctionSignatureParserTest.java @@ -43,7 +43,7 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration public void setUp() throws Exception { ProgramBuilder builder = new ToyProgramBuilder("test", false); StructureDataType s = new StructureDataType("StructA", 0); - s.setInternallyAligned(true); + s.setPackingEnabled(true); s.add(IntegerDataType.dataType); builder.addDataType(s); program = builder.getProgram(); @@ -241,7 +241,7 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration int txId = program.startTransaction("Add Struct"); try { StructureDataType s = new StructureDataType(new CategoryPath("/Test"), "StructA", 0); - s.setInternallyAligned(true); + s.setPackingEnabled(true); s.add(ByteDataType.dataType); program.getDataTypeManager().addDataType(s, null); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java index 1886a7178e..3637f7be2f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java @@ -51,7 +51,7 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT TaskMonitor.DUMMY, false); struct = createStructure("Test", 0); - struct.setInternallyAligned(true); + struct.setPackingEnabled(true); struct.addBitField(IntegerDataType.dataType, 3, "bf1", "Nuts"); struct.addBitField(IntegerDataType.dataType, 24, "bf2", null); struct.addBitField(IntegerDataType.dataType, 4, "bf3", null); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java index 0e94570a97..45bd5f52af 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ConflictHandlerTest.java @@ -398,118 +398,118 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { /** * Tests the * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} - * conflict handler to be sure that, if all else is the same, the aligned version is chosen - * over the unaligned version. + * conflict handler to be sure that, if all else is the same, the packed version is chosen + * over the non-packed version. *

    - * Success is the aligned version is chosen over the unaligned version. + * Success is the packed version is chosen over the non-packed version. */ @Test - public void testChooseNewAlignedOverExistingUnalignedWhenAllElseIsEqualForEmptyStructures() { - // Unaligned exists first. - Structure empty1Unaligned = new StructureDataType(root, "empty1", 0, dataMgr); - Composite empty1AlignedToAdd = (Composite) empty1Unaligned.copy(dataMgr); - empty1AlignedToAdd.setInternallyAligned(true); + public void testChooseNewPackedOverExistingNonPackedWhenAllElseIsEqualForEmptyStructures() { + // NonPacked exists first. + Structure empty1NonPacked = new StructureDataType(root, "empty1", 0, dataMgr); + Composite empty1PackedToAdd = (Composite) empty1NonPacked.copy(dataMgr); + empty1PackedToAdd.setPackingEnabled(true); - String empty1UnalignedString = empty1Unaligned.toString(); - String empty1AlignedToAddString = empty1AlignedToAdd.toString(); + String empty1NonPackedString = empty1NonPacked.toString(); + String empty1PackedToAddString = empty1PackedToAdd.toString(); - Structure empty1AddResult = (Structure) dataMgr.addDataType(empty1AlignedToAdd, + Structure empty1AddResult = (Structure) dataMgr.addDataType(empty1PackedToAdd, DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); String empty1AddResultString = empty1AddResult.toString(); - assertEquals(empty1AlignedToAddString, empty1AddResultString); - assertNotEquals(empty1UnalignedString, empty1AddResultString); + assertEquals(empty1PackedToAddString, empty1AddResultString); + assertNotEquals(empty1NonPackedString, empty1AddResultString); } /** * Tests the * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} - * conflict handler to be sure that, if all else is the same, the aligned version is chosen - * over the unaligned version. + * conflict handler to be sure that, if all else is the same, the packed version is chosen + * over the non-packed version. *

    - * Success is the aligned version is chosen over the unaligned version. + * Success is the packed version is chosen over the non-packed version. */ @Test - public void testChooseNewAlignedOverExistingUnalignedWhenAllElseIsEqualForNonEmptyStructures() { - // Unaligned exists first. - StructureDataType struct1Unaligned = createPopulated(dataMgr); - Composite struct1AlignedToAdd = (Composite) struct1Unaligned.copy(dataMgr); - struct1AlignedToAdd.setInternallyAligned(true); + public void testChooseNewPackedOverExistingNonPackedWhenAllElseIsEqualForNonEmptyStructures() { + // NonPacked exists first. + StructureDataType struct1NonPacked = createPopulated(dataMgr); + Composite struct1PackedToAdd = (Composite) struct1NonPacked.copy(dataMgr); + struct1PackedToAdd.setPackingEnabled(true); - String struct1UnalignedString = struct1Unaligned.toString(); - String struct1AlignedToAddString = struct1AlignedToAdd.toString(); + String struct1NonPackedString = struct1NonPacked.toString(); + String struct1PackedToAddString = struct1PackedToAdd.toString(); - Structure struct1AddResult = (Structure) dataMgr.addDataType(struct1AlignedToAdd, + Structure struct1AddResult = (Structure) dataMgr.addDataType(struct1PackedToAdd, DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); String struct1AddResultString = struct1AddResult.toString(); - assertEquals(struct1AlignedToAddString, struct1AddResultString); - assertNotEquals(struct1UnalignedString, struct1AddResultString); + assertEquals(struct1PackedToAddString, struct1AddResultString); + assertNotEquals(struct1NonPackedString, struct1AddResultString); } /** * Tests the * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} - * conflict handler to be sure that, if all else is the same, the new unaligned version is - * chosen over the existing unaligned version. + * conflict handler to be sure that, if all else is the same, the new non-packed version is + * chosen over the existing non-packed version. *

    - * Success is the new unaligned version is chosen over the existing aligned version. + * Success is the new non-packed version is chosen over the existing packed version. */ // TODO: consider whether we want to change the logic of the conflict handler to favor - // aligned over unaligned. + // packed over non-packed. @Test - public void testChooseNewUnalignedOverExistingAlignedWhenAllElseIsEqualForEmptyStructures() { + public void testChooseNewNonPackedOverExistingPackedWhenAllElseIsEqualForEmptyStructures() { - // Aligned exists first. - Structure empty2Aligned = new StructureDataType(root, "empty2", 0, dataMgr); - Composite empty2UnalignedToAdd = (Composite) empty2Aligned.copy(dataMgr); - // aligning only after making unaligned copy. - empty2Aligned.setInternallyAligned(true); + // Packed exists first. + Structure empty2Packed = new StructureDataType(root, "empty2", 0, dataMgr); + Composite empty2NonPackedToAdd = (Composite) empty2Packed.copy(dataMgr); + // aligning only after making non-packed copy. + empty2Packed.setPackingEnabled(true); - String empty2AlignedString = empty2Aligned.toString(); - String empty2UnalignedToAddString = empty2UnalignedToAdd.toString(); + String empty2PackedString = empty2Packed.toString(); + String empty2NonPackedToAddString = empty2NonPackedToAdd.toString(); - Structure empty2AddResult = (Structure) dataMgr.addDataType(empty2UnalignedToAdd, + Structure empty2AddResult = (Structure) dataMgr.addDataType(empty2NonPackedToAdd, DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); String empty2AddResultString = empty2AddResult.toString(); - assertEquals(empty2UnalignedToAddString, empty2AddResultString); - assertNotEquals(empty2AlignedString, empty2AddResultString); + assertEquals(empty2NonPackedToAddString, empty2AddResultString); + assertNotEquals(empty2PackedString, empty2AddResultString); } /** * Tests the * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} - * conflict handler to be sure that, if all else is the same, the new unaligned version is - * chosen over the existing aligned version. + * conflict handler to be sure that, if all else is the same, the new non-packed version is + * chosen over the existing packed version. *

    - * Success is the new unaligned version is chosen over the existing aligned version. + * Success is the new non-packed version is chosen over the existing packed version. */ // TODO: consider whether we want to change the logic of the conflict handler to favor - // aligned over unaligned. + // packed over non-packed. @Test - public void testChooseNewUnalignedOverExistingAlignedWhenAllElseIsEqualForNonEmptyStructures() { + public void testChooseNewNonPackedOverExistingPackedWhenAllElseIsEqualForNonEmptyStructures() { - // Aligned exists first. - StructureDataType struct2Aligned = createPopulated(dataMgr); - Composite struct2UnalignedToAdd = (Composite) struct2Aligned.copy(dataMgr); - // aligning only after making unaligned copy. - struct2Aligned.setInternallyAligned(true); + // Packed exists first. + StructureDataType struct2Packed = createPopulated(dataMgr); + Composite struct2NonPackedToAdd = (Composite) struct2Packed.copy(dataMgr); + // aligning only after making non-packed copy. + struct2Packed.setPackingEnabled(true); - String struct2AlignedString = struct2Aligned.toString(); - String struct2UnalignedToAddString = struct2UnalignedToAdd.toString(); + String struct2PackedString = struct2Packed.toString(); + String struct2NonPackedToAddString = struct2NonPackedToAdd.toString(); - Structure struct2AddResult = (Structure) dataMgr.addDataType(struct2UnalignedToAdd, + Structure struct2AddResult = (Structure) dataMgr.addDataType(struct2NonPackedToAdd, DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); String struct2AddResultString = struct2AddResult.toString(); - assertEquals(struct2UnalignedToAddString, struct2AddResultString); - assertNotEquals(struct2AlignedString, struct2AddResultString); + assertEquals(struct2NonPackedToAddString, struct2AddResultString); + assertNotEquals(struct2PackedString, struct2AddResultString); } /** * Tests the * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} - * conflict handler to be sure that, if all else is the same, the aligned version is chosen - * over the unaligned version. + * conflict handler to be sure that, if all else is the same, the packed version is chosen + * over the non-packed version. *

    - * Success is the aligned version is chosen over the unaligned version. + * Success is the packed version is chosen over the non-packed version. */ @Test public void testResolveDataTypeNonStructConflict() throws Exception { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/model/data/DataTypeTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/model/data/DataTypeTest.java index 6ad3dd3e85..565b13b6e2 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/model/data/DataTypeTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/model/data/DataTypeTest.java @@ -61,7 +61,7 @@ public class DataTypeTest extends AbstractGhidraHeadedIntegrationTest { Structure structA = new StructureDataType("structA", 0); structA.setCategoryPath(new CategoryPath("/catA")); - structA.setInternallyAligned(true); + structA.setPackingEnabled(true); Pointer p = new PointerDataType(structA); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/data/DataTypeParserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/data/DataTypeParserTest.java index baa1c1446e..3a3c02f0ff 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/data/DataTypeParserTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/data/DataTypeParserTest.java @@ -39,7 +39,7 @@ public class DataTypeParserTest extends AbstractEditorTest { try { DataTypeManager dataTypeManager = cat.getDataTypeManager(); if (dt.getDataTypeManager() != dataTypeManager) { - dt = (Union) dt.clone(dataTypeManager); + dt = dt.clone(dataTypeManager); } CategoryPath categoryPath = cat.getCategoryPath(); if (!dt.getCategoryPath().equals(categoryPath)) { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java index f764e7ece4..a1098f72f1 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/StructureDataTypeTest.java @@ -20,6 +20,7 @@ package ghidra.program.database.data; import static org.junit.Assert.*; +import org.apache.commons.compress.utils.Sets; import org.junit.*; import generic.test.AbstractGTest; @@ -45,7 +46,7 @@ public class StructureDataTypeTest extends AbstractGTest { // transition default little-endian structure to big-endian DataTypeManager beDtm = new MyBigEndianDataTypeManager(); - struct = (Structure) struct.clone(beDtm); + struct = struct.clone(beDtm); } private Structure createStructure(String name, int length) { @@ -420,7 +421,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + " 1 word 2 null \"Comment2\"\n" + @@ -438,7 +439,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + " 1 word 2 null \"Comment2\"\n" + @@ -461,7 +462,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + " 1 word 2 null \"Comment2\"\n" + @@ -481,7 +482,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + " 1 word 2 null \"Comment2\"\n" + @@ -506,7 +507,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -525,7 +526,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -545,7 +546,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -573,7 +574,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -607,7 +608,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -626,7 +627,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -646,7 +647,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -665,7 +666,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -693,7 +694,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + // " 1 undefined 1 null \"\"\n" + @@ -894,6 +895,79 @@ public class StructureDataTypeTest extends AbstractGTest { assertEquals(7, comps[3].getOrdinal()); } + @Test + public void testDeleteMany() { + + struct.growStructure(20); + struct.insertAtOffset(12, WordDataType.dataType, -1, "A", null); + struct.insertAtOffset(16, WordDataType.dataType, -1, "B", null); + + assertEquals(32, struct.getLength()); + assertEquals(26, struct.getNumComponents()); + assertEquals(6, struct.getNumDefinedComponents()); + + struct.delete(Sets.newHashSet(1, 4, 5)); + + assertEquals(28, struct.getLength()); + assertEquals(23, struct.getNumComponents()); + assertEquals(5, struct.getNumDefinedComponents()); + + DataTypeComponent[] comps = struct.getDefinedComponents(); + assertEquals(WordDataType.class, comps[3].getDataType().getClass()); + assertEquals(5, comps[3].getOrdinal()); + assertEquals(8, comps[3].getOffset()); + } + + @Test + public void testDeleteManyBF() 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, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\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, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\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() { struct.delete(1); @@ -904,6 +978,144 @@ public class StructureDataTypeTest extends AbstractGTest { assertEquals(1, comps[1].getOffset()); } + @Test + public void testDeleteBF() 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, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\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(6); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\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 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(3); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + +// " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + + " 2 int:15(6) 3 bf3 \"bf3Comment\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(3); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + +// " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + +// " 3 undefined 1 null \"\"\n" + + " 4 int:11(5) 2 bf4 \"bf4Comment\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(4); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 byte 1 field1 \"Comment1\"\n" + +// " 1 undefined 1 null \"\"\n" + + " 2 int:3(0) 1 bf1 \"bf1Comment\"\n" + +// " 3 undefined 1 null \"\"\n" + +// " 4 undefined 1 null \"\"\n" + +// " 5 undefined 1 null \"\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(2); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\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 undefined 1 null \"\"\n" + + " 6 dword 4 field3 \"\"\n" + + " 10 byte 1 field4 \"Comment4\"\n" + + "}\n" + + "Size = 11 Actual Alignment = 1", struct); + //@formatter:on + + struct.delete(2); + + //@formatter:off + CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\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 testDeleteAtOffset() { struct.deleteAtOffset(2); @@ -936,15 +1148,51 @@ public class StructureDataTypeTest extends AbstractGTest { @Test public void testDeleteAll() { + struct.setPackingEnabled(true); // enable packing for struct + assertEquals(12, struct.getLength()); + Structure s = new StructureDataType("test1", 0); s.add(new ByteDataType()); s.add(new FloatDataType()); + assertEquals(1, s.getAlignment()); + assertEquals(5, s.getLength()); struct.add(s); + assertEquals(16, struct.getLength()); + s.deleteAll(); assertEquals(1, s.getLength()); assertTrue(s.isNotYetDefined()); + assertTrue(s.isZeroLength()); assertEquals(0, s.getNumComponents()); + + assertEquals(12, struct.getLength()); + + } + + @Test + public void testAlignmentAndPacking() { + + struct.setPackingEnabled(true); // enable packing for struct + assertEquals(12, struct.getLength()); + + Structure s = new StructureDataType("test1", 0); + s.add(new ByteDataType()); + s.add(new FloatDataType()); + assertEquals(1, s.getAlignment()); + assertEquals(5, s.getLength()); + + struct.add(s); + assertEquals(16, struct.getLength()); + + s.setExplicitMinimumAlignment(8); // does not force packing (s length unaffected) + + assertEquals(5, s.getLength()); + assertFalse(s.isNotYetDefined()); + assertFalse(s.isZeroLength()); + assertEquals(2, s.getNumComponents()); + + assertEquals(24, struct.getLength()); } @Test @@ -1129,9 +1377,9 @@ public class StructureDataTypeTest extends AbstractGTest { @Test public void testReplaceWith2() throws InvalidDataTypeException { - // NOTE: unaligned bitfields should remain unchanged when + // NOTE: pack(disabled) bitfields should remain unchanged when // transitioning endianess even though it makes little sense. - // Unaligned structures are not intended to be portable! + // pack(disabled) structures are not intended to be portable! TypeDef td = new TypedefDataType("Foo", IntegerDataType.dataType); @@ -1144,7 +1392,7 @@ public class StructureDataTypeTest extends AbstractGTest { //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure TestStruct {\n" + " 0 byte 1 field1 \"Comment1\"\n" + " 1 word 2 null \"Comment2\"\n" + @@ -1169,7 +1417,7 @@ public class StructureDataTypeTest 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" + diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java index e98836e83d..1ab193bacc 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/data/UnionDataTypeTest.java @@ -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.*; @@ -152,7 +154,7 @@ public class UnionDataTypeTest 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" + @@ -170,11 +172,11 @@ public class UnionDataTypeTest 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" + @@ -191,7 +193,7 @@ public class UnionDataTypeTest 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" + @@ -214,7 +216,7 @@ public class UnionDataTypeTest 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" + @@ -261,6 +263,42 @@ public class UnionDataTypeTest 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); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java index 136221cc0a..ee17f1ce98 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/AbstractCompositeImplBitFieldTest.java @@ -15,7 +15,7 @@ */ package ghidra.program.model.data; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.FileNotFoundException; import java.io.InputStream; @@ -36,7 +36,7 @@ public abstract class AbstractCompositeImplBitFieldTest extends AbstractGTest { DataTypeManager dataMgr = getDataTypeManager(); if (dataMgr.getDataTypeCount(false) != 0) { - Msg.info(this, "Using previously parsed data types"); +// Msg.info(this, "Using previously parsed data types"); return; // already have types } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java index cb22ff12be..399932725b 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCStructureImplBitFieldTest.java @@ -40,7 +40,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("A1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + - "Aligned\n" + + "pack()\n" + "Structure A1 {\n" + " 0 char[5] 5 a \"\"\n" + " 8 int:3(0) 1 b \"\"\n" + @@ -57,7 +57,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("A2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + - "Aligned\n" + + "pack()\n" + "Structure A2 {\n" + " 0 oddStruct 5 a \"\"\n" + " 8 int:3(0) 1 b \"\"\n" + @@ -74,7 +74,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("A3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + - "Aligned\n" + + "pack()\n" + "Structure A3 {\n" + " 0 char[5] 5 a \"\"\n" + " 8 int:3(0) 1 b \"\"\n" + @@ -97,7 +97,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + - "Aligned\n" + + "pack()\n" + "Structure B1 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -113,7 +113,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B1flex"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + - "Aligned\n" + + "pack()\n" + "Structure B1flex {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -130,7 +130,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + - "Aligned\n" + + "pack()\n" + "Structure B2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -146,7 +146,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + - "Aligned\n" + + "pack()\n" + "Structure B3 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -163,7 +163,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + - "Aligned\n" + + "pack()\n" + "Structure Z1 {\n" + " 0 char 1 a \"\"\n" + " 2 int:0(0) 1 \"\"\n" + @@ -180,7 +180,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + - "Aligned\n" + + "pack()\n" + "Structure Z2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -197,7 +197,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + - "Aligned\n" + + "pack()\n" + "Structure Z3 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -214,7 +214,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + - "Aligned\n" + + "pack()\n" + "Structure Z4 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -231,7 +231,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z5"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + - "Aligned\n" + + "pack()\n" + "Structure Z5 {\n" + " 0 char 1 a \"\"\n" + " 8 int:0(0) 1 \"\"\n" + @@ -248,7 +248,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z6"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + - "Aligned\n" + + "pack()\n" + "Structure Z6 {\n" + " 0 char 1 a \"\"\n" + " 8 int:0(0) 1 \"\"\n" + @@ -268,7 +268,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B1p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -284,7 +284,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B2p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -300,7 +300,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B3p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B3p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -316,7 +316,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z1p1 {\n" + " 0 char 1 a \"\"\n" + " 1 int:0(0) 1 \"\"\n" + @@ -333,7 +333,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z2p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -350,7 +350,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z3p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z3p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -367,7 +367,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z3p1T"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + - "Aligned\n" + + "pack()\n" + "Structure Z3p1T {\n" + " 0 char 1 a \"\"\n" + " 1 Z3p1 7 z3p1 \"\"\n" + @@ -381,7 +381,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z4p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z4p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -398,7 +398,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B1p2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -414,7 +414,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B2p2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -430,7 +430,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B3p2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -446,7 +446,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("B4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B4p2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -463,7 +463,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z1p2 {\n" + " 0 char 1 a \"\"\n" + " 2 int:0(0) 1 \"\"\n" + @@ -480,7 +480,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z1p2x"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z1p2x {\n" + " 0 char 1 a \"\"\n" + " 2 int:0(0) 1 \"\"\n" + @@ -515,7 +515,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z2p2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -532,7 +532,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z3p2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -549,7 +549,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z4p2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:6(0) 1 b \"\"\n" + @@ -566,7 +566,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z5p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z5p2 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:12(0) 2 b \"\"\n" + @@ -583,7 +583,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("x1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x1p2 {\n" + " 0 char 1 a \"\"\n" + "}\n" + @@ -596,7 +596,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("x2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x2p2 {\n" + " 0 char 1 a \"\"\n" + " 2 int:27(0) 4 b \"\"\n" + @@ -610,7 +610,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("x3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x3p2 {\n" + " 0 char 1 a \"\"\n" + " 2 short:0(0) 1 \"\"\n" + @@ -625,7 +625,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("x4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x4p2 {\n" + " 0 char 1 a \"\"\n" + " 2 int:27(0) 4 b \"\"\n" + @@ -640,7 +640,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("Z5p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure Z5p4 {\n" + " 0 char 1 a \"\"\n" + " 2 ushort:12(0) 2 b \"\"\n" + @@ -657,7 +657,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("x1p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x1p4 {\n" + " 0 char 1 a \"\"\n" + "}\n" + @@ -670,7 +670,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("x2p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x2p4 {\n" + " 0 char 1 a \"\"\n" + " 4 int:27(0) 4 b \"\"\n" + @@ -684,7 +684,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("x3p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x3p4 {\n" + " 0 char 1 a \"\"\n" + " 4 short:0(0) 1 \"\"\n" + @@ -699,7 +699,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("x4p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x4p4 {\n" + " 0 char 1 a \"\"\n" + " 4 int:27(0) 4 b \"\"\n" + @@ -714,7 +714,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("T1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + - "Aligned\n" + + "pack()\n" + "Structure T1 {\n" + " 0 charTypedef 1 a \"\"\n" + " 4 myEnum:3(0) 1 b \"\"\n" + @@ -730,7 +730,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("T2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + - "Aligned\n" + + "pack()\n" + "Structure T2 {\n" + " 0 charTypedef 1 a \"\"\n" + " 4 intTypedef:17(0) 3 b \"\"\n" + @@ -746,7 +746,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("S1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + - "Aligned\n" + + "pack()\n" + "Structure S1 {\n" + " 0 B1 12 b1 \"\"\n" + " 12 B2 8 b2 \"\"\n" + @@ -763,7 +763,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("S1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure S1p1 {\n" + " 0 B1 12 b1 \"\"\n" + " 12 B2 8 b2 \"\"\n" + @@ -780,7 +780,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("S2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure S2p1 {\n" + " 0 B1p1 9 b1p1 \"\"\n" + " 9 B2p1 7 b2p1 \"\"\n" + @@ -797,7 +797,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("S1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure S1p2 {\n" + " 0 B1 12 b1 \"\"\n" + " 12 B2 8 b2 \"\"\n" + @@ -814,7 +814,7 @@ public class MSVCStructureImplBitFieldTest extends AbstractCompositeImplBitField Structure struct = getStructure("S2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure S2p2 {\n" + " 0 B1p2 10 b1p2 \"\"\n" + " 10 B2p2 8 b2p2 \"\"\n" + diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java index 951d422b0f..43d07140ed 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/MSVCUnionImplBitFieldTest.java @@ -38,7 +38,7 @@ public class MSVCUnionImplBitFieldTest extends AbstractCompositeImplBitFieldTest Union struct = getUnion("U1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + - "Aligned\n" + + "pack()\n" + "Union U1 {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 int:2(0) 1 b \"\"\n" + @@ -52,7 +52,7 @@ public class MSVCUnionImplBitFieldTest extends AbstractCompositeImplBitFieldTest Union struct = getUnion("U1z"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + - "Aligned\n" + + "pack()\n" + "Union U1z {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 longlong:0(0) 1 \"\"\n" + @@ -67,7 +67,7 @@ public class MSVCUnionImplBitFieldTest extends AbstractCompositeImplBitFieldTest Union struct = getUnion("U1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Union U1p1 {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 int:2(0) 1 b \"\"\n" + @@ -81,7 +81,7 @@ public class MSVCUnionImplBitFieldTest extends AbstractCompositeImplBitFieldTest Union struct = getUnion("U1p1z"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Union U1p1z {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 longlong:0(0) 1 \"\"\n" + @@ -96,7 +96,7 @@ public class MSVCUnionImplBitFieldTest extends AbstractCompositeImplBitFieldTest Union struct = getUnion("U1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Union U1p2 {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 int:2(0) 1 b \"\"\n" + diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java index 17b4f28478..038288dc39 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplBigEndianBitFieldTest.java @@ -42,7 +42,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("A1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + - "Aligned\n" + + "pack()\n" + "Structure A1 {\n" + " 0 char[5] 5 a \"\"\n" + " 5 int:3(5) 1 b \"\"\n" + @@ -59,7 +59,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("A2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + - "Aligned\n" + + "pack()\n" + "Structure A2 {\n" + " 0 oddStruct 5 a \"\"\n" + " 5 int:3(5) 1 b \"\"\n" + @@ -76,7 +76,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("A3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + - "Aligned\n" + + "pack()\n" + "Structure A3 {\n" + " 0 char[5] 5 a \"\"\n" + " 5 int:3(5) 1 b \"\"\n" + @@ -99,7 +99,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + - "Aligned\n" + + "pack()\n" + "Structure B1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -115,7 +115,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B1flex"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + - "Aligned\n" + + "pack()\n" + "Structure B1flex {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -132,7 +132,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + - "Aligned\n" + + "pack()\n" + "Structure B2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -148,7 +148,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + - "Aligned\n" + + "pack()\n" + "Structure B3 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -165,7 +165,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + - "Aligned\n" + + "pack()\n" + "Structure Z1 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(7) 1 \"\"\n" + @@ -182,7 +182,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + - "Aligned\n" + + "pack()\n" + "Structure Z2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -199,7 +199,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + - "Aligned\n" + + "pack()\n" + "Structure Z3 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -216,7 +216,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + - "Aligned\n" + + "pack()\n" + "Structure Z4 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -233,7 +233,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z5"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + - "Aligned\n" + + "pack()\n" + "Structure Z5 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(7) 1 \"\"\n" + @@ -250,7 +250,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z6"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + - "Aligned\n" + + "pack()\n" + "Structure Z6 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(7) 1 \"\"\n" + @@ -270,7 +270,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B1p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -286,7 +286,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B2p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -302,7 +302,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B3p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B3p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -318,7 +318,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z1p1 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(7) 1 \"\"\n" + @@ -335,7 +335,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z2p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -352,7 +352,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z3p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z3p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -369,7 +369,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z3p1T"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + - "Aligned\n" + + "pack()\n" + "Structure Z3p1T {\n" + " 0 char 1 a \"\"\n" + " 1 Z3p1 8 z3p1 \"\"\n" + @@ -383,7 +383,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z4p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z4p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -400,7 +400,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B1p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -416,7 +416,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B2p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -432,7 +432,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B3p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -448,7 +448,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("B4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B4p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -465,7 +465,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z1p2 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(7) 1 \"\"\n" + @@ -482,7 +482,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z1p2x"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z1p2x {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(7) 1 \"\"\n" + @@ -517,7 +517,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z2p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -534,7 +534,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z3p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -551,7 +551,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z4p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(2) 1 b \"\"\n" + @@ -568,7 +568,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z5p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z5p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:12(4) 2 b \"\"\n" + @@ -585,7 +585,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("x1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x1p2 {\n" + " 0 char 1 a \"\"\n" + "}\n" + @@ -598,7 +598,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("x2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x2p2 {\n" + " 0 char 1 a \"\"\n" + " 1 int:27(5) 4 b \"\"\n" + @@ -612,7 +612,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("x3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x3p2 {\n" + " 0 char 1 a \"\"\n" + " 2 short:0(7) 1 \"\"\n" + @@ -627,7 +627,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("x4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x4p2 {\n" + " 0 char 1 a \"\"\n" + " 1 int:27(5) 4 b \"\"\n" + @@ -642,7 +642,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("Z5p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure Z5p4 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:12(4) 2 b \"\"\n" + @@ -659,7 +659,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("x1p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x1p4 {\n" + " 0 char 1 a \"\"\n" + "}\n" + @@ -672,7 +672,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("x2p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x2p4 {\n" + " 0 char 1 a \"\"\n" + " 1 int:27(5) 4 b \"\"\n" + @@ -686,7 +686,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("x3p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x3p4 {\n" + " 0 char 1 a \"\"\n" + " 2 short:0(7) 1 \"\"\n" + @@ -701,7 +701,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("x4p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x4p4 {\n" + " 0 char 1 a \"\"\n" + " 1 int:27(5) 4 b \"\"\n" + @@ -716,7 +716,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("T1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + - "Aligned\n" + + "pack()\n" + "Structure T1 {\n" + " 0 charTypedef 1 a \"\"\n" + " 1 myEnum:3(5) 1 b \"\"\n" + @@ -732,7 +732,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("T2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + - "Aligned\n" + + "pack()\n" + "Structure T2 {\n" + " 0 charTypedef 1 a \"\"\n" + " 1 intTypedef:17(7) 3 b \"\"\n" + @@ -748,7 +748,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("S1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + - "Aligned\n" + + "pack()\n" + "Structure S1 {\n" + " 0 B1 4 b1 \"\"\n" + " 4 B2 4 b2 \"\"\n" + @@ -765,7 +765,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("S1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure S1p1 {\n" + " 0 B1 4 b1 \"\"\n" + " 4 B2 4 b2 \"\"\n" + @@ -782,7 +782,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("S2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure S2p1 {\n" + " 0 B1p1 4 b1p1 \"\"\n" + " 4 B2p1 4 b2p1 \"\"\n" + @@ -799,7 +799,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("S1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure S1p2 {\n" + " 0 B1 4 b1 \"\"\n" + " 4 B2 4 b2 \"\"\n" + @@ -816,7 +816,7 @@ public class StructureImplBigEndianBitFieldTest extends AbstractCompositeImplBit Structure struct = getStructure("S2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure S2p2 {\n" + " 0 B1p2 4 b1p2 \"\"\n" + " 4 B2p2 4 b2p2 \"\"\n" + diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java index 626cf6b186..adb984cfb0 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureImplLittleEndianBitFieldTest.java @@ -73,7 +73,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("A1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A1\n" + - "Aligned\n" + + "pack()\n" + "Structure A1 {\n" + " 0 char[5] 5 a \"\"\n" + " 5 int:3(0) 1 b \"\"\n" + @@ -90,7 +90,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("A2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A2\n" + - "Aligned\n" + + "pack()\n" + "Structure A2 {\n" + " 0 oddStruct 5 a \"\"\n" + " 5 int:3(0) 1 b \"\"\n" + @@ -107,7 +107,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("A3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/A3\n" + - "Aligned\n" + + "pack()\n" + "Structure A3 {\n" + " 0 char[5] 5 a \"\"\n" + " 5 int:3(0) 1 b \"\"\n" + @@ -130,7 +130,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1\n" + - "Aligned\n" + + "pack()\n" + "Structure B1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -146,7 +146,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B1flex"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1flex\n" + - "Aligned\n" + + "pack()\n" + "Structure B1flex {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -163,7 +163,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2\n" + - "Aligned\n" + + "pack()\n" + "Structure B2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -179,7 +179,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3\n" + - "Aligned\n" + + "pack()\n" + "Structure B3 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + // gcc groups with previous non-bitfield @@ -196,7 +196,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1\n" + - "Aligned\n" + + "pack()\n" + "Structure Z1 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(0) 1 \"\"\n" + @@ -213,7 +213,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2\n" + - "Aligned\n" + + "pack()\n" + "Structure Z2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -230,7 +230,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z3"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3\n" + - "Aligned\n" + + "pack()\n" + "Structure Z3 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -247,7 +247,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4\n" + - "Aligned\n" + + "pack()\n" + "Structure Z4 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -264,7 +264,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z5"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5\n" + - "Aligned\n" + + "pack()\n" + "Structure Z5 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(0) 1 \"\"\n" + @@ -281,7 +281,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z6"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z6\n" + - "Aligned\n" + + "pack()\n" + "Structure Z6 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(0) 1 \"\"\n" + @@ -301,7 +301,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B1p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -317,7 +317,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B2p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -333,7 +333,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B3p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure B3p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -349,7 +349,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z1p1 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(0) 1 \"\"\n" + @@ -366,7 +366,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z2p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -383,7 +383,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z3p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z3p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -400,7 +400,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z3p1T"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p1T\n" + - "Aligned\n" + + "pack()\n" + "Structure Z3p1T {\n" + " 0 char 1 a \"\"\n" + " 1 Z3p1 8 z3p1 \"\"\n" + @@ -414,7 +414,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z4p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure Z4p1 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -431,7 +431,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B1p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -447,7 +447,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B2p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -463,7 +463,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B3p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -479,7 +479,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("B4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/B4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure B4p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -496,7 +496,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z1p2 {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(0) 1 \"\"\n" + @@ -513,7 +513,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z1p2x"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z1p2x\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z1p2x {\n" + " 0 char 1 a \"\"\n" + " 4 int:0(0) 1 \"\"\n" + @@ -548,7 +548,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z2p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -565,7 +565,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z3p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -582,7 +582,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z4p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:6(0) 1 b \"\"\n" + @@ -599,7 +599,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z5p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure Z5p2 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:12(0) 2 b \"\"\n" + @@ -616,7 +616,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("x1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x1p2 {\n" + " 0 char 1 a \"\"\n" + "}\n" + @@ -629,7 +629,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("x2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x2p2 {\n" + " 0 char 1 a \"\"\n" + " 1 int:27(0) 4 b \"\"\n" + @@ -643,7 +643,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("x3p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x3p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x3p2 {\n" + " 0 char 1 a \"\"\n" + " 2 short:0(0) 1 \"\"\n" + @@ -658,7 +658,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("x4p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x4p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure x4p2 {\n" + " 0 char 1 a \"\"\n" + " 1 int:27(0) 4 b \"\"\n" + @@ -673,7 +673,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("Z5p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/Z5p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure Z5p4 {\n" + " 0 char 1 a \"\"\n" + " 1 ushort:12(0) 2 b \"\"\n" + @@ -690,7 +690,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("x1p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x1p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x1p4 {\n" + " 0 char 1 a \"\"\n" + "}\n" + @@ -703,7 +703,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("x2p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x2p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x2p4 {\n" + " 0 char 1 a \"\"\n" + " 1 int:27(0) 4 b \"\"\n" + @@ -717,7 +717,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("x3p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x3p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x3p4 {\n" + " 0 char 1 a \"\"\n" + " 2 short:0(0) 1 \"\"\n" + @@ -732,7 +732,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("x4p4"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/x4p4\n" + - "Aligned pack(4)\n" + + "pack(4)\n" + "Structure x4p4 {\n" + " 0 char 1 a \"\"\n" + " 1 int:27(0) 4 b \"\"\n" + @@ -747,7 +747,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("T1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/T1\n" + - "Aligned\n" + + "pack()\n" + "Structure T1 {\n" + " 0 charTypedef 1 a \"\"\n" + " 1 myEnum:3(0) 1 b \"\"\n" + @@ -763,7 +763,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("T2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/T2\n" + - "Aligned\n" + + "pack()\n" + "Structure T2 {\n" + " 0 charTypedef 1 a \"\"\n" + " 1 intTypedef:17(0) 3 b \"\"\n" + @@ -779,7 +779,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("S1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1\n" + - "Aligned\n" + + "pack()\n" + "Structure S1 {\n" + " 0 B1 4 b1 \"\"\n" + " 4 B2 4 b2 \"\"\n" + @@ -796,7 +796,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("S1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure S1p1 {\n" + " 0 B1 4 b1 \"\"\n" + " 4 B2 4 b2 \"\"\n" + @@ -813,7 +813,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("S2p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S2p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Structure S2p1 {\n" + " 0 B1p1 4 b1p1 \"\"\n" + " 4 B2p1 4 b2p1 \"\"\n" + @@ -830,7 +830,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("S1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure S1p2 {\n" + " 0 B1 4 b1 \"\"\n" + " 4 B2 4 b2 \"\"\n" + @@ -847,7 +847,7 @@ public class StructureImplLittleEndianBitFieldTest extends AbstractCompositeImpl Structure struct = getStructure("S2p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/S2p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Structure S2p2 {\n" + " 0 B1p2 4 b1p2 \"\"\n" + " 4 B2p2 4 b2p2 \"\"\n" + diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java index 0f87f38031..89316bea5a 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplBigEndianBitFieldTest.java @@ -40,7 +40,7 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel Union struct = getUnion("U1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + - "Aligned\n" + + "pack()\n" + "Union U1 {\n" + " 0 int:4(4) 1 a \"\"\n" + " 0 int:2(6) 1 b \"\"\n" + @@ -54,7 +54,7 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel Union struct = getUnion("U1z"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + - "Aligned\n" + + "pack()\n" + "Union U1z {\n" + " 0 int:4(4) 1 a \"\"\n" + " 0 longlong:0(7) 1 \"\"\n" + // has no impact @@ -69,7 +69,7 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel Union struct = getUnion("U1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Union U1p1 {\n" + " 0 int:4(4) 1 a \"\"\n" + " 0 int:2(6) 1 b \"\"\n" + @@ -83,7 +83,7 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel Union struct = getUnion("U1p1z"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Union U1p1z {\n" + " 0 int:4(4) 1 a \"\"\n" + " 0 longlong:0(7) 1 \"\"\n" + // has no impact @@ -98,7 +98,7 @@ public class UnionImplBigEndianBitFieldTest extends AbstractCompositeImplBitFiel Union struct = getUnion("U1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Union U1p2 {\n" + " 0 int:4(4) 1 a \"\"\n" + " 0 int:2(6) 1 b \"\"\n" + diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java index 26122e850f..964902b10d 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/UnionImplLittleEndianBitFieldTest.java @@ -40,7 +40,7 @@ public class UnionImplLittleEndianBitFieldTest extends AbstractCompositeImplBitF Union struct = getUnion("U1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1\n" + - "Aligned\n" + + "pack()\n" + "Union U1 {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 int:2(0) 1 b \"\"\n" + @@ -54,7 +54,7 @@ public class UnionImplLittleEndianBitFieldTest extends AbstractCompositeImplBitF Union struct = getUnion("U1z"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1z\n" + - "Aligned\n" + + "pack()\n" + "Union U1z {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 longlong:0(0) 1 \"\"\n" + // has no impact @@ -69,7 +69,7 @@ public class UnionImplLittleEndianBitFieldTest extends AbstractCompositeImplBitF Union struct = getUnion("U1p1"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p1\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Union U1p1 {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 int:2(0) 1 b \"\"\n" + @@ -83,7 +83,7 @@ public class UnionImplLittleEndianBitFieldTest extends AbstractCompositeImplBitF Union struct = getUnion("U1p1z"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p1z\n" + - "Aligned pack(1)\n" + + "pack(1)\n" + "Union U1p1z {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 longlong:0(0) 1 \"\"\n" + // has no impact @@ -98,7 +98,7 @@ public class UnionImplLittleEndianBitFieldTest extends AbstractCompositeImplBitF Union struct = getUnion("U1p2"); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/U1p2\n" + - "Aligned pack(2)\n" + + "pack(2)\n" + "Union U1p2 {\n" + " 0 int:4(0) 1 a \"\"\n" + " 0 int:2(0) 1 b \"\"\n" + diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/FillOutStructureCmd.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/FillOutStructureCmd.java index 101b6d4006..262c57ecc1 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/FillOutStructureCmd.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/FillOutStructureCmd.java @@ -450,13 +450,8 @@ public class FillOutStructureCmd extends BackgroundCommand { structDT = createNewStruct(var, (int) componentMap.getSize(), f, isThisParam); } else { - int len; - if (structDT.isNotYetDefined()) { - len = 0; - } - else { - len = structDT.getLength(); - } + // FIXME: How should an existing packed structure be handled? Growing and offset-based placement does not apply + int len = structDT.isZeroLength() ? 0 : structDT.getLength(); if (componentMap.getSize() > len) { structDT.growStructure((int) componentMap.getSize() - len); } @@ -521,13 +516,8 @@ public class FillOutStructureCmd extends BackgroundCommand { return null; } Structure structDT = VariableUtilities.findOrCreateClassStruct(f); - int len; - if (structDT.isNotYetDefined()) { - len = 0; // getLength reports as at least size 1 - } - else { - len = structDT.getLength(); - } +// FIXME: How should an existing packed structure be handled? Growing and offset-based placement does not apply + int len = structDT.isZeroLength() ? 0 : structDT.getLength(); if (len < size) { structDT.growStructure(size - len); } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RenameStructureFieldTask.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RenameStructureFieldTask.java index 527382b72e..08024495b4 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RenameStructureFieldTask.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/RenameStructureFieldTask.java @@ -20,6 +20,7 @@ import ghidra.app.decompiler.component.DecompilerPanel; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; +import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; @@ -37,9 +38,14 @@ public class RenameStructureFieldTask extends RenameTask { @Override public void commit() throws DuplicateNameException, InvalidInputException { - if (structure.isNotYetDefined()) { - DataType newtype = new Undefined1DataType(); - structure.insert(0, newtype); + // FIXME: How should an existing packed structure be handled? Growing and offset-based placement does not apply + int len = structure.isZeroLength() ? 0 : structure.getLength(); + if (len < offset) { + if (!structure.isPackingEnabled()) { + Msg.warn(this, "Structure '" + structure.getName() + "' converted to non-packed"); + structure.setPackingEnabled(false); + } + structure.growStructure(offset); } DataTypeComponent comp = structure.getComponentAt(offset); if (comp.getDataType() == DataType.DEFAULT) { // Is this just a placeholder diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java index 138bbe65e1..1aae0e185d 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/DefaultCompositeMember.java @@ -266,7 +266,7 @@ public class DefaultCompositeMember extends CompositeMember { } /** - * Adjust unaligned structure following member reconstruction. + * Adjust non-packed structure following member reconstruction. * @param preferredSize preferred size */ private void adjustSize(int preferredSize) { @@ -325,18 +325,21 @@ public class DefaultCompositeMember extends CompositeMember { Composite copy = (Composite) composite.copy(dataTypeManager); int pack = 0; - copy.setPackingValue(pack); + copy.setToDefaultPacking(); boolean alignOK = isGoodAlignment(copy, preferredSize); - if (!alignOK) { - pack = 1; - copy.setPackingValue(pack); - alignOK = isGoodAlignment(copy, preferredSize); - } if (alignOK) { - composite.setPackingValue(pack); + composite.setToDefaultPacking(); } - else if (errorConsumer != null && !isClass) { // don't complain about Class structs which always fail + else { + pack = 1; + copy.setExplicitPackingValue(pack); + alignOK = isGoodAlignment(copy, preferredSize); + if (alignOK) { + composite.setExplicitPackingValue(pack); + } + } + if (!alignOK && errorConsumer != null && !isClass) { // don't complain about Class structs which always fail String anonymousStr = parent != null ? " anonymous " : ""; errorConsumer.accept("PDB " + anonymousStr + memberType + " reconstruction failed to align " + composite.getPathName()); @@ -352,11 +355,11 @@ public class DefaultCompositeMember extends CompositeMember { if (alignOK && isStructureContainer()) { // verify that components did not move Structure struct = (Structure) memberDataType; - DataTypeComponent[] unalignedComponents = struct.getDefinedComponents(); + DataTypeComponent[] nonPackedComponents = struct.getDefinedComponents(); int index = 0; for (DataTypeComponent dtc : testComposite.getComponents()) { - DataTypeComponent unalignedDtc = unalignedComponents[index++]; - if (!isComponentUnchanged(dtc, unalignedDtc)) { + DataTypeComponent nonPackedDtc = nonPackedComponents[index++]; + if (!isComponentUnchanged(dtc, nonPackedDtc)) { alignOK = false; break; } @@ -365,18 +368,18 @@ public class DefaultCompositeMember extends CompositeMember { return alignOK; } - private boolean isComponentUnchanged(DataTypeComponent dtc, DataTypeComponent unalignedDtc) { - if (unalignedDtc.getOffset() != dtc.getOffset() || - unalignedDtc.getLength() != dtc.getLength() || - unalignedDtc.isBitFieldComponent() != dtc.isBitFieldComponent()) { + private boolean isComponentUnchanged(DataTypeComponent dtc, DataTypeComponent nonPackedDtc) { + if (nonPackedDtc.getOffset() != dtc.getOffset() || + nonPackedDtc.getLength() != dtc.getLength() || + nonPackedDtc.isBitFieldComponent() != dtc.isBitFieldComponent()) { return false; } if (dtc.isBitFieldComponent()) { // both components are bit fields BitFieldDataType bitfieldDt = (BitFieldDataType) dtc.getDataType(); - BitFieldDataType unalignedBitfieldDt = (BitFieldDataType) unalignedDtc.getDataType(); - if (bitfieldDt.getBitOffset() != unalignedBitfieldDt.getBitOffset() || - bitfieldDt.getBitSize() != unalignedBitfieldDt.getBitSize()) { + BitFieldDataType nonPackedBitfieldDt = (BitFieldDataType) nonPackedDtc.getDataType(); + if (bitfieldDt.getBitOffset() != nonPackedBitfieldDt.getBitOffset() || + bitfieldDt.getBitSize() != nonPackedBitfieldDt.getBitSize()) { return false; } } diff --git a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbUtil.java b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbUtil.java index 64264b2778..364ec85fd6 100644 --- a/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbUtil.java +++ b/Ghidra/Features/PDB/src/main/java/ghidra/app/util/bin/format/pdb/PdbUtil.java @@ -105,32 +105,6 @@ final class PdbUtil { return false; } -// final static void ensureSize(int expectedLength, Composite composite, MessageLog log) { -// int actualLength = composite.getLength(); -// if (actualLength < expectedLength) { -// -// composite.setInternallyAligned(false); -// if (composite instanceof Structure) { -// Structure struct = (Structure) composite; -// // if this is an empty structure, the structure will lie to us -// // and say it has one element so add 1 to growth factor -// struct.growStructure( -// expectedLength - actualLength + (struct.isNotYetDefined() ? 1 : 0)); -// } -// // must be a union data type -// else { -// DataType datatype = new ArrayDataType(DataType.DEFAULT, expectedLength, -// DataType.DEFAULT.getLength()); -// composite.add(datatype); -// } -// } -// else if (actualLength > expectedLength) { -// log.appendMsg("PDB", "Composite data type generated from PDB has size mismatch. " + -// composite.getName() + ": expected 0x" + Integer.toHexString(expectedLength) + -// ", but was 0x" + Integer.toHexString(actualLength)); -// } -// } - final static void clearComponents(Composite composite) { if (composite instanceof Structure) { ((Structure) composite).deleteAll(); diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java index d90b454dff..58094a4358 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/bin/format/pdb/CompositeMemberTest.java @@ -124,7 +124,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/struct\n" + - "Aligned\n" + + "pack()\n" + "Structure struct {\n" + " 0 uchar 1 a \"\"\n" + " 1 uchar:4(0) 1 b \"\"\n" + @@ -159,7 +159,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/struct\n" + - "Aligned\n" + + "pack()\n" + "Structure struct {\n" + " 0 uchar 1 a \"\"\n" + " 1 uchar:2(0) 1 padding \"\"\n" + @@ -222,20 +222,20 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/struct\n" + - "Aligned\n" + + "pack()\n" + "Structure struct {\n" + " 0 struct_u_0 1 null \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/struct/struct_u_0\n" + - "Aligned\n" + + "pack()\n" + "Union struct_u_0 {\n" + " 0 struct_u_0_s_0 1 _s_0 \"\"\n" + " 0 struct_u_0_s_1 1 _s_1 \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/struct/struct_u_0/struct_u_0_s_0\n" + - "Aligned\n" + + "pack()\n" + "Structure struct_u_0_s_0 {\n" + " 0 char:1(0) 1 a0 \"\"\n" + " 0 char:1(1) 1 padding \"\"\n" + @@ -245,7 +245,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 1 Actual Alignment = 1\n" + "/struct/struct_u_0/struct_u_0_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure struct_u_0_s_1 {\n" + " 0 char:1(0) 1 padding \"\"\n" + " 0 char:1(1) 1 a1 \"\"\n" + @@ -285,7 +285,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/union\n" + - "Aligned\n" + + "pack()\n" + "Union union {\n" + " 0 uchar 1 a \"\"\n" + " 0 union_s_1 1 _s_1 \"\"\n" + @@ -294,14 +294,14 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 2 Actual Alignment = 2\n" + "/union/union_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_1 {\n" + " 0 uchar:4(0) 1 b \"\"\n" + " 0 uchar:4(4) 1 c \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/union/union_s_2\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_2 {\n" + " 0 uchar:4(0) 1 d \"\"\n" + " 0 uchar:4(4) 1 e \"\"\n" + @@ -328,7 +328,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/struct\n" + - "Aligned\n" + + "pack()\n" + "Structure struct {\n" + " 0 char 1 a \"\"\n" + " char[0] 0 e \"\"\n" + @@ -353,20 +353,20 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest new MyPdbMember("f", "char[0]", 8)); //@formatter:on - assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 12, members, this, + assertTrue(DefaultCompositeMember.applyDataTypeMembers(struct, false, 16, members, this, TaskMonitor.DUMMY)); //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/union\n" + - "Aligned\n" + + "pack()\n" + "Union union {\n" + " 0 union_s_0 12 _s_0 \"\"\n" + " 0 union_s_1 8 _s_1 \"\"\n" + "}\n" + - "Size = 12 Actual Alignment = 8\n" + + "Size = 16 Actual Alignment = 8\n" + "/union/union_s_0\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_0 {\n" + " 0 int 4 a \"\"\n" + " 4 int 4 b \"\"\n" + @@ -375,7 +375,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 12 Actual Alignment = 4\n" + "/union/union_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_1 {\n" + " 0 longlong 8 e \"\"\n" + " char[0] 0 f \"\"\n" + @@ -405,14 +405,14 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/union\n" + - "Aligned\n" + + "pack()\n" + "Union union {\n" + " 0 union_s_0 12 _s_0 \"\"\n" + " 0 union_s_1 1 _s_1 \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/union/union_s_0\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_0 {\n" + " 0 int 4 a \"\"\n" + " 4 int 4 b \"\"\n" + @@ -421,7 +421,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 12 Actual Alignment = 4\n" + "/union/union_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_1 {\n" + " char[0] 0 f \"\"\n" + "}\n" + @@ -449,21 +449,21 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/union\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Union union {\n" + " 0 union_s_0 1 _s_0 \"\"\n" + " 0 union_s_1 2 _s_1 \"\"\n" + "}\n" + "Size = 2 Actual Alignment = 1\n" + "/union/union_s_0\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_0 {\n" + " 0 char 1 a \"\"\n" + " char[0] 0 flex \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/union/union_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_1 {\n" + " 0 char 1 b \"\"\n" + " 1 char 1 c \"\"\n" + @@ -506,21 +506,21 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/struct\n" + - "Aligned\n" + + "pack()\n" + "Structure struct {\n" + " 0 struct_u_0 1 null \"\"\n" + " 8 struct_u_8 8 null \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/struct/struct_u_0\n" + - "Aligned\n" + + "pack()\n" + "Union struct_u_0 {\n" + " 0 struct_u_0_s_0 1 _s_0 \"\"\n" + " 0 struct_u_0_s_1 1 _s_1 \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/struct/struct_u_0/struct_u_0_s_0\n" + - "Aligned\n" + + "pack()\n" + "Structure struct_u_0_s_0 {\n" + " 0 char:1(0) 1 s0 \"\"\n" + " 0 char:1(1) 1 s1 \"\"\n" + @@ -533,7 +533,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 1 Actual Alignment = 1\n" + "/struct/struct_u_0/struct_u_0_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure struct_u_0_s_1 {\n" + " 0 uchar:1(0) 1 u0 \"\"\n" + " 0 uchar:1(1) 1 u1 \"\"\n" + @@ -546,7 +546,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 1 Actual Alignment = 1\n" + "/struct/struct_u_8\n" + - "Aligned\n" + + "pack()\n" + "Union struct_u_8 {\n" + " 0 ulong 4 a \"\"\n" + " 0 longlong 8 b \"\"\n" + @@ -590,7 +590,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/union\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Union union {\n" + " 0 ulong 4 a \"\"\n" + " 0 longlong 8 b \"\"\n" + @@ -598,7 +598,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 8 Actual Alignment = 1\n" + "/union/union_s_2\n" + - "Aligned\n" + + "pack()\n" + "Structure union_s_2 {\n" + " 0 char:1(0) 1 s0 \"\"\n" + " 0 char:1(1) 1 s1 \"\"\n" + @@ -700,7 +700,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest //@formatter:off CompositeTestUtils.assertExpectedComposite(this, "/MoreComplicated_s\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s {\n" + " 0 MoreComplicated_s_u_0 1 null \"\"\n" + " 8 MoreComplicated_s_u_8 8 null \"\"\n" + @@ -710,14 +710,14 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 56 Actual Alignment = 8\n" + "/MoreComplicated_s/MoreComplicated_s_u_0\n" + - "Aligned\n" + + "pack()\n" + "Union MoreComplicated_s_u_0 {\n" + " 0 MoreComplicated_s_u_0_s_0 1 _s_0 \"\"\n" + " 0 MoreComplicated_s_u_0_s_1 1 _s_1 \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/MoreComplicated_s/MoreComplicated_s_u_0/MoreComplicated_s_u_0_s_0\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_0_s_0 {\n" + " 0 char:1(0) 1 s0 \"\"\n" + " 0 char:1(1) 1 s1 \"\"\n" + @@ -730,7 +730,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 1 Actual Alignment = 1\n" + "/MoreComplicated_s/MoreComplicated_s_u_0/MoreComplicated_s_u_0_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_0_s_1 {\n" + " 0 uchar:1(0) 1 u0 \"\"\n" + " 0 uchar:1(1) 1 u1 \"\"\n" + @@ -743,35 +743,35 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 1 Actual Alignment = 1\n" + "/MoreComplicated_s/MoreComplicated_s_u_16\n" + - "Aligned\n" + + "pack()\n" + "Union MoreComplicated_s_u_16 {\n" + " 0 MoreComplicated_s_u_16_s_0 16 _s_0 \"\"\n" + " 0 MoreComplicated_s_u_16_s_1 16 _s_1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/MoreComplicated_s/MoreComplicated_s_u_16/MoreComplicated_s_u_16_s_0\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_16_s_0 {\n" + " 0 double 8 da \"\"\n" + " 8 char[8] 8 ca \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/MoreComplicated_s/MoreComplicated_s_u_16/MoreComplicated_s_u_16_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_16_s_1 {\n" + " 0 char[8] 8 cb \"\"\n" + " 8 double 8 db \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/MoreComplicated_s/MoreComplicated_s_u_44\n" + - "Aligned\n" + + "pack()\n" + "Union MoreComplicated_s_u_44 {\n" + " 0 MoreComplicated_s_u_44_s_0 12 _s_0 \"\"\n" + " 0 MoreComplicated_s_u_44_s_1 1 _s_1 \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/MoreComplicated_s/MoreComplicated_s_u_44/MoreComplicated_s_u_44_s_0\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_44_s_0 {\n" + " 0 int 4 fromAddress \"\"\n" + " 4 int 4 toAddress \"\"\n" + @@ -780,13 +780,13 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 12 Actual Alignment = 4\n" + "/MoreComplicated_s/MoreComplicated_s_u_44/MoreComplicated_s_u_44_s_1\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_44_s_1 {\n" + " char[0] 0 buf \"\"\n" + "}\n" + "Size = 1 Actual Alignment = 1\n" + "/MoreComplicated_s/MoreComplicated_s_u_8\n" + - "Aligned\n" + + "pack()\n" + "Union MoreComplicated_s_u_8 {\n" + " 0 ulong 4 val \"\"\n" + " 0 double 8 d \"\"\n" + @@ -796,7 +796,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 8 Actual Alignment = 8\n" + "/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_2\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_8_s_2 {\n" + " 0 ulong:4(0) 1 n0 \"\"\n" + " 0 ulong:4(4) 1 n1 \"\"\n" + @@ -817,7 +817,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 8 Actual Alignment = 4\n" + "/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_3\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_8_s_3 {\n" + " 0 ulong:1(0) 1 x1 \"\"\n" + " 0 ulong:2(1) 1 x2 \"\"\n" + @@ -832,7 +832,7 @@ public class CompositeMemberTest extends AbstractGhidraHeadlessIntegrationTest "}\n" + "Size = 8 Actual Alignment = 4\n" + "/MoreComplicated_s/MoreComplicated_s_u_8/MoreComplicated_s_u_8_s_4\n" + - "Aligned\n" + + "pack()\n" + "Structure MoreComplicated_s_u_8_s_4 {\n" + " 0 uchar:1(0) 1 y1 \"\"\n" + " 0 uchar:2(1) 1 y2 \"\"\n" + diff --git a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java index d1d4de31a1..5dc07097aa 100644 --- a/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java +++ b/Ghidra/Features/PDB/src/test/java/ghidra/app/util/pdb/pdbapplicator/CppCompositeTypeTest.java @@ -15,7 +15,7 @@ */ package ghidra.app.util.pdb.pdbapplicator; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.util.*; @@ -204,8 +204,8 @@ public class CppCompositeTypeTest extends AbstractGenericTest { return; } Address address = iter.next(); - for (int index = 0; index < offsets.size(); index++) { - addresses.add(address.add(offsets.get(index))); + for (Integer offset : offsets) { + addresses.add(address.add(offset)); } } @@ -3002,13 +3002,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/A\n" + - "Aligned\n" + + "pack()\n" + "Structure A {\n" + " 0 A_direct 8 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + @@ -3058,13 +3058,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/A\n" + - "Aligned\n" + + "pack()\n" + "Structure A {\n" + " 0 A_direct 8 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + @@ -3112,13 +3112,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/C\n" + - "Aligned\n" + + "pack()\n" + "Structure C {\n" + " 0 C_direct 4 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + @@ -3165,13 +3165,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/C\n" + - "Aligned\n" + + "pack()\n" + "Structure C {\n" + " 0 C_direct 4 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + @@ -3218,13 +3218,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/CC1\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1 {\n" + " 0 CC1_direct 4 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC1/CC1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1_direct {\n" + " 0 undefined4 4 cc11 \"\"\n" + "}\n" + @@ -3271,13 +3271,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/CC1\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1 {\n" + " 0 CC1_direct 4 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC1/CC1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1_direct {\n" + " 0 undefined4 4 cc11 \"\"\n" + "}\n" + @@ -3324,13 +3324,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/CC2\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2 {\n" + " 0 CC2_direct 4 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + @@ -3377,13 +3377,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/CC2\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2 {\n" + " 0 CC2_direct 4 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + @@ -3428,7 +3428,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/CC3\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure CC3 {\n" + "}\n" + "Size = 1 Actual Alignment = 1"; @@ -3472,7 +3472,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/CC3\n" + - "Unaligned\n" + + "pack(disabled)\n" + "Structure CC3 {\n" + "}\n" + "Size = 1 Actual Alignment = 1"; @@ -3521,19 +3521,19 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/D\n" + - "Aligned\n" + + "pack()\n" + "Structure D {\n" + " 0 D_direct 8 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/D/D_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure D_direct {\n" + " 0 C_direct 4 \"\"\n" + " 4 undefined4 4 d1 \"\"\n" + @@ -3584,19 +3584,19 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/D\n" + - "Aligned\n" + + "pack()\n" + "Structure D {\n" + " 0 D_direct 8 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/D/D_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure D_direct {\n" + " 0 C_direct 4 \"\"\n" + " 4 undefined4 4 d1 \"\"\n" + @@ -3644,13 +3644,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/E\n" + - "Aligned\n" + + "pack()\n" + "Structure E {\n" + " 0 E_direct 4 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + @@ -3697,13 +3697,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/E\n" + - "Aligned\n" + + "pack()\n" + "Structure E {\n" + " 0 E_direct 4 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + @@ -3756,25 +3756,25 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/F\n" + - "Aligned\n" + + "pack()\n" + "Structure F {\n" + " 0 F_direct 12 \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/F/F_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure F_direct {\n" + " 0 C_direct 4 \"\"\n" + " 4 E_direct 4 \"\"\n" + @@ -3829,25 +3829,25 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/F\n" + - "Aligned\n" + + "pack()\n" + "Structure F {\n" + " 0 F_direct 12 \"\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/F/F_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure F_direct {\n" + " 0 C_direct 4 \"\"\n" + " 4 E_direct 4 \"\"\n" + @@ -3906,20 +3906,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/G\n" + - "Aligned\n" + + "pack()\n" + "Structure G {\n" + " 0 G_direct 8 \"\"\n" + " 8 C_direct 4 \"(Virtual Base C)\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g1 \"\"\n" + @@ -3978,20 +3978,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/G\n" + - "Aligned\n" + + "pack()\n" + "Structure G {\n" + " 0 G_direct 16 \"\"\n" + " 16 C_direct 4 \"(Virtual Base C)\"\n" + "}\n" + "Size = 24 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g1 \"\"\n" + @@ -4049,20 +4049,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/H\n" + - "Aligned\n" + + "pack()\n" + "Structure H {\n" + " 0 H_direct 8 \"\"\n" + " 8 C_direct 4 \"(Virtual Base C)\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h1 \"\"\n" + @@ -4121,20 +4121,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/H\n" + - "Aligned\n" + + "pack()\n" + "Structure H {\n" + " 0 H_direct 16 \"\"\n" + " 16 C_direct 4 \"(Virtual Base C)\"\n" + "}\n" + "Size = 24 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h1 \"\"\n" + @@ -4197,7 +4197,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/G1\n" + - "Aligned\n" + + "pack()\n" + "Structure G1 {\n" + " 0 G1_direct 8 \"\"\n" + " 8 C_direct 4 \"(Virtual Base C)\"\n" + @@ -4205,19 +4205,19 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 16 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + @@ -4281,7 +4281,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/G1\n" + - "Aligned\n" + + "pack()\n" + "Structure G1 {\n" + " 0 G1_direct 16 \"\"\n" + " 16 C_direct 4 \"(Virtual Base C)\"\n" + @@ -4289,19 +4289,19 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 24 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + @@ -4364,7 +4364,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/H1\n" + - "Aligned\n" + + "pack()\n" + "Structure H1 {\n" + " 0 H1_direct 8 \"\"\n" + " 8 E_direct 4 \"(Virtual Base E)\"\n" + @@ -4372,19 +4372,19 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 16 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h11 \"\"\n" + @@ -4448,7 +4448,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/H1\n" + - "Aligned\n" + + "pack()\n" + "Structure H1 {\n" + " 0 H1_direct 16 \"\"\n" + " 16 E_direct 4 \"(Virtual Base E)\"\n" + @@ -4456,19 +4456,19 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 24 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h11 \"\"\n" + @@ -4526,20 +4526,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/GG1\n" + - "Aligned\n" + + "pack()\n" + "Structure GG1 {\n" + " 0 GG1_direct 8 \"\"\n" + " 8 CC1_direct 4 \"(Virtual Base CC1)\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/CC1/CC1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1_direct {\n" + " 0 undefined4 4 cc11 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/GG1/GG1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg11 \"\"\n" + @@ -4598,20 +4598,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/GG1\n" + - "Aligned\n" + + "pack()\n" + "Structure GG1 {\n" + " 0 GG1_direct 16 \"\"\n" + " 16 CC1_direct 4 \"(Virtual Base CC1)\"\n" + "}\n" + "Size = 24 Actual Alignment = 8\n" + "/CC1/CC1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1_direct {\n" + " 0 undefined4 4 cc11 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/GG1/GG1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg11 \"\"\n" + @@ -4669,20 +4669,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/GG2\n" + - "Aligned\n" + + "pack()\n" + "Structure GG2 {\n" + " 0 GG2_direct 8 \"\"\n" + " 8 CC2_direct 4 \"(Virtual Base CC2)\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/GG2/GG2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG2_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg21 \"\"\n" + @@ -4741,20 +4741,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/GG2\n" + - "Aligned\n" + + "pack()\n" + "Structure GG2 {\n" + " 0 GG2_direct 16 \"\"\n" + " 16 CC2_direct 4 \"(Virtual Base CC2)\"\n" + "}\n" + "Size = 24 Actual Alignment = 8\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/GG2/GG2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG2_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg21 \"\"\n" + @@ -4812,20 +4812,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/GG3\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3 {\n" + " 0 GG3_direct 8 \"\"\n" + " 8 CC2_direct 4 \"(Virtual Base CC2)\"\n" + "}\n" + "Size = 12 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/GG3/GG3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg31 \"\"\n" + @@ -4884,20 +4884,20 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/GG3\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3 {\n" + " 0 GG3_direct 16 \"\"\n" + " 16 CC2_direct 4 \"(Virtual Base CC2)\"\n" + "}\n" + "Size = 24 Actual Alignment = 8\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/GG3/GG3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg31 \"\"\n" + @@ -4958,13 +4958,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/GG4\n" + - "Aligned\n" + + "pack()\n" + "Structure GG4 {\n" + " 0 GG4_direct 8 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/GG4/GG4_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG4_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg41 \"\"\n" + @@ -5026,13 +5026,13 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/GG4\n" + - "Aligned\n" + + "pack()\n" + "Structure GG4 {\n" + " 0 GG4_direct 16 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/GG4/GG4_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG4_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg41 \"\"\n" + @@ -5102,34 +5102,34 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I\n" + - "Aligned\n" + + "pack()\n" + "Structure I {\n" + " 0 I_direct 20 \"\"\n" + " 20 C_direct 4 \"(Virtual Base C)\"\n" + "}\n" + "Size = 24 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I/I_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I_direct {\n" + " 0 G_direct 8 \"\"\n" + " 8 H_direct 8 \"\"\n" + @@ -5204,34 +5204,34 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I\n" + - "Aligned\n" + + "pack()\n" + "Structure I {\n" + " 0 I_direct 40 \"\"\n" + " 40 C_direct 4 \"(Virtual Base C)\"\n" + "}\n" + "Size = 48 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I/I_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I_direct {\n" + " 0 G_direct 16 \"\"\n" + " 16 H_direct 16 \"\"\n" + @@ -5306,7 +5306,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I1\n" + - "Aligned\n" + + "pack()\n" + "Structure I1 {\n" + " 0 I1_direct 20 \"\"\n" + " 20 C_direct 4 \"(Virtual Base C)\"\n" + @@ -5314,33 +5314,33 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 28 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I1/I1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I1_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 H_direct 8 \"\"\n" + @@ -5419,7 +5419,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I1\n" + - "Aligned\n" + + "pack()\n" + "Structure I1 {\n" + " 0 I1_direct 40 \"\"\n" + " 40 C_direct 4 \"(Virtual Base C)\"\n" + @@ -5427,33 +5427,33 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 48 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I1/I1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I1_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 H_direct 16 \"\"\n" + @@ -5529,7 +5529,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I2\n" + - "Aligned\n" + + "pack()\n" + "Structure I2 {\n" + " 0 I2_direct 20 \"\"\n" + " 20 C_direct 4 \"(Virtual Base C)\"\n" + @@ -5537,33 +5537,33 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 28 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I2/I2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I2_direct {\n" + " 0 G_direct 8 \"\"\n" + " 8 H1_direct 8 \"\"\n" + @@ -5643,7 +5643,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I2\n" + - "Aligned\n" + + "pack()\n" + "Structure I2 {\n" + " 0 I2_direct 40 \"\"\n" + " 40 C_direct 4 \"(Virtual Base C)\"\n" + @@ -5651,33 +5651,33 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 48 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I2/I2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I2_direct {\n" + " 0 G_direct 16 \"\"\n" + " 16 H1_direct 16 \"\"\n" + @@ -5753,7 +5753,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I3\n" + - "Aligned\n" + + "pack()\n" + "Structure I3 {\n" + " 0 I3_direct 20 \"\"\n" + " 20 C_direct 4 \"(Virtual Base C)\"\n" + @@ -5761,33 +5761,33 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 28 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I3/I3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I3_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 H1_direct 8 \"\"\n" + @@ -5866,7 +5866,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I3\n" + - "Aligned\n" + + "pack()\n" + "Structure I3 {\n" + " 0 I3_direct 40 \"\"\n" + " 40 C_direct 4 \"(Virtual Base C)\"\n" + @@ -5874,33 +5874,33 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 48 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I3/I3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I3_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 H1_direct 16 \"\"\n" + @@ -5967,7 +5967,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I4\n" + - "Aligned\n" + + "pack()\n" + "Structure I4 {\n" + " 0 I4_direct 12 \"\"\n" + " 12 C_direct 4 \"(Virtual Base C)\"\n" + @@ -5975,26 +5975,26 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I4/I4_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I4_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 undefined4 4 i41 \"\"\n" + @@ -6062,7 +6062,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I4\n" + - "Aligned\n" + + "pack()\n" + "Structure I4 {\n" + " 0 I4_direct 24 \"\"\n" + " 24 C_direct 4 \"(Virtual Base C)\"\n" + @@ -6070,26 +6070,26 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 32 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I4/I4_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I4_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 undefined4 4 i41 \"\"\n" + @@ -6155,7 +6155,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I5\n" + - "Aligned\n" + + "pack()\n" + "Structure I5 {\n" + " 0 I5_direct 12 \"\"\n" + " 12 E_direct 4 \"(Virtual Base E)\"\n" + @@ -6163,26 +6163,26 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I5/I5_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I5_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 undefined4 4 i51 \"\"\n" + @@ -6201,7 +6201,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I5\n" + - "Aligned\n" + + "pack()\n" + "Structure I5 {\n" + " 0 I5_direct 12 \"\"\n" + " 12 C_direct 4 \"((Speculative Placement) Virtual Base C)\"\n" + @@ -6209,26 +6209,26 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I5/I5_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I5_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 undefined4 4 i51 \"\"\n" + @@ -6292,7 +6292,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I5\n" + - "Aligned\n" + + "pack()\n" + "Structure I5 {\n" + " 0 I5_direct 24 \"\"\n" + " 24 E_direct 4 \"(Virtual Base E)\"\n" + @@ -6300,26 +6300,26 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 32 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I5/I5_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I5_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 undefined4 4 i51 \"\"\n" + @@ -6338,7 +6338,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/I5\n" + - "Aligned\n" + + "pack()\n" + "Structure I5 {\n" + " 0 I5_direct 24 \"\"\n" + " 24 C_direct 4 \"((Speculative Placement) Virtual Base C)\"\n" + @@ -6346,26 +6346,26 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 32 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I5/I5_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I5_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 undefined4 4 i51 \"\"\n" + @@ -6459,7 +6459,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J1\n" + - "Aligned\n" + + "pack()\n" + "Structure J1 {\n" + " 0 J1_direct 44 \"\"\n" + " 44 C_direct 4 \"(Virtual Base C)\"\n" + @@ -6467,47 +6467,47 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 52 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I1/I1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I1_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 H_direct 8 \"\"\n" + @@ -6515,7 +6515,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/I2/I2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I2_direct {\n" + " 0 G_direct 8 \"\"\n" + " 8 H1_direct 8 \"\"\n" + @@ -6523,7 +6523,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/J1/J1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J1_direct {\n" + " 0 I1_direct 20 \"\"\n" + " 20 I2_direct 20 \"\"\n" + @@ -6631,7 +6631,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J1\n" + - "Aligned\n" + + "pack()\n" + "Structure J1 {\n" + " 0 J1_direct 88 \"\"\n" + " 88 C_direct 4 \"(Virtual Base C)\"\n" + @@ -6639,47 +6639,47 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 96 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I1/I1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I1_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 H_direct 16 \"\"\n" + @@ -6687,7 +6687,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/I2/I2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I2_direct {\n" + " 0 G_direct 16 \"\"\n" + " 16 H1_direct 16 \"\"\n" + @@ -6695,7 +6695,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/J1/J1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J1_direct {\n" + " 0 I1_direct 40 \"\"\n" + " 40 I2_direct 40 \"\"\n" + @@ -6794,7 +6794,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J2\n" + - "Aligned\n" + + "pack()\n" + "Structure J2 {\n" + " 0 J2_direct 44 \"\"\n" + " 44 C_direct 4 \"(Virtual Base C)\"\n" + @@ -6802,47 +6802,47 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 52 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I1/I1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I1_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 H_direct 8 \"\"\n" + @@ -6850,7 +6850,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/I2/I2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I2_direct {\n" + " 0 G_direct 8 \"\"\n" + " 8 H1_direct 8 \"\"\n" + @@ -6858,7 +6858,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/J2/J2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J2_direct {\n" + " 0 I2_direct 20 \"\"\n" + " 20 I1_direct 20 \"\"\n" + @@ -6966,7 +6966,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J2\n" + - "Aligned\n" + + "pack()\n" + "Structure J2 {\n" + " 0 J2_direct 88 \"\"\n" + " 88 C_direct 4 \"(Virtual Base C)\"\n" + @@ -6974,47 +6974,47 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 96 Actual Alignment = 8\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I1/I1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I1_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 H_direct 16 \"\"\n" + @@ -7022,7 +7022,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/I2/I2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I2_direct {\n" + " 0 G_direct 16 \"\"\n" + " 16 H1_direct 16 \"\"\n" + @@ -7030,7 +7030,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/J2/J2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J2_direct {\n" + " 0 I2_direct 40 \"\"\n" + " 40 I1_direct 40 \"\"\n" + @@ -7134,7 +7134,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J3\n" + - "Aligned\n" + + "pack()\n" + "Structure J3 {\n" + " 0 J3_direct 52 \"\"\n" + " 52 C_direct 4 \"(Virtual Base C)\"\n" + @@ -7142,54 +7142,54 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 60 Actual Alignment = 4\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I1/I1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I1_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 H_direct 8 \"\"\n" + @@ -7197,7 +7197,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/I2/I2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I2_direct {\n" + " 0 G_direct 8 \"\"\n" + " 8 H1_direct 8 \"\"\n" + @@ -7205,7 +7205,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/J3/J3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J3_direct {\n" + " 0 I2_direct 20 \"\"\n" + " 20 I1_direct 20 \"\"\n" + @@ -7319,7 +7319,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J3\n" + - "Aligned\n" + + "pack()\n" + "Structure J3 {\n" + " 0 J3_direct 96 \"\"\n" + " 96 C_direct 4 \"(Virtual Base C)\"\n" + @@ -7327,54 +7327,54 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 104 Actual Alignment = 8\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I1/I1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I1_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 H_direct 16 \"\"\n" + @@ -7382,7 +7382,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/I2/I2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I2_direct {\n" + " 0 G_direct 16 \"\"\n" + " 16 H1_direct 16 \"\"\n" + @@ -7390,7 +7390,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/J3/J3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J3_direct {\n" + " 0 I2_direct 40 \"\"\n" + " 40 I1_direct 40 \"\"\n" + @@ -7532,7 +7532,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J4\n" + - "Aligned\n" + + "pack()\n" + "Structure J4 {\n" + " 0 J4_direct 60 \"\"\n" + " 60 C_direct 4 \"(Virtual Base C)\"\n" + @@ -7544,87 +7544,87 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 92 Actual Alignment = 4\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC1/CC1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1_direct {\n" + " 0 undefined4 4 cc11 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/GG1/GG1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/GG2/GG2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG2_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg21 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/GG3/GG3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg31 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I/I_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I_direct {\n" + " 0 G_direct 8 \"\"\n" + " 8 H_direct 8 \"\"\n" + @@ -7632,7 +7632,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/I3/I3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I3_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 H1_direct 8 \"\"\n" + @@ -7640,7 +7640,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/J4/J4_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J4_direct {\n" + " 0 I3_direct 20 \"\"\n" + " 20 GG1_direct 8 \"\"\n" + @@ -7799,7 +7799,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J4\n" + - "Aligned\n" + + "pack()\n" + "Structure J4 {\n" + " 0 J4_direct 112 \"\"\n" + " 112 C_direct 4 \"(Virtual Base C)\"\n" + @@ -7811,87 +7811,87 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 160 Actual Alignment = 8\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC1/CC1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1_direct {\n" + " 0 undefined4 4 cc11 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/GG1/GG1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/GG2/GG2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG2_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg21 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/GG3/GG3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg31 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I/I_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I_direct {\n" + " 0 G_direct 16 \"\"\n" + " 16 H_direct 16 \"\"\n" + @@ -7899,7 +7899,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/I3/I3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I3_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 H1_direct 16 \"\"\n" + @@ -7907,7 +7907,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/J4/J4_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J4_direct {\n" + " 0 I3_direct 40 \"\"\n" + " 40 GG1_direct 16 \"\"\n" + @@ -8052,7 +8052,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J5\n" + - "Aligned\n" + + "pack()\n" + "Structure J5 {\n" + " 0 J5_direct 60 \"\"\n" + " 60 CC2_direct 4 \"(Virtual Base CC2)\"\n" + @@ -8064,87 +8064,87 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 92 Actual Alignment = 4\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC1/CC1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1_direct {\n" + " 0 undefined4 4 cc11 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/GG1/GG1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/GG2/GG2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG2_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg21 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/GG3/GG3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg31 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/I/I_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I_direct {\n" + " 0 G_direct 8 \"\"\n" + " 8 H_direct 8 \"\"\n" + @@ -8152,7 +8152,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/I3/I3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I3_direct {\n" + " 0 G1_direct 8 \"\"\n" + " 8 H1_direct 8 \"\"\n" + @@ -8160,7 +8160,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 20 Actual Alignment = 4\n" + "/J5/J5_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J5_direct {\n" + " 0 I3_direct 20 \"\"\n" + " 20 GG1_direct 8 \"\"\n" + @@ -8327,7 +8327,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J5\n" + - "Aligned\n" + + "pack()\n" + "Structure J5 {\n" + " 0 J5_direct 112 \"\"\n" + " 112 CC2_direct 4 \"(Virtual Base CC2)\"\n" + @@ -8339,87 +8339,87 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 168 Actual Alignment = 8\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/C/C_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure C_direct {\n" + " 0 undefined4 4 c1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC1/CC1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC1_direct {\n" + " 0 undefined4 4 cc11 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/E/E_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure E_direct {\n" + " 0 undefined4 4 e1 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/G/G_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/G1/G1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure G1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 g11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/GG1/GG1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/GG2/GG2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG2_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg21 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/GG3/GG3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg31 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H/H_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h1 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/H1/H1_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure H1_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 h11 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/I/I_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I_direct {\n" + " 0 G_direct 16 \"\"\n" + " 16 H_direct 16 \"\"\n" + @@ -8427,7 +8427,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/I3/I3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure I3_direct {\n" + " 0 G1_direct 16 \"\"\n" + " 16 H1_direct 16 \"\"\n" + @@ -8435,7 +8435,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 40 Actual Alignment = 8\n" + "/J5/J5_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J5_direct {\n" + " 0 I3_direct 40 \"\"\n" + " 40 GG1_direct 16 \"\"\n" + @@ -8532,7 +8532,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J6\n" + - "Aligned\n" + + "pack()\n" + "Structure J6 {\n" + " 0 J6_direct 16 \"\"\n" + " 16 GG4_direct 8 \"(Virtual Base GG4)\"\n" + @@ -8541,34 +8541,34 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 36 Actual Alignment = 4\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/GG3/GG3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg31 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/GG4/GG4_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG4_direct {\n" + " 0 int * 4 {vbptr} \"\"\n" + " 4 undefined4 4 gg41 \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/J6/J6_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J6_direct {\n" + " 0 A_direct 8 \"\"\n" + " 8 int * 4 {vbptr} \"\"\n" + @@ -8668,7 +8668,7 @@ public class CppCompositeTypeTest extends AbstractGenericTest { String expected = //@formatter:off "/J6\n" + - "Aligned\n" + + "pack()\n" + "Structure J6 {\n" + " 0 J6_direct 24 \"\"\n" + " 24 GG4_direct 16 \"(Virtual Base GG4)\"\n" + @@ -8677,34 +8677,34 @@ public class CppCompositeTypeTest extends AbstractGenericTest { "}\n" + "Size = 64 Actual Alignment = 8\n" + "/A/A_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure A_direct {\n" + " 0 undefined1 1 c \"\"\n" + " 4 undefined4 4 i \"\"\n" + "}\n" + "Size = 8 Actual Alignment = 4\n" + "/CC2/CC2_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure CC2_direct {\n" + " 0 undefined4 4 cc21 \"\"\n" + "}\n" + "Size = 4 Actual Alignment = 4\n" + "/GG3/GG3_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG3_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg31 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/GG4/GG4_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure GG4_direct {\n" + " 0 int * 8 {vbptr} \"\"\n" + " 8 undefined4 4 gg41 \"\"\n" + "}\n" + "Size = 16 Actual Alignment = 8\n" + "/J6/J6_direct\n" + - "Aligned\n" + + "pack()\n" + "Structure J6_direct {\n" + " 0 A_direct 8 \"\"\n" + " 8 int * 8 {vbptr} \"\"\n" + diff --git a/Ghidra/Framework/Generic/src/main/java/generic/test/TestUtils.java b/Ghidra/Framework/Generic/src/main/java/generic/test/TestUtils.java index dabae31d2e..205afad00e 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/test/TestUtils.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/test/TestUtils.java @@ -126,6 +126,7 @@ public class TestUtils { result = field.get(ownerInstance); } catch (Exception e) { + e.printStackTrace(); throw new RuntimeException("Unable to use reflection to obtain " + "field: " + fieldName + " from class: " + objectClass, e); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java index 5cb68fb9c3..6f7982bd1d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DataTypeArchiveDB.java @@ -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; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java index 907c388cfc..c7b62cced7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java @@ -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; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java index 735e684a72..214ed21de9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java @@ -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 { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java index 10303f9fdf..1c66f7ded6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDB.java @@ -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. + *

    + * NOTE: If modifications to stored length are made prior to invoking this method, + * detection of a size change may not be possible. + *

    + * 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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java index 06642b19eb..de084a5d83 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapter.java @@ -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. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java index 42f5c382b2..c5bc3f7d13 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV0.java @@ -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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java index bc75c3ccec..6f59f436aa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV1.java @@ -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); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V3.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java similarity index 54% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V3.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java index 5a2b99a956..c3a3180179 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V3.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV2V4.java @@ -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; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5.java new file mode 100644 index 0000000000..5d7ba54b78 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CompositeDBAdapterV5.java @@ -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; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java index fd6036582f..02b679a601 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeComponentDB.java @@ -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(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java index 385460394f..8d2bca488c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java @@ -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) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java index d13259b763..4c16940a5a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java @@ -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(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java index 7e9bfc7fae..25fae1ab2b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java index f9cdd08e93..a69b9e5c17 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/FunctionDefinitionDB.java @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParameterDefinitionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParameterDefinitionDB.java index 81194843c7..2c9a15fced 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParameterDefinitionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ParameterDefinitionDB.java @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java index 3732d38535..a9c41bdb60 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramDataTypeManager.java @@ -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 diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java index 009b565cba..a7ad073dd5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProjectDataTypeManager.java @@ -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 diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBManager.java index 951d095629..43308626e6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBManager.java @@ -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 list = new ArrayList(); 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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java index 8361f1ddcc..e9007008df 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java @@ -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.*; @@ -33,17 +33,15 @@ import ghidra.util.exception.AssertException; * * */ -class StructureDB extends CompositeDB implements Structure { - private static OrdinalComparator ordinalComparator = new OrdinalComparator(); - private static OffsetComparator offsetComparator = new OffsetComparator(); - private static ComponentComparator componentComparator = new ComponentComparator(); - protected static Comparator bitOffsetComparatorLE = new BitOffsetComparator(false); - protected static Comparator bitOffsetComparatorBE = new BitOffsetComparator(true); +class StructureDB extends CompositeDB implements StructureInternal { + private int structLength; - private int numComponents; // If aligned, this does not include the undefined data types. - private ArrayList components; + private int structAlignment; // reflects stored alignment, -1 if not yet stored + private int computedAlignment = -1; // cached alignment if not yet stored + + private int numComponents; // If packed, this does not include the undefined components. + private List components; private DataTypeComponentDB flexibleArrayComponent; - private int alignment = -1; /** * Constructor @@ -83,9 +81,11 @@ class StructureDB extends CompositeDB implements Structure { dataMgr.dbError(e); } - Collections.sort(components, componentComparator); + Collections.sort(components, ComponentComparator.INSTANCE); structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL); + structAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL); + computedAlignment = -1; numComponents = record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL); } @@ -109,13 +109,16 @@ class StructureDB extends CompositeDB implements Structure { } private DataTypeComponent doAdd(DataType dataType, int length, String name, String comment, - boolean validateAlignAndNotify) + boolean validatePackAndNotify) throws DataTypeDependencyException, IllegalArgumentException { + + // TODO: May want to standardize flex-array use with StructureDataType + lock.acquire(); try { checkDeleted(); - if (validateAlignAndNotify) { + if (validatePackAndNotify) { validateDataType(dataType); dataType = resolve(dataType); checkAncestry(dataType); @@ -124,6 +127,7 @@ class StructureDB extends CompositeDB implements Structure { DataTypeComponentDB dtc = null; try { if (dataType == DataType.DEFAULT) { + // FIXME: verify - does not appear to modify structure dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, key, numComponents, structLength); } @@ -137,22 +141,20 @@ class StructureDB extends CompositeDB implements Structure { } int structureGrowth = dtc.getLength(); - if (!isInternallyAligned() && length > 0) { + if (!isPackingEnabled() && length > 0) { structureGrowth = length; } ++numComponents; structLength += structureGrowth; - if (validateAlignAndNotify) { - + if (validatePackAndNotify) { + repack(false, false); // may not recognize length change record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents); record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); - compositeAdapter.updateRecord(record, true); - - adjustInternalAlignment(false); - notifySizeChanged(); + compositeAdapter.updateRecord(record, true); // update timestamp + notifySizeChanged(false); } } catch (IOException e) { @@ -166,13 +168,16 @@ class StructureDB extends CompositeDB implements Structure { } private DataTypeComponent doAddFlexArray(DataType dataType, String name, String comment, - boolean validateAlignAndNotify) + boolean validatePackAndNotify) throws DataTypeDependencyException, IllegalArgumentException { + + // TODO: May want to standardize implementation with StructureDataType + lock.acquire(); try { checkDeleted(); - if (validateAlignAndNotify) { + if (validatePackAndNotify) { validateDataType(dataType); dataType = resolve(dataType); if (isInvalidFlexArrayDataType(dataType)) { @@ -185,8 +190,6 @@ class StructureDB extends CompositeDB implements Structure { DataTypeComponentDB dtc = null; try { - int oldLength = structLength; - if (flexibleArrayComponent != null) { flexibleArrayComponent.getDataType().removeParent(this); componentAdapter.removeRecord(flexibleArrayComponent.getKey()); @@ -199,14 +202,10 @@ class StructureDB extends CompositeDB implements Structure { dataType.addParent(this); flexibleArrayComponent = dtc; - if (validateAlignAndNotify) { - adjustInternalAlignment(false); - if (oldLength != structLength) { - notifySizeChanged(); - } - else { - dataMgr.dataTypeChanged(this); - } + if (validatePackAndNotify) { + repack(false, false); + compositeAdapter.updateRecord(record, true); + notifySizeChanged(false); } } catch (IOException e) { @@ -221,14 +220,15 @@ class StructureDB extends CompositeDB implements Structure { @Override public void growStructure(int amount) { + if (isPackingEnabled()) { + return; + } lock.acquire(); try { checkDeleted(); - if (!isInternallyAligned()) { - doGrowStructure(amount); - } - adjustInternalAlignment(true); - notifySizeChanged(); + doGrowStructure(amount); + repack(false, false); + notifySizeChanged(false); } finally { lock.release(); @@ -236,7 +236,7 @@ class StructureDB extends CompositeDB implements Structure { } private void doGrowStructure(int amount) { - if (!isInternallyAligned()) { + if (!isPackingEnabled()) { numComponents += amount; } record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents); @@ -257,7 +257,7 @@ class StructureDB extends CompositeDB implements Structure { try { checkDeleted(); if (ordinal < 0 || ordinal > numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } if (ordinal == numComponents) { return add(dataType, length, name, comment); @@ -268,14 +268,14 @@ class StructureDB extends CompositeDB implements Structure { checkAncestry(dataType); int idx; - if (isInternallyAligned()) { + if (isPackingEnabled()) { idx = ordinal; } else { // TODO: could improve insertion of bitfield which does not intersect // existing ordinal bitfield at the bit-level idx = Collections.binarySearch(components, Integer.valueOf(ordinal), - ordinalComparator); + OrdinalComparator.INSTANCE); if (idx > 0) { DataTypeComponentDB existingDtc = components.get(idx); if (existingDtc.isBitFieldComponent()) { @@ -291,9 +291,9 @@ class StructureDB extends CompositeDB implements Structure { idx = -idx - 1; } if (dataType == DataType.DEFAULT) { - // assume unaligned insert of DEFAULT + // assume non-packed insert of DEFAULT shiftOffsets(idx, 1, 1); - notifySizeChanged(); + notifySizeChanged(false); return getComponent(ordinal); } @@ -306,8 +306,8 @@ class StructureDB extends CompositeDB implements Structure { dataType.addParent(this); shiftOffsets(idx, 1, dtc.getLength()); components.add(idx, dtc); - adjustInternalAlignment(true); - notifySizeChanged(); + repack(false, false); + notifySizeChanged(false); return dtc; } catch (DataTypeDependencyException e) { @@ -336,16 +336,16 @@ class StructureDB extends CompositeDB implements Structure { @Override public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, DataType baseDataType, int bitSize, String componentName, String comment) - throws InvalidDataTypeException, ArrayIndexOutOfBoundsException { + throws InvalidDataTypeException, IndexOutOfBoundsException { lock.acquire(); try { checkDeleted(); if (ordinal < 0 || ordinal > numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } - if (!isInternallyAligned()) { + if (!isPackingEnabled()) { int offset = structLength; if (ordinal < numComponents) { offset = getComponent(ordinal).getOffset(); @@ -399,7 +399,7 @@ class StructureDB extends CompositeDB implements Structure { byteWidth, effectiveBitSize, bitOffset, bigEndian); Comparator bitOffsetComparator = - bigEndian ? bitOffsetComparatorBE : bitOffsetComparatorLE; + bigEndian ? BitOffsetComparator.INSTANCE_BE : BitOffsetComparator.INSTANCE_LE; int startIndex = Collections.binarySearch(components, Integer.valueOf(startBitOffset), bitOffsetComparator); if (startIndex < 0) { @@ -425,7 +425,7 @@ class StructureDB extends CompositeDB implements Structure { ordinal = startIndex; } - if (isInternallyAligned()) { + if (isPackingEnabled()) { insertBitField(ordinal, 0, 0, baseDataType, effectiveBitSize, componentName, comment); } @@ -480,8 +480,8 @@ class StructureDB extends CompositeDB implements Structure { bitfieldDt.addParent(this); // has no affect components.add(startIndex, dtc); - adjustUnalignedComponents(); - notifySizeChanged(); + adjustNonPackedComponents(true); + notifySizeChanged(false); return dtc; } catch (IOException e) { @@ -499,26 +499,26 @@ class StructureDB extends CompositeDB implements Structure { try { checkDeleted(); if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } int idx; - if (isInternallyAligned()) { + if (isPackingEnabled()) { idx = ordinal; } else { idx = Collections.binarySearch(components, Integer.valueOf(ordinal), - ordinalComparator); + OrdinalComparator.INSTANCE); } if (idx >= 0) { - doDelete(idx); - adjustInternalAlignment(false); + doDelete(idx); // updates timestamp } else { - // assume unaligned removal of DEFAULT + // assume non-packed removal of DEFAULT idx = -idx - 1; - shiftOffsets(idx, -1, -1); + shiftOffsets(idx, -1, -1); // updates timestamp } - notifySizeChanged(); + repack(false, false); + notifySizeChanged(false); } finally { lock.release(); @@ -534,7 +534,7 @@ class StructureDB extends CompositeDB implements Structure { catch (IOException e) { dataMgr.dbError(e); } - if (isInternallyAligned()) { + if (isPackingEnabled()) { return; } int shiftAmount = dtc.isBitFieldComponent() ? 0 : dtc.getLength(); @@ -542,42 +542,84 @@ class StructureDB extends CompositeDB implements Structure { } @Override - public void delete(int[] ordinals) { + public void delete(Set ordinals) { lock.acquire(); try { checkDeleted(); - for (int ordinal : ordinals) { - if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + + if (ordinals.isEmpty()) { + return; + } + + boolean bitFieldRemoved = false; + + TreeSet treeSet = null; + if (!isPackingEnabled()) { + // treeSet only used to track undefined filler removal + treeSet = new TreeSet<>(ordinals); + } + + List newComponents = new ArrayList<>(); + int ordinalAdjustment = 0; + int offsetAdjustment = 0; + int lastDefinedOrdinal = -1; + for (DataTypeComponentDB dtc : components) { + int ordinal = dtc.getOrdinal(); + if (treeSet != null && lastDefinedOrdinal < (ordinal - 1)) { + // Identify removed filler since last defined component + Set removedFillerSet = treeSet.subSet(lastDefinedOrdinal + 1, ordinal); + if (!removedFillerSet.isEmpty()) { + int undefinedRemoveCount = removedFillerSet.size(); + ordinalAdjustment -= undefinedRemoveCount; + offsetAdjustment -= undefinedRemoveCount; + } + } + if (ordinals.contains(ordinal)) { + // defined component removed + if (dtc.isBitFieldComponent()) { + // defer reconciling bitfield space to repack + bitFieldRemoved = true; + } + else { + offsetAdjustment -= dtc.getLength(); + } + --ordinalAdjustment; + lastDefinedOrdinal = ordinal; + } + else { + if (ordinalAdjustment != 0) { + shiftOffset(dtc, ordinalAdjustment, offsetAdjustment); + } + newComponents.add(dtc); + lastDefinedOrdinal = ordinal; + } + } + if (treeSet != null) { + // Identify removed filler after last defined component + Set removedFillerSet = + treeSet.subSet(lastDefinedOrdinal + 1, numComponents); + if (!removedFillerSet.isEmpty()) { + int undefinedRemoveCount = removedFillerSet.size(); + ordinalAdjustment -= undefinedRemoveCount; + offsetAdjustment -= undefinedRemoveCount; } } - // delete ordinals in reverse order so that they remain valid - // during individual deletes - int[] sortedOrdinals = ordinals.clone(); - Arrays.sort(sortedOrdinals); + components = newComponents; + updateNumComponents(numComponents + ordinalAdjustment); - for (int i = sortedOrdinals.length - 1; i >= 0; i--) { - int ordinal = sortedOrdinals[i]; - int idx; - if (isInternallyAligned()) { - idx = ordinal; - } - else { - idx = Collections.binarySearch(components, Integer.valueOf(ordinal), - ordinalComparator); - } - if (idx >= 0) { - doDelete(idx); - } - else { - // assume unaligned removal of DEFAULT - idx = -idx - 1; - shiftOffsets(idx, -1, -1); + if (isPackingEnabled()) { + if (!repack(false, true)) { + dataMgr.dataTypeChanged(this, false); } } - adjustInternalAlignment(true); - notifySizeChanged(); + else { + structLength += offsetAdjustment; + if (bitFieldRemoved) { + repack(false, false); + } + notifySizeChanged(false); + } } finally { lock.release(); @@ -642,13 +684,14 @@ class StructureDB extends CompositeDB implements Structure { return flexibleArrayComponent; } if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } - if (isInternallyAligned()) { + if (isPackingEnabled()) { return components.get(ordinal); } int idx = - Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator); + Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); if (idx >= 0) { return components.get(idx); } @@ -688,7 +731,7 @@ class StructureDB extends CompositeDB implements Structure { /** * Create copy of structure for target dtm (source archive information is discarded). *

    - * WARNING! copying unaligned structures which contain bitfields can produce invalid results when + * WARNING! copying non-packed structures which contain bitfields can produce invalid results when * switching endianess due to the differences in packing order. * * @param dtm target data type manager @@ -705,7 +748,7 @@ class StructureDB extends CompositeDB implements Structure { /** * Create cloned structure for target dtm preserving source archive information. WARNING! - * cloning unaligned structures which contain bitfields can produce invalid results when + * cloning non-packed structures which contain bitfields can produce invalid results when * switching endianess due to the differences in packing order. * * @param dtm target data type manager @@ -714,7 +757,7 @@ class StructureDB extends CompositeDB implements Structure { @Override public Structure clone(DataTypeManager dtm) { StructureDataType struct = - new StructureDataType(getCategoryPath(), getName(), getLength(), getUniversalID(), + new StructureDataType(getCategoryPath(), getName(), structLength, getUniversalID(), getSourceArchive(), getLastChangeTime(), getLastChangeTimeInSourceArchive(), dtm); struct.setDescription(getDescription()); struct.replaceWith(this); @@ -727,16 +770,38 @@ class StructureDB extends CompositeDB implements Structure { } @Override - public int getAlignment() { - if (!isInternallyAligned()) { - return 1; // Unaligned + protected int getComputedAlignment(boolean updateRecord) { + if (structAlignment > 0) { + return structAlignment; } - if (alignment <= 0) { - // just in case - alignment should have been previously determined and stored - StructurePackResult packResult = AlignedStructureInspector.packComponents(this); - alignment = packResult.alignment; + if (computedAlignment <= 0) { + if (isPackingEnabled()) { + StructurePackResult packResult = AlignedStructureInspector.packComponents(this); + computedAlignment = packResult.alignment; + } + else { + computedAlignment = getNonPackedAlignment(); + } } - return alignment; + 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); + } + structAlignment = computedAlignment; + computedAlignment = -1; + return structAlignment; + } + return computedAlignment; + } + + @Override + public boolean isZeroLength() { + return structLength == 0; } @Override @@ -759,17 +824,16 @@ class StructureDB extends CompositeDB implements Structure { lock.acquire(); try { checkDeleted(); + if (isPackingEnabled()) { + delete(ordinal); + return; + } if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); - } - int idx; - if (isInternallyAligned()) { - idx = ordinal; - } - else { - idx = Collections.binarySearch(components, Integer.valueOf(ordinal), - ordinalComparator); + throw new IndexOutOfBoundsException(ordinal); } + + int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); if (idx >= 0) { DataTypeComponentDB dtc = components.remove(idx); dtc.getDataType().removeParent(this); @@ -781,12 +845,18 @@ class StructureDB extends CompositeDB implements Structure { } int len = dtc.getLength(); if (len > 1) { - shiftOffsets(idx, len - 1, 0); + shiftOffsets(idx, len - 1, 0); // updates timestamp } - adjustInternalAlignment(true); - dataMgr.dataTypeChanged(this); + else { + compositeAdapter.updateRecord(record, true); // update timestamp + } + repack(false, false); + dataMgr.dataTypeChanged(this, false); } } + catch (IOException e) { + dataMgr.dbError(e); + } finally { lock.release(); } @@ -797,7 +867,7 @@ class StructureDB extends CompositeDB implements Structure { * normal components the specified ordinal will be returned, however for bit-fields the ordinal * of the first bit-field containing the specified offset will be returned. * - * @param ordinal component ordinal + * @param index defined component index * @param offset offset within structure * @return index of first defined component containing specific offset. */ @@ -822,7 +892,7 @@ class StructureDB extends CompositeDB implements Structure { * normal components the specified ordinal will be returned, however for bit-fields the ordinal * of the last bit-field containing the specified offset will be returned. * - * @param ordinal component ordinal + * @param index defined component index * @param offset offset within structure * @return index of last defined component containing specific offset. */ @@ -851,7 +921,8 @@ class StructureDB extends CompositeDB implements Structure { return; } int index = - Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); + Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); int offsetDelta = 0; int ordinalDelta = 0; @@ -859,22 +930,21 @@ class StructureDB extends CompositeDB implements Structure { index = -index - 1; --ordinalDelta; offsetDelta = -1; - shiftOffsets(index, ordinalDelta, offsetDelta); + shiftOffsets(index, ordinalDelta, offsetDelta); // updates timestamp } else { index = advanceToLastComponentContainingOffset(index, offset); DataTypeComponentDB dtc = components.get(index); while (dtc.containsOffset(offset)) { - doDelete(index); + doDelete(index); // updates timestamp if (--index < 0) { break; } dtc = components.get(index); } } - - adjustInternalAlignment(true); - notifySizeChanged(); + repack(false, false); + notifySizeChanged(false); } finally { lock.release(); @@ -890,7 +960,8 @@ class StructureDB extends CompositeDB implements Structure { return null; } int index = - Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); + Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); if (index >= 0) { DataTypeComponent dtc = components.get(index); if (dtc.isBitFieldComponent()) { @@ -899,7 +970,7 @@ class StructureDB extends CompositeDB implements Structure { } return dtc; } - else if (isInternallyAligned()) { + else if (isPackingEnabled()) { return null; } index = -index - 1; @@ -981,13 +1052,14 @@ class StructureDB extends CompositeDB implements Structure { dataType = resolve(dataType); checkAncestry(dataType); - if ((offset > structLength) && !isInternallyAligned()) { + if ((offset > structLength) && !isPackingEnabled()) { numComponents = numComponents + (offset - structLength); structLength = offset; } int index = - Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); + Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); int additionalShift = 0; if (index >= 0) { @@ -1006,10 +1078,10 @@ class StructureDB extends CompositeDB implements Structure { } if (dataType == DataType.DEFAULT) { - // assume unaligned insert of DEFAULT + // assume non-packed insert of DEFAULT shiftOffsets(index, 1 + additionalShift, 1 + additionalShift); - adjustInternalAlignment(true); - notifySizeChanged(); + repack(false, false); + notifySizeChanged(false); return new DataTypeComponentDB(dataMgr, componentAdapter, this, key, ordinal, offset); } @@ -1022,8 +1094,8 @@ class StructureDB extends CompositeDB implements Structure { DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); shiftOffsets(index, 1 + additionalShift, dtc.getLength() + additionalShift); components.add(index, dtc); - adjustInternalAlignment(true); - notifySizeChanged(); + repack(false, false); + notifySizeChanged(false); return dtc; } catch (DataTypeDependencyException e) { @@ -1040,13 +1112,13 @@ class StructureDB extends CompositeDB implements Structure { @Override public DataTypeComponent replace(int ordinal, DataType dataType, int length, String name, - String comment) throws IllegalArgumentException { + String comment) { lock.acquire(); try { checkDeleted(); if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } validateDataType(dataType); @@ -1068,13 +1140,22 @@ class StructureDB extends CompositeDB implements Structure { length = getPreferredComponentLength(dataType, length); DataTypeComponent replaceComponent = - replaceComponent(origDtc, dataType, length, name, comment, true); - adjustInternalAlignment(true); + replaceComponent(origDtc, dataType, length, name, comment); + + repack(false, false); + record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); + compositeAdapter.updateRecord(record, true); + notifySizeChanged(false); + return replaceComponent; } catch (DataTypeDependencyException e) { throw new IllegalArgumentException(e.getMessage(), e); } + catch (IOException e) { + dataMgr.dbError(e); + return null; + } finally { lock.release(); } @@ -1121,14 +1202,22 @@ class StructureDB extends CompositeDB implements Structure { length = getPreferredComponentLength(dataType, length); DataTypeComponent replaceComponent = - replaceComponent(origDtc, dataType, length, name, comment, true); + replaceComponent(origDtc, dataType, length, name, comment); + + repack(false, false); + record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); + compositeAdapter.updateRecord(record, true); + notifySizeChanged(false); - adjustInternalAlignment(true); return replaceComponent; } catch (DataTypeDependencyException e) { throw new IllegalArgumentException(e.getMessage(), e); } + catch (IOException e) { + dataMgr.dbError(e); + return null; + } finally { lock.release(); } @@ -1146,14 +1235,14 @@ class StructureDB extends CompositeDB implements Structure { */ @Override public void replaceWith(DataType dataType) { - if (!(dataType instanceof Structure)) { + if (!(dataType instanceof StructureInternal)) { throw new IllegalArgumentException(); } lock.acquire(); boolean isResolveCacheOwner = dataMgr.activateResolveCache(); try { checkDeleted(); - doReplaceWith((Structure) dataType, true); + doReplaceWith((StructureInternal) dataType, true); } catch (DataTypeDependencyException e) { throw new IllegalArgumentException(e.getMessage(), e); @@ -1170,15 +1259,15 @@ class StructureDB extends CompositeDB implements Structure { } /** - * Perform component replacement. + * Replaces the internal components of this structure with components of the given structure + * including packing and alignment settings. * - * @param struct - * @param notify - * @return true if fully completed else false if pointer component post resolve required - * @throws DataTypeDependencyException - * @throws IOException + * @param struct structure to be copied + * @param notify provide notification if true + * @throws DataTypeDependencyException if circular dependency detected + * @throws IOException if database IO error occurs */ - void doReplaceWith(Structure struct, boolean notify) + void doReplaceWith(StructureInternal struct, boolean notify) throws DataTypeDependencyException, IOException { // pre-resolved component types to catch dependency issues early @@ -1197,16 +1286,10 @@ class StructureDB extends CompositeDB implements Structure { } } - int oldLength = structLength; - int oldMinAlignment = getMinimumAlignment(); - for (DataTypeComponentDB dtc : components) { dtc.getDataType().removeParent(this); componentAdapter.removeRecord(dtc.getKey()); } - components.clear(); - numComponents = 0; - structLength = 0; if (flexibleArrayComponent != null) { flexibleArrayComponent.getDataType().removeParent(this); @@ -1214,9 +1297,15 @@ class StructureDB extends CompositeDB implements Structure { flexibleArrayComponent = null; } - setAlignment(struct, false); + components.clear(); + numComponents = 0; + structLength = 0; + structAlignment = -1; + computedAlignment = -1; - if (struct.isInternallyAligned()) { + doSetPackingAndAlignment(struct); // updates timestamp + + if (struct.isPackingEnabled()) { doReplaceWithAligned(struct, resolvedDts); } else { @@ -1228,23 +1317,16 @@ class StructureDB extends CompositeDB implements Structure { false); } + repack(false, false); + + // must force record update record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents); record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); - - compositeAdapter.updateRecord(record, false); - - adjustInternalAlignment(false); + record.setIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL, structAlignment); + compositeAdapter.updateRecord(record, notify); if (notify) { - if (oldMinAlignment != getMinimumAlignment()) { - notifyAlignmentChanged(); - } - else if (oldLength != structLength) { - notifySizeChanged(); - } - else { - dataMgr.dataTypeChanged(this); - } + notifySizeChanged(false); } if (pointerPostResolveRequired) { @@ -1271,7 +1353,7 @@ class StructureDB extends CompositeDB implements Structure { private void doReplaceWithUnaligned(Structure struct, DataType[] resolvedDts) throws IOException { // assumes components is clear and that alignment characteristics have been set. - if (struct.isNotYetDefined()) { + if (struct.isZeroLength()) { return; } @@ -1308,7 +1390,7 @@ class StructureDB extends CompositeDB implements Structure { new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); components.add(newDtc); } - adjustComponents(false); + repack(false, false); } @Override @@ -1338,12 +1420,12 @@ class StructureDB extends CompositeDB implements Structure { lock.acquire(); try { checkDeleted(); - boolean didChange = false; + boolean changed = false; if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == dt) { flexibleArrayComponent.getDataType().removeParent(this); componentAdapter.removeRecord(flexibleArrayComponent.getKey()); flexibleArrayComponent = null; - didChange = true; + changed = true; } int n = components.size(); for (int i = n - 1; i >= 0; i--) { @@ -1355,15 +1437,16 @@ class StructureDB extends CompositeDB implements Structure { } if (removeBitFieldComponent || dtc.getDataType() == dt) { dt.removeParent(this); +// FIXME: Consider replacing with undefined type instead of removing (don't remove bitfield) components.remove(i); shiftOffsets(i, dtc.getLength() - 1, 0); // ordinals only componentAdapter.removeRecord(dtc.getKey()); - didChange = true; + --numComponents; // may be revised by repack + changed = true; } } - if (didChange) { - adjustInternalAlignment(true); - notifySizeChanged(); + if (changed && !repack(false, true)) { + dataMgr.dataTypeChanged(this, false); } } catch (IOException e) { @@ -1382,18 +1465,20 @@ class StructureDB extends CompositeDB implements Structure { lock.acquire(); try { checkDeleted(); - if (isInternallyAligned()) { - adjustComponents(true); // notifies parents + if (isPackingEnabled()) { + if (!repack(true, true)) { + dataMgr.dataTypeChanged(this, true); + } return; } - boolean didChange = false; + int oldLength = structLength; + boolean changed = false; boolean warn = false; int n = components.size(); for (int i = 0; i < n; i++) { DataTypeComponentDB dtc = components.get(i); if (dtc.getDataType() == dt) { - // assume no impact to bitfields since base types - // should not change size + // assume no impact to bitfields since base types should not change size int dtcLen = dtc.getLength(); int length = dt.getLength(); if (length <= 0) { @@ -1402,14 +1487,14 @@ class StructureDB extends CompositeDB implements Structure { if (length < dtcLen) { dtc.setLength(length, true); shiftOffsets(i + 1, dtcLen - length, 0); - didChange = true; + changed = true; } else if (length > dtcLen) { int consumed = consumeBytesAfter(i, length - dtcLen); if (consumed > 0) { dtc.updateRecord(); shiftOffsets(i + 1, -consumed, 0); - didChange = true; + changed = true; } } if (dtc.getLength() != length) { @@ -1417,14 +1502,19 @@ class StructureDB extends CompositeDB implements Structure { } } } - if (didChange) { - adjustInternalAlignment(false); - notifySizeChanged(); // notifies parents - } if (warn) { Msg.warn(this, "Failed to resize one or more structure components: " + getPathName()); } + if (changed) { + repack(false, false); + if (oldLength != structLength) { + notifySizeChanged(false); + } + else { + dataMgr.dataTypeChanged(this, false); + } + } } finally { lock.release(); @@ -1432,14 +1522,8 @@ class StructureDB extends CompositeDB implements Structure { } @Override - protected void fixupComponents() { - if (isInternallyAligned()) { - // Do not notify parents - if (adjustComponents(false)) { - dataMgr.dataTypeChanged(this); - } - return; - } + protected void fixupComponents() throws IOException { + boolean isPacked = isPackingEnabled(); boolean didChange = false; boolean warn = false; int n = components.size(); @@ -1456,7 +1540,11 @@ class StructureDB extends CompositeDB implements Structure { length = dtcLen; } if (dtcLen != length) { - if (length < dtcLen) { + if (isPacked) { + dtc.setLength(length, true); + didChange = true; + } + else if (length < dtcLen) { dtc.setLength(length, true); shiftOffsets(i + 1, dtcLen - length, 0); didChange = true; @@ -1475,9 +1563,10 @@ class StructureDB extends CompositeDB implements Structure { } } if (didChange) { - // Do not notify parents - adjustInternalAlignment(false); - dataMgr.dataTypeChanged(this); + // Do not notify parents - must be invoked in composite dependency order + repack(false, false); + compositeAdapter.updateRecord(record, true); + dataMgr.dataTypeChanged(this, false); } if (warn) { Msg.warn(this, "Failed to resize one or more structure components: " + getPathName()); @@ -1488,8 +1577,10 @@ class StructureDB extends CompositeDB implements Structure { public void dataTypeAlignmentChanged(DataType dt) { lock.acquire(); try { - checkDeleted(); - adjustInternalAlignment(true); + if (isPackingEnabled()) { + checkDeleted(); + repack(true, true); + } } finally { lock.release(); @@ -1501,7 +1592,7 @@ class StructureDB extends CompositeDB implements Structure { if (dataType == this) { return true; } - if (!(dataType instanceof Structure)) { + if (!(dataType instanceof StructureInternal)) { return false; } @@ -1520,13 +1611,12 @@ class StructureDB extends CompositeDB implements Structure { try { isEquivalent = false; - Structure struct = (Structure) dataType; - if (isInternallyAligned() != struct.isInternallyAligned() || - isDefaultAligned() != struct.isDefaultAligned() || - isMachineAligned() != struct.isMachineAligned() || - getMinimumAlignment() != struct.getMinimumAlignment() || - getPackingValue() != struct.getPackingValue() || - (!isInternallyAligned() && (getLength() != struct.getLength()))) { + StructureInternal struct = (StructureInternal) dataType; + int otherLength = struct.isZeroLength() ? 0 : struct.getLength(); + int packing = getStoredPackingValue(); + if (packing != struct.getStoredPackingValue() || + getStoredMinimumAlignment() != struct.getStoredMinimumAlignment() || + (packing == NO_PACKING && structLength != otherLength)) { return false; } @@ -1611,7 +1701,7 @@ class StructureDB extends CompositeDB implements Structure { shiftOffset(dtc, deltaOrdinal, deltaOffset); } structLength += deltaOffset; - if (!isInternallyAligned()) { + if (!isPackingEnabled()) { numComponents += deltaOrdinal; } record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents); @@ -1640,78 +1730,63 @@ class StructureDB extends CompositeDB implements Structure { * @param name the field name of the new component * @param comment the comment for the new component * @return the new component or null if the new component couldn't fit. + * @throws IOException if database IO error occurs */ private DataTypeComponent replaceComponent(DataTypeComponent origDtc, DataType resolvedDataType, - int length, String name, String comment, boolean doNotify) { + int length, String name, String comment) throws IOException { -// FIXME: Unsure how to support replace operation with bit-fields. Within unaligned structure +// FIXME: Unsure how to support replace operation with bit-fields. Within non-packed structure // the packing behavior for bit-fields prevents a one-for-one replacement and things may shift -// around which the unaligned structure tries to avoid. Insert and delete are less of a concern +// around which the non-packed structure tries to avoid. Insert and delete are less of a concern // since movement already can occur, although insert at offset may not retain the offset if it // interacts with bit-fields. - try { - int ordinal = origDtc.getOrdinal(); - int newOffset = origDtc.getOffset(); - int dtcLength = origDtc.getLength(); - int bytesNeeded = length - dtcLength; - int deltaOrdinal = -bytesNeeded; - int origStructLength = structLength; - if (!isInternallyAligned() && bytesNeeded > 0) { - int bytesAvailable = getNumUndefinedBytes(ordinal + 1); - if (bytesAvailable < bytesNeeded) { - if (ordinal == getLastDefinedComponentIndex()) { - growStructure(bytesNeeded - bytesAvailable); - } - else { - throw new IllegalArgumentException("Not enough undefined bytes to fit " + - resolvedDataType.getPathName() + " in structure " + getPathName() + - " at offset 0x" + Integer.toHexString(newOffset) + "." + " It needs " + - (bytesNeeded - bytesAvailable) + " more byte(s) to be able to fit."); - } + int ordinal = origDtc.getOrdinal(); + int newOffset = origDtc.getOffset(); + int dtcLength = origDtc.getLength(); + int bytesNeeded = length - dtcLength; + int deltaOrdinal = -bytesNeeded; + if (!isPackingEnabled() && bytesNeeded > 0) { + int bytesAvailable = getNumUndefinedBytes(ordinal + 1); + if (bytesAvailable < bytesNeeded) { + if (ordinal == getLastDefinedComponentIndex()) { + growStructure(bytesNeeded - bytesAvailable); + } + else { + throw new IllegalArgumentException("Not enough undefined bytes to fit " + + resolvedDataType.getPathName() + " in structure " + getPathName() + + " at offset 0x" + Integer.toHexString(newOffset) + "." + " It needs " + + (bytesNeeded - bytesAvailable) + " more byte(s) to be able to fit."); } } - DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(resolvedDataType), key, - length, ordinal, newOffset, name, comment); - resolvedDataType.addParent(this); - DataTypeComponentDB newDtc = - new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); - int index; - if (isInternallyAligned()) { - index = ordinal; - } - else { - index = Collections.binarySearch(components, Integer.valueOf(ordinal), - ordinalComparator); - } - if (index < 0) { - index = -index - 1; - } - else { - DataTypeComponentDB dataTypeComponentDB = components.get(index); // TODO Remove this. - dataTypeComponentDB.getDataType().removeParent(this); - DataTypeComponentDB dtc = components.remove(index); - componentAdapter.removeRecord(dtc.getKey()); - } - components.add(index, newDtc); - if (deltaOrdinal != 0) { - shiftOffsets(index + 1, deltaOrdinal, 0); - } - if (structLength != origStructLength) { - record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); - compositeAdapter.updateRecord(record, true); - adjustInternalAlignment(false); - notifySizeChanged(); - } - else if (doNotify) { - dataMgr.dataTypeChanged(this); - } - return newDtc; } - catch (IOException e) { - dataMgr.dbError(e); + DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(resolvedDataType), key, + length, ordinal, newOffset, name, comment); + resolvedDataType.addParent(this); + DataTypeComponentDB newDtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec); + int index; + if (isPackingEnabled()) { + index = ordinal; } - return null; + else { + index = + Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); + } + if (index < 0) { + index = -index - 1; + } + else { + DataTypeComponentDB dataTypeComponentDB = components.get(index); // TODO Remove this. + dataTypeComponentDB.getDataType().removeParent(this); + DataTypeComponentDB dtc = components.remove(index); + componentAdapter.removeRecord(dtc.getKey()); + } + components.add(index, newDtc); + if (deltaOrdinal != 0) { + shiftOffsets(index + 1, deltaOrdinal, 0); + } + return newDtc; } /** @@ -1722,13 +1797,14 @@ class StructureDB extends CompositeDB implements Structure { * @return the number of contiguous undefined bytes */ private int getNumUndefinedBytes(int ordinal) { - if (isInternallyAligned()) { + if (isPackingEnabled()) { return 0; } if (ordinal >= numComponents) { return 0; } - int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator); + int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); DataTypeComponentDB dtc = null; if (idx < 0) { idx = -idx - 1; @@ -1804,7 +1880,7 @@ class StructureDB extends CompositeDB implements Structure { } } else if (comp.getDataType() == oldDt) { - if (replacementDt == DEFAULT && isInternallyAligned()) { + if (replacementDt == DEFAULT && isPackingEnabled()) { Msg.error(this, "Invalid replacement type " + newDt.getName() + ", removing component " + comp.getDataType().getName() + ": " + @@ -1826,8 +1902,9 @@ class StructureDB extends CompositeDB implements Structure { } } if (changed) { - adjustInternalAlignment(false); - notifySizeChanged(); + repack(false, false); + compositeAdapter.updateRecord(record, true); // update timestamp + notifySizeChanged(false); } } catch (IOException e) { @@ -1841,38 +1918,44 @@ class StructureDB extends CompositeDB implements Structure { private void setComponentDataType(DataTypeComponentDB comp, DataType replacementDt, int nextIndex) { - comp.getDataType().removeParent(this); - comp.setDataType(replacementDt); - replacementDt.addParent(this); - - if (isInternallyAligned()) { - return; // caller must invoke adjustInternalAlignment + int oldLen = comp.getLength(); + int len = replacementDt.getLength(); + if (len < 1) { + len = oldLen; } - int len = replacementDt.getLength(); - int oldLen = comp.getLength(); - if (len > 0) { - if (len < oldLen) { + comp.getDataType().removeParent(this); + + if (isPackingEnabled()) { + comp.setLength(len, false); // do before record save below + } + comp.setDataType(replacementDt); // saves component record + replacementDt.addParent(this); + + if (isPackingEnabled()) { + return; + } + + if (len < oldLen) { + comp.setLength(len, true); + shiftOffsets(nextIndex, oldLen - len, 0); + } + else if (len > oldLen) { + int bytesAvailable = getNumUndefinedBytes(comp.getOrdinal() + 1); + int bytesNeeded = len - oldLen; + if (bytesNeeded <= bytesAvailable) { comp.setLength(len, true); - shiftOffsets(nextIndex, oldLen - len, 0); + shiftOffsets(nextIndex, -bytesNeeded, 0); } - else if (len > oldLen) { - int bytesAvailable = getNumUndefinedBytes(comp.getOrdinal() + 1); - int bytesNeeded = len - oldLen; - if (bytesNeeded <= bytesAvailable) { - comp.setLength(len, true); - shiftOffsets(nextIndex, -bytesNeeded, 0); - } - else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { - // we are the last defined component, grow structure - doGrowStructure(bytesNeeded - bytesAvailable); - comp.setLength(len, true); - shiftOffsets(nextIndex, -bytesNeeded, 0); - } - else { - comp.setLength(oldLen + bytesAvailable, true); - shiftOffsets(nextIndex, -bytesAvailable, 0); - } + else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { + // we are the last defined component, grow structure + doGrowStructure(bytesNeeded - bytesAvailable); + comp.setLength(len, true); + shiftOffsets(nextIndex, -bytesNeeded, 0); + } + else { + comp.setLength(oldLen + bytesAvailable, true); + shiftOffsets(nextIndex, -bytesAvailable, 0); } } } @@ -1909,8 +1992,7 @@ class StructureDB extends CompositeDB implements Structure { record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, 0); record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, 0); compositeAdapter.updateRecord(record, true); - adjustInternalAlignment(true); - notifySizeChanged(); + notifySizeChanged(false); } catch (IOException e) { dataMgr.dbError(e); @@ -1921,74 +2003,56 @@ class StructureDB extends CompositeDB implements Structure { } /** - * ComponentComparator provides ability to compare two DataTypeComponent objects - * based upon their ordinal. Intended to be used to sort components based upon ordinal. + * Perform structure member repack. + * Perform lazy update of stored alignment introduced with v5 adapter. */ - private static class ComponentComparator implements Comparator { - @Override - public int compare(DataTypeComponent dtc1, DataTypeComponent dtc2) { - return dtc1.getOrdinal() - dtc2.getOrdinal(); - } - } - - /** - * Adjust the alignment, packing and padding of components within this structure based upon the - * current alignment and packing attributes for this structure. This method should be called to - * basically fix up the layout of the internal components of the structure after other code has - * changed the attributes of the structure.
    - * When switching between internally aligned and unaligned this method corrects the component - * ordinal numbering also. - * - * @param notify if true this method will do data type change notification when it changes the - * layout of the components or when it changes the overall size of the structure. - * @return true if the structure was changed by this method. - */ - private boolean adjustComponents(boolean notify) { + @Override + protected boolean repack(boolean isAutoChange, boolean notify) { lock.acquire(); try { checkDeleted(); - boolean changed = false; - alignment = -1; - - if (!isInternallyAligned()) { - changed |= adjustUnalignedComponents(); - if (notify && changed) { - dataMgr.dataTypeChanged(this); - } - return changed; - } - int oldLength = structLength; - - StructurePackResult packResult = - AlignedStructurePacker.packComponents(this, components); - changed = packResult.componentsChanged; - - // Adjust the structure - changed |= updateComposite(packResult.numComponents, packResult.structureLength, - packResult.alignment, false); - - if (changed) { - if (notify) { - if (oldLength != structLength) { - notifySizeChanged(); - } - else { - dataMgr.dataTypeChanged(this); - } - } - return true; + int oldAlignment = getComputedAlignment(true); // ensure that alignment has been stored + + computedAlignment = -1; // clear cached alignment + + boolean changed; + if (!isPackingEnabled()) { + changed = adjustNonPackedComponents(!isAutoChange); } - return false; + else { + StructurePackResult packResult = + AlignedStructurePacker.packComponents(this, components); + changed = packResult.componentsChanged; + changed |= updateComposite(packResult.numComponents, packResult.structureLength, + packResult.alignment, !isAutoChange); + } + + if (changed && notify) { + if (oldLength != structLength) { + notifySizeChanged(isAutoChange); + } + else if (oldAlignment != structAlignment) { + notifyAlignmentChanged(isAutoChange); + } + dataMgr.dataTypeChanged(this, isAutoChange); + } + return changed; } finally { lock.release(); } } - private boolean adjustUnalignedComponents() { + /** + * Updates non-packed component ordinals and numComponents. + * If numComponents changes record update will be performed + * with new timestamp. + * @return true if change detected else false + */ + private boolean adjustNonPackedComponents(boolean setLastChangeTime) { boolean changed = false; int componentCount = 0; int currentOffset = 0; @@ -2006,11 +2070,11 @@ class StructureDB extends CompositeDB implements Structure { } componentCount++; } + int numUndefinedsAfter = structLength - currentOffset; componentCount += numUndefinedsAfter; - if (updateNumComponents(componentCount)) { - changed = true; - } + changed |= updateComposite(componentCount, structLength, getNonPackedAlignment(), + setLastChangeTime); return changed; } @@ -2039,6 +2103,7 @@ class StructureDB extends CompositeDB implements Structure { if (numComponents != currentNumComponents) { numComponents = currentNumComponents; record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents); + setLastChangeTime = true; compositeChanged = true; } if (structLength != currentLength) { @@ -2046,8 +2111,9 @@ class StructureDB extends CompositeDB implements Structure { record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength); compositeChanged = true; } - if (alignment != currentAlignment) { - alignment = currentAlignment; + if (structAlignment != currentAlignment) { + structAlignment = currentAlignment; + record.setIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL, structAlignment); compositeChanged = true; } if (compositeChanged) { @@ -2062,23 +2128,6 @@ class StructureDB extends CompositeDB implements Structure { return false; } - @Override - public void realign() { - if (isInternallyAligned()) { - adjustInternalAlignment(true); - } - } - - @Override - public void pack(int packingSize) { - setPackingValue(packingSize); - } - - @Override - protected void adjustInternalAlignment(boolean notify) { - adjustComponents(notify); - } - @Override public boolean hasFlexibleArrayComponent() { return flexibleArrayComponent != null; @@ -2128,30 +2177,16 @@ class StructureDB extends CompositeDB implements Structure { catch (IOException e) { dataMgr.dbError(e); } - adjustInternalAlignment(true); - notifySizeChanged(); + repack(false, false); + compositeAdapter.updateRecord(record, true); + notifySizeChanged(false); + } + catch (IOException e) { + dataMgr.dbError(e); } finally { lock.release(); } } - @Override - protected void dumpComponents(StringBuilder buffer, String pad) { - super.dumpComponents(buffer, pad); - DataTypeComponent dtc = 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"); - } - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java index 054ebb7bc9..44088e7654 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java index 3a069e2aa0..be09f16686 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java @@ -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 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 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 ordinals) { + if (ordinals.isEmpty()) { + return; + } + + lock.acquire(); + try { + checkDeleted(); + + if (isPackingEnabled()) { + getComputedAlignment(true); // ensure previous alignment has been stored + } + + List 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 { - @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); - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java index 05b433dba6..dd767e8b15 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java @@ -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 diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractFloatDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractFloatDataType.java index c316674273..c32d402abb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractFloatDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractFloatDataType.java @@ -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(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedComponentPacker.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedComponentPacker.java index 2aa9e1d85f..cd79ff9e9e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedComponentPacker.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedComponentPacker.java @@ -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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java index 71d8178b12..57842c80f5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructureInspector.java @@ -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 getComponentWrappers(Structure structure) { List 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(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java index 78525c8db2..6ca069481d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignedStructurePacker.java @@ -27,7 +27,7 @@ import java.util.List; */ public class AlignedStructurePacker { - private final Structure structure; + private final StructureInternal structure; private final List 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 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 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 components) { AlignedStructurePacker packer = new AlignedStructurePacker(structure, components); return packer.pack(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignmentType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignmentType.java new file mode 100644 index 0000000000..96665a19ff --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AlignmentType.java @@ -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; + +/** + * AlignmentType 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 { + /** + * DEFAULT - 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, + /** + * MACHINE - 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, + /** + * MACHINE - 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; +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java index fc39685ced..60913b8964 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ArrayDataType.java @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java index dd4887bd7e..ae01c91e41 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Composite.java @@ -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. - *
    This controls how this data type will be aligned within other data types. - *
    Default Aligned 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. - *
    Machine Aligned means this data type's alignment will use a minimum alignment that - * is the machine alignment specified by the data organization. - *
    Align By Value 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. - *
    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. - *
    Note: Removal of bitfields from an unaligned structure will - * not shift other components with vacated bytes reverting to undefined. + *
    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. - *
    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. + *
    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 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. + *
    + * 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. - *
    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: + *

      + *
    • Structures utilize explicit component offsets and produce undefined filler + * components where defined components do not consume space.
    • + *
    • Unions always place components at offset 0 and do not pad for alignment. + *
    + * 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(); + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java index 51e8f63007..beedd5cdfa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeAlignmentHelper.java @@ -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); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java index 810734a006..0e7295c295 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeDataTypeImpl.java @@ -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. + *

    + * NOTE: If modifications to stored length are made prior to invoking this method, + * detection of a size change may not be possible. + *

    + * 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(); + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeInternal.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeInternal.java new file mode 100644 index 0000000000..7742102263 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CompositeInternal.java @@ -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). + *
    + * Special packing values which may be specified include: + *

      + *
    • {@value #DEFAULT_PACKING} will perform default packing based upon the alignment + * requirements of the individual components.
    • + *
    • {@link #NO_PACKING} (or any negative value) will disable packing
    • + *
    + * @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(); + + /** + * ComponentComparator 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 { + + public static final ComponentComparator INSTANCE = new ComponentComparator(); + + @Override + public int compare(DataTypeComponent dtc1, DataTypeComponent dtc2) { + return dtc1.getOrdinal() - dtc2.getOrdinal(); + } + } + + /** + * OffsetComparator 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 { + + 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; + } + + } + + /** + * OrdinalComparator 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. + *

    + */ + public static class OrdinalComparator implements Comparator { + + 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; + } + + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java index f99045fb88..d3c4bacc23 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganization.java @@ -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); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java index c4543a9c75..efc8d0cd22 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataOrganizationImpl.java @@ -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 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 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("\n"); - for (int key : keys) { + for (int key : sizeAlignmentMap.keySet()) { 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 keys = sizeAlignmentMap.keySet(); + Set 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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java index ef9f8ed813..285a4004f5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java @@ -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. *
    - * 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. *
    * @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. + *
    + * TODO: This method is reserved for internal DB use. + *
    + * @param dt the dataType that has changed. + */ + public void dataTypeAlignmentChanged(DataType dt); + /** * Informs this dataType that the given dataType has been deleted. *
    - * 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. *
    * @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 *
    - * 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. *
    * @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. *
    - * 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. *
    * @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 *
    - * 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. *
    * @param dt parent data type */ @@ -328,8 +350,7 @@ public interface DataType { /** * Remove a parent data type *
    - * 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. *
    * @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. *
    - * 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. *
    * @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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java index 464511a870..6dcbd43876 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeComponentImpl.java @@ -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 diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeImpl.java index e7dd5aeea4..57116ad57e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeImpl.java @@ -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 parentList; + private List> 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> iterator = parentList.iterator(); + while (iterator.hasNext()) { + WeakReference 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 parents = new ArrayList<>(); + Iterator> iterator = parentList.iterator(); + while (iterator.hasNext()) { + WeakReference 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 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 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 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 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 consumer) { + Iterator> iterator = parentList.iterator(); + while (iterator.hasNext()) { + WeakReference ref = iterator.next(); + DataType dataType = ref.get(); + if (dataType == null) { + iterator.remove(); + } + else { + consumer.accept(dataType); + } } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java index d03c5c938f..b585844da4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java @@ -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. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java index 1e336b6f9a..4bcbe8e474 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java @@ -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(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/GenericDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/GenericDataType.java index 421a6ce21c..f751925721 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/GenericDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/GenericDataType.java @@ -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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PackingType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PackingType.java new file mode 100644 index 0000000000..d9d618591d --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PackingType.java @@ -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; + +/** + * PackingType specifies the pack setting which applies to a composite data type. + * This can be DISABLED, DEFAULT, EXPLICIT. + */ +public enum PackingType { + /** + * DISABLED - indicates that automatic component placement should not be performed, with + * components placed at specified offsets and undefined 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, + /** + * DEFAULT - 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, + /** + * EXPLICIT - 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; +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java index ad0e66a975..215a1e2d30 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ReadOnlyDataTypeComponent.java @@ -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? diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java index b96be093ea..e470429cf4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Structure.java @@ -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. *

    - * 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. *

    - * 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. *

    - * 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. *

    - * 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. *

    * - * 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. *

    * @@ -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 <= 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 * <= 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 { * <= 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. + *

    + * 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 < 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); - /** * BitOffsetComparator 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 { + public static final Comparator INSTANCE_LE = new BitOffsetComparator(false); + public static final Comparator INSTANCE_BE = new BitOffsetComparator(true); + private boolean bigEndian; public BitOffsetComparator(boolean bigEndian) { @@ -439,47 +439,4 @@ public interface Structure extends Composite { } - /** - * OffsetComparator 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 { - - @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; - } - - } - - /** - * OrdinalComparator 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 { - - @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; - } - - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java index 3ee3002c1a..4433e44031 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java @@ -25,19 +25,18 @@ import ghidra.util.UniversalID; import ghidra.util.exception.AssertException; /** - * Basic implementation of the structure data type + * Basic implementation of the structure data type. + * NOTE: Implementation is not thread safe when being modified. */ -public class StructureDataType extends CompositeDataTypeImpl implements Structure { - private final static long serialVersionUID = 1; - private static Comparator ordinalComparator = new OrdinalComparator(); - protected static Comparator offsetComparator = new OffsetComparator(); - protected static Comparator bitOffsetComparatorLE = new BitOffsetComparator(false); - protected static Comparator bitOffsetComparatorBE = new BitOffsetComparator(true); +public class StructureDataType extends CompositeDataTypeImpl implements StructureInternal { + protected int structLength; + private int structAlignment; + protected int numComponents; // excludes optional flexible array component protected List components; + private DataTypeComponentImpl flexibleArrayComponent; - private int alignment = -1; /** * Construct a new structure with the given name and length. The root category will be used. @@ -133,19 +132,23 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public boolean isNotYetDefined() { - return structLength == 0 && flexibleArrayComponent == null; + return structLength == 0 && flexibleArrayComponent == null && + isDefaultAligned() && !isPackingEnabled(); } @Override public int getAlignment() { - if (!isInternallyAligned()) { - return 1; // Unaligned + if (structAlignment > 0) { + return structAlignment; } - if (alignment <= 0) { + if (isPackingEnabled()) { StructurePackResult packResult = AlignedStructureInspector.packComponents(this); - alignment = packResult.alignment; + structAlignment = packResult.alignment; } - return alignment; + else { + structAlignment = getNonPackedAlignment(); + } + return structAlignment; } @Override @@ -153,7 +156,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (offset >= structLength || offset < 0) { return null; } - int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); + int index = Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); if (index >= 0) { DataTypeComponent dtc = components.get(index); if (dtc.isBitFieldComponent()) { @@ -162,7 +166,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } return dtc; } - else if (isInternallyAligned()) { + else if (isPackingEnabled()) { return null; } index = -index - 1; @@ -186,10 +190,15 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return dtc; } + @Override + public boolean isZeroLength() { + return structLength == 0; + } + @Override public int getLength() { if (structLength == 0) { - return 1; // lie about our length if not yet defined + return 1; // 0-length datatype not supported } return structLength; } @@ -197,31 +206,32 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public void delete(int ordinal) { if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } int idx; - if (isInternallyAligned()) { + if (isPackingEnabled()) { idx = ordinal; } else { - idx = Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator); + idx = Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); } if (idx >= 0) { doDelete(idx); - adjustInternalAlignment(); } else { - // assume unaligned removal of DEFAULT + // assume non-packed removal of DEFAULT idx = -idx - 1; shiftOffsets(idx, -1, -1); } + repack(false); notifySizeChanged(); } private void doDelete(int index) { DataTypeComponentImpl dtc = components.remove(index); dtc.getDataType().removeParent(this); - if (isInternallyAligned()) { + if (isPackingEnabled()) { return; } int shiftAmount = dtc.isBitFieldComponent() ? 0 : dtc.getLength(); @@ -229,40 +239,79 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public void delete(int[] ordinals) { + public void delete(Set ordinals) { - for (int ordinal : ordinals) { - if (ordinal < 0 || ordinal >= numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + if (ordinals.isEmpty()) { + return; + } + + boolean bitFieldRemoved = false; + + TreeSet treeSet = null; + if (!isPackingEnabled()) { + // treeSet only used to track undefined filler removal + treeSet = new TreeSet<>(ordinals); + } + + List newComponents = new ArrayList<>(); + int ordinalAdjustment = 0; + int offsetAdjustment = 0; + int lastDefinedOrdinal = -1; + for (DataTypeComponentImpl dtc : components) { + int ordinal = dtc.getOrdinal(); + if (treeSet != null && lastDefinedOrdinal < (ordinal - 1)) { + // Identify removed filler since last defined component + Set removedFillerSet = treeSet.subSet(lastDefinedOrdinal + 1, ordinal); + if (!removedFillerSet.isEmpty()) { + int undefinedRemoveCount = removedFillerSet.size(); + ordinalAdjustment -= undefinedRemoveCount; + offsetAdjustment -= undefinedRemoveCount; + } + } + if (ordinals.contains(ordinal)) { + // defined component removed + if (dtc.isBitFieldComponent()) { + // defer reconciling bitfield space to repack + bitFieldRemoved = true; + } + else { + offsetAdjustment -= dtc.getLength(); + } + --ordinalAdjustment; + lastDefinedOrdinal = ordinal; + } + else { + + if (ordinalAdjustment != 0) { + shiftOffset(dtc, ordinalAdjustment, offsetAdjustment); + } + newComponents.add(dtc); + lastDefinedOrdinal = ordinal; + } + } + if (treeSet != null) { + // Identify removed filler after last defined component + Set removedFillerSet = treeSet.subSet(lastDefinedOrdinal + 1, numComponents); + if (!removedFillerSet.isEmpty()) { + int undefinedRemoveCount = removedFillerSet.size(); + ordinalAdjustment -= undefinedRemoveCount; + offsetAdjustment -= undefinedRemoveCount; } } - // delete ordinals in reverse order so that they remain valid - // during individual deletes - int[] sortedOrdinals = ordinals.clone(); - Arrays.sort(sortedOrdinals); + components = newComponents; + numComponents += ordinalAdjustment; - for (int i = sortedOrdinals.length - 1; i >= 0; i--) { - int ordinal = sortedOrdinals[i]; - int idx; - if (isInternallyAligned()) { - idx = ordinal; - } - else { - idx = Collections.binarySearch(components, Integer.valueOf(ordinal), - ordinalComparator); - } - if (idx >= 0) { - doDelete(idx); - } - else { - // assume unaligned removal of DEFAULT - idx = -idx - 1; - shiftOffsets(idx, -1, -1); - } + if (isPackingEnabled()) { + repack(true); + } + else { + structLength += offsetAdjustment; + if (bitFieldRemoved) { + repack(false); + } + notifySizeChanged(); } - adjustInternalAlignment(); - notifySizeChanged(); } private void shiftOffsets(int index, int deltaOrdinal, int deltaOffset) { @@ -285,13 +334,14 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return flexibleArrayComponent; } if (index < 0 || index >= numComponents) { - throw new ArrayIndexOutOfBoundsException(index); + throw new IndexOutOfBoundsException(index); } - int idx = Collections.binarySearch(components, Integer.valueOf(index), ordinalComparator); + int idx = Collections.binarySearch(components, Integer.valueOf(index), + OrdinalComparator.INSTANCE); if (idx >= 0) { return components.get(idx); } - // assume unaligned DEFAULT + // assume non-packed DEFAULT int offset = 0; idx = -idx - 1; if (idx == 0) { @@ -315,6 +365,24 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return components.size(); } + @Override + protected int getPreferredComponentLength(DataType dataType, int length) { + if (isPackingEnabled() && !(dataType instanceof Dynamic)) { + length = -1; + } + return super.getPreferredComponentLength(dataType, length); + } + + @Override + protected void validateDataType(DataType dataType) { +// TODO: need tests - questionable if transitioning to/from no-packing works properly + if (isPackingEnabled() && dataType == DataType.DEFAULT) { + throw new IllegalArgumentException( + "The DEFAULT data type is not allowed in an aligned composite data type."); + } + super.validateDataType(dataType); + } + @Override public final DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length) { return insertAtOffset(offset, dataType, length, null, null); @@ -347,12 +415,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur dataType = dataType.clone(dataMgr); checkAncestry(dataType); - if ((offset > structLength) && !isInternallyAligned()) { + if ((offset > structLength) && !isPackingEnabled()) { numComponents = numComponents + (offset - structLength); structLength = offset; } - int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); + int index = Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); int additionalShift = 0; if (index >= 0) { @@ -371,7 +440,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } if (dataType == DataType.DEFAULT) { - // assume unaligned insert of DEFAULT + // assume non-packed insert of DEFAULT shiftOffsets(index, 1 + additionalShift, 1 + additionalShift); return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset); } @@ -383,7 +452,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur dataType.addParent(this); shiftOffsets(index, 1 + additionalShift, dtc.getLength() + additionalShift); components.add(index, dtc); - adjustInternalAlignment(); + repack(false); notifySizeChanged(); return dtc; } @@ -391,13 +460,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public DataTypeComponent add(DataType dataType, int length, String componentName, String comment) { - return doAdd(dataType, length, false, componentName, comment); + return doAdd(dataType, length, false, componentName, comment, true); } /** * Add a new component to the end of this structure. *

    - * NOTE: This method differs from inserting to the end the structure for the unaligned case in + * NOTE: This method differs from inserting to the end the structure for the non-packed case in * that this method will always grow the structure by the positive length specified while the * insert may limit its growth by the length of a smaller fixed-length dataType. * @@ -408,22 +477,30 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur * @param isFlexibleArray if true length is ignored and the trailing flexible array will be set * based upon the specified fixed-length dataType; * @param componentName component name - * @param comment componetn comment + * @param comment component comment + * @param packAndNotify if true perform repack and provide change notification * @return newly added component * @throws IllegalArgumentException if the specified data type is not allowed to be added to * this composite data type or an invalid length is specified. */ private DataTypeComponent doAdd(DataType dataType, int length, boolean isFlexibleArray, - String componentName, String comment) throws IllegalArgumentException { + String componentName, String comment, boolean packAndNotify) + throws IllegalArgumentException { validateDataType(dataType); dataType = dataType.clone(dataMgr); + if (isFlexibleArray && isInvalidFlexArrayDataType(dataType)) { + throw new IllegalArgumentException( + "Unsupported flexType: " + dataType.getDisplayName()); + } checkAncestry(dataType); DataTypeComponentImpl dtc; if (dataType == DataType.DEFAULT) { - dtc = new DataTypeComponentImpl(DataType.DEFAULT, this, 1, numComponents, structLength); + // FIXME: verify - does not appear to modify structure + dtc = + new DataTypeComponentImpl(DataType.DEFAULT, this, 1, numComponents, structLength); } else { @@ -455,31 +532,36 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (!isFlexibleArray) { int structureGrowth = dtc.getLength(); - if (!isInternallyAligned() && length > 0) { + if (!isPackingEnabled() && length > 0) { structureGrowth = length; } numComponents++; structLength += structureGrowth; } - adjustInternalAlignment(); - notifySizeChanged(); + if (packAndNotify) { + repack(false); + notifySizeChanged(); + } return dtc; } @Override public void growStructure(int amount) { + if (isPackingEnabled()) { + return; + } numComponents += amount; structLength += amount; - adjustInternalAlignment(); + repack(false); notifySizeChanged(); } @Override public DataTypeComponent insert(int index, DataType dataType, int length, String componentName, - String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException { + String comment) throws IndexOutOfBoundsException, IllegalArgumentException { if (index < 0 || index > numComponents) { - throw new ArrayIndexOutOfBoundsException(index); + throw new IndexOutOfBoundsException(index); } if (index == numComponents) { return add(dataType, length, componentName, comment); @@ -490,13 +572,14 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur checkAncestry(dataType); int idx; - if (isInternallyAligned()) { + if (isPackingEnabled()) { idx = index; } else { // TODO: could improve insertion of bitfield which does not intersect // existing ordinal bitfield at the bit-level - idx = Collections.binarySearch(components, Integer.valueOf(index), ordinalComparator); + idx = Collections.binarySearch(components, Integer.valueOf(index), + OrdinalComparator.INSTANCE); if (idx > 0) { DataTypeComponentImpl existingDtc = components.get(idx); if (existingDtc.isBitFieldComponent()) { @@ -512,7 +595,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur idx = -idx - 1; } if (dataType == DataType.DEFAULT) { - // assume unaligned insert of DEFAULT + // assume non-packed insert of DEFAULT shiftOffsets(idx, 1, 1); return getComponent(index); } @@ -525,7 +608,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur dataType.addParent(this); shiftOffsets(idx, 1, dtc.getLength()); components.add(idx, dtc); - adjustInternalAlignment(); + repack(false); notifySizeChanged(); return dtc; } @@ -544,16 +627,16 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public DataTypeComponent insertBitField(int ordinal, int byteWidth, int bitOffset, DataType baseDataType, int bitSize, String componentName, String comment) - throws InvalidDataTypeException, ArrayIndexOutOfBoundsException { + throws InvalidDataTypeException, IndexOutOfBoundsException { if (ordinal < 0 || ordinal > numComponents) { - throw new ArrayIndexOutOfBoundsException(ordinal); + throw new IndexOutOfBoundsException(ordinal); } BitFieldDataType.checkBaseDataType(baseDataType); baseDataType = baseDataType.clone(dataMgr); - if (!isInternallyAligned()) { + if (!isPackingEnabled()) { int offset = structLength; if (ordinal < numComponents) { offset = getComponent(ordinal).getOffset(); @@ -601,7 +684,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur effectiveBitSize, bitOffset, bigEndian); Comparator bitOffsetComparator = - bigEndian ? bitOffsetComparatorBE : bitOffsetComparatorLE; + bigEndian ? BitOffsetComparator.INSTANCE_BE : BitOffsetComparator.INSTANCE_LE; int startIndex = Collections.binarySearch(components, Integer.valueOf(startBitOffset), bitOffsetComparator); if (startIndex < 0) { @@ -627,7 +710,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur ordinal = startIndex; } - if (isInternallyAligned()) { + if (isPackingEnabled()) { insertBitField(ordinal, 0, 0, baseDataType, effectiveBitSize, componentName, comment); } @@ -679,7 +762,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur bitfieldDt.addParent(this); // currently has no affect components.add(startIndex, dtc); - adjustUnalignedComponents(); + adjustNonPackedComponents(); notifySizeChanged(); return dtc; } @@ -689,7 +772,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur * normal components the specified ordinal will be returned, however for bit-fields the ordinal * of the first bit-field containing the specified offset will be returned. * - * @param ordinal component ordinal + * @param index component ordinal * @param offset offset within structure * @return index of first defined component containing specific offset. */ @@ -714,7 +797,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur * normal components the specified ordinal will be returned, however for bit-fields the ordinal * of the last bit-field containing the specified offset will be returned. * - * @param ordinal component ordinal + * @param index defined component index * @param offset offset within structure * @return index of last defined component containing specific offset. */ @@ -739,7 +822,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (offset >= structLength) { return; } - int index = Collections.binarySearch(components, Integer.valueOf(offset), offsetComparator); + int index = Collections.binarySearch(components, Integer.valueOf(offset), + OffsetComparator.INSTANCE); int offsetDelta = 0; int ordinalDelta = 0; @@ -760,8 +844,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur dtc = components.get(index); } } - - adjustInternalAlignment(); + repack(false); notifySizeChanged(); return; } @@ -772,17 +855,16 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (dataType == this) { return true; } - if (!(dataType instanceof Structure)) { + if (!(dataType instanceof StructureInternal)) { return false; } - Structure struct = (Structure) dataType; - if (isInternallyAligned() != struct.isInternallyAligned() || - isDefaultAligned() != struct.isDefaultAligned() || - isMachineAligned() != struct.isMachineAligned() || - getMinimumAlignment() != struct.getMinimumAlignment() || - getPackingValue() != struct.getPackingValue() || - (!isInternallyAligned() && (getLength() != struct.getLength()))) { + StructureInternal struct = (StructureInternal) dataType; + int otherLength = struct.isZeroLength() ? 0 : struct.getLength(); + if (packing != struct.getStoredPackingValue() || + minimumAlignment != struct.getStoredMinimumAlignment() || + isZeroLength() != struct.isZeroLength() || + (packing == NO_PACKING && structLength != otherLength)) { return false; } @@ -821,11 +903,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (dt instanceof BitFieldDataType) { return; // unsupported } - if (isInternallyAligned()) { - adjustInternalAlignment(); + if (isPackingEnabled()) { + repack(true); return; } - boolean didChange = false; + int oldLength = structLength; + boolean changed = false; int n = components.size(); for (int i = 0; i < n; i++) { DataTypeComponentImpl dtc = components.get(i); @@ -840,33 +923,35 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (length < dtcLen) { dtc.setLength(length); shiftOffsets(i + 1, dtcLen - length, 0); - didChange = true; + changed = true; } else if (length > dtcLen) { int consumed = consumeBytesAfter(i, length - dtcLen); if (consumed > 0) { shiftOffsets(i + 1, 0 - consumed, 0); - didChange = true; + changed = true; } } } } - if (didChange) { - adjustInternalAlignment(); - notifySizeChanged(); + if (changed) { + repack(false); + if (oldLength != structLength) { + notifySizeChanged(); + } } } @Override public void dataTypeAlignmentChanged(DataType dt) { - if (isInternallyAligned()) { - adjustInternalAlignment(); + if (isPackingEnabled()) { + repack(true); } } /** * - * @param index the index of the defined component that is consuming the bytes. + * @param definedComponentIndex the index of the defined component that is consuming the bytes. * @param numBytes the number of undefined bytes to consume * @return the number of bytes actually consumed */ @@ -900,7 +985,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur /** * Create copy of structure for target dtm (source archive information is discarded). *

    - * WARNING! copying unaligned structures which contain bitfields can produce invalid results when + * WARNING! copying non-packed structures which contain bitfields can produce invalid results when * switching endianess due to the differences in packing order. * * @param dtm target data type manager @@ -908,7 +993,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur */ @Override public DataType copy(DataTypeManager dtm) { - StructureDataType struct = new StructureDataType(categoryPath, getName(), getLength(), dtm); + StructureDataType struct = + new StructureDataType(categoryPath, getName(), structLength, dtm); struct.setDescription(getDescription()); struct.replaceWith(this); return struct; @@ -916,7 +1002,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur /** * Create cloned structure for target dtm preserving source archive information. WARNING! - * cloning unaligned structures which contain bitfields can produce invalid results when + * cloning non-packed structures which contain bitfields can produce invalid results when * switching endianess due to the differences in packing order. * * @param dtm target data type manager @@ -928,7 +1014,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return this; } StructureDataType struct = - new StructureDataType(categoryPath, getName(), getLength(), getUniversalID(), + new StructureDataType(categoryPath, getName(), structLength, getUniversalID(), getSourceArchive(), getLastChangeTime(), getLastChangeTimeInSourceArchive(), dtm); struct.setDescription(getDescription()); struct.replaceWith(this); @@ -937,11 +1023,16 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public void clearComponent(int index) { - if (index < 0 || index >= numComponents) { - throw new ArrayIndexOutOfBoundsException(index); + public void clearComponent(int ordinal) { + if (isPackingEnabled()) { + delete(ordinal); + return; } - int idx = Collections.binarySearch(components, Integer.valueOf(index), ordinalComparator); + if (ordinal < 0 || ordinal >= numComponents) { + throw new IndexOutOfBoundsException(ordinal); + } + int idx = Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); if (idx >= 0) { DataTypeComponent dtc = components.remove(idx); dtc.getDataType().removeParent(this); @@ -949,12 +1040,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (len > 1) { shiftOffsets(idx, len - 1, 0); } + repack(false); } - adjustInternalAlignment(); } /** - * Replaces the internal components of this structure with components of the given structure. + * Replaces the internal components of this structure with components of the given structure + * including packing and alignment settings. * * @param dataType the structure to get the component information from. * @throws IllegalArgumentException if any of the component data types are not allowed to @@ -964,22 +1056,26 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur */ @Override public void replaceWith(DataType dataType) { - if (!(dataType instanceof Structure)) { + if (!(dataType instanceof StructureInternal)) { throw new IllegalArgumentException(); } - Structure struct = (Structure) dataType; - - int oldLength = structLength; + StructureInternal struct = (StructureInternal) dataType; components.clear(); - structLength = 0; numComponents = 0; - flexibleArrayComponent = null; + structLength = 0; + structAlignment = -1; - setAlignment(struct); + if (flexibleArrayComponent != null) { + flexibleArrayComponent.getDataType().removeParent(this); + flexibleArrayComponent = null; + } - if (struct.isInternallyAligned()) { + this.packing = struct.getStoredPackingValue(); + this.minimumAlignment = struct.getStoredMinimumAlignment(); + + if (struct.isPackingEnabled()) { doReplaceWithAligned(struct); } else { @@ -988,28 +1084,29 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur DataTypeComponent flexComponent = struct.getFlexibleArrayComponent(); if (flexComponent != null) { - setFlexibleArrayComponent(flexComponent.getDataType(), flexComponent.getFieldName(), - flexComponent.getComment()); + doAdd(flexComponent.getDataType(), 0, true, flexComponent.getFieldName(), + flexComponent.getComment(), false); } - if (oldLength != structLength) { - notifySizeChanged(); - } + repack(false); + notifySizeChanged(); } +// TODO: Rename private void doReplaceWithAligned(Structure struct) { // assumes components is clear and that alignment characteristics have been set DataTypeComponent[] otherComponents = struct.getDefinedComponents(); for (DataTypeComponent dtc : otherComponents) { DataType dt = dtc.getDataType(); int length = (dt instanceof Dynamic) ? dtc.getLength() : -1; - add(dt, length, dtc.getFieldName(), dtc.getComment()); + doAdd(dt, length, false, dtc.getFieldName(), dtc.getComment(), false); } } +// TODO: Rename private void doReplaceWithUnaligned(Structure struct) throws IllegalArgumentException { // assumes components is clear and that alignment characteristics have been set. - if (struct.isNotYetDefined()) { + if (struct.isZeroLength()) { return; } @@ -1042,16 +1139,16 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur components.add(new DataTypeComponentImpl(dt, this, length, dtc.getOrdinal(), dtc.getOffset(), dtc.getFieldName(), dtc.getComment())); } - adjustComponents(); + repack(false); } @Override public void dataTypeDeleted(DataType dt) { - boolean didChange = false; + boolean changed = false; if (flexibleArrayComponent != null && flexibleArrayComponent.getDataType() == dt) { flexibleArrayComponent.getDataType().removeParent(this); flexibleArrayComponent = null; - didChange = true; + changed = true; } int n = components.size(); for (int i = n - 1; i >= 0; i--) { @@ -1063,14 +1160,15 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } if (removeBitFieldComponent || dtc.getDataType() == dt) { dt.removeParent(this); +// FIXME: Consider replacing with undefined type instead of removing (don't remove bitfield) components.remove(i); shiftOffsets(i, dtc.getLength() - 1, 0); - didChange = true; + --numComponents; // may be revised by repack + changed = true; } } - if (didChange) { - adjustInternalAlignment(); - notifySizeChanged(); + if (changed) { + repack(true); } } @@ -1126,7 +1224,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } } else if (comp.getDataType() == oldDt) { - if (replacementDt == DEFAULT && isInternallyAligned()) { + if (replacementDt == DEFAULT && isPackingEnabled()) { Msg.error(this, "Invalid replacement type " + newDt.getName() + ", removing component " + comp.getDataType().getName() + ": " + getPathName()); @@ -1147,38 +1245,47 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } if (changed) { - adjustInternalAlignment(); + repack(false); notifySizeChanged(); } } private void setComponentDataType(DataTypeComponentImpl comp, DataType newDt, int nextIndex) { + + int oldLen = comp.getLength(); + int len = newDt.getLength(); + if (len < 1) { + len = oldLen; + } + comp.getDataType().removeParent(this); comp.setDataType(newDt); newDt.addParent(this); - int len = newDt.getLength(); - int oldLen = comp.getLength(); - if (len > 0) { - if (len < oldLen) { + + if (isPackingEnabled()) { + comp.setLength(len); + return; + } + + if (len < oldLen) { + comp.setLength(len); + shiftOffsets(nextIndex, oldLen - len, 0); + } + else if (len > oldLen) { + int bytesAvailable = getNumUndefinedBytes(comp.getOrdinal() + 1); + int bytesNeeded = len - oldLen; + if (bytesNeeded <= bytesAvailable) { comp.setLength(len); - shiftOffsets(nextIndex, oldLen - len, 0); + shiftOffsets(nextIndex, -bytesNeeded, 0); } - else if (len > oldLen) { - int bytesAvailable = getNumUndefinedBytes(comp.getOrdinal() + 1); - int bytesNeeded = len - oldLen; - if (bytesNeeded <= bytesAvailable) { - comp.setLength(len); - shiftOffsets(nextIndex, -bytesNeeded, 0); - } - else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { // we are the last defined component, grow structure - doGrowStructure(bytesNeeded - bytesAvailable); - comp.setLength(len); - shiftOffsets(nextIndex, -bytesNeeded, 0); - } - else { - comp.setLength(oldLen + bytesAvailable); - shiftOffsets(nextIndex, -bytesAvailable, 0); - } + else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { // we are the last defined component, grow structure + doGrowStructure(bytesNeeded - bytesAvailable); + comp.setLength(len); + shiftOffsets(nextIndex, -bytesNeeded, 0); + } + else { + comp.setLength(oldLen + bytesAvailable); + shiftOffsets(nextIndex, -bytesAvailable, 0); } } } @@ -1199,9 +1306,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur @Override public DataTypeComponent replace(int index, DataType dataType, int length, String componentName, - String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException { + String comment) throws IndexOutOfBoundsException, IllegalArgumentException { if (index < 0 || index >= numComponents) { - throw new ArrayIndexOutOfBoundsException(index); + throw new IndexOutOfBoundsException(index); } validateDataType(dataType); @@ -1223,7 +1330,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur DataTypeComponent replacement = replaceComponent(origDtc, dataType, length, componentName, comment); - adjustInternalAlignment(); + repack(false); + notifySizeChanged(); return replacement; } @@ -1239,8 +1347,11 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur throw new IllegalArgumentException("Offset cannot be negative."); } if (offset >= structLength) { + if (!isPackingEnabled() && offset < getLength()) { + return insertAtOffset(offset, dataType, length, componentName, comment); + } throw new IllegalArgumentException( - "Offset " + offset + " is beyond end of structure (" + structLength + ")."); + "Offset " + offset + " is beyond end of structure (" + getLength() + ")."); } validateDataType(dataType); @@ -1264,7 +1375,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur DataTypeComponent replacement = replaceComponent(origDtc, dataType, length, componentName, comment); - adjustInternalAlignment(); + repack(false); + notifySizeChanged(); return replacement; } @@ -1281,15 +1393,15 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur * component in this composite data type. For example, suppose dt1 contains dt2. * Therefore it is not valid to replace a dt2 component with dt1 since this would * cause a cyclic dependency. In addition, any attempt to replace an existing - * bit-field component or specify a {@link BitFieldDatatype} will produce this + * bit-field component or specify a {@link BitFieldDataType} will produce this * error. */ private DataTypeComponent replaceComponent(DataTypeComponentImpl origDtc, DataType dataType, int length, String componentName, String comment) { -// FIXME: Unsure how o support replace operation with bit-fields. Within unaligned structure +// FIXME: Unsure how o support replace operation with bit-fields. Within non-packed structure // the packing behavior for bit-fields prevents a one-for-one replacement and things may shift -// around which the unaligned structure tries to avoid. Insert and delete are less of a concern +// around which the non-packed structure tries to avoid. Insert and delete are less of a concern // since movement already can occur, although insert at offset may not retain the offset if it // interacts with bit-fields. @@ -1317,7 +1429,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } } int index = - Collections.binarySearch(components, Integer.valueOf(ordinal), ordinalComparator); + Collections.binarySearch(components, Integer.valueOf(ordinal), + OrdinalComparator.INSTANCE); if (index < 0) { index = -index - 1; } @@ -1352,7 +1465,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (index >= numComponents) { return 0; } - int idx = Collections.binarySearch(components, Integer.valueOf(index), ordinalComparator); + int idx = Collections.binarySearch(components, Integer.valueOf(index), + OrdinalComparator.INSTANCE); DataTypeComponent dtc = null; if (idx < 0) { idx = -idx - 1; @@ -1366,10 +1480,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } - @Override - public void dataTypeNameChanged(DataType dt, String oldName) { - } - @Override public boolean dependsOn(DataType dt) { return false; @@ -1384,7 +1494,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur structLength = 0; numComponents = 0; flexibleArrayComponent = null; - adjustInternalAlignment(); notifySizeChanged(); } @@ -1394,59 +1503,39 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } @Override - public void realign() { - adjustInternalAlignment(); - } - - @Override - public void pack(int packingSize) { - setPackingValue(packingSize); - } - - /** - * Adjust the alignment, packing and padding of components within this structure based upon the - * current alignment and packing attributes for this structure. This method should be called to - * fix up the layout of the internal components of the structure after other code has changed - * the attributes of the structure.
    - * When switching between internally aligned and unaligned this method corrects the component - * ordinal numbering also. - * - * @return true if the structure was changed by this method. - */ - protected boolean adjustComponents() { + public boolean repack(boolean notify) { int oldLength = structLength; + int oldAlignment = getAlignment(); - boolean changed = false; - alignment = -1; - - if (!isInternallyAligned()) { - changed |= adjustUnalignedComponents(); - if (changed) { - if (oldLength != structLength) { - notifySizeChanged(); - } - } - return changed; + boolean changed; + if (!isPackingEnabled()) { + changed = adjustNonPackedComponents(); + } + else { + StructurePackResult packResult = + AlignedStructurePacker.packComponents(this, components); + changed = packResult.componentsChanged; + changed |= (structLength != packResult.structureLength) || + (structAlignment != packResult.alignment) || + (numComponents != packResult.numComponents); + structLength = packResult.structureLength; + structAlignment = packResult.alignment; + numComponents = packResult.numComponents; } - StructurePackResult packResult = AlignedStructurePacker.packComponents(this, components); - changed = packResult.componentsChanged; - - // Adjust the structure - changed |= updateComposite(packResult.numComponents, packResult.structureLength, - packResult.alignment); - - if (changed) { + if (changed && notify) { if (oldLength != structLength) { notifySizeChanged(); } - return true; + else if (oldAlignment != structAlignment) { + notifyAlignmentChanged(); + } } - return false; + return changed; } - private boolean adjustUnalignedComponents() { + private boolean adjustNonPackedComponents() { boolean changed = false; int componentCount = 0; int currentOffset = 0; @@ -1464,44 +1553,28 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur } componentCount++; } + int numUndefinedsAfter = structLength - currentOffset; componentCount += numUndefinedsAfter; - if (updateComposite(componentCount, structLength, 1)) { + if (numComponents != componentCount) { + numComponents = componentCount; + changed = true; + } + int alignment = getNonPackedAlignment(); + if (alignment != structAlignment) { + structAlignment = alignment; changed = true; } return changed; } - private boolean updateComposite(int currentNumComponents, int currentLength, - int currentAlignment) { - boolean compositeChanged = false; - if (numComponents != currentNumComponents) { - numComponents = currentNumComponents; - compositeChanged = true; - } - if (structLength != currentLength) { - structLength = currentLength; - compositeChanged = true; - } - if (alignment != currentAlignment) { - alignment = currentAlignment; - compositeChanged = true; - } - return compositeChanged; - } - private void doGrowStructure(int amount) { - if (!isInternallyAligned()) { + if (!isPackingEnabled()) { numComponents += amount; } structLength += amount; } - @Override - public void adjustInternalAlignment() { - adjustComponents(); - } - @Override public boolean hasFlexibleArrayComponent() { return flexibleArrayComponent != null; @@ -1525,7 +1598,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur throw new IllegalArgumentException( "Unsupported flexType: " + flexType.getDisplayName()); } - return doAdd(flexType, 0, true, name, comment); + DataTypeComponent dtc = doAdd(flexType, 0, true, name, comment, false); + repack(false); + notifySizeChanged(); + return dtc; } @Override @@ -1534,25 +1610,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return; } flexibleArrayComponent = null; - adjustInternalAlignment(); + repack(false); notifySizeChanged(); } - @Override - protected void dumpComponents(StringBuilder buffer, String pad) { - super.dumpComponents(buffer, pad); - DataTypeComponent dtc = 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"); - } - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureInternal.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureInternal.java new file mode 100644 index 0000000000..73ac8176a3 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureInternal.java @@ -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 { + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java index f20222b209..1f94fa83c5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java @@ -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) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Union.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Union.java index 44acf0da71..8ede124ba1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Union.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Union.java @@ -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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java index 298bcbd5c2..be7e7c4ff8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionDataType.java @@ -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 components; +public class UnionDataType extends CompositeDataTypeImpl implements UnionInternal { + private int unionLength; + private int unionAlignment; + + private List 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 ordinals) { + + if (ordinals.isEmpty()) { + return; + } + + int oldAlignment = getAlignment(); + + List 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 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); - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionInternal.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionInternal.java new file mode 100644 index 0000000000..d387fa8d63 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/UnionInternal.java @@ -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 { + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java index 86937a9c36..fe8710697b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java @@ -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()); } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java index ed910b11a0..d63611dc97 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/UnionDBTest.java @@ -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" + diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataType.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataType.java index 8c7069498b..adb30d2312 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataType.java @@ -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(); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java index 0cca736b6c..bb5969fc34 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java @@ -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(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java index 023252a05e..140ca5282b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java @@ -156,7 +156,7 @@ public class TestDummyDataTypeManager implements DataTypeManager { } @Override - public void dataTypeChanged(DataType dataType) { + public void dataTypeChanged(DataType dataType, boolean isAutoChange) { // stub } diff --git a/Ghidra/Framework/Utility/hs_err_pid16061.log b/Ghidra/Framework/Utility/hs_err_pid16061.log new file mode 100644 index 0000000000..4966b9436f --- /dev/null +++ b/Ghidra/Framework/Utility/hs_err_pid16061.log @@ -0,0 +1,33 @@ +# +# A fatal error has been detected by the Java Runtime Environment: +# +# SIGSEGV (0xb) at pc=0x00007fa057435098, pid=16061, tid=16081 +# +# JRE version: OpenJDK Runtime Environment Corretto-11.0.10.9.1 (11.0.10+9) (build 11.0.10+9-LTS) +# Java VM: OpenJDK 64-Bit Server VM Corretto-11.0.10.9.1 (11.0.10+9-LTS, mixed mode, tiered, compressed oops, g1 gc, linux-amd64) +# Problematic frame: +# C [libjimage.so+0x4098] ImageStrings::find(Endian*, char const*, int*, unsigned int)+0x68 +# +# No core dump will be written. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again +# +# If you would like to submit a bug report, please visit: +# https://github.com/corretto/corretto-11/issues/ +# + +--------------- S U M M A R Y ------------ + +Command Line: -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:37943 -XX:+IgnoreUnrecognizedVMOptions -Djava.system.class.loader=ghidra.GhidraClassLoader -Xshare:off -Dfile.encoding=UTF8 -Duser.country=US -Duser.language=en -Dsun.java2d.pmoffscreen=false -Dsun.java2d.xrender=true -Dsun.java2d.d3d=false -Xdock:name=Ghidra -Dvisualvm.display.name=Ghidra -Dpython.console.encoding=UTF-8 -DContinuesInterceptor.disabled=true --illegal-access=permit -javaagent:/home/jdirish/eclipse/java-2020-06/eclipse/configuration/org.eclipse.osgi/537/0/.cp/lib/javaagent-shaded.jar ghidra.GhidraLauncher ghidra.GhidraRun + +Host: Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz, 4 cores, 15G, Amazon Linux release 2 (Karoo) +Time: Fri Apr 9 19:29:24 2021 EDT elapsed time: 20998.917517 seconds (0d 5h 49m 58s) + +--------------- T H R E A D --------------- + +Current thread (0x00007fa050287800): JavaThread "Signal Dispatcher" daemon [_thread_in_vm, id=16081, stack(0x00007fa0380ac000,0x00007fa0381ad000)] + +Stack: [0x00007fa0380ac000,0x00007fa0381ad000], sp=0x00007fa0381aa140, free space=1016k +Native frames: (J=compiled Java code, A=aot compiled Java code, j=interpreted, Vv=VM code, C=native code) +C [libjimage.so+0x4098] ImageStrings::find(Endian*, char const*, int*, unsigned int)+0x68 +C [libjimage.so+0x4baa] ImageFileReader::find_location_index(char const*, unsigned long long*) const+0x3a +C [libjimage.so+0x5070] JIMAGE_FindResource+0xf0 +V [libjvm.so+0x5e578e] \ No newline at end of file diff --git a/Ghidra/Processors/PIC/src/main/java/ghidra/app/plugin/core/analysis/Pic24DInitAnalyzer.java b/Ghidra/Processors/PIC/src/main/java/ghidra/app/plugin/core/analysis/Pic24DInitAnalyzer.java index 95f5ac3ae7..afcf743da7 100644 --- a/Ghidra/Processors/PIC/src/main/java/ghidra/app/plugin/core/analysis/Pic24DInitAnalyzer.java +++ b/Ghidra/Processors/PIC/src/main/java/ghidra/app/plugin/core/analysis/Pic24DInitAnalyzer.java @@ -75,7 +75,7 @@ public class Pic24DInitAnalyzer extends AbstractAnalyzer { long available = dinitBlock.getSize(); Structure dataRecordType = new StructureDataType("data_record", 0, program.getDataTypeManager()); - dataRecordType.setInternallyAligned(true); + dataRecordType.setPackingEnabled(true); dataRecordType.add(PointerDataType.dataType, "dst", null); // NOTE: long is used instead of int to ensure that 4-bytes within ROM are consumed dataRecordType.add(LongDataType.dataType, "len", null); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java index 8578f9cace..7b460868be 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java @@ -227,7 +227,7 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { } @Test - public void testStructureEditorAligned() { + public void testStructureEditorPacked() { createDetailedStructure(0x40d2b8, true, false); @@ -285,9 +285,9 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { } @Test - public void testUnionEditorAligned() { + public void testUnionEditorPacked() { - createAlignedUnion(0x40d2b8); + createPackedUnion(0x40d2b8); goToListing(0x40d2b8, true); performAction("Edit Data Type", "DataPlugin", true); @@ -299,18 +299,18 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { goToListing(address); - StructureDataType struct = new StructureDataType("MyUnalignedStruct", 0); - struct.add(new ByteDataType(), "myByteElement", "unaligned byte"); + StructureDataType struct = new StructureDataType("MyNonPackedStruct", 0); + struct.add(new ByteDataType(), "myByteElement", "non-packed byte"); struct.add(new ByteDataType(), "", "undefined element"); - struct.add(new WordDataType(), "myWordElement", "unaligned word"); - struct.add(new ByteDataType(), "myByteElement2", "another unaligned byte"); - struct.add(new DWordDataType(), "myDWordElement", "unaligned dword"); + struct.add(new WordDataType(), "myWordElement", "non-packed word"); + struct.add(new ByteDataType(), "myByteElement2", "another non-packed byte"); + struct.add(new DWordDataType(), "myDWordElement", "non-packed dword"); if (includeFlexArray) { struct.setFlexibleArrayComponent(CharDataType.dataType, "flex", "unsized flexible array"); } struct.clearComponent(1); - struct.setDescription("This is an example of an unaligned structure " + + struct.setDescription("This is an example of an non-packed structure " + (includeFlexArray ? "with a flexible char array" : "of size 9") + "."); CreateDataCmd createDataCmd = new CreateDataCmd(addr(address), struct); @@ -318,13 +318,13 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { waitForBusyTool(tool); } - private void createDetailedStructure(long address, boolean aligned, + private void createDetailedStructure(long address, boolean packed, boolean includeBitFieldsAndFlexArray) { goToListing(address); - StructureDataType struct = new StructureDataType("MyAlignedStruct", 0); - struct.setInternallyAligned(true); // allow proper default packing + StructureDataType struct = new StructureDataType("MyPackedStruct", 0); + struct.setPackingEnabled(true); // allow proper default packing struct.add(new ByteDataType(), "myByteElement", "alignment 1"); struct.add(new ByteDataType(), "", "This is my undefined element"); struct.add(new WordDataType(), "myWordElement", "alignment 2"); @@ -345,11 +345,11 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { "unsized flexible array"); } struct.clearComponent(1); - struct.setDescription("Members internally aligned " + + struct.setDescription("Members packed " + (includeBitFieldsAndFlexArray ? "with bitfields and a flexible char array" : "according to their alignment size") + ". "); - struct.setInternallyAligned(aligned); + struct.setPackingEnabled(packed); CreateDataCmd createDataCmd = new CreateDataCmd(addr(address), struct); tool.execute(createDataCmd, program); @@ -361,28 +361,28 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator { goToListing(address); UnionDataType union = new UnionDataType("MyUnion"); - union.add(new ByteDataType(), "myByteElement", "unaligned byte"); - union.add(new WordDataType(), "myWordElement", "unaligned word"); - union.add(new DWordDataType(), "myDWordElement", "unaligned dword"); - union.add(new QWordDataType(), "myQWordElement", "unaligned qword"); - union.setDescription("This is an example of an unaligned union."); + union.add(new ByteDataType(), "myByteElement", "non-packed byte"); + union.add(new WordDataType(), "myWordElement", "non-packed word"); + union.add(new DWordDataType(), "myDWordElement", "non-packed dword"); + union.add(new QWordDataType(), "myQWordElement", "non-packed qword"); + union.setDescription("This is an example of an non-packed union."); CreateDataCmd createDataCmd = new CreateDataCmd(addr(address), union); tool.execute(createDataCmd, program); waitForBusyTool(tool); } - private void createAlignedUnion(long address) { + private void createPackedUnion(long address) { goToListing(address); UnionDataType union = new UnionDataType("MyUnion"); - union.add(new ByteDataType(), "myByteElement", "aligned byte"); - union.add(new WordDataType(), "myWordElement", "aligned word"); - union.add(new DWordDataType(), "myDWordElement", "aligned dword"); - union.add(new QWordDataType(), "myQWordElement", "aligned qword"); - union.setDescription("This is an example of an aligned union."); - union.setInternallyAligned(true); + union.add(new ByteDataType(), "myByteElement", "packed byte"); + union.add(new WordDataType(), "myWordElement", "packed word"); + union.add(new DWordDataType(), "myDWordElement", "packed dword"); + union.add(new QWordDataType(), "myQWordElement", "packed qword"); + union.setDescription("This is an example of an packed union."); + union.setPackingEnabled(true); CreateDataCmd createDataCmd = new CreateDataCmd(addr(address), union); tool.execute(createDataCmd, program);