diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeUtilitiesTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeUtilitiesTest.java index 30f54dc4ba..90ac08ab20 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeUtilitiesTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeUtilitiesTest.java @@ -63,6 +63,52 @@ public class DataTypeUtilitiesTest extends AbstractGenericTest { } } + @Test + public void testIsSameKindDataType() { + + assertTrue( + DataTypeUtilities.isSameKindDataType(IntegerDataType.dataType, ShortDataType.dataType)); + assertFalse( + DataTypeUtilities.isSameKindDataType(FloatDataType.dataType, ShortDataType.dataType)); + + assertTrue( + DataTypeUtilities.isSameKindDataType(new PointerDataType(IntegerDataType.dataType), + new PointerDataType(ShortDataType.dataType))); + assertFalse( + DataTypeUtilities.isSameKindDataType(new PointerDataType(FloatDataType.dataType), + new PointerDataType(ShortDataType.dataType))); + + assertTrue( + DataTypeUtilities.isSameKindDataType(new StructureDataType("X", 10), + new StructureDataType("Y", 5))); + assertTrue( + DataTypeUtilities.isSameKindDataType(new UnionDataType("X"), new UnionDataType("Y"))); + assertFalse( + DataTypeUtilities.isSameKindDataType(new StructureDataType("X", 10), + new UnionDataType("Y"))); + + assertTrue( + DataTypeUtilities.isSameKindDataType( + new PointerDataType(new StructureDataType("X", 10)), + new PointerDataType(new StructureDataType("Y", 5)))); + assertTrue( + DataTypeUtilities.isSameKindDataType(new PointerDataType(new UnionDataType("X")), + new PointerDataType(new UnionDataType("Y")))); + assertFalse( + DataTypeUtilities.isSameKindDataType( + new PointerDataType(new StructureDataType("X", 10)), + new PointerDataType(new UnionDataType("Y")))); + + assertTrue( + DataTypeUtilities.isSameKindDataType( + new TypedefDataType("Foo", new PointerDataType(new StructureDataType("X", 10))), + new PointerDataType(new StructureDataType("Y", 5)))); + assertFalse( + DataTypeUtilities.isSameKindDataType( + new TypedefDataType("Foo", new PointerDataType(new StructureDataType("X", 10))), + new PointerDataType(new UnionDataType("Y")))); + } + @Test public void testEqualsIgnoreConflictviaManagedDataTypes() throws Exception { 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 45bd5f52af..2a68c9e4e2 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 @@ -24,6 +24,8 @@ import ghidra.program.database.ProgramDB; import ghidra.program.model.data.*; import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult; import ghidra.test.AbstractGhidraHeadedIntegrationTest; +import ghidra.util.UniversalID; +import ghidra.util.UniversalIdGenerator; /** * Tests for the {@link DataTypeConflictHandler conflict handler} stuff. @@ -153,7 +155,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { } /** - * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler to ensure that adding a empty conflicting structure resolves to a previous * populated structure. */ @@ -168,7 +170,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { } /** - * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler to ensure that adding a populated structure replaces an existing * 'empty' structure. 'Empty' means either 0 byte length or 1 byte length structs * as previous versions of Ghidra did not allow truly empty structs. @@ -227,7 +229,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { } /** - * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler to ensure that adding a conflicting typedef to a conflicting stub structure * (when there is already a typedef to a populated structure) correctly uses the * existing populated structure and existing typedef to the populated structure. @@ -253,7 +255,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { } /** - * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler to ensure that adding truly conflicting structures and typedefs * are treated as new data types and are renamed to a different name when added. */ @@ -283,7 +285,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { } /** - * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler when adding a conflicting typedef impl that is referred to multiple * times during a single addDataType() call. *
@@ -328,7 +330,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { } /** - * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler when adding a conflicting typedef impl (but equiv) that is referred to multiple * times during a single addDataType() call. *
@@ -368,7 +370,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { } /** - * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler when adding a typedef to a populated when there is already a typedef * to a stub structure. */ @@ -397,7 +399,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { /** * Tests the - * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler to be sure that, if all else is the same, the packed version is chosen * over the non-packed version. *
@@ -422,7 +424,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { /** * Tests the - * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler to be sure that, if all else is the same, the packed version is chosen * over the non-packed version. *
@@ -447,7 +449,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { /** * Tests the - * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * 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. *
@@ -476,7 +478,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { /** * Tests the - * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler to be sure that, if all else is the same, the new non-packed version is * chosen over the existing packed version. *
@@ -505,7 +507,7 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { /** * Tests the - * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH} + * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} * conflict handler to be sure that, if all else is the same, the packed version is chosen * over the non-packed version. *
@@ -515,24 +517,147 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest { public void testResolveDataTypeNonStructConflict() throws Exception { DataTypeManager dtm = new StandAloneDataTypeManager("Test"); int id = dtm.startTransaction(""); - Category otherRoot = dataMgr.getRootCategory(); - Category subc = otherRoot.createCategory("subc"); + try { + Category otherRoot = dataMgr.getRootCategory(); + Category subc = otherRoot.createCategory("subc"); - EnumDataType e = new EnumDataType(subc.getCategoryPath(), "Enum", 2); + EnumDataType e = new EnumDataType(subc.getCategoryPath(), "Enum", 2); - DataType resolvedEnum = - dtm.resolve(e, DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); - assertTrue(e.isEquivalent(resolvedEnum)); - assertEquals("/subc/Enum", resolvedEnum.getPathName()); + DataType resolvedEnum = + dtm.resolve(e, + DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); + assertTrue(e.isEquivalent(resolvedEnum)); + assertEquals("/subc/Enum", resolvedEnum.getPathName()); - e.add("xyz", 1); + e.add("xyz", 1); - resolvedEnum = - dtm.resolve(e, DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); - assertTrue(e.isEquivalent(resolvedEnum)); - assertEquals("/subc/Enum.conflict", resolvedEnum.getPathName()); + resolvedEnum = + dtm.resolve(e, + DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER); + assertTrue(e.isEquivalent(resolvedEnum)); + assertEquals("/subc/Enum.conflict", resolvedEnum.getPathName()); + } + finally { + dtm.endTransaction(id, true); + dtm.close(); + } + } + + /** + * Tests the + * {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER} + * conflict handler to be sure that and empty local structure will be replaced by + * a structure with source. + *
+ * Success is the source version is chosen over the empty local version.
+ */
+ @Test
+ public void testChooseStructWithSourceOverExistingEmptyStructures() throws Exception {
+
+ Structure struct = new StructureDataType(root, "TestStruct", 0, dataMgr);
+ struct = (Structure) dataMgr.resolve(struct, null);
+
+ SourceArchive source = new DummySourceArchive("Test");
+
+ Structure structWithSource = new StructureDataType(root, "TestStruct", 0, dataMgr);
+ structWithSource.setSourceArchive(source);
+ structWithSource.add(ByteDataType.dataType);
+
+ structWithSource = (Structure) dataMgr.resolve(structWithSource,
+ DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
+
+ assertEquals("TestStruct", structWithSource.getName());
+ assertTrue(struct == structWithSource);
+ SourceArchive sourceArchive = struct.getSourceArchive();
+ assertEquals(source.getSourceArchiveID(), sourceArchive.getSourceArchiveID());
+ assertEquals(source.getName(), sourceArchive.getName());
+ }
+
+ @Test
+ public void testResolvePointerConflict() {
+
+ DataType ptr1 =
+ new PointerDataType(new TypedefDataType("size_t", UnsignedIntegerDataType.dataType));
+ DataType ptr2 =
+ new PointerDataType(new TypedefDataType("size_t", IntegerDataType.dataType));
+
+ DataType ptr1resolved = dataMgr.resolve(ptr1, DataTypeConflictHandler.DEFAULT_HANDLER);
+ assertEquals("size_t *", ptr1resolved.getName());
+
+ DataType ptr2resolvedA = dataMgr.resolve(ptr2, DataTypeConflictHandler.KEEP_HANDLER);
+ assertTrue(ptr2resolvedA == ptr1resolved);
+
+ DataType ptr2resolvedB = dataMgr.resolve(ptr2, DataTypeConflictHandler.DEFAULT_HANDLER);
+ assertEquals("size_t.conflict *", ptr2resolvedB.getName());
+
+ DataType ptr2resolvedC = dataMgr.resolve(ptr2, DataTypeConflictHandler.REPLACE_HANDLER);
+ assertTrue(ptr2resolvedC == ptr2resolvedB);
+ }
+
+ @Test
+ public void testResolveArrayConflict() {
+
+ DataType array1 =
+ new ArrayDataType(new TypedefDataType("size_t", UnsignedIntegerDataType.dataType), 2,
+ -1);
+ DataType array2 =
+ new ArrayDataType(new TypedefDataType("size_t", IntegerDataType.dataType), 2, -1);
+
+ DataType array1resolved = dataMgr.resolve(array1, DataTypeConflictHandler.DEFAULT_HANDLER);
+ assertEquals("size_t[2]", array1resolved.getName());
+
+ DataType array2resolvedA = dataMgr.resolve(array2, DataTypeConflictHandler.KEEP_HANDLER);
+ assertTrue(array2resolvedA == array1resolved);
+
+ DataType array2resolvedB = dataMgr.resolve(array2, DataTypeConflictHandler.DEFAULT_HANDLER);
+ assertEquals("size_t.conflict[2]", array2resolvedB.getName());
+
+ DataType array2resolvedC = dataMgr.resolve(array2, DataTypeConflictHandler.REPLACE_HANDLER);
+ assertTrue(array2resolvedC == array2resolvedB);
+ }
+
+ private static class DummySourceArchive implements SourceArchive {
+
+ private final UniversalID id;
+ private final String archiveName;
+
+ public DummySourceArchive(String archiveName) {
+ this.id = UniversalIdGenerator.nextID();
+ this.archiveName = archiveName;
+ }
+
+ public ArchiveType getArchiveType() {
+ return ArchiveType.FILE;
+ }
+
+ public String getDomainFileID() {
+ return null;
+ }
+
+ public long getLastSyncTime() {
+ return 0;
+ }
+
+ public String getName() {
+ return archiveName;
+ }
+
+ public UniversalID getSourceArchiveID() {
+ return id;
+ }
+
+ public boolean isDirty() {
+ return false;
+ }
+
+ public void setDirtyFlag(boolean dirty) {
+ }
+
+ public void setLastSyncTime(long time) {
+ }
+
+ public void setName(String name) {
+ }
- dtm.endTransaction(id, true);
- dtm.close();
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java
index c2954f3926..023d56c265 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/CategoryDB.java
@@ -540,7 +540,7 @@ class CategoryDB extends DatabaseObject implements Category {
return;
}
if (existing != null) {
- ConflictResult result = mgr.resolveConflict(handler, movedDataType, existing);
+ ConflictResult result = handler.resolveConflict(movedDataType, existing);
if (result == ConflictResult.REPLACE_EXISTING) { // replace existing dt with new dt.
mgr.replaceDataType(existing, movedDataType, true);
}
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 9ef0243cd2..41eb5c283a 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
@@ -1049,16 +1049,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
else if (sourceArchive == null || dataType.getUniversalID() == null) {
// if the dataType has no source or it has no ID (datatypes with no ID are
// always local i.e. pointers)
- resolvedDataType = resolveNoSourceDataType(dataType, currentHandler);
+ resolvedDataType = resolveDataTypeNoSource(dataType, currentHandler);
}
else if (!sourceArchive.getSourceArchiveID().equals(getUniversalID()) &&
sourceArchive.getArchiveType() == ArchiveType.PROGRAM) {
// dataTypes from a different program don't carry over their identity.
- resolvedDataType = resolveNoSourceDataType(dataType, currentHandler);
+ resolvedDataType = resolveDataTypeNoSource(dataType, currentHandler);
}
else {
- resolvedDataType =
- resolveDataTypeWithSource(dataType, sourceArchive, currentHandler);
+ resolvedDataType = resolveDataTypeWithSource(dataType, currentHandler);
}
cacheResolvedDataType(dataType, resolvedDataType);
if (resolvedDataType instanceof DataTypeDB) {
@@ -1083,6 +1082,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
private DataType resolveBuiltIn(DataType dataType, DataTypeConflictHandler handler) {
+
+ if (dataType instanceof Pointer) {
+ // treat built-in pointers like other datatypes without a source
+ return resolveDataTypeNoSource(dataType, currentHandler);
+ }
+
// can't do this check now because Pointers from the BuiltinDataTypeManager are
// not instances of BuiltInDataType because the BuiltInDataTypeManger converts
// pointers from BuiltIns to PointerDBs (Probably shouldn't, but the
@@ -1094,7 +1099,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
return existingDataType;
}
// oops a non-builtin dataType exists with the same name. Only option is to rename existing
- String dtName = getUnusedConflictName(dataType.getCategoryPath(), dataType.getName());
+ String dtName = getUnusedConflictName(dataType);
try {
existingDataType.setName(dtName);
}
@@ -1135,84 +1140,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
}
- /**
- * Either finds an equivalent dataType with the same categoryPath and name (or
- * conflict name) to the given dataType. Otherwise, it creates a new dataType in
- * this archive equivalent to the given dataType. If a dataType exists with same
- * path and name but is not equivalent, the handler will resolve the problem in
- * one of 3 ways. 1) A new dataType will be created, but with a .conflict name
- * 2) The existing dataType will be replaced by a resolved copy of the given
- * dataType. 3) The existing dataType will be returned instead of a resolved
- * version of the given dataType.
- *
- * @param dataType the dataType for which to return an equivalent dataType in
- * this manager
- * @param handler Used to handle collisions with dataTypes with same path and
- * name that is
- * @return resolved datatype
- */
- private DataType resolveNoSourceDataType(DataType dataType, DataTypeConflictHandler handler) {
-
- DataType existingDataType = findEquivalentDataTypeSameLocation(dataType, handler);
- if (existingDataType != null) {
- return existingDataType;
- }
- existingDataType = getDataType(dataType.getCategoryPath(), dataType.getName());
- if (existingDataType == null) {
- return createDataType(dataType, dataType.getName(), null, handler);
- }
-
- // So we have a dataType with the same path and name, but not equivalent, so use
- // the conflictHandler to decide what to do.
- ConflictResult result = resolveConflict(handler, dataType, existingDataType);
- switch (result) {
-
- case REPLACE_EXISTING: // new type replaces old conflicted type
- try {
- if (updateExistingDataType(existingDataType, dataType)) {
- return existingDataType;
- }
- renameToUnusedConflictName(existingDataType);
- DataType newDataType =
- createDataType(dataType, dataType.getName(), null, handler);
- try {
- replace(existingDataType, newDataType);
- }
- catch (DataTypeDependencyException e) {
- throw new IllegalArgumentException(
- "Invalid datatype replacement: " + newDataType.getName(), e);
- }
- return newDataType;
- }
- catch (DataTypeDependencyException e) {
- // new type refers to old type - fallthrough to RENAME_AND_ADD
- // TODO: alternatively we could throw an exception
- }
-
- case RENAME_AND_ADD: // default handler behavior
- String dtName =
- getUnusedConflictName(dataType.getCategoryPath(), dataType.getName());
- DataType newDataType = createDataType(dataType, dtName, null, handler);
-
- // resolving child data types could result in another copy of dataType in the
- // manager depending upon the conflict handler - check again
- existingDataType = findEquivalentDataTypeSameLocation(dataType, handler);
- // If there is an equivalent datatype, remove the added type and return the existing
- if (existingDataType != null && existingDataType != newDataType) {
- removeInternal(newDataType, TaskMonitor.DUMMY);
- return existingDataType;
- }
- return newDataType;
-
- case USE_EXISTING: // new type is discarded and old conflicted type is returned
- return existingDataType;
- }
- return null;
- }
-
private void renameToUnusedConflictName(DataType dataType) {
- String dtName = dataType.getName();
- String name = getUnusedConflictName(dataType.getCategoryPath(), dtName);
+ String name = getUnusedConflictName(dataType);
try {
dataType.setName(name);
}
@@ -1232,15 +1161,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
*
* @param existingDataType existing datatype
* @param dataType new datatype
+ * @param sourceArchive source archive associated with new type (may be null).
+ * If not null the existingDataType will be updated with source info.
* @return true if replacement approach was successful, else false
* @throws DataTypeDependencyException if datatype contains dependency issues
* during resolve process
*/
- private boolean updateExistingDataType(DataType existingDataType, DataType dataType)
- throws DataTypeDependencyException {
-
- // TODO: this approach could be added to other DB datatypes to avoid
- // unnecessary creation and removal.
+ private boolean updateExistingDataType(DataType existingDataType, DataType dataType,
+ SourceArchive sourceArchive) throws DataTypeDependencyException {
try {
if (existingDataType instanceof StructureDB) {
@@ -1249,7 +1177,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
StructureDB existingStruct = (StructureDB) existingDataType;
existingStruct.doReplaceWith((StructureInternal) dataType, true);
- return true;
}
else if (existingDataType instanceof UnionDB) {
if (!(dataType instanceof UnionInternal)) {
@@ -1257,8 +1184,37 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
}
UnionDB existingUnion = (UnionDB) existingDataType;
existingUnion.doReplaceWith((UnionInternal) dataType, true);
- return true;
}
+ else if (existingDataType instanceof FunctionDefinitionDB) {
+ if (!(dataType instanceof FunctionDefinition)) {
+ return false;
+ }
+ existingDataType.replaceWith(dataType);
+ }
+ else if (existingDataType instanceof EnumDB) {
+ if (!(dataType instanceof Enum)) {
+ return false;
+ }
+ existingDataType.replaceWith(dataType);
+ }
+ else if (existingDataType instanceof TypedefDB) {
+ if (!(dataType instanceof TypeDef)) {
+ return false;
+ }
+ existingDataType.replaceWith(dataType);
+ }
+ else {
+ return false;
+ }
+
+ if (sourceArchive != null) {
+ existingDataType.setSourceArchive(sourceArchive);
+ ((DataTypeDB) existingDataType).setUniversalID(dataType.getUniversalID());
+ long lastChangeTime = dataType.getLastChangeTime();
+ existingDataType.setLastChangeTime(lastChangeTime);
+ existingDataType.setLastChangeTimeInSourceArchive(lastChangeTime);
+ }
+ return true;
}
catch (IOException e) {
dbError(e);
@@ -1266,6 +1222,21 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
return false;
}
+ /**
+ * This method gets a ".conflict" name that is not currently used by any data
+ * types in the indicated category of the data type manager.
+ * @param dt datatype who name is used to establish non-conflict base name
+ * @return the unused conflict name or original name for datatypes whose name is automatic
+ */
+ public String getUnusedConflictName(DataType dt) {
+ String name = dt.getName();
+ if ((dt instanceof Array) || (dt instanceof Pointer) || (dt instanceof BuiltInDataType)) {
+ // name not used - anything will do
+ return name;
+ }
+ return getUnusedConflictName(dt.getCategoryPath(), name);
+ }
+
/**
* This method gets a ".conflict" name that is not currently used by any data
* types in the indicated category of the data type manager.
@@ -1283,20 +1254,59 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
if (index > 0) {
name = name.substring(0, index);
}
+ // Name sequence:
+ *
+ * Other uses of {@link BuiltInDataType} must match the specific implementation class.
+ * @param dataType1 first data type
+ * @param dataType2 second data type
+ * @return true if the two dataTypes are the same basic kind else false
+ */
+ public static boolean isSameKindDataType(DataType dataType1, DataType dataType2) {
+
+ while (true) {
+ if (dataType1 == dataType2) {
+ return true;
+ }
+
+ // Ignore the use of typedefs - strip away
+ if (dataType1 instanceof TypeDef td1) {
+ dataType1 = td1.getBaseDataType();
+ }
+ if (dataType2 instanceof TypeDef td2) {
+ dataType2 = td2.getBaseDataType();
+ }
+
+ if (dataType1 instanceof Pointer p1 && dataType2 instanceof Pointer p2) {
+ dataType1 = p1.getDataType();
+ dataType2 = p2.getDataType();
+ }
+ else if (dataType2 instanceof Array a1 && dataType2 instanceof Array a2) {
+ dataType1 = a1.getDataType();
+ dataType2 = a2.getDataType();
+ }
+ else if (dataType1 instanceof Enum) {
+ return dataType2 instanceof Enum;
+ }
+ else if (dataType1 instanceof Structure) {
+ return dataType2 instanceof Structure;
+ }
+ else if (dataType1 instanceof Union) {
+ return dataType2 instanceof Union;
+ }
+ else if (dataType1 instanceof BuiltInDataType dt1) {
+ return isSameKindBuiltInDataType(dt1, dataType2);
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ private static boolean isSameKindBuiltInDataType(BuiltInDataType dataType1,
+ DataType dataType2) {
+ if (dataType1 instanceof BuiltIn) {
+ // Same kind if both types share a common BuiltIn implementation
+ Class> baseClass = dataType1.getClass().getSuperclass();
+ Class> superClass;
+ while ((superClass = baseClass.getSuperclass()) != BuiltIn.class) {
+ baseClass = superClass;
+ }
+ return baseClass.isAssignableFrom(dataType2.getClass());
+ }
+ // Ensure built-in implementation class is the same
+ return dataType1.getClass().equals(dataType2.getClass());
+ }
+
/**
* Get the name of a data type with all conflict naming patterns removed.
*
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveUpgradeMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveUpgradeMap.java
index 179dbf3219..3b59b71c03 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveUpgradeMap.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SourceArchiveUpgradeMap.java
@@ -90,54 +90,54 @@ public class SourceArchiveUpgradeMap {
return new String[] { "short", "int", "long", "longlong", "wchar_t", "bool" };
}
-}
+ private static class SourceArchiveImpl implements SourceArchive {
-class SourceArchiveImpl implements SourceArchive {
+ private final UniversalID id;
+ private final String archiveName;
- private final UniversalID id;
- private final String archiveName;
+ public SourceArchiveImpl(UniversalID id, String archiveName) {
+ this.id = id;
+ this.archiveName = archiveName;
+ }
- public SourceArchiveImpl(UniversalID id, String archiveName) {
- this.id = id;
- this.archiveName = archiveName;
- }
+ public SourceArchiveImpl() {
+ id = DataTypeManager.LOCAL_ARCHIVE_UNIVERSAL_ID;
+ archiveName = "";
+ }
- public SourceArchiveImpl() {
- id = DataTypeManager.LOCAL_ARCHIVE_UNIVERSAL_ID;
- archiveName = "";
- }
+ public ArchiveType getArchiveType() {
+ return ArchiveType.FILE;
+ }
- public ArchiveType getArchiveType() {
- return ArchiveType.FILE;
- }
+ public String getDomainFileID() {
+ return null;
+ }
- public String getDomainFileID() {
- return null;
- }
+ public long getLastSyncTime() {
+ return 0;
+ }
- public long getLastSyncTime() {
- return 0;
- }
+ public String getName() {
+ return archiveName;
+ }
- public String getName() {
- return archiveName;
- }
+ public UniversalID getSourceArchiveID() {
+ return id;
+ }
- public UniversalID getSourceArchiveID() {
- return id;
- }
+ public boolean isDirty() {
+ return false;
+ }
- public boolean isDirty() {
- return false;
- }
+ public void setDirtyFlag(boolean dirty) {
+ }
- public void setDirtyFlag(boolean dirty) {
- }
+ public void setLastSyncTime(long time) {
+ }
- public void setLastSyncTime(long time) {
- }
+ public void setName(String name) {
+ }
- public void setName(String name) {
}
}