GP-7307 Corrected data type merge conflict fixup

This commit is contained in:
ghidra1
2026-09-22 17:24:55 -04:00
parent ce80e056b8
commit 6c6f274d91
9 changed files with 315 additions and 65 deletions

View File

@@ -529,7 +529,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
sb.append(dt.getDisplayName());
sb.append(", ");
if (info.resultOrdinal < 0) {
if (info.resultIndex < 0) {
if (dt instanceof FunctionDefinition) {
sb.append("return-type");
}
@@ -540,25 +540,25 @@ public class DataTypeMergeManager implements MergeResolver {
else {
if (dt instanceof FunctionDefinition) {
sb.append("param-");
sb.append(info.resultOrdinal);
sb.append(info.resultIndex);
}
else if (dt instanceof Union) {
sb.append("component-");
sb.append(info.resultOrdinal);
sb.append(info.resultIndex);
}
else if (dt instanceof Structure) {
Structure resultStruct = (Structure) info.ht.get(info.id);
sb.append("component-");
sb.append(info.resultOrdinal);
sb.append("defined-component-");
sb.append(info.resultIndex);
if (!resultStruct.isPackingEnabled()) {
sb.append(", offset-");
sb.append(", source-offset-");
sb.append("0x");
sb.append(Integer.toHexString(info.offset));
}
}
else {
sb.append("resultOrdinal-"); // unknown use case
sb.append(info.resultOrdinal);
sb.append("resultIndex-"); // unknown use case
sb.append(info.resultIndex);
}
}
@@ -1234,6 +1234,38 @@ public class DataTypeMergeManager implements MergeResolver {
return nextOrdinal;
}
/**
* Get the index of a defined component in the structure's defined-component sequence.
* Unlike its ordinal, this index is unaffected by changes to implicit undefined bytes.
* @param struct destination structure
* @param component newly inserted defined component
* @return index in the destination's defined-component sequence
*/
private static int getDefinedComponentIndex(Structure struct, DataTypeComponent component) {
if (component.getDataType() == DataType.DEFAULT) {
throw new IllegalArgumentException("Expected defined component");
}
int ordinal = component.getOrdinal();
int low = 0;
int high = struct.getNumDefinedComponents() - 1;
// Defined components have unique, increasing ordinals even when offsets overlap.
while (low <= high) {
int mid = low + (high - low) / 2;
int midOrdinal = struct.getDefinedComponent(mid).getOrdinal();
if (midOrdinal < ordinal) {
low = mid + 1;
}
else if (midOrdinal > ordinal) {
high = mid - 1;
}
else {
return mid;
}
}
throw new AssertException(
"Expected defined component at ordinal " + ordinal + " in " + struct.getPathName());
}
private void updateStructure(long sourceDtID, Structure sourceDt, Structure destStruct,
MyIdentityHashMap<Long, DataType> resolvedDataTypes) {
@@ -1434,15 +1466,14 @@ public class DataTypeMergeManager implements MergeResolver {
}
}
else if (packed) {
// Add fixup placeholder to prevent the ordinal values and component sizes from
// changing. Nothing we can do about packing which may be affected.
// These should get fixed-up later.
// Preserve a defined component for later fixup. Packing and dependency size
// changes may alter its offset and ordinal, but not its defined-component index.
resultComp = destStruct.add(BadDataType.dataType, sourceComp.getLength(),
sourceComp.getFieldName(), comment);
}
else {
// Add fixup placeholder to prevent the ordinal values and component sizes from
// changing. These should get fixed-up later.
// Preserve a defined component and its reserved space for later fixup.
// Changes to preceding undefined bytes may alter its ordinal.
resultComp = destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
sourceComp.getLength(), sourceComp.getFieldName(), comment);
}
@@ -1450,7 +1481,7 @@ public class DataTypeMergeManager implements MergeResolver {
if (fixupRequired) {
// Component datatype has not been added/resolved yet, put an entry in the fixup list
fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID,
resultComp.getOrdinal(), sourceComp, resolvedDataTypes));
getDefinedComponentIndex(destStruct, resultComp), sourceComp, resolvedDataTypes));
fixUpIDSet.add(sourceDtID);
}
}
@@ -1607,7 +1638,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
}
else {
// Add fixup placeholder to prevent the ordinal values and component sizes from
// Add fixup placeholder to prevent the ordinal values and component sizes from
// changing. Nothing we can do about packing which may be affected.
// These should get fixed-up later.
destUnion.add(BadDataType.dataType, sourceComp.getLength(),
@@ -2470,7 +2501,7 @@ public class DataTypeMergeManager implements MergeResolver {
/**
* NOTE: It is important that fixups for a Structure are processed bottom-up
* (i.e., last-ordinal to first-ordinal) to ensure that subsequent fixup ordinal
* (i.e., last defined-component index to first) to ensure that subsequent fixup
* indexes remain valid even after components are removed as a result of processing
* a Structure fixup.
*/
@@ -2542,22 +2573,22 @@ public class DataTypeMergeManager implements MergeResolver {
long lastChangeTime = fd.getLastChangeTime(); // Don't let the time change.
try {
if (dt != null) {
if (info.resultOrdinal < 0) { // -1 for return type
if (info.resultIndex < 0) { // -1 for return type
fd.setReturnType(dt);
}
else {
ParameterDefinition[] args = fd.getArguments();
args[info.resultOrdinal].setDataType(dt);
args[info.resultIndex].setDataType(dt);
}
return true;
}
if (info.resultOrdinal < 0) { // -1 for return type
if (info.resultIndex < 0) { // -1 for return type
// nowhere to set error comment
}
else {
ParameterDefinition[] args = fd.getArguments();
ParameterDefinition arg = args[info.resultOrdinal];
ParameterDefinition arg = args[info.resultIndex];
String comment =
buildDataTypeFailureComment(info.componentDataType, null, arg.getComment());
arg.setComment(comment);
@@ -2577,16 +2608,8 @@ public class DataTypeMergeManager implements MergeResolver {
* @return false if component not found, else true
*/
private boolean fixUpPackedStructureComponent(FixUpInfo info, Structure struct, DataType dt) {
int ordinal = info.resultOrdinal;
DataTypeComponent dtc;
if (ordinal >= 0 || ordinal < struct.getNumComponents()) {
dtc = struct.getComponent(ordinal);
}
else {
throw new AssertException(
"Expected fixup component at ordinal " + ordinal + " in " + struct.getPathName());
}
DataTypeComponent dtc = getStructureFixupComponent(info, struct);
int ordinal = dtc.getOrdinal();
long lastChangeTime = struct.getLastChangeTime(); // Don't let the time change.
try {
@@ -2674,16 +2697,8 @@ public class DataTypeMergeManager implements MergeResolver {
private boolean fixUpNonPackedStructureComponent(FixUpInfo info, Structure struct,
DataType dt) {
int ordinal = info.resultOrdinal;
DataTypeComponent dtc;
if (ordinal >= 0 || ordinal < struct.getNumComponents()) {
dtc = struct.getComponent(ordinal);
}
else {
throw new AssertException(
"Expected fixup component at ordinal " + ordinal + " in " + struct.getPathName());
}
DataTypeComponent dtc = getStructureFixupComponent(info, struct);
int ordinal = dtc.getOrdinal();
long lastChangeTime = struct.getLastChangeTime(); // Don't let the time change.
try {
@@ -2760,6 +2775,21 @@ public class DataTypeMergeManager implements MergeResolver {
return true;
}
/**
* Locate a fixup after dependency resolution, which may have changed the structure's layout.
* Do not retain component instances or ordinals across fixups or packing changes.
* @param info fixup identifying a defined component
* @param struct current result structure
* @return current component to fix up
*/
private static DataTypeComponent getStructureFixupComponent(FixUpInfo info, Structure struct) {
if (info.resultIndex < 0 || info.resultIndex >= struct.getNumDefinedComponents()) {
throw new AssertException("Expected fixup component at defined-component index " +
info.resultIndex + " in " + struct.getPathName());
}
return struct.getDefinedComponent(info.resultIndex);
}
/**
* Fix up the structure using the fix up info for a component.
* @param info fixup info
@@ -2780,10 +2810,11 @@ public class DataTypeMergeManager implements MergeResolver {
String loc;
if (struct.isPackingEnabled()) {
loc = "ordinal " + info.resultOrdinal;
loc = "defined-component index " + info.resultIndex;
}
else {
loc = "offset 0x" + Integer.toHexString(info.offset);
loc = "defined-component index " + info.resultIndex + " (source offset 0x" +
Integer.toHexString(info.offset) + ")";
}
Msg.warn(this, "Structure Merge: Failed to resolve data type '" +
info.componentDataType.getName() + "' at " + loc + " in " + struct.getPathName());
@@ -2794,7 +2825,7 @@ public class DataTypeMergeManager implements MergeResolver {
DataType compDt = resolve(info.compID, info.getDataTypeManager(), info.ht);
int ordinal = info.resultOrdinal;
int ordinal = info.resultIndex;
DataTypeComponent dtc = null;
if (ordinal >= 0 && ordinal <= union.getNumComponents()) {
@@ -2865,7 +2896,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
Msg.warn(this,
"Union Merge: Failed to resolve data type '" + info.componentDataType.getName() +
"' at ordinal " + info.resultOrdinal + " in " + union.getPathName());
"' at ordinal " + info.resultIndex + " in " + union.getPathName());
return false;
}
@@ -2889,7 +2920,7 @@ public class DataTypeMergeManager implements MergeResolver {
FixUpInfo info = fixUpList.get(i); // assume info applies to union
if (!fixUpUnionComponent(union, info)) {
Msg.warn(this, "Union Merge: Failed to apply data type at ordinal " +
info.resultOrdinal + " in " + union.getPathName());
info.resultIndex + " in " + union.getPathName());
unresolvedFixups.add(info);
}
}
@@ -3427,10 +3458,12 @@ public class DataTypeMergeManager implements MergeResolver {
final long compID; // Source datatype ID for component
final DataType componentDataType; // Source component datatype
// Result ordinal when id represents a container such as a structure/union or
// function definition. In such cases, 'compID' corresponds to the component datatype.
// A -1 may be used when not applicable.
final int resultOrdinal;
// Result defined-component index for a structure, component ordinal for a union,
// or parameter index for a function definition. A -1 denotes a return type or
// a dependency without a component index. Structure indexes exclude implicit
// undefined bytes and survive layout changes, but must be regenerated when the
// selected definition is rebuilt (see removeFixUps).
final int resultIndex;
final MyIdentityHashMap<Long, DataType> ht;
@@ -3449,16 +3482,16 @@ public class DataTypeMergeManager implements MergeResolver {
* @param id source data type ID needing to be fixed up
* @param compID source datatype ID of either param/component or bitfield base type
* @param componentDataType source component/dependency datatype
* @param resultOrdinal the result ordinal into a structure/union; or
* parameter/return ordinal; or -1 for other cases where index is not used
* @param resultIndex result defined-component index for a structure, union component
* ordinal, function parameter index, or -1 for a return type/other dependency
* @param resolvedDataTypes hashtable used for resolving the data type
*/
FixUpInfo(long id, long compID, DataType componentDataType, int resultOrdinal,
FixUpInfo(long id, long compID, DataType componentDataType, int resultIndex,
MyIdentityHashMap<Long, DataType> resolvedDataTypes) {
this.id = id;
this.compID = compID;
this.componentDataType = componentDataType;
this.resultOrdinal = resultOrdinal;
this.resultIndex = resultIndex;
this.ht = resolvedDataTypes;
if (componentDataType instanceof BitFieldDataType) {
@@ -3472,13 +3505,13 @@ public class DataTypeMergeManager implements MergeResolver {
* or components were resolved.
* @param id id of data type needing to be fixed up
* @param compID datatype ID of either param/component or bitfield base type
* @param resultOrdinal component ordinal within result composite
* @param resultIndex defined-component index within a result structure or ordinal in a union
* @param sourceDtc associated composite datatype component
* @param resolvedDataTypes hashtable used for resolving the data type
*/
FixUpInfo(long id, long compID, int resultOrdinal, DataTypeComponent sourceDtc,
FixUpInfo(long id, long compID, int resultIndex, DataTypeComponent sourceDtc,
MyIdentityHashMap<Long, DataType> resolvedDataTypes) {
this(id, compID, getDataType(sourceDtc), resultOrdinal, resolvedDataTypes);
this(id, compID, getDataType(sourceDtc), resultIndex, resolvedDataTypes);
offset = sourceDtc.getOffset();
if (sourceDtc.isBitFieldComponent()) {
BitFieldDataType bfDt = (BitFieldDataType) sourceDtc.getDataType();
@@ -3497,11 +3530,11 @@ public class DataTypeMergeManager implements MergeResolver {
@Override
public int compareTo(FixUpInfo o) {
// Compare such that items are grouped by id and sort such that the greatest resultOrdinal
// Compare such that items are grouped by id and sort such that the greatest resultIndex
// is first within that group.
long c = id - o.id;
if (c == 0) {
c = Integer.toUnsignedLong(o.resultOrdinal) - Integer.toUnsignedLong(resultOrdinal);
c = Integer.toUnsignedLong(o.resultIndex) - Integer.toUnsignedLong(resultIndex);
}
if (c == 0) {
return 0;
@@ -3527,7 +3560,7 @@ public class DataTypeMergeManager implements MergeResolver {
}
return "\n" + "ID = " + Long.toHexString(id) + ",\ndt = " + dtm.getDataType(id) +
",\ncomponent ID = " + Long.toHexString(compID) + ",\ncomponent dt = " +
dtm.getDataType(compID) + ",\nresultOrdinal = " + resultOrdinal + ",\n" + bitInfo +
dtm.getDataType(compID) + ",\nresultIndex = " + resultIndex + ",\n" + bitInfo +
"ht = " +
htStr + "\n";
}

View File

@@ -1098,6 +1098,12 @@ class StackFrameDataType implements Structure {
return dtc != null ? new StackComponentWrapper(dtc) : null;
}
@Override
public DataTypeComponent getDefinedComponent(int index) throws IndexOutOfBoundsException {
DataTypeComponent dtc = wrappedStruct.getDefinedComponent(index);
return new StackComponentWrapper(dtc);
}
@Override
public StackComponentWrapper getComponent(int ordinal) throws IndexOutOfBoundsException {
DataTypeComponent dtc = wrappedStruct.getComponent(ordinal);

View File

@@ -1169,6 +1169,72 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest {
checkConflictCount(0);
}
@Test
public void testMultiEdits14() throws Exception {
mtf.initialize("notepad2", new ProgramModifierListener() {
@Override
public void modifyLatest(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
// Create a conflict while retaining Bar's original six-byte length.
assertEquals(6, bar.getLength());
bar.getComponent(0).setComment("Latest Bar component");
int junk = 0;
}
@Override
public void modifyPrivate(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
CategoryPath path = new CategoryPath("/MISC");
Structure bar = (Structure) dtm.getDataType(path, "Bar");
bar.delete(1); // Remove the pointer, shrinking Bar from six bytes to two.
assertEquals(2, bar.getLength());
// Allocate the container before its target so that the merge records a fixup
// for the as-yet-unresolved target when processing the added container.
Structure container = (Structure) dtm.addDataType(
new StructureDataType(path, "FixupContainer", 0),
DataTypeConflictHandler.DEFAULT_HANDLER);
Structure target = new StructureDataType(path, "FixupTarget", 0);
target.add(ByteDataType.dataType);
target = (Structure) dtm.addDataType(target, DataTypeConflictHandler.DEFAULT_HANDLER);
container.add(bar, "bar", null);
container.growStructure(4);
container.add(target, "target", null);
assertFalse(container.isPackingEnabled());
assertEquals(6, container.getComponent(5).getOffset());
int junk = 0;
}
});
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// The added container initially uses Latest's six-byte Bar, so the placeholder
// at offset 6 has ordinal 1. Choosing MY Bar shrinks that component to two bytes,
// introducing four undefined components before the placeholder. Its ordinal is
// now 5, but FixUpInfo still records 1. The merge must not use that stale ordinal.
chooseOption(DataTypeMergeManager.OPTION_MY);
waitForCompletion();
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
Structure container =
(Structure) dtm.getDataType(new CategoryPath("/MISC"), "FixupContainer");
DataType target = dtm.getDataType(new CategoryPath("/MISC"), "FixupTarget");
assertEquals(2, bar.getLength());
assertEquals(7, container.getLength());
assertEquals(2, container.getNumDefinedComponents());
assertEquals(bar, container.getComponent(0).getDataType());
assertEquals(6, container.getComponent(5).getOffset());
assertEquals(target, container.getComponent(5).getDataType());
checkConflictCount(0);
}
@Test
public void testDeletedBaseTypeDef() throws Exception {

View File

@@ -28,6 +28,107 @@ import ghidra.program.model.data.*;
*/
public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest {
@Test
public void testDefinedComponentFixupAfterResize() throws Exception {
checkDefinedComponentFixupAfterLayoutChange(false, false);
}
@Test
public void testDefinedComponentFixupAfterEnablingPacking() throws Exception {
checkDefinedComponentFixupAfterLayoutChange(false, true);
}
@Test
public void testDefinedComponentFixupAfterDisablingPacking() throws Exception {
checkDefinedComponentFixupAfterLayoutChange(true, false);
}
private void checkDefinedComponentFixupAfterLayoutChange(boolean initiallyPacked,
boolean finallyPacked) throws Exception {
CategoryPath path = new CategoryPath("/MISC");
mtf.initialize("notepad2", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
// Ensure the zero-length type is already resolved when the container is added.
program.getDataTypeManager().addDataType(new ArrayDataType(ByteDataType.dataType, 0),
DataTypeConflictHandler.DEFAULT_HANDLER);
}
@Override
public void modifyLatest(ProgramDB program) throws Exception {
Structure bar = (Structure) program.getDataTypeManager().getDataType(path, "Bar");
bar.getComponent(0).setComment("Latest Bar component");
}
@Override
public void modifyPrivate(ProgramDB program) throws Exception {
DataTypeManager dtm = program.getDataTypeManager();
Structure bar = (Structure) dtm.getDataType(path, "Bar");
bar.delete(1);
assertEquals(2, bar.getLength());
// Allocate the container first so its target components require deferred fixups.
Structure container = (Structure) dtm.addDataType(
new StructureDataType(path, "LayoutContainer", 0),
DataTypeConflictHandler.DEFAULT_HANDLER);
Structure target = new StructureDataType(path, "LayoutTarget", 0);
target.add(ByteDataType.dataType);
target = (Structure) dtm.addDataType(target, DataTypeConflictHandler.DEFAULT_HANDLER);
container.add(bar, "bar", null);
container.growStructure(4);
container.insertBitFieldAt(6, 1, 0, ByteDataType.dataType, 3, "bits1", null);
container.insertBitFieldAt(6, 1, 3, ByteDataType.dataType, 2, "bits2", null);
container.add(new ArrayDataType(ByteDataType.dataType, 0), "zero1", null);
container.add(new ArrayDataType(ByteDataType.dataType, 0), "zero2", null);
container.add(target, "target1", null);
container.add(target, "target2", null);
container.setPackingEnabled(initiallyPacked);
}
});
executeMerge();
waitForPrompting(); // Wait until additions are complete and the Bar conflict is displayed.
DataTypeManager dtm = resultProgram.getDataTypeManager();
Structure container = (Structure) dtm.getDataType(path, "LayoutContainer");
assertNotNull(container);
DataTypeComponent[] components = container.getDefinedComponents();
assertEquals(7, components.length);
assertEquals(BadDataType.dataType, components[5].getDataType());
assertEquals(BadDataType.dataType, components[6].getDataType());
// The merge is paused at the Bar conflict, after the container fixups were recorded.
// Exercise a packing transition without replacing its defined-component sequence.
int tx = resultProgram.startTransaction("Change packing with pending fixups");
try {
container.setPackingEnabled(finallyPacked);
}
finally {
resultProgram.endTransaction(tx, true);
}
chooseOption(DataTypeMergeManager.OPTION_MY);
waitForCompletion();
container = (Structure) dtm.getDataType(path, "LayoutContainer");
components = container.getDefinedComponents();
assertEquals(finallyPacked, container.isPackingEnabled());
assertEquals(7, components.length);
assertEquals(2, components[0].getLength());
assertTrue(components[1].isBitFieldComponent());
assertTrue(components[2].isBitFieldComponent());
assertEquals(components[1].getOffset(), components[2].getOffset());
assertEquals(0, components[3].getLength());
assertEquals(0, components[4].getLength());
assertEquals(components[3].getOffset(), components[4].getOffset());
DataType target = dtm.getDataType(path, "LayoutTarget");
assertNotNull(target);
assertEquals("target1", components[5].getFieldName());
assertEquals(target, components[5].getDataType());
assertEquals("target2", components[6].getFieldName());
assertEquals(target, components[6].getDataType());
checkConflictCount(0);
}
private void setupRemoveInnerVsAddOuterContainingChangedInner() throws Exception {
final CategoryPath rootPath = new CategoryPath("/");

View File

@@ -746,10 +746,22 @@ class StructureDB extends CompositeDB implements StructureInternal {
@Override
public int getNumDefinedComponents() {
try (Closeable c = lock.read()) {
refreshIfNeeded();
return components.size();
}
}
@Override
public DataTypeComponentDB getDefinedComponent(int index) throws IndexOutOfBoundsException {
try (Closeable c = lock.read()) {
refreshIfNeeded();
if (index < 0 || index >= components.size()) {
throw new IndexOutOfBoundsException(index);
}
return components.get(index);
}
}
@Override
public DataTypeComponentDB getComponent(int ordinal) {
try (Closeable c = lock.read()) {

View File

@@ -389,16 +389,21 @@ class UnionDB extends CompositeDB implements UnionInternal {
}
@Override
public DataTypeComponentDB getComponent(int ordinal) {
public DataTypeComponentDB getDefinedComponent(int index) throws IndexOutOfBoundsException {
try (Closeable c = lock.read()) {
refreshIfNeeded();
if (ordinal < 0 || ordinal >= components.size()) {
return null;
if (index < 0 || index >= components.size()) {
throw new IndexOutOfBoundsException(index);
}
return components.get(ordinal);
return components.get(index);
}
}
@Override
public DataTypeComponentDB getComponent(int ordinal) {
return getDefinedComponent(ordinal);
}
@Override
public DataTypeComponentDB[] getComponents() {
try (Closeable c = lock.read()) {

View File

@@ -47,6 +47,17 @@ public interface Composite extends DataType {
*/
public abstract int getNumDefinedComponents();
/**
* Returns the defined component of this data type at the specified index
* within the ordered sequence of {@link #getDefinedComponents() defined components}.
* See {@link #getNumDefinedComponents()}.
* @param index the index of the defined component (numbering starts at 0).
* @return the data type component.
* @throws IndexOutOfBoundsException if the index is out of bounds
*/
public abstract DataTypeComponent getDefinedComponent(int index)
throws IndexOutOfBoundsException;
/**
* Returns the component of this data type with the indicated ordinal.
* @param ordinal the component's ordinal (numbering starts at 0).

View File

@@ -448,6 +448,14 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
dtc.setOrdinal(dtc.getOrdinal() + deltaOrdinal);
}
@Override
public DataTypeComponentImpl getDefinedComponent(int index) throws IndexOutOfBoundsException {
if (index < 0 || index >= components.size()) {
throw new IndexOutOfBoundsException(index);
}
return components.get(index);
}
@Override
public DataTypeComponentImpl getComponent(int ordinal) {
if (ordinal < 0 || ordinal >= numComponents) {

View File

@@ -104,8 +104,16 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna
}
@Override
public DataTypeComponent getComponent(int ordinal) {
return components.get(ordinal);
public DataTypeComponentImpl getDefinedComponent(int index) throws IndexOutOfBoundsException {
if (index < 0 || index >= components.size()) {
throw new IndexOutOfBoundsException(index);
}
return components.get(index);
}
@Override
public DataTypeComponentImpl getComponent(int ordinal) throws IndexOutOfBoundsException {
return getDefinedComponent(ordinal);
}
@Override