mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 20:22:44 +00:00
Merge remote-tracking branch 'origin/GP-3632_ghidra1_DataTypeResolve--SQUASHED'
This commit is contained in:
commit
7b621c8989
@ -193,9 +193,8 @@ public class DBTraceDataTypeManager extends ProgramBasedDataTypeManagerDB
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDataTypeIDs(LinkedList<Long> deletedIds, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
trace.getCodeManager().clearData(deletedIds, monitor);
|
||||
protected void deleteDataTypeIDs(LinkedList<Long> deletedIds) {
|
||||
trace.getCodeManager().clearData(deletedIds, TaskMonitor.DUMMY);
|
||||
trace.getSymbolManager().invalidateCache(false);
|
||||
}
|
||||
|
||||
|
@ -22,10 +22,8 @@ import javax.swing.tree.TreePath;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.DefaultDataTypeArchiveService;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeComparator;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeManagerChangeListener;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
// FIXME!! TESTING
|
||||
@ -83,9 +81,8 @@ public class DefaultDataTypeManagerService extends DefaultDataTypeArchiveService
|
||||
public List<DataType> getSortedDataTypeList() {
|
||||
List<DataType> dataTypes =
|
||||
builtInDataTypesManager.getDataTypes(BuiltInSourceArchive.INSTANCE);
|
||||
dataTypes.sort(new DataTypeComparator());
|
||||
dataTypes.sort(DataTypeComparator.INSTANCE);
|
||||
return dataTypes;
|
||||
// throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,7 +19,6 @@ import java.util.*;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeComparator;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
@ -32,7 +31,6 @@ import ghidra.util.task.*;
|
||||
public class DataTypeIndexer {
|
||||
private List<DataTypeManager> dataTypeManagers = new ArrayList<>();
|
||||
private List<DataType> dataTypeList = Collections.emptyList();
|
||||
private Comparator<DataType> dataTypeComparator = new DataTypeComparator();
|
||||
private DataTypeIndexUpdateListener listener = new DataTypeIndexUpdateListener();
|
||||
|
||||
private volatile boolean isStale = true;
|
||||
@ -59,7 +57,8 @@ public class DataTypeIndexer {
|
||||
|
||||
/**
|
||||
* Returns a sorted list of the data types open in the current tool. The sorting of the list
|
||||
* is done using the {@link DataTypeComparator}.
|
||||
* is done using the {@link DataTypeComparator} whose primary sort is based upon the
|
||||
* {@link DataTypeNameComparator}.
|
||||
*
|
||||
* @return a sorted list of the data types open in the current tool.
|
||||
*/
|
||||
@ -128,16 +127,13 @@ public class DataTypeIndexer {
|
||||
monitor.initialize(dataTypeManagers.size());
|
||||
monitor.setMessage("Preparing to index data types...");
|
||||
|
||||
Iterator<DataTypeManager> iterator = dataTypeManagers.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
DataTypeManager dataTypeManager = iterator.next();
|
||||
|
||||
for (DataTypeManager dataTypeManager : dataTypeManagers) {
|
||||
monitor.setMessage("Searching " + dataTypeManager.getName());
|
||||
dataTypeManager.getAllDataTypes(list);
|
||||
monitor.incrementProgress(1);
|
||||
}
|
||||
|
||||
Collections.sort(list, dataTypeComparator);
|
||||
Collections.sort(list, DataTypeComparator.INSTANCE);
|
||||
}
|
||||
|
||||
List<DataType> getList() {
|
||||
|
@ -50,8 +50,9 @@ public class DataTypeNode extends DataTypeTreeNode {
|
||||
|
||||
@Override
|
||||
public int compareTo(GTreeNode node) {
|
||||
if (node instanceof DataTypeNode) {
|
||||
return super.compareTo(node);
|
||||
if (node instanceof DataTypeNode other) {
|
||||
return DataTypeNameComparator.INSTANCE.compare(dataType.getName(),
|
||||
other.dataType.getName());
|
||||
}
|
||||
|
||||
return 1; // DataTypeNodes always come after ****everything else****
|
||||
|
@ -1,52 +0,0 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.datamgr.util;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class DataTypeComparator implements Comparator<DataType> {
|
||||
|
||||
public int compare(DataType dt1, DataType dt2) {
|
||||
String name1 = dt1.getName();
|
||||
String name2 = dt2.getName();
|
||||
|
||||
// TODO: should built-ins always come first in the list? (in case we have an 'a' named archive?)
|
||||
|
||||
// if the names are the same, then sort by the path
|
||||
if ( name1.equalsIgnoreCase( name2 ) ) {
|
||||
|
||||
if ( !name1.equals( name2 ) ) {
|
||||
// let equivalent names be sorted by case ('-' for lower-case first)
|
||||
return -name1.compareTo( name2 );
|
||||
}
|
||||
|
||||
String dtmName1 = dt1.getDataTypeManager().getName();
|
||||
String dtmName2 = dt2.getDataTypeManager().getName();
|
||||
|
||||
// if they have the same name, and are in the same DTM, then compare paths
|
||||
if ( dtmName1.equalsIgnoreCase( dtmName2 ) ) {
|
||||
return dt1.getPathName().compareToIgnoreCase( dt2.getPathName() );
|
||||
}
|
||||
|
||||
return dtmName1.compareToIgnoreCase( dtmName2 );
|
||||
}
|
||||
|
||||
return name1.compareToIgnoreCase( name2 );
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ package ghidra.app.services;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
/**
|
||||
* Simplified datatype service interface to provide query capabilities
|
||||
@ -28,7 +28,9 @@ public interface DataTypeQueryService {
|
||||
/**
|
||||
* Gets the sorted list of all datatypes known by this service via it's owned DataTypeManagers.
|
||||
* This method can be called frequently, as the underlying data is indexed and only updated
|
||||
* as changes are made.
|
||||
* as changes are made. The sorting of the list is done using the {@link DataTypeComparator}
|
||||
* whose primary sort is based upon the {@link DataTypeNameComparator}.
|
||||
*
|
||||
* @return the sorted list of known data types.
|
||||
*/
|
||||
public List<DataType> getSortedDataTypeList();
|
||||
|
@ -27,8 +27,7 @@ import org.junit.Test;
|
||||
import generic.test.ConcurrentTestExceptionHandler;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
public class StructureEditorUnlockedCellEdit2Test
|
||||
extends AbstractStructureEditorTest {
|
||||
public class StructureEditorUnlockedCellEdit2Test extends AbstractStructureEditorTest {
|
||||
|
||||
@Test
|
||||
public void testF2EditKey() throws Exception {
|
||||
@ -764,7 +763,7 @@ public class StructureEditorUnlockedCellEdit2Test
|
||||
enter();
|
||||
assertIsEditingField(rowNum, colNum);
|
||||
|
||||
assertEquals("simpleStructure doesn't fit within 4 bytes, need 29 bytes",
|
||||
assertEquals("simpleStructure doesn't fit within 4 bytes, need 12 bytes",
|
||||
model.getStatus());
|
||||
|
||||
escape();
|
||||
@ -778,9 +777,9 @@ public class StructureEditorUnlockedCellEdit2Test
|
||||
|
||||
DataType newDt = getDataType(22);
|
||||
assertEquals("simpleStructure", newDt.getDisplayName());
|
||||
assertEquals(29, newDt.getLength());
|
||||
assertEquals(29, getLength(22));
|
||||
assertEquals(350, model.getLength());
|
||||
assertEquals(12, newDt.getLength());
|
||||
assertEquals(12, getLength(22));
|
||||
assertEquals(333, model.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -304,8 +304,8 @@ public class UnionEditorCellEditTest extends AbstractUnionEditorTest {
|
||||
init(simpleUnion, pgmBbCat, false);
|
||||
|
||||
startTransaction("addExternal");
|
||||
ExternalLocation extLoc = program.getExternalManager().addExtFunction(Library.UNKNOWN,
|
||||
"extLabel", null, SourceType.USER_DEFINED);
|
||||
ExternalLocation extLoc = program.getExternalManager()
|
||||
.addExtFunction(Library.UNKNOWN, "extLabel", null, SourceType.USER_DEFINED);
|
||||
Function function = extLoc.createFunction();
|
||||
endTransaction(true);
|
||||
|
||||
@ -726,9 +726,9 @@ public class UnionEditorCellEditTest extends AbstractUnionEditorTest {
|
||||
assertNotEditingField();
|
||||
DataType newDt = getDataType(rowNum);
|
||||
assertEquals("simpleStructure", newDt.getName());
|
||||
assertEquals(29, newDt.getLength());
|
||||
assertEquals(29, getLength(rowNum));
|
||||
assertEquals(29, model.getLength());
|
||||
assertEquals(12, newDt.getLength());
|
||||
assertEquals(12, getLength(rowNum));
|
||||
assertEquals(12, model.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -60,7 +60,7 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration
|
||||
// with Tool-based service.
|
||||
dtList = new ArrayList<>(super.getSortedDataTypeList());
|
||||
program.getDataTypeManager().getAllDataTypes(dtList);
|
||||
Collections.sort(dtList, new NameComparator());
|
||||
Collections.sort(dtList, DataTypeComparator.INSTANCE);
|
||||
return dtList;
|
||||
}
|
||||
|
||||
@ -83,17 +83,6 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration
|
||||
parser = new FunctionSignatureParser(program.getDataTypeManager(), service);
|
||||
}
|
||||
|
||||
private class NameComparator implements Comparator<DataType> {
|
||||
@Override
|
||||
public int compare(DataType d1, DataType d2) {
|
||||
int c = d1.getName().compareTo(d2.getName());
|
||||
if (c == 0) {
|
||||
return d1.getCategoryPath().compareTo(d2.getCategoryPath());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubstitute() {
|
||||
assertEquals("barxxxbar", parser.substitute("barfoobar", "foo", "xxx"));
|
||||
@ -391,16 +380,16 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration
|
||||
FunctionSignature f = fun("int", "Bob");
|
||||
FunctionDefinitionDataType dt =
|
||||
parser.parse(f, "unsigned long[3] Foo(unsigned long long *, signed int[3], StructA*)");
|
||||
assertTrue((new ArrayDataType(UnsignedLongDataType.dataType, 3, -1)).isEquivalent(
|
||||
dt.getReturnType()));
|
||||
assertTrue((new ArrayDataType(UnsignedLongDataType.dataType, 3, -1))
|
||||
.isEquivalent(dt.getReturnType()));
|
||||
assertEquals("Foo", dt.getName());
|
||||
ParameterDefinition[] args = dt.getArguments();
|
||||
assertEquals(3, args.length);
|
||||
assertTrue((new PointerDataType(UnsignedLongLongDataType.dataType)).isEquivalent(
|
||||
args[0].getDataType()));
|
||||
assertTrue((new PointerDataType(UnsignedLongLongDataType.dataType))
|
||||
.isEquivalent(args[0].getDataType()));
|
||||
assertEquals("", args[0].getName());
|
||||
assertTrue((new ArrayDataType(IntegerDataType.dataType, 3, -1)).isEquivalent(
|
||||
args[1].getDataType()));
|
||||
assertTrue((new ArrayDataType(IntegerDataType.dataType, 3, -1))
|
||||
.isEquivalent(args[1].getDataType()));
|
||||
assertEquals("", args[1].getName());
|
||||
assertTrue(args[2].getDataType() instanceof Pointer);
|
||||
assertEquals("", args[2].getName());
|
||||
@ -430,11 +419,11 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration
|
||||
"unsigned long[3] Bob(unsigned long long *foo, signed int[3] bar, StructA *s)");
|
||||
ParameterDefinition[] args = dt.getArguments();
|
||||
assertEquals(3, args.length);
|
||||
assertTrue((new PointerDataType(UnsignedLongLongDataType.dataType)).isEquivalent(
|
||||
args[0].getDataType()));
|
||||
assertTrue((new PointerDataType(UnsignedLongLongDataType.dataType))
|
||||
.isEquivalent(args[0].getDataType()));
|
||||
assertEquals("foo", args[0].getName());
|
||||
assertTrue((new ArrayDataType(IntegerDataType.dataType, 3, -1)).isEquivalent(
|
||||
args[1].getDataType()));
|
||||
assertTrue((new ArrayDataType(IntegerDataType.dataType, 3, -1))
|
||||
.isEquivalent(args[1].getDataType()));
|
||||
assertEquals("bar", args[1].getName());
|
||||
assertTrue(args[2].getDataType() instanceof Pointer);
|
||||
assertEquals("s", args[2].getName());
|
||||
|
@ -402,12 +402,7 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertTrue(dts[i].isEquivalent(newdts[i]));
|
||||
}
|
||||
DataType[] d = s.getDataTypes();
|
||||
Arrays.sort(d, new Comparator<DataType>() {
|
||||
@Override
|
||||
public int compare(DataType o1, DataType o2) {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
Arrays.sort(d, DataTypeComparator.INSTANCE);
|
||||
assertEquals(dts.length, d.length);
|
||||
assertTrue(newdts[0] == d[0]);
|
||||
}
|
||||
@ -852,7 +847,8 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath, CategoryPath newPath) {
|
||||
public void categoryRenamed(DataTypeManager dtm, CategoryPath oldPath,
|
||||
CategoryPath newPath) {
|
||||
events.add(new Event("Cat Renamed", null, newPath, oldPath.getName(), null));
|
||||
}
|
||||
|
||||
@ -877,12 +873,13 @@ public class CategoryTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
@Override
|
||||
public void dataTypeRemoved(DataTypeManager dtm, DataTypePath path) {
|
||||
events.add(new Event("DT Removed", path.getCategoryPath(), null,
|
||||
path.getDataTypeName(), null));
|
||||
events.add(new Event("DT Removed", path.getCategoryPath(), null, path.getDataTypeName(),
|
||||
null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath, DataTypePath newPath) {
|
||||
public void dataTypeRenamed(DataTypeManager dtm, DataTypePath oldPath,
|
||||
DataTypePath newPath) {
|
||||
DataType dataType = dtm.getDataType(newPath);
|
||||
events.add(new Event("DT Renamed", null, null, oldPath.getDataTypeName(), dataType));
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ package ghidra.program.database.data;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
@ -26,6 +28,8 @@ import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.util.UniversalID;
|
||||
import ghidra.util.UniversalIdGenerator;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Tests for the {@link DataTypeConflictHandler conflict handler} stuff.
|
||||
@ -523,17 +527,15 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
EnumDataType e = new EnumDataType(subc.getCategoryPath(), "Enum", 2);
|
||||
|
||||
DataType resolvedEnum =
|
||||
dtm.resolve(e,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
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);
|
||||
|
||||
resolvedEnum =
|
||||
dtm.resolve(e,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
resolvedEnum = dtm.resolve(e,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
assertTrue(e.isEquivalent(resolvedEnum));
|
||||
assertEquals("/subc/Enum.conflict", resolvedEnum.getPathName());
|
||||
}
|
||||
@ -597,9 +599,8 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
@Test
|
||||
public void testResolveArrayConflict() {
|
||||
|
||||
DataType array1 =
|
||||
new ArrayDataType(new TypedefDataType("size_t", UnsignedIntegerDataType.dataType), 2,
|
||||
-1);
|
||||
DataType array1 = new ArrayDataType(
|
||||
new TypedefDataType("size_t", UnsignedIntegerDataType.dataType), 2, -1);
|
||||
DataType array2 =
|
||||
new ArrayDataType(new TypedefDataType("size_t", IntegerDataType.dataType), 2, -1);
|
||||
|
||||
@ -616,6 +617,203 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertTrue(array2resolvedC == array2resolvedB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveWithCircularDependency() {
|
||||
|
||||
Structure struct1 = new StructureDataType("s1", 0, dataMgr);
|
||||
struct1.setPackingEnabled(true);
|
||||
|
||||
Structure struct2 = new StructureDataType("s2", 0, dataMgr);
|
||||
struct2.setPackingEnabled(true);
|
||||
|
||||
struct1.add(new PointerDataType(struct2));
|
||||
struct2.add(new PointerDataType(struct1));
|
||||
|
||||
Structure struct1a = (Structure) dataMgr.resolve(struct1,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
|
||||
Structure struct1b = (Structure) dataMgr.resolve(struct1,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
|
||||
assertTrue(struct1a == struct1b);
|
||||
|
||||
assertEquals(5, dataMgr.getDataTypeRecordCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexResolveWithConflictReplacement() {
|
||||
|
||||
// FIXME: Add typedef to struct1 pointer used as struct2 component
|
||||
// FIXME: Add add array of struct1 pointers used as struct1 component
|
||||
|
||||
Structure struct1a = new StructureDataType("s1", 0, dataMgr);
|
||||
struct1a.setPackingEnabled(true);
|
||||
Structure struct2a = new StructureDataType("s2", 0, dataMgr);
|
||||
struct2a.add(ByteDataType.dataType);
|
||||
struct2a.add(new PointerDataType(struct1a, dataMgr));
|
||||
|
||||
struct1a.add(new PointerDataType(struct1a, dataMgr));
|
||||
struct1a.add(new PointerDataType(struct2a, dataMgr));
|
||||
struct1a.add(new ArrayDataType(struct2a, 2, -1, dataMgr));
|
||||
struct1a.add(new TypedefDataType("S2TD", struct2a));
|
||||
|
||||
struct1a = (Structure) dataMgr.resolve(struct1a,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
|
||||
Structure struct1b = new StructureDataType("s1", 0, dataMgr);
|
||||
struct1b.setPackingEnabled(true);
|
||||
Structure struct2b = new StructureDataType("s2", 0, dataMgr); // not-yet-defined - will get replaced by struct2a
|
||||
|
||||
struct1b.add(new PointerDataType(struct1b, dataMgr));
|
||||
struct1b.add(new PointerDataType(struct2b, dataMgr));
|
||||
struct1b.add(new ArrayDataType(struct2b, 2, -1, dataMgr));
|
||||
struct1b.add(new TypedefDataType("S2TD", struct2b));
|
||||
|
||||
struct1b = (Structure) dataMgr.resolve(struct1b,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
|
||||
assertTrue(struct1a == struct1b);
|
||||
|
||||
assertNoConflict("s1");
|
||||
assertNoConflict("s2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexResolveWithConflictReplacement2() {
|
||||
|
||||
// FIXME: Add typedef to struct1 pointer used as struct2 component
|
||||
// FIXME: Add add array of struct1 pointers used as struct1 component
|
||||
|
||||
Structure struct1a = new StructureDataType("s1", 0, dataMgr);
|
||||
struct1a.setPackingEnabled(true);
|
||||
Structure struct2a = new StructureDataType("s2", 0, dataMgr); // not-yet-defined - will get replaced by struct2b
|
||||
|
||||
struct1a.add(new PointerDataType(struct1a, dataMgr));
|
||||
struct1a.add(new PointerDataType(struct2a, dataMgr));
|
||||
struct1a.add(new ArrayDataType(struct2a, 2, -1, dataMgr));
|
||||
struct1a.add(new TypedefDataType("S2TD", struct2a));
|
||||
|
||||
struct1a = (Structure) dataMgr.resolve(struct1a,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
|
||||
System.out.println("-- After First Resolve --");
|
||||
System.out.println(struct1a);
|
||||
Pointer ptr2a = (Pointer) struct1a.getComponent(1).getDataType();
|
||||
struct2a = (Structure) ptr2a.getDataType();
|
||||
System.out.println(struct2a);
|
||||
|
||||
Structure struct1b = new StructureDataType("s1", 0, dataMgr);
|
||||
struct1b.setPackingEnabled(true);
|
||||
Structure struct2b = new StructureDataType("s2", 0, dataMgr);
|
||||
struct2b.setPackingEnabled(true);
|
||||
struct2b.add(ByteDataType.dataType);
|
||||
struct2b.add(new PointerDataType(struct1b, dataMgr));
|
||||
|
||||
struct1b.add(new PointerDataType(struct1b, dataMgr));
|
||||
struct1b.add(new PointerDataType(struct2b, dataMgr));
|
||||
struct1b.add(new ArrayDataType(struct2b, 2, -1, dataMgr));
|
||||
struct1b.add(new TypedefDataType("S2TD", struct2b));
|
||||
|
||||
struct1b = (Structure) dataMgr.resolve(struct1b,
|
||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
||||
|
||||
System.out.println("-- After Second Resolve (original instances - s2 content replaced) --");
|
||||
System.out.println(struct1a);
|
||||
System.out.println(struct2a);
|
||||
|
||||
System.out.println("-- After Second Resolve (bad s1.conflict) --");
|
||||
System.out.println(struct1b);
|
||||
Pointer ptr2b = (Pointer) struct1b.getComponent(1).getDataType();
|
||||
struct2b = (Structure) ptr2b.getDataType();
|
||||
System.out.println(struct2b);
|
||||
|
||||
// struct1a should get eliminated and replaced by struct1b
|
||||
assertTrue(struct1a == struct1b);
|
||||
|
||||
assertNoConflict("s1");
|
||||
assertNoConflict("s2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDedupeAllConflicts() throws CancelledException {
|
||||
|
||||
Structure struct1a = new StructureDataType("s1", 0, dataMgr);
|
||||
struct1a.setPackingEnabled(true);
|
||||
|
||||
struct1a.add(ByteDataType.dataType);
|
||||
Structure s1 = (Structure) dataMgr.resolve(struct1a, null);
|
||||
|
||||
struct1a.add(ByteDataType.dataType);
|
||||
Structure s2 = (Structure) dataMgr.resolve(struct1a, null);
|
||||
|
||||
struct1a.add(ByteDataType.dataType);
|
||||
Structure s3 = (Structure) dataMgr.resolve(struct1a, null);
|
||||
|
||||
// force all conflicts to become equivalent
|
||||
s1.deleteAll();
|
||||
s2.deleteAll();
|
||||
s3.deleteAll();
|
||||
|
||||
ArrayList<DataType> list = new ArrayList<>();
|
||||
dataMgr.findDataTypes("s1", list);
|
||||
assertEquals(3, list.size());
|
||||
|
||||
dataMgr.dedupeAllConflicts(TaskMonitor.DUMMY);
|
||||
|
||||
list.clear();
|
||||
dataMgr.findDataTypes("s1", list);
|
||||
assertEquals(1, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDedupeConflicts() {
|
||||
|
||||
Structure struct1a = new StructureDataType("s1", 0, dataMgr);
|
||||
struct1a.setPackingEnabled(true);
|
||||
|
||||
struct1a.add(ByteDataType.dataType);
|
||||
Structure s1 = (Structure) dataMgr.resolve(struct1a, null);
|
||||
|
||||
struct1a.add(ByteDataType.dataType);
|
||||
Structure s2 = (Structure) dataMgr.resolve(struct1a, null);
|
||||
|
||||
struct1a.add(ByteDataType.dataType);
|
||||
Structure s3 = (Structure) dataMgr.resolve(struct1a, null);
|
||||
|
||||
// force two of the conflicts to become equivalent
|
||||
s1.deleteAll();
|
||||
// leave s2 unchanged
|
||||
s3.deleteAll();
|
||||
|
||||
ArrayList<DataType> list = new ArrayList<>();
|
||||
dataMgr.findDataTypes("s1", list);
|
||||
assertEquals(3, list.size());
|
||||
|
||||
dataMgr.dedupeConflicts(s3);
|
||||
assertTrue(s3.isDeleted());
|
||||
|
||||
list.clear();
|
||||
dataMgr.findDataTypes("s1", list);
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
private void assertNoConflict(String dtName) {
|
||||
DataType dt1 = dataMgr.getDataType("/" + dtName);
|
||||
assertNotNull("DataType not found: " + dtName, dt1);
|
||||
|
||||
DataType dt2 = dataMgr.getDataType("/" + dtName + ".conflict");
|
||||
if (dt2 != null) {
|
||||
System.out.println("Original type: " + dt1.toString());
|
||||
System.out.println("Conflict type: " + dt2.toString());
|
||||
|
||||
if (dt1.isEquivalent(dt2)) {
|
||||
System.out.println(dtName + " - TYPES ARE EQUIVALENT");
|
||||
}
|
||||
|
||||
fail("DataType conflict found: " + dt2.getPathName());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummySourceArchive implements SourceArchive {
|
||||
|
||||
private final UniversalID id;
|
||||
@ -626,36 +824,45 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
this.archiveName = archiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArchiveType getArchiveType() {
|
||||
return ArchiveType.FILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDomainFileID() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastSyncTime() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return archiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniversalID getSourceArchiveID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirtyFlag(boolean dirty) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastSyncTime(long time) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.DataTypeComparator;
|
||||
import ghidra.program.model.data.StubDataType;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.UniversalIdGenerator;
|
||||
|
||||
public class DataTypeUtilsTest {
|
||||
@ -48,7 +46,7 @@ public class DataTypeUtilsTest {
|
||||
}
|
||||
|
||||
// sort them how our data will be sorted
|
||||
Collections.sort(data, new DataTypeComparator());
|
||||
Collections.sort(data, DataTypeComparator.INSTANCE);
|
||||
List<DataType> finalData = Collections.unmodifiableList(data);
|
||||
|
||||
// a
|
||||
|
@ -191,7 +191,7 @@ public class ProjectDataTypeManager extends StandAloneDataTypeManager
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDataTypeIDs(LinkedList<Long> deletedIds, TaskMonitor monitor) {
|
||||
protected void deleteDataTypeIDs(LinkedList<Long> deletedIds) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ class ArrayDB extends DataTypeDB implements Array {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
if ( displayName == null ) {
|
||||
if (displayName == null) {
|
||||
displayName = DataTypeUtilities.getDisplayName(this, false);
|
||||
}
|
||||
return displayName;
|
||||
@ -223,27 +223,45 @@ class ArrayDB extends DataTypeDB implements Array {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
protected boolean isEquivalent(DataType dt, DataTypeConflictHandler handler) {
|
||||
if (dt == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(dt instanceof Array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Array array = (Array) dt;
|
||||
if (getNumElements() != array.getNumElements()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataType dataType = getDataType();
|
||||
if (!dataType.isEquivalent(array.getDataType())) {
|
||||
DataType otherDataType = array.getDataType();
|
||||
|
||||
// if they contain datatypes that have same ids, then we are essentially equivalent.
|
||||
if (DataTypeUtilities.isSameDataType(dataType, otherDataType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handler != null) {
|
||||
handler = handler.getSubsequentHandler();
|
||||
}
|
||||
if (!DataTypeDB.isEquivalent(dataType, otherDataType, handler)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dataType instanceof Dynamic && getElementLength() != array.getElementLength()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
return isEquivalent(dt, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeReplaced(DataType oldDt, DataType newDt) {
|
||||
lock.acquire();
|
||||
@ -255,13 +273,37 @@ class ArrayDB extends DataTypeDB implements Array {
|
||||
|
||||
if (oldDt == getDataType()) {
|
||||
|
||||
int oldElementLength = getElementLength();
|
||||
int newElementLength =
|
||||
elementLength = newDt.getLength() < 0 ? oldElementLength : -1;
|
||||
|
||||
// check for existing pointer to newDt
|
||||
ArrayDataType newArray =
|
||||
new ArrayDataType(newDt, getNumElements(), newElementLength, dataMgr);
|
||||
DataType existingArray =
|
||||
dataMgr.getDataType(newDt.getCategoryPath(), newArray.getName());
|
||||
if (existingArray != null) {
|
||||
// avoid duplicate array - replace this array with existing one
|
||||
dataMgr.addDataTypeToReplace(this, existingArray);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newDt.getCategoryPath().equals(oldDt.getCategoryPath())) {
|
||||
// move this pointer to same category as newDt
|
||||
try {
|
||||
super.setCategoryPath(newDt.getCategoryPath());
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
throw new RuntimeException(e); // already checked
|
||||
}
|
||||
}
|
||||
|
||||
oldDt.removeParent(this);
|
||||
newDt.addParent(this);
|
||||
|
||||
String myOldName = getOldName();
|
||||
int oldLength = getLength();
|
||||
int oldAlignment = getAlignment();
|
||||
int oldElementLength = getElementLength();
|
||||
|
||||
record.setLongValue(ArrayDBAdapter.ARRAY_DT_ID_COL, dataMgr.getResolvedID(newDt));
|
||||
if (newDt instanceof Dynamic || newDt instanceof FactoryDataType) {
|
||||
@ -276,7 +318,7 @@ class ArrayDB extends DataTypeDB implements Array {
|
||||
dataMgr.dbError(e);
|
||||
}
|
||||
refreshName();
|
||||
if (!getName().equals(myOldName)) {
|
||||
if (!oldDt.getName().equals(newDt.getName())) {
|
||||
notifyNameChanged(myOldName);
|
||||
}
|
||||
if (getLength() != oldLength || oldElementLength != getElementLength()) {
|
||||
|
@ -46,8 +46,7 @@ abstract class ArrayDBAdapter {
|
||||
* @throws CancelledException task cancelled
|
||||
*/
|
||||
static ArrayDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix,
|
||||
TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
TaskMonitor monitor) throws VersionException, IOException, CancelledException {
|
||||
if (openMode == DBConstants.CREATE) {
|
||||
return new ArrayDBAdapterV1(handle, tablePrefix, true);
|
||||
}
|
||||
@ -71,8 +70,8 @@ abstract class ArrayDBAdapter {
|
||||
}
|
||||
|
||||
private static ArrayDBAdapter upgrade(DBHandle handle, ArrayDBAdapter oldAdapter,
|
||||
String tablePrefix,
|
||||
TaskMonitor monitor) throws VersionException, IOException, CancelledException {
|
||||
String tablePrefix, TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
|
||||
DBHandle tmpHandle = new DBHandle();
|
||||
long id = tmpHandle.startTransaction();
|
||||
@ -116,4 +115,10 @@ abstract class ArrayDBAdapter {
|
||||
|
||||
abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of array datatype records
|
||||
* @return total number of composite records
|
||||
*/
|
||||
public abstract int getRecordCount();
|
||||
|
||||
}
|
||||
|
@ -91,7 +91,8 @@ class ArrayDBAdapterV0 extends ArrayDBAdapter {
|
||||
DBRecord rec = ArrayDBAdapter.SCHEMA.createRecord(oldRec.getKey());
|
||||
rec.setLongValue(ArrayDBAdapter.ARRAY_DT_ID_COL, oldRec.getLongValue(V0_ARRAY_DT_ID_COL));
|
||||
rec.setIntValue(ArrayDBAdapter.ARRAY_DIM_COL, oldRec.getIntValue(V0_ARRAY_DIM_COL));
|
||||
rec.setIntValue(ArrayDBAdapter.ARRAY_ELEMENT_LENGTH_COL, oldRec.getIntValue(V0_ARRAY_ELEMENT_LENGTH_COL));
|
||||
rec.setIntValue(ArrayDBAdapter.ARRAY_ELEMENT_LENGTH_COL,
|
||||
oldRec.getIntValue(V0_ARRAY_ELEMENT_LENGTH_COL));
|
||||
rec.setLongValue(ArrayDBAdapter.ARRAY_CAT_COL, 0);
|
||||
return rec;
|
||||
}
|
||||
@ -141,4 +142,9 @@ class ArrayDBAdapterV0 extends ArrayDBAdapter {
|
||||
return Field.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -118,4 +118,8 @@ class ArrayDBAdapterV1 extends ArrayDBAdapter {
|
||||
return table.findRecords(new LongField(categoryID), V1_ARRAY_CAT_COL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,8 @@ public abstract class BuiltinDBAdapter {
|
||||
* @return new record
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
abstract DBRecord createRecord(String name, String className, long categoryID) throws IOException;
|
||||
abstract DBRecord createRecord(String name, String className, long categoryID)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the Built-in data type record with the indicated ID.
|
||||
@ -92,4 +93,10 @@ public abstract class BuiltinDBAdapter {
|
||||
* @throws IOException if IO error occurs
|
||||
*/
|
||||
abstract RecordIterator getRecords() throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of built-in datatype records
|
||||
* @return total number of composite records
|
||||
*/
|
||||
public abstract int getRecordCount();
|
||||
}
|
||||
|
@ -52,8 +52,7 @@ class BuiltinDBAdapterV0 extends BuiltinDBAdapter {
|
||||
|
||||
String tableName = tablePrefix + BUILT_IN_TABLE_NAME;
|
||||
if (create) {
|
||||
table = handle.createTable(tableName, V0_SCHEMA,
|
||||
new int[] { V0_BUILT_IN_CAT_COL });
|
||||
table = handle.createTable(tableName, V0_SCHEMA, new int[] { V0_BUILT_IN_CAT_COL });
|
||||
}
|
||||
else {
|
||||
table = handle.getTable(tableName);
|
||||
@ -87,7 +86,8 @@ class BuiltinDBAdapterV0 extends BuiltinDBAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord createRecord(String name, String className, long categoryID) throws IOException {
|
||||
public DBRecord createRecord(String name, String className, long categoryID)
|
||||
throws IOException {
|
||||
|
||||
long tableKey = table.getKey();
|
||||
if (tableKey <= 100) {
|
||||
@ -108,4 +108,9 @@ class BuiltinDBAdapterV0 extends BuiltinDBAdapter {
|
||||
return table.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,8 +49,7 @@ abstract class CompositeDBAdapter implements DBRecordAdapter {
|
||||
CompositeDBAdapterV5V6.V5V6_COMPOSITE_SOURCE_SYNC_TIME_COL;
|
||||
static final int COMPOSITE_LAST_CHANGE_TIME_COL =
|
||||
CompositeDBAdapterV5V6.V5V6_COMPOSITE_LAST_CHANGE_TIME_COL;
|
||||
static final int COMPOSITE_PACKING_COL =
|
||||
CompositeDBAdapterV5V6.V5V6_COMPOSITE_PACK_COL;
|
||||
static final int COMPOSITE_PACKING_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_PACK_COL;
|
||||
static final int COMPOSITE_MIN_ALIGN_COL = CompositeDBAdapterV5V6.V5V6_COMPOSITE_MIN_ALIGN_COL;
|
||||
|
||||
// Stored Packing and Minimum Alignment values are consistent with CompositeInternal
|
||||
@ -217,6 +216,7 @@ abstract class CompositeDBAdapter implements DBRecordAdapter {
|
||||
* @return the composite data type record iterator.
|
||||
* @throws IOException if the database can't be accessed.
|
||||
*/
|
||||
@Override
|
||||
public abstract RecordIterator getRecords() throws IOException;
|
||||
|
||||
/**
|
||||
@ -272,9 +272,10 @@ abstract class CompositeDBAdapter implements DBRecordAdapter {
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of composite records
|
||||
* Get the number of composite datatype records
|
||||
* @return total number of composite records
|
||||
*/
|
||||
@Override
|
||||
public abstract int getRecordCount();
|
||||
|
||||
}
|
||||
|
@ -295,8 +295,15 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
|
||||
return myDt.getClass() == otherDt.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataTypeComponent dtc) {
|
||||
static boolean isEquivalent(DataTypeComponent existingDtc, DataTypeComponent dtc,
|
||||
DataTypeConflictHandler handler) {
|
||||
if (existingDtc instanceof DataTypeComponentDB existingDtcDB) {
|
||||
return existingDtcDB.isEquivalent(dtc, handler);
|
||||
}
|
||||
return existingDtc.isEquivalent(dtc);
|
||||
}
|
||||
|
||||
boolean isEquivalent(DataTypeComponent dtc, DataTypeConflictHandler handler) {
|
||||
DataType myDt = getDataType();
|
||||
DataType otherDt = dtc.getDataType();
|
||||
// SCR #11220 - this may fix the null pointer exception - not sure as it is hard
|
||||
@ -319,7 +326,16 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DataTypeUtilities.isSameOrEquivalentDataType(myDt, otherDt);
|
||||
if (DataTypeUtilities.isSameDataType(myDt, otherDt)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return DataTypeDB.isEquivalent(myDt, otherDt, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataTypeComponent dtc) {
|
||||
return isEquivalent(dtc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -413,8 +429,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
|
||||
// TODO: Need to check field name and throw DuplicateNameException
|
||||
// name = checkFieldName(name);
|
||||
record.setString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL, name);
|
||||
record.setLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL,
|
||||
dataMgr.getResolvedID(dt));
|
||||
record.setLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL, dataMgr.getResolvedID(dt));
|
||||
record.setString(ComponentDBAdapter.COMPONENT_COMMENT_COL, comment);
|
||||
updateRecord(false);
|
||||
}
|
||||
|
@ -585,4 +585,33 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
|
||||
throw new DataTypeEncodeException("Encoding not supported", repr, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform equivalence check while resolving the specified dataType. If the specified conflict
|
||||
* handler under a conflict situation indicates that the existing data type (i.e., this type)
|
||||
* be used in place of the specified dataType this method will return true.
|
||||
* @param dataType datatype being resolved
|
||||
* @param handler resolve conflict handler (if null perform normal {@link #isEquivalent(DataType)}
|
||||
* @return true if the specified dataType should be considered equivalent to this datatype.
|
||||
*/
|
||||
protected abstract boolean isEquivalent(DataType dataType, DataTypeConflictHandler handler);
|
||||
|
||||
/**
|
||||
* If possible, perform equivalence check while resolving the specified dataType if the
|
||||
* existingDataType is an instance of DataTypeDB. Otherwise, perform a normal
|
||||
* isEquivalent operation. If the specified conflict
|
||||
* handler under a conflict situation indicates that the existing data type (i.e., this type)
|
||||
* be used in place of the specified dataType this method will return true.
|
||||
* @param existingDataType existing datatype
|
||||
* @param otherDataType datatype being resolved
|
||||
* @param handler resolve conflict handler (if null perform normal {@link #isEquivalent(DataType)}
|
||||
* @return true if the specified dataType should be considered equivalent to this datatype.
|
||||
*/
|
||||
static boolean isEquivalent(DataType existingDataType, DataType otherDataType,
|
||||
DataTypeConflictHandler handler) {
|
||||
if (existingDataType instanceof DataTypeDB existingDataTypeDB) {
|
||||
return existingDataTypeDB.isEquivalent(otherDataType, handler);
|
||||
}
|
||||
return existingDataType.isEquivalent(otherDataType);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import javax.help.UnsupportedOperationException;
|
||||
import db.*;
|
||||
import db.util.ErrorHandler;
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.stl.Pair;
|
||||
import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive;
|
||||
import ghidra.docking.settings.*;
|
||||
import ghidra.framework.Application;
|
||||
@ -145,19 +146,21 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
private List<InvalidatedListener> invalidatedListeners = new ArrayList<>();
|
||||
protected DataTypeManagerChangeListenerHandler defaultListener =
|
||||
new DataTypeManagerChangeListenerHandler();
|
||||
private NameComparator nameComparator = new NameComparator();
|
||||
private Comparator<DataType> nameComparator = DataTypeComparator.INSTANCE;
|
||||
private int creatingDataType = 0;
|
||||
protected UniversalID universalID;
|
||||
|
||||
private Map<UniversalID, SourceArchive> sourceArchiveMap;
|
||||
private LinkedList<Long> idsToDelete = new LinkedList<>();
|
||||
private LinkedList<Pair<DataType, DataType>> typesToReplace = new LinkedList<>();
|
||||
private List<DataType> favoritesList = new ArrayList<>();
|
||||
private IdsToDataTypeMap idsToDataTypeMap = new IdsToDataTypeMap();
|
||||
|
||||
private ThreadLocal<EquivalenceCache> equivalenceCache = new ThreadLocal<>();
|
||||
|
||||
private IdentityHashMap<DataType, DataType> resolveCache;
|
||||
private TreeSet<ResolvePair> resolveQueue;
|
||||
private TreeSet<ResolvePair> resolveQueue; // TODO: is TreeSet really needed?
|
||||
private LinkedList<DataType> conflictQueue = new LinkedList<>();
|
||||
|
||||
private boolean isBulkRemoving;
|
||||
|
||||
@ -971,14 +974,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
if (sortedDataTypes == null) {
|
||||
return;
|
||||
}
|
||||
// find and remove exact match from sortedDataTypes list
|
||||
String name = dataTypePath.getDataTypeName();
|
||||
DataType compareDataType = new TypedefDataType(name, DataType.DEFAULT);
|
||||
try {
|
||||
compareDataType.setCategoryPath(dataTypePath.getCategoryPath());
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
// will not happen - compareDataType not in dataTypeManager
|
||||
}
|
||||
DataType compareDataType =
|
||||
new TypedefDataType(dataTypePath.getCategoryPath(), name, DataType.DEFAULT, this);
|
||||
int index = Collections.binarySearch(sortedDataTypes, compareDataType, nameComparator);
|
||||
if (index >= 0) {
|
||||
sortedDataTypes.remove(index);
|
||||
@ -995,6 +994,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
sortedDataTypes.add(index, dataType);
|
||||
}
|
||||
else {
|
||||
// NOTE: ideally, this should never happen and may indicate presence of duplicate
|
||||
sortedDataTypes.set(index, dataType);
|
||||
}
|
||||
}
|
||||
@ -1236,40 +1236,37 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
|
||||
resolvedDataType = getCachedResolve(dataType);
|
||||
if (resolvedDataType != null) {
|
||||
return resolvedDataType;
|
||||
if (resolvedDataType == null) {
|
||||
SourceArchive sourceArchive = dataType.getSourceArchive();
|
||||
if (sourceArchive != null &&
|
||||
sourceArchive.getArchiveType() == ArchiveType.BUILT_IN) {
|
||||
resolvedDataType = resolveBuiltIn(dataType);
|
||||
}
|
||||
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 = resolveDataTypeNoSource(dataType);
|
||||
}
|
||||
else if (!sourceArchive.getSourceArchiveID().equals(getUniversalID()) &&
|
||||
sourceArchive.getArchiveType() == ArchiveType.PROGRAM) {
|
||||
// dataTypes from a different program don't carry over their identity.
|
||||
resolvedDataType = resolveDataTypeNoSource(dataType);
|
||||
}
|
||||
else {
|
||||
resolvedDataType = resolveDataTypeWithSource(dataType);
|
||||
}
|
||||
cacheResolvedDataType(dataType, resolvedDataType);
|
||||
if (resolvedDataType instanceof DataTypeDB) {
|
||||
setCachedEquivalence((DataTypeDB) resolvedDataType, dataType);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: delayed pointer-resolve use of "undefined *" could cause unintended
|
||||
// equivalence match. May need to use an internal reserved type instead.
|
||||
|
||||
SourceArchive sourceArchive = dataType.getSourceArchive();
|
||||
if (sourceArchive != null && sourceArchive.getArchiveType() == ArchiveType.BUILT_IN) {
|
||||
resolvedDataType = resolveBuiltIn(dataType, currentHandler);
|
||||
}
|
||||
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 = 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 = resolveDataTypeNoSource(dataType, currentHandler);
|
||||
}
|
||||
else {
|
||||
resolvedDataType = resolveDataTypeWithSource(dataType, currentHandler);
|
||||
}
|
||||
cacheResolvedDataType(dataType, resolvedDataType);
|
||||
if (resolvedDataType instanceof DataTypeDB) {
|
||||
setCachedEquivalence((DataTypeDB) resolvedDataType, dataType);
|
||||
}
|
||||
return resolvedDataType;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (isResolveCacheOwner) {
|
||||
flushResolveQueue(true); // may throw exception - incomplete resolve
|
||||
// Process resolve queue and allowed resolvedDataType to be replaced
|
||||
// during conflict processing
|
||||
resolvedDataType = processResolveQueue(true, resolvedDataType);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
@ -1280,13 +1277,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
return resolvedDataType;
|
||||
}
|
||||
|
||||
private DataType resolveBuiltIn(DataType dataType, DataTypeConflictHandler handler) {
|
||||
private DataType resolveBuiltIn(DataType dataType) {
|
||||
|
||||
if (dataType instanceof Pointer) {
|
||||
// treat built-in pointers like other datatypes without a source
|
||||
return resolveDataTypeNoSource(dataType, currentHandler);
|
||||
return resolveDataTypeNoSource(dataType);
|
||||
}
|
||||
|
||||
// can't do this check now because Pointers from the BuiltinDataTypeManager are
|
||||
@ -1309,7 +1307,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
"Failed to rename conflicting datatype: " + existingDataType.getPathName(), e);
|
||||
}
|
||||
}
|
||||
return createDataType(dataType, dataType.getName(), BuiltInSourceArchive.INSTANCE, handler);
|
||||
return createDataType(dataType, dataType.getName(), BuiltInSourceArchive.INSTANCE,
|
||||
currentHandler);
|
||||
}
|
||||
|
||||
private DataType resolveBitFieldDataType(BitFieldDataType bitFieldDataType,
|
||||
@ -1394,12 +1393,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
if (!(dataType instanceof Enum)) {
|
||||
return false;
|
||||
}
|
||||
// TODO: implement doReplaceWith
|
||||
existingDataType.replaceWith(dataType);
|
||||
}
|
||||
else if (existingDataType instanceof TypedefDB) {
|
||||
if (!(dataType instanceof TypeDef)) {
|
||||
return false;
|
||||
}
|
||||
// TODO: implement doReplaceWith
|
||||
existingDataType.replaceWith(dataType);
|
||||
}
|
||||
else {
|
||||
@ -1480,8 +1481,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
return category.getDataTypesByBaseName(dataType.getName());
|
||||
}
|
||||
|
||||
// Handle pointers and arrays
|
||||
|
||||
DataType existingDataType = category.getDataType(dataType.getName());
|
||||
|
||||
DataType baseDataType = DataTypeUtilities.getBaseDataType(dataType);
|
||||
@ -1523,13 +1522,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
// If the existing Data type is currently being resolved, its isEquivalent
|
||||
// method is short circuited such that it will return true. So it is important
|
||||
// to call the isEquivalent on the existing datatype and not the dataType.
|
||||
if (existingDataType != null && existingDataType.isEquivalent(dataType)) {
|
||||
if (existingDataType != null &&
|
||||
DataTypeDB.isEquivalent(existingDataType, dataType, currentHandler)) {
|
||||
return existingDataType;
|
||||
}
|
||||
|
||||
List<DataType> relatedByName = findDataTypesSameLocation(dataType);
|
||||
for (DataType candidate : relatedByName) {
|
||||
if (candidate != existingDataType && candidate.isEquivalent(dataType)) {
|
||||
if (candidate != existingDataType &&
|
||||
DataTypeDB.isEquivalent(candidate, dataType, currentHandler)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
@ -1571,18 +1572,16 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
*
|
||||
* @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 resolveDataTypeNoSource(DataType dataType, DataTypeConflictHandler handler) {
|
||||
private DataType resolveDataTypeNoSource(DataType dataType) {
|
||||
|
||||
DataType existingDataType = findEquivalentDataTypeSameLocation(dataType);
|
||||
if (existingDataType != null) {
|
||||
return existingDataType;
|
||||
}
|
||||
|
||||
return resolveNoEquivalentFound(dataType, null, handler);
|
||||
return resolveNoEquivalentFound(dataType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1591,19 +1590,17 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
*
|
||||
* @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 resolveDataTypeWithSource(DataType dataType, DataTypeConflictHandler handler) {
|
||||
private DataType resolveDataTypeWithSource(DataType dataType) {
|
||||
|
||||
SourceArchive sourceArchive = dataType.getSourceArchive();
|
||||
|
||||
// Do we have that dataType already resolved and associated with the source archive?
|
||||
DataType existingDataType = getDataType(sourceArchive, dataType.getUniversalID());
|
||||
if (existingDataType != null) {
|
||||
if (!existingDataType.isEquivalent(dataType) &&
|
||||
handler.shouldUpdate(dataType, existingDataType)) {
|
||||
if (!DataTypeDB.isEquivalent(existingDataType, dataType, currentHandler) &&
|
||||
currentHandler.shouldUpdate(dataType, existingDataType)) {
|
||||
existingDataType.replaceWith(dataType);
|
||||
existingDataType.setLastChangeTime(dataType.getLastChangeTime());
|
||||
}
|
||||
@ -1614,9 +1611,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
// Avoid conflict handling for types with a source which matches
|
||||
// this archive, although a conflict name may still be used.
|
||||
// This can occur when a semi-mirrored archive instance is used
|
||||
// such as the CompositeViewerDataTypeManager which uses the same
|
||||
// Archive UniversalID as the edited datatype's source.
|
||||
return createDataType(dataType, sourceArchive, handler);
|
||||
// such as the CompositeViewerDataTypeManager or Program Merge
|
||||
// which uses the same Archive UniversalID.
|
||||
return createConflictDataType(dataType, sourceArchive);
|
||||
}
|
||||
|
||||
// If we have the same path name and the existing data type is a local data type
|
||||
@ -1630,7 +1627,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
return existingDataType;
|
||||
}
|
||||
|
||||
return resolveNoEquivalentFound(dataType, sourceArchive, handler);
|
||||
return resolveNoEquivalentFound(dataType, sourceArchive);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1639,11 +1636,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
* using the specified conflict handler.
|
||||
* @param dataType datatype being resolved
|
||||
* @param sourceArchive source archive associated with new type (may be null)
|
||||
* @param handler datatype conflict handler
|
||||
* @return resolved datatype (may be existing or newly added datatype)
|
||||
*/
|
||||
private DataType resolveNoEquivalentFound(DataType dataType, SourceArchive sourceArchive,
|
||||
DataTypeConflictHandler handler) {
|
||||
private DataType resolveNoEquivalentFound(DataType dataType, SourceArchive sourceArchive) {
|
||||
|
||||
if (sourceArchive != null && sourceArchive.getArchiveType() == ArchiveType.PROGRAM) {
|
||||
sourceArchive = null; // do not preserve program as a source archive
|
||||
@ -1653,13 +1648,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
// (preference is given to similar kind of datatype when checking existing conflict types)
|
||||
DataType existingDataType = findDataTypeSameLocation(dataType);
|
||||
if (existingDataType == null) {
|
||||
return createDataType(dataType, sourceArchive, handler);
|
||||
return createDataType(dataType, getUnusedConflictName(dataType), sourceArchive,
|
||||
currentHandler);
|
||||
}
|
||||
|
||||
// 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 = handler.resolveConflict(dataType, existingDataType);
|
||||
switch (result) {
|
||||
ConflictResult conflictResult = currentHandler.resolveConflict(dataType, existingDataType);
|
||||
switch (conflictResult) {
|
||||
|
||||
case REPLACE_EXISTING: // new type replaces old conflicted type
|
||||
try {
|
||||
@ -1668,7 +1664,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
renameToUnusedConflictName(existingDataType);
|
||||
DataType newDataType =
|
||||
createDataType(dataType, dataType.getName(), sourceArchive, handler);
|
||||
createDataType(dataType, dataType.getName(), sourceArchive, currentHandler);
|
||||
try {
|
||||
replace(existingDataType, newDataType);
|
||||
}
|
||||
@ -1684,25 +1680,22 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
|
||||
case RENAME_AND_ADD: // default handler behavior
|
||||
return createDataType(dataType, sourceArchive, handler);
|
||||
return createConflictDataType(dataType, sourceArchive);
|
||||
|
||||
default: // USE_EXISTING - new type is discarded and old conflicted type is returned
|
||||
return existingDataType;
|
||||
}
|
||||
}
|
||||
|
||||
private DataType createDataType(DataType dataType, SourceArchive sourceArchive,
|
||||
DataTypeConflictHandler handler) {
|
||||
private DataType createConflictDataType(DataType dataType, SourceArchive sourceArchive) {
|
||||
String dtName = getUnusedConflictName(dataType);
|
||||
DataType newDataType = createDataType(dataType, dtName, sourceArchive, handler);
|
||||
DataType newDataType = createDataType(dataType, dtName, sourceArchive, currentHandler);
|
||||
|
||||
// resolving child data types could result in another copy of dataType in the
|
||||
// manager depending upon the conflict handler - check again
|
||||
DataType existingDataType = findEquivalentDataTypeSameLocation(dataType);
|
||||
// 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;
|
||||
// NOTE: queue conflict datatype for delayed check for equivalent type.
|
||||
// This is not needed for Pointer or Array which will update if/when
|
||||
// referenced type gets replaced.
|
||||
if (!(newDataType instanceof Pointer) && !(newDataType instanceof Array)) {
|
||||
conflictQueue.add(newDataType);
|
||||
}
|
||||
return newDataType;
|
||||
}
|
||||
@ -1748,14 +1741,14 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
monitor.checkCancelled();
|
||||
resolve(dt, handler);
|
||||
if (isResolveCacheOwner) {
|
||||
flushResolveQueue(false);
|
||||
processResolveQueue(false);
|
||||
}
|
||||
monitor.setProgress(++i);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (isResolveCacheOwner) {
|
||||
flushResolveQueue(true);
|
||||
processResolveQueue(true);
|
||||
}
|
||||
if (isEquivalenceCacheOwner) {
|
||||
clearEquivalenceCache();
|
||||
@ -1865,6 +1858,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
|
||||
replace(existingDt, replacementDt);
|
||||
|
||||
if (fixupName) {
|
||||
try {
|
||||
long lastChangeTime = replacementDt.getLastChangeTime();
|
||||
@ -1897,22 +1891,44 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
|
||||
private void replace(DataType existingDt, DataType replacementDt)
|
||||
throws DataTypeDependencyException {
|
||||
if (existingDt == replacementDt) {
|
||||
|
||||
if (!contains(existingDt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypePath replacedDtPath = existingDt.getDataTypePath();
|
||||
long replacedId = getID(existingDt);
|
||||
|
||||
UniversalID id = existingDt.getUniversalID();
|
||||
idsToDataTypeMap.removeDataType(existingDt.getSourceArchive(), id);
|
||||
|
||||
if (replacementDt.dependsOn(existingDt)) {
|
||||
throw new DataTypeDependencyException("Replace failed: " +
|
||||
replacementDt.getDisplayName() + " depends on " + existingDt.getDisplayName());
|
||||
}
|
||||
|
||||
replaceUsesInOtherDataTypes(existingDt, replacementDt);
|
||||
addDataTypeToReplace(existingDt, replacementDt);
|
||||
replaceQueuedDataTypes();
|
||||
}
|
||||
|
||||
private void replaceQueuedDataTypes() {
|
||||
|
||||
// collect all datatypes to be replaced and notify children which may also get queued
|
||||
// for removal.
|
||||
LinkedList<Pair<DataType, DataType>> dataTypeReplacements = new LinkedList<>();
|
||||
while (!typesToReplace.isEmpty()) {
|
||||
Pair<DataType, DataType> dataTypeReplacement = typesToReplace.removeFirst();
|
||||
replaceUsesInOtherDataTypes(dataTypeReplacement.first, dataTypeReplacement.second);
|
||||
dataTypeReplacements.addFirst(dataTypeReplacement);
|
||||
}
|
||||
|
||||
// perform actual database updates (e.g., record updates, change notifications, etc.)
|
||||
for (Pair<DataType, DataType> dataTypeReplacement : dataTypeReplacements) {
|
||||
replaceDataType(dataTypeReplacement.first, dataTypeReplacement.second);
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceDataType(DataType existingDt, DataType replacementDt) {
|
||||
|
||||
DataTypePath replacedDtPath = existingDt.getDataTypePath();
|
||||
long replacedId = getID(existingDt);
|
||||
|
||||
UniversalID id = existingDt.getUniversalID();
|
||||
idsToDataTypeMap.removeDataType(existingDt.getSourceArchive(), id);
|
||||
|
||||
try {
|
||||
replaceDataTypeIDs(replacedId, getID(replacementDt));
|
||||
@ -1929,13 +1945,20 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
|
||||
private void replaceUsesInOtherDataTypes(DataType existingDt, DataType newDt) {
|
||||
if (existingDt instanceof DataTypeDB) {
|
||||
// Notify parents that a dependency has been replaced. For pointers and arrays
|
||||
// it may require their subsequent category change or removal to avoid duplication.
|
||||
for (DataType dt : existingDt.getParents()) {
|
||||
dt.dataTypeReplaced(existingDt, newDt);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Since we do not track parents of non-DB types we must assume that all data types
|
||||
// must be modified. Use of the sortedDataTypes list is the simplest way to do this.
|
||||
// A copy of the list must be used since it will changed if other types get removed
|
||||
// in the process.
|
||||
buildSortedDataTypeList();
|
||||
for (DataType dt : new ArrayList<>(sortedDataTypes)) {
|
||||
List<DataType> sortedDataTypesCopy = new ArrayList<>(sortedDataTypes);
|
||||
for (DataType dt : sortedDataTypesCopy) {
|
||||
dt.dataTypeReplaced(existingDt, newDt);
|
||||
}
|
||||
}
|
||||
@ -2008,17 +2031,23 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
list.add(DataType.DEFAULT);
|
||||
return;
|
||||
}
|
||||
// ignore .conflict in both name and result matches
|
||||
lock.acquire();
|
||||
try {
|
||||
buildSortedDataTypeList();
|
||||
DataType compareDataType = new TypedefDataType(name, DataType.DEFAULT);
|
||||
// Use exemplar datatype in root category without .conflict to position at start
|
||||
// of possible matches
|
||||
name = DataTypeUtilities.getNameWithoutConflict(name);
|
||||
DataType compareDataType =
|
||||
new TypedefDataType(CategoryPath.ROOT, name, DataType.DEFAULT, this);
|
||||
int index = Collections.binarySearch(sortedDataTypes, compareDataType, nameComparator);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
}
|
||||
// add all matches to list
|
||||
while (index < sortedDataTypes.size()) {
|
||||
DataType dt = sortedDataTypes.get(index);
|
||||
if (!name.equals(dt.getName())) {
|
||||
if (!name.equals(DataTypeUtilities.getNameWithoutConflict(dt, false))) {
|
||||
break;
|
||||
}
|
||||
list.add(dt);
|
||||
@ -2206,43 +2235,38 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
* Remove the given datatype from this manager (assumes the lock has already been acquired).
|
||||
*
|
||||
* @param dataType the dataType to be removed
|
||||
* @param monitor the task monitor
|
||||
*/
|
||||
private boolean removeInternal(DataType dataType, TaskMonitor monitor) {
|
||||
private void removeInternal(DataType dataType) {
|
||||
if (!contains(dataType)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
LinkedList<Long> deletedIds = new LinkedList<>();
|
||||
|
||||
long id = getID(dataType);
|
||||
|
||||
if (id < 0) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
idsToDelete.add(Long.valueOf(id));
|
||||
removeQueuedDataTypes();
|
||||
}
|
||||
|
||||
private void removeQueuedDataTypes() {
|
||||
|
||||
// collect all datatype to be removed and notify children which may also get queued
|
||||
// for removal.
|
||||
LinkedList<Long> deletedIds = new LinkedList<>();
|
||||
while (!idsToDelete.isEmpty()) {
|
||||
Long l = idsToDelete.removeFirst();
|
||||
id = l.longValue();
|
||||
long id = idsToDelete.removeFirst();
|
||||
removeUseOfDataType(id);
|
||||
|
||||
deletedIds.addFirst(l);
|
||||
deletedIds.addFirst(id);
|
||||
}
|
||||
|
||||
for (Long l : deletedIds) {
|
||||
deleteDataType(l.longValue());
|
||||
// perform actual database updates (e.g., record removal, change notifications, etc.)
|
||||
for (long id : deletedIds) {
|
||||
deleteDataType(id);
|
||||
}
|
||||
|
||||
try {
|
||||
deleteDataTypeIDs(deletedIds, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Remove all uses of datatypes external to datatype manager
|
||||
deleteDataTypeIDs(deletedIds);
|
||||
}
|
||||
|
||||
private void removeUseOfDataType(long id) {
|
||||
@ -2267,11 +2291,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
public boolean remove(DataType dataType, TaskMonitor monitor) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return removeInternal(dataType, monitor);
|
||||
if (contains(dataType)) {
|
||||
removeInternal(dataType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2358,18 +2386,29 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
return (sourceArchive.equals(dtm.getLocalSourceArchive()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a datatype to deleted in response to another datatype being deleted.
|
||||
* @param id datatype ID to be removed
|
||||
*/
|
||||
protected void addDataTypeToDelete(long id) {
|
||||
idsToDelete.add(Long.valueOf(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a datatype to be replaced by another datatype in response to its referenced
|
||||
* datatype being replaced.
|
||||
* @param oldDataType datatype to be replaced
|
||||
* @param replacementDataType datatype which is the replacement
|
||||
*/
|
||||
protected void addDataTypeToReplace(DataType oldDataType, DataType replacementDataType) {
|
||||
typesToReplace.add(new Pair<>(oldDataType, replacementDataType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all datatype uses external to the datatype manager if applicable.
|
||||
* @param deletedIds old datatype IDs which were deleted
|
||||
* @param monitor task monitor
|
||||
* @throws CancelledException if operation cancelled
|
||||
*/
|
||||
abstract protected void deleteDataTypeIDs(LinkedList<Long> deletedIds, TaskMonitor monitor)
|
||||
throws CancelledException;
|
||||
abstract protected void deleteDataTypeIDs(LinkedList<Long> deletedIds);
|
||||
|
||||
private void notifyDeleted(long dataTypeID) {
|
||||
DataType dataType = getDataType(dataTypeID);
|
||||
@ -2377,9 +2416,16 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
return;
|
||||
}
|
||||
if (dataType instanceof DataTypeDB dt) {
|
||||
// Notify datatype that it has been deleted which in-turn will notify all of its
|
||||
// parents. Parent datatype which are no longer valid (i.e., pointers, arrays)
|
||||
// may invoke addDataTypeToDelete method to schedule their subsequent removal.
|
||||
dt.notifyDeleted();
|
||||
}
|
||||
else {
|
||||
// Since we do not track parents of non-DB types we must assume that all data types
|
||||
// must be modified. Use of the sortedDataTypes list is the simplest way to do this.
|
||||
// A copy of the list must be used since it will changed if other types get removed
|
||||
// in the process.
|
||||
buildSortedDataTypeList();
|
||||
List<DataType> sortedDataTypesCopy = new ArrayList<>(sortedDataTypes);
|
||||
for (DataType dt : sortedDataTypesCopy) {
|
||||
@ -3036,6 +3082,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
if (name == null || name.length() == 0) {
|
||||
throw new IllegalArgumentException("Data type must have a valid name");
|
||||
}
|
||||
|
||||
DataType dataType = resolve(typedef.getDataType(), getDependencyConflictHandler());
|
||||
boolean isAutoNamed = typedef.isAutoNamed();
|
||||
short flags = 0;
|
||||
@ -3386,30 +3433,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private class NameComparator implements Comparator<DataType> {
|
||||
/**
|
||||
* Compares its two arguments for order. Returns a negative integer, zero, or a
|
||||
* positive integer as the first argument is less than, equal to, or greater
|
||||
* than the second.
|
||||
* <p>
|
||||
*
|
||||
* @param d1 the first datatype to be compared
|
||||
* @param d2 the second datatype to be compared
|
||||
* @return a negative integer, zero, or a positive integer as the first argument
|
||||
* is less than, equal to, or greater than the second
|
||||
* @throws ClassCastException if the arguments' types prevent them from being
|
||||
* compared by this Comparator
|
||||
*/
|
||||
@Override
|
||||
public int compare(DataType d1, DataType d2) {
|
||||
int c = d1.getName().compareTo(d2.getName());
|
||||
if (c == 0) {
|
||||
return d1.getCategoryPath().compareTo(d2.getCategoryPath());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles IOExceptions
|
||||
*
|
||||
@ -4325,10 +4348,208 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
return GraphAlgorithms.getVerticesInPostOrder(graph, GraphNavigator.topDownNavigator());
|
||||
}
|
||||
|
||||
/**
|
||||
* De-duplicate equivalent conflict datatypes which share a common base data type name and
|
||||
* are found to be equivalent.
|
||||
*
|
||||
* @param dataType data type whose related conflict types should be de-duplicated
|
||||
* @return true if one or more datatypes were de-duplicted or dde-conflicted, else false
|
||||
*/
|
||||
public boolean dedupeConflicts(DataType dataType) {
|
||||
if (!(dataType instanceof DataTypeDB)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lock.acquire();
|
||||
try {
|
||||
if (dataType instanceof Pointer || dataType instanceof Array) {
|
||||
dataType = DataTypeUtilities.getBaseDataType(dataType);
|
||||
}
|
||||
if (!contains(dataType)) {
|
||||
return false;
|
||||
}
|
||||
List<DataType> relatedByName = findDataTypesSameLocation(dataType);
|
||||
if (relatedByName.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
Collections.sort(relatedByName, DataTypeComparator.INSTANCE);
|
||||
|
||||
boolean success = false;
|
||||
for (int n = relatedByName.size() - 1; n != 0; n--) {
|
||||
DataType targetDt = relatedByName.get(n);
|
||||
|
||||
for (int m = 0; m < n; m++) {
|
||||
DataType candidate = relatedByName.get(m);
|
||||
if (candidate.isEquivalent(targetDt)) {
|
||||
try {
|
||||
replace(targetDt, candidate);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
throw new AssertionError("Unexpected condition", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private record DedupedConflicts(int processCnt, int replaceCnt) {
|
||||
}
|
||||
|
||||
private DedupedConflicts doDedupeConflicts(DataType dataType) {
|
||||
|
||||
List<DataType> relatedByName = findDataTypesSameLocation(dataType);
|
||||
if (relatedByName.size() < 2) {
|
||||
return new DedupedConflicts(1, 0);
|
||||
}
|
||||
Collections.sort(relatedByName, DataTypeComparator.INSTANCE);
|
||||
|
||||
int replaceCnt = 0;
|
||||
for (int n = relatedByName.size() - 1; n != 0; n--) {
|
||||
DataType targetDt = relatedByName.get(n);
|
||||
|
||||
for (int m = 0; m < n; m++) {
|
||||
DataType candidate = relatedByName.get(m);
|
||||
if (candidate.isEquivalent(targetDt)) {
|
||||
try {
|
||||
replace(targetDt, candidate);
|
||||
++replaceCnt;
|
||||
break;
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
throw new AssertionError("Unexpected condition", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new DedupedConflicts(relatedByName.size(), replaceCnt);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* De-duplicate equivalent conflict datatypes which share a common base data type name and
|
||||
* are found to be equivalent.
|
||||
*
|
||||
* @param monitor task monitor
|
||||
* @throws CancelledException if task is cancelled
|
||||
*/
|
||||
public void dedupeAllConflicts(TaskMonitor monitor) throws CancelledException {
|
||||
lock.acquire();
|
||||
try {
|
||||
List<DataType> conflictList = new ArrayList<>();
|
||||
int total = popuplateConflictList(conflictList, getRootCategory());
|
||||
monitor.initialize(total);
|
||||
int processed = 0;
|
||||
int replacements = 0;
|
||||
for (DataType conflictDt : conflictList) {
|
||||
monitor.checkCancelled();
|
||||
DedupedConflicts result = doDedupeConflicts(conflictDt);
|
||||
processed += result.processCnt;
|
||||
replacements += result.replaceCnt;
|
||||
monitor.setProgress(processed);
|
||||
}
|
||||
Msg.info(this, "Evaluated " + processed + " conflict types, replaced " + replacements +
|
||||
" base type conflicts");
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private int popuplateConflictList(List<DataType> conflictList, Category category) {
|
||||
|
||||
int count = 0;
|
||||
for (Category childCategory : category.getCategories()) {
|
||||
count += popuplateConflictList(conflictList, childCategory);
|
||||
}
|
||||
|
||||
DataType[] dataTypes = category.getDataTypes();
|
||||
Arrays.sort(dataTypes, DataTypeComparator.INSTANCE);
|
||||
|
||||
String lastBaseName = null;
|
||||
boolean lastHadConflict = false;
|
||||
|
||||
for (DataType dt : dataTypes) {
|
||||
if (dt instanceof Pointer || dt instanceof Array) {
|
||||
continue;
|
||||
}
|
||||
boolean isConflict = dt.getName().contains(DataType.CONFLICT_SUFFIX);
|
||||
String name = DataTypeUtilities.getNameWithoutConflict(dt, false);
|
||||
if (!name.equals(lastBaseName)) {
|
||||
// base name changed
|
||||
lastBaseName = name;
|
||||
lastHadConflict = false;
|
||||
if (isConflict) {
|
||||
// non-conflict type is not present
|
||||
conflictList.add(dt);
|
||||
lastHadConflict = true;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
else if (isConflict) {
|
||||
if (!lastHadConflict) {
|
||||
// account for non-conflict type in count
|
||||
conflictList.add(dt);
|
||||
lastHadConflict = true;
|
||||
++count;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the conflict queue of newly created conflict datatypes. Processing performs one
|
||||
* last attempt at locating an equivalent datatype
|
||||
*
|
||||
* @param dataType final resolved data type.
|
||||
* If a newly created conflict type and this method and is able to replace with an equivalent
|
||||
* data type the replacement datatype will be returned, otherwise the specified {@code dataType}
|
||||
* instance will be returned.
|
||||
* @return final resolved data type
|
||||
*/
|
||||
private DataType processConflictQueue(DataType dataType) {
|
||||
|
||||
while (!conflictQueue.isEmpty()) {
|
||||
|
||||
// Process last conflict first (LIFO) to ensure conflicts with larger
|
||||
// numbers are discarded first if applicable - although unlikely to occur
|
||||
// during the same resolve-cycle.
|
||||
DataType dt = conflictQueue.removeLast();
|
||||
|
||||
List<DataType> relatedByName = findDataTypesSameLocation(dt);
|
||||
for (DataType candidate : relatedByName) {
|
||||
if (candidate != dt && DataTypeDB.isEquivalent(candidate, dt,
|
||||
DataTypeConflictHandler.DEFAULT_HANDLER)) {
|
||||
try {
|
||||
replace(dt, candidate);
|
||||
if (dt == dataType) {
|
||||
// switch final type
|
||||
dataType = candidate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
// ignore - try next if available
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return dataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate resolveCache and associated resolveQueue if not already active. If
|
||||
* this method returns true caller is responsible for flushing resolveQueue and
|
||||
* invoking {@link #flushResolveQueue(boolean)} when resolve complete.
|
||||
* invoking {@link #processResolveQueue(boolean)} when resolve complete.
|
||||
* For each completed resolve {@link #cacheResolvedDataType(DataType, DataType)}
|
||||
* should be invoked.
|
||||
*
|
||||
@ -4356,7 +4577,42 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
resolveQueue.add(new ResolvePair(resolvedDt, definitionDt));
|
||||
}
|
||||
|
||||
void flushResolveQueue(boolean deactivateCache) {
|
||||
/**
|
||||
* Process resolve queue, which includes:
|
||||
* <ul>
|
||||
* <li>Process any deferred pointer resolves in {@code resolveQueue}</li>
|
||||
* <li>If deactivating cache {@code deactivateCache==true} the conflict queue will be
|
||||
* processed</li>
|
||||
* <li>If deactivating cache {@code deactivateCache==true} the {@code resolveCache} will be
|
||||
* disposed.</li>
|
||||
* </ul>
|
||||
* @param deactivateCache true if caller is {@code resolveCache} owner as determined by call
|
||||
* to {@link #activateResolveCache()}) at start of resolve cycle, else false.
|
||||
*/
|
||||
void processResolveQueue(boolean deactivateCache) {
|
||||
processResolveQueue(deactivateCache, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process resolve queue, which includes:
|
||||
* <ul>
|
||||
* <li>Process any deferred pointer resolves in {@code resolveQueue}</li>
|
||||
* <li>If deactivating cache {@code deactivateCache==true} the conflict queue will be
|
||||
* processed</li>
|
||||
* <li>If deactivating cache {@code deactivateCache==true} the {@code resolveCache} will be
|
||||
* disposed.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param deactivateCache true if caller is {@code resolveCache} owner as determined by call
|
||||
* to {@link #activateResolveCache()}) at start of resolve cycle, else false.
|
||||
* @param dataType final resolved data type. If {@code deactivateCache==true} and this type
|
||||
* is a newly created conflict type and this method and is able to replace with an equivalent
|
||||
* data type the replacement datatype will be returned, otherwise the specified {@code dataType}
|
||||
* instance will be returned.
|
||||
* @return final resolved data type
|
||||
*/
|
||||
private DataType processResolveQueue(boolean deactivateCache, DataType dataType) {
|
||||
|
||||
try {
|
||||
if (resolveQueue != null) {
|
||||
DataTypeConflictHandler handler = getDependencyConflictHandler();
|
||||
@ -4364,7 +4620,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
ResolvePair resolvePair = resolveQueue.pollFirst();
|
||||
DataTypeDB resolvedDt = resolvePair.resolvedDt;
|
||||
try {
|
||||
resolvedDt.postPointerResolve(resolvePair.definitionDt, handler);
|
||||
if (!resolvedDt.isDeleted()) {
|
||||
resolvedDt.postPointerResolve(resolvePair.definitionDt, handler);
|
||||
}
|
||||
}
|
||||
// TODO: catch exceptions if needed
|
||||
finally {
|
||||
@ -4373,6 +4631,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deactivateCache) {
|
||||
return processConflictQueue(dataType);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
resolveQueue = null;
|
||||
@ -4380,6 +4641,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
resolveCache = null;
|
||||
}
|
||||
}
|
||||
return dataType;
|
||||
}
|
||||
|
||||
private DataType getCachedResolve(DataType dt) {
|
||||
@ -4639,6 +4901,26 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostic method to determine actual number of datatype records which exist. This
|
||||
* may differ from the total number of datatypes reported via {@link DataTypeManager#getAllDataTypes()}
|
||||
* due to the manner in which datatypes are held in-memory (i.e., name-based indexed) if
|
||||
* duplicate datatype names exist within a category.
|
||||
* @return total number of datatype records.
|
||||
*/
|
||||
int getDataTypeRecordCount() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return builtinAdapter.getRecordCount() + compositeAdapter.getRecordCount() +
|
||||
arrayAdapter.getRecordCount() + pointerAdapter.getRecordCount() +
|
||||
typedefAdapter.getRecordCount() + functionDefAdapter.getRecordCount() +
|
||||
enumAdapter.getRecordCount();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CategoryCache extends FixedSizeHashMap<String, Category> {
|
||||
|
@ -16,7 +16,7 @@
|
||||
package ghidra.program.database.data;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.*;
|
||||
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.app.util.SymbolPathParser;
|
||||
@ -306,7 +306,49 @@ public class DataTypeUtilities {
|
||||
*/
|
||||
public static String getNameWithoutConflict(DataType dataType, boolean includeCategoryPath) {
|
||||
String name = includeCategoryPath ? dataType.getPathName() : dataType.getName();
|
||||
return DATATYPE_CONFLICT_PATTERN.matcher(name).replaceAll("");
|
||||
return getNameWithoutConflict(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of a data type with all conflict naming patterns removed.
|
||||
*
|
||||
* @param dataTypeName data type name with optional category path included
|
||||
* @return name with optional category path included
|
||||
*/
|
||||
public static String getNameWithoutConflict(String dataTypeName) {
|
||||
return DATATYPE_CONFLICT_PATTERN.matcher(dataTypeName).replaceAll("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the conflict value string associated with a conflict datatype name.
|
||||
*
|
||||
* @param dataType datatype to be checked
|
||||
* @return conflict value string. Will be null if name is not a conflict name, or
|
||||
* empty string if conflict has no number. Otherwise a decimal value string will be returned.
|
||||
*/
|
||||
public static String getConflictString(DataType dataType) {
|
||||
return getConflictString(dataType.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the conflict value string associated with a conflict datatype name.
|
||||
*
|
||||
* @param dataTypeName datatype name to be checked
|
||||
* @return conflict value string. Will be one of the following:
|
||||
* <ol>
|
||||
* <li>A null value if not a conflict name,</li>
|
||||
* <li>an empty string if conflict name without a number, or</li>
|
||||
* <li>a decimal string value which corresponds to the conflict number in the name.</li>
|
||||
* </ol>
|
||||
*/
|
||||
public static String getConflictString(String dataTypeName) {
|
||||
Matcher matcher = DATATYPE_CONFLICT_PATTERN.matcher(dataTypeName);
|
||||
if (matcher.find()) {
|
||||
MatchResult matchResult = matcher.toMatchResult();
|
||||
return dataTypeName.substring(matchResult.start() + DataType.CONFLICT_SUFFIX.length(),
|
||||
matchResult.end());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,7 @@ import ghidra.docking.settings.SettingsDefinition;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
@ -601,7 +602,7 @@ class EnumDB extends DataTypeDB implements Enum {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
protected boolean isEquivalent(DataType dt, DataTypeConflictHandler handler) {
|
||||
if (dt == this) {
|
||||
return true;
|
||||
}
|
||||
@ -610,15 +611,26 @@ class EnumDB extends DataTypeDB implements Enum {
|
||||
}
|
||||
|
||||
Enum enumm = (Enum) dt;
|
||||
if (!DataTypeUtilities.equalsIgnoreConflict(getName(), enumm.getName()) ||
|
||||
getLength() != enumm.getLength() || getCount() != enumm.getCount()) {
|
||||
if (!DataTypeUtilities.equalsIgnoreConflict(getName(), enumm.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isEachValueEquivalent(enumm)) {
|
||||
if (handler != null &&
|
||||
ConflictResult.USE_EXISTING == handler.resolveConflict(enumm, this)) {
|
||||
// treat this type as equivalent if existing type will be used
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getLength() != enumm.getLength() || getCount() != enumm.getCount()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
return isEachValueEquivalent(enumm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
return isEquivalent(dt, null);
|
||||
}
|
||||
|
||||
private boolean isEachValueEquivalent(Enum enumm) {
|
||||
|
@ -54,8 +54,7 @@ abstract class EnumDBAdapter {
|
||||
* @throws CancelledException if task cancelled
|
||||
*/
|
||||
static EnumDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix,
|
||||
TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
TaskMonitor monitor) throws VersionException, IOException, CancelledException {
|
||||
if (openMode == DBConstants.CREATE) {
|
||||
return new EnumDBAdapterV1(handle, tablePrefix, true);
|
||||
}
|
||||
@ -103,8 +102,7 @@ abstract class EnumDBAdapter {
|
||||
* @throws CancelledException if task cancelled
|
||||
*/
|
||||
private static EnumDBAdapter upgrade(DBHandle handle, EnumDBAdapter oldAdapter,
|
||||
String tablePrefix,
|
||||
TaskMonitor monitor)
|
||||
String tablePrefix, TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
|
||||
DBHandle tmpHandle = new DBHandle();
|
||||
@ -216,4 +214,10 @@ abstract class EnumDBAdapter {
|
||||
abstract DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of enum datatype records
|
||||
* @return total number of composite records
|
||||
*/
|
||||
public abstract int getRecordCount();
|
||||
|
||||
}
|
||||
|
@ -79,4 +79,8 @@ class EnumDBAdapterNoTable extends EnumDBAdapter {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -128,4 +128,9 @@ class EnumDBAdapterV0 extends EnumDBAdapter implements RecordTranslator {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return enumTable.getRecordCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -139,8 +139,8 @@ class EnumDBAdapterV1 extends EnumDBAdapter {
|
||||
Field[] keys = enumTable.findRecords(new LongField(datatypeID.getValue()),
|
||||
V1_ENUM_UNIVERSAL_DT_ID_COL);
|
||||
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
DBRecord record = enumTable.getRecord(keys[i]);
|
||||
for (Field key : keys) {
|
||||
DBRecord record = enumTable.getRecord(key);
|
||||
if (record.getLongValue(V1_ENUM_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) {
|
||||
return record;
|
||||
}
|
||||
@ -148,4 +148,9 @@ class EnumDBAdapterV1 extends EnumDBAdapter {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return enumTable.getRecordCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import ghidra.docking.settings.Settings;
|
||||
import ghidra.docking.settings.SettingsImpl;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionSignature;
|
||||
@ -198,7 +199,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||
}
|
||||
finally {
|
||||
if (isResolveCacheOwner) {
|
||||
dataMgr.flushResolveQueue(true);
|
||||
dataMgr.processResolveQueue(true);
|
||||
}
|
||||
lock.release();
|
||||
}
|
||||
@ -429,12 +430,12 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dataType) {
|
||||
protected boolean isEquivalent(DataType dataType, DataTypeConflictHandler handler) {
|
||||
|
||||
if (dataType == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(dataType instanceof FunctionDefinition)) {
|
||||
if (!(dataType instanceof FunctionDefinition sig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -452,7 +453,18 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||
}
|
||||
|
||||
try {
|
||||
isEquivalent = isEquivalentSignature((FunctionSignature) dataType);
|
||||
|
||||
if (handler != null &&
|
||||
ConflictResult.USE_EXISTING == handler.resolveConflict(sig, this)) {
|
||||
// treat this type as equivalent if existing type will be used
|
||||
isEquivalent = true;
|
||||
}
|
||||
else {
|
||||
if (handler != null) {
|
||||
handler = handler.getSubsequentHandler();
|
||||
}
|
||||
isEquivalent = isEquivalentSignature(sig, handler);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
dataMgr.putCachedEquivalence(this, dataType, isEquivalent);
|
||||
@ -461,7 +473,12 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalentSignature(FunctionSignature signature) {
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
return isEquivalent(dt, null);
|
||||
}
|
||||
|
||||
private boolean isEquivalentSignature(FunctionSignature signature,
|
||||
DataTypeConflictHandler handler) {
|
||||
if (signature == this) {
|
||||
return true;
|
||||
}
|
||||
@ -476,10 +493,10 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||
(hasVarArgs() == signature.hasVarArgs()) &&
|
||||
(hasNoReturn() == signature.hasNoReturn())) {
|
||||
ParameterDefinition[] args = signature.getArguments();
|
||||
ParameterDefinition[] thisArgs = this.getArguments();
|
||||
ParameterDefinitionDB[] thisArgs = this.getArguments();
|
||||
if (args.length == thisArgs.length) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (!thisArgs[i].isEquivalent(args[i])) {
|
||||
if (!thisArgs[i].isEquivalent(args[i], handler)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -489,6 +506,11 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalentSignature(FunctionSignature signature) {
|
||||
return isEquivalentSignature(signature, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetCategoryPathRecord(long categoryID) throws IOException {
|
||||
record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_CAT_ID_COL, categoryID);
|
||||
|
@ -152,6 +152,30 @@ final class ParameterDefinitionDB implements ParameterDefinition {
|
||||
return record.getIntValue(FunctionParameterAdapter.PARAMETER_ORDINAL_COL);
|
||||
}
|
||||
|
||||
boolean isEquivalent(ParameterDefinition parm, DataTypeConflictHandler handler) {
|
||||
if (parm == null) {
|
||||
return false;
|
||||
}
|
||||
if (getOrdinal() != parm.getOrdinal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataType dataType = getDataType();
|
||||
DataType otherDataType = parm.getDataType();
|
||||
|
||||
// if they contain datatypes that have same ids, then we are essentially equivalent.
|
||||
if (DataTypeUtilities.isSameDataType(dataType, otherDataType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return DataTypeDB.isEquivalent(dataType, otherDataType, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(ParameterDefinition parm) {
|
||||
return isEquivalent(parm, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(Variable otherVar) {
|
||||
if (otherVar == null) {
|
||||
@ -169,20 +193,6 @@ final class ParameterDefinitionDB implements ParameterDefinition {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(ParameterDefinition parm) {
|
||||
if (parm == null) {
|
||||
return false;
|
||||
}
|
||||
if (getOrdinal() != parm.getOrdinal()) {
|
||||
return false;
|
||||
}
|
||||
if (!DataTypeUtilities.isSameOrEquivalentDataType(getDataType(), parm.getDataType())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ParameterDefinition p) {
|
||||
return getOrdinal() - p.getOrdinal();
|
||||
|
@ -126,7 +126,8 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||
return this;
|
||||
}
|
||||
// don't clone referenced data-type to avoid potential circular reference
|
||||
return new PointerDataType(getDataType(), hasLanguageDependantLength() ? -1 : getLength(), dtm);
|
||||
return new PointerDataType(getDataType(), hasLanguageDependantLength() ? -1 : getLength(),
|
||||
dtm);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -143,7 +144,7 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
if ( displayName == null ) {
|
||||
if (displayName == null) {
|
||||
// NOTE: Pointer display name only specifies length if null base type
|
||||
DataType dt = getDataType();
|
||||
if (dt == null) {
|
||||
@ -284,7 +285,7 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
protected boolean isEquivalent(DataType dt, DataTypeConflictHandler handler) {
|
||||
if (dt == null) {
|
||||
return false;
|
||||
}
|
||||
@ -322,7 +323,7 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: The pointer deep-dive equivalence checking on the referenced datatype can
|
||||
// NOTE: The pointer deep-dive equivalence checking on the referenced datatype can
|
||||
// cause types containing pointers (composites, functions) to conflict when in
|
||||
// reality the referenced type simply has multiple implementations which differ.
|
||||
// Although without doing this Ghidra may fail to resolve dependencies which differ
|
||||
@ -337,13 +338,21 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||
|
||||
isEquivalentActive.set(true);
|
||||
try {
|
||||
return getDataType().isEquivalent(otherDataType);
|
||||
if (handler != null) {
|
||||
handler = handler.getSubsequentHandler();
|
||||
}
|
||||
return DataTypeDB.isEquivalent(referencedDataType, otherDataType, handler);
|
||||
}
|
||||
finally {
|
||||
isEquivalentActive.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
return isEquivalent(dt, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataTypeReplaced(DataType oldDt, DataType newDt) {
|
||||
if (newDt == this) {
|
||||
@ -351,12 +360,36 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||
}
|
||||
lock.acquire();
|
||||
try {
|
||||
String myOldName = getOldName();
|
||||
if (checkIsValid() && getDataType() == oldDt) {
|
||||
|
||||
// check for existing pointer to newDt
|
||||
PointerDataType newPtr = new PointerDataType(newDt,
|
||||
hasLanguageDependantLength() ? -1 : getLength(), dataMgr);
|
||||
DataType existingPtr =
|
||||
dataMgr.getDataType(newDt.getCategoryPath(), newPtr.getName());
|
||||
if (existingPtr != null) {
|
||||
// avoid duplicate pointer - replace this pointer with existing one
|
||||
dataMgr.addDataTypeToReplace(this, existingPtr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newDt.getCategoryPath().equals(oldDt.getCategoryPath())) {
|
||||
// move this pointer to same category as newDt
|
||||
try {
|
||||
super.setCategoryPath(newDt.getCategoryPath());
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
throw new RuntimeException(e); // already checked
|
||||
}
|
||||
}
|
||||
|
||||
String myOldName = getOldName();
|
||||
oldDt.removeParent(this);
|
||||
newDt.addParent(this);
|
||||
|
||||
record.setLongValue(PointerDBAdapter.PTR_DT_ID_COL, dataMgr.getResolvedID(newDt));
|
||||
refreshName();
|
||||
|
||||
if (!oldDt.getName().equals(newDt.getName())) {
|
||||
notifyNameChanged(myOldName);
|
||||
}
|
||||
|
@ -163,4 +163,10 @@ abstract class PointerDBAdapter implements RecordTranslator {
|
||||
* @throws IOException if the database can't be accessed.
|
||||
*/
|
||||
abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of pointer datatype records
|
||||
* @return total number of composite records
|
||||
*/
|
||||
public abstract int getRecordCount();
|
||||
}
|
||||
|
@ -96,4 +96,9 @@ class PointerDBAdapterV0 extends PointerDBAdapter {
|
||||
handle.deleteTable(POINTER_TABLE_NAME);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
}
|
||||
|
@ -97,4 +97,9 @@ class PointerDBAdapterV1 extends PointerDBAdapter {
|
||||
handle.deleteTable(POINTER_TABLE_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -102,4 +102,9 @@ class PointerDBAdapterV2 extends PointerDBAdapter {
|
||||
public DBRecord translateRecord(DBRecord rec) {
|
||||
return rec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
package ghidra.program.database.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import db.*;
|
||||
@ -252,18 +251,21 @@ public class ProgramDataTypeManager extends ProgramBasedDataTypeManagerDB implem
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDataTypeIDs(LinkedList<Long> deletedIds, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
protected void deleteDataTypeIDs(LinkedList<Long> deletedIds) {
|
||||
// TODO: SymbolManager/FunctionManager do not appear to handle datatype removal update.
|
||||
// Suspect it handles indirectly through detection of deleted datatype. Old deleted ID
|
||||
// use could be an issue.
|
||||
long[] ids = new long[deletedIds.size()];
|
||||
Iterator<Long> it = deletedIds.iterator();
|
||||
int i = 0;
|
||||
while (it.hasNext()) {
|
||||
ids[i++] = it.next().longValue();
|
||||
for (Long deletedId : deletedIds) {
|
||||
ids[i++] = deletedId.longValue();
|
||||
}
|
||||
try {
|
||||
program.getCodeManager().clearData(ids, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// won't happen
|
||||
}
|
||||
program.getCodeManager().clearData(ids, monitor);
|
||||
program.getFunctionManager().invalidateCache(false);
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import ghidra.docking.settings.Settings;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
|
||||
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.AssertException;
|
||||
@ -1584,7 +1585,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
|
||||
}
|
||||
finally {
|
||||
if (isResolveCacheOwner) {
|
||||
dataMgr.flushResolveQueue(true);
|
||||
dataMgr.processResolveQueue(true);
|
||||
}
|
||||
lock.release();
|
||||
}
|
||||
@ -1941,11 +1942,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dataType) {
|
||||
protected boolean isEquivalent(DataType dataType, DataTypeConflictHandler handler) {
|
||||
if (dataType == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(dataType instanceof StructureInternal)) {
|
||||
if (!(dataType instanceof StructureInternal struct)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1964,7 +1965,14 @@ class StructureDB extends CompositeDB implements StructureInternal {
|
||||
|
||||
try {
|
||||
isEquivalent = false;
|
||||
StructureInternal struct = (StructureInternal) dataType;
|
||||
|
||||
if (handler != null &&
|
||||
ConflictResult.USE_EXISTING == handler.resolveConflict(struct, this)) {
|
||||
// treat this type as equivalent if existing type will be used
|
||||
isEquivalent = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int otherLength = struct.isZeroLength() ? 0 : struct.getLength();
|
||||
int packing = getStoredPackingValue();
|
||||
if (packing != struct.getStoredPackingValue() ||
|
||||
@ -1982,10 +1990,14 @@ class StructureDB extends CompositeDB implements StructureInternal {
|
||||
if (otherDefinedComponents.length != myNumComps) { // safety check
|
||||
return false;
|
||||
}
|
||||
if (handler != null) {
|
||||
handler = handler.getSubsequentHandler();
|
||||
//dataMgr.getPostResolve(this);
|
||||
}
|
||||
for (int i = 0; i < myNumComps; i++) {
|
||||
DataTypeComponent myDtc = components.get(i);
|
||||
DataTypeComponent otherDtc = otherDefinedComponents[i];
|
||||
if (!myDtc.isEquivalent(otherDtc)) {
|
||||
if (!DataTypeComponentDB.isEquivalent(myDtc, otherDtc, handler)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1997,6 +2009,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
return isEquivalent(dt, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust length of specified component (by index) by consuming available undefined
|
||||
* bytes upto the specified number of bytes (numBytes). The associated component record will
|
||||
|
@ -22,6 +22,7 @@ import ghidra.docking.settings.Settings;
|
||||
import ghidra.docking.settings.SettingsDefinition;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.util.UniversalID;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
@ -249,29 +250,54 @@ class TypedefDB extends DataTypeDB implements TypeDef {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType obj) {
|
||||
if (obj == this) {
|
||||
protected boolean isEquivalent(DataType dt, DataTypeConflictHandler handler) {
|
||||
if (dt == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || !(obj instanceof TypeDef)) {
|
||||
if (dt == null || !(dt instanceof TypeDef)) {
|
||||
return false;
|
||||
}
|
||||
TypeDef td = (TypeDef) obj;
|
||||
TypeDef td = (TypeDef) dt;
|
||||
validate(lock);
|
||||
|
||||
boolean autoNamed = isAutoNamed();
|
||||
if (autoNamed != td.isAutoNamed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!autoNamed && !DataTypeUtilities.equalsIgnoreConflict(getName(), td.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasSameTypeDefSettings(td)) {
|
||||
return false;
|
||||
}
|
||||
return DataTypeUtilities.isSameOrEquivalentDataType(getDataType(), td.getDataType());
|
||||
|
||||
if (handler != null && ConflictResult.USE_EXISTING == handler.resolveConflict(td, this)) {
|
||||
// treat this type as equivalent if existing type will be used
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: add pointer-post-resolve logic with resolving bypass (similar to StructureDB components)
|
||||
|
||||
DataType dataType = getDataType();
|
||||
DataType otherDataType = td.getDataType();
|
||||
if (DataTypeUtilities.isSameDataType(dataType, otherDataType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handler != null) {
|
||||
handler = handler.getSubsequentHandler();
|
||||
}
|
||||
return DataTypeDB.isEquivalent(dataType, otherDataType, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
return isEquivalent(dt, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCategoryPath(CategoryPath path) throws DuplicateNameException {
|
||||
if (isAutoNamed()) {
|
||||
return; // ignore category change if auto-naming enabled
|
||||
@ -387,6 +413,7 @@ class TypedefDB extends DataTypeDB implements TypeDef {
|
||||
return getDataType().getTypeDefSettingsDefinitions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings doGetDefaultSettings() {
|
||||
DataTypeSettingsDB settings = new DataTypeSettingsDB(dataMgr, this, key);
|
||||
settings.setLock(dataMgr instanceof BuiltInDataTypeManager);
|
||||
|
@ -60,8 +60,7 @@ abstract class TypedefDBAdapter {
|
||||
* @throws CancelledException if task is cancelled
|
||||
*/
|
||||
static TypedefDBAdapter getAdapter(DBHandle handle, int openMode, String tablePrefix,
|
||||
TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
TaskMonitor monitor) throws VersionException, IOException, CancelledException {
|
||||
try {
|
||||
return new TypedefDBAdapterV2(handle, tablePrefix, openMode == DBConstants.CREATE);
|
||||
}
|
||||
@ -217,4 +216,10 @@ abstract class TypedefDBAdapter {
|
||||
abstract DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of typedef datatype records
|
||||
* @return total number of composite records
|
||||
*/
|
||||
public abstract int getRecordCount();
|
||||
|
||||
}
|
||||
|
@ -123,4 +123,8 @@ class TypedefDBAdapterV0 extends TypedefDBAdapter implements RecordTranslator {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
}
|
||||
|
@ -108,8 +108,8 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter implements RecordTranslator {
|
||||
DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException {
|
||||
Field[] keys =
|
||||
table.findRecords(new LongField(datatypeID.getValue()), V1_TYPEDEF_UNIVERSAL_DT_ID_COL);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
DBRecord record = table.getRecord(keys[i]);
|
||||
for (Field key : keys) {
|
||||
DBRecord record = table.getRecord(key);
|
||||
if (record.getLongValue(V1_TYPEDEF_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) {
|
||||
return translateRecord(record);
|
||||
}
|
||||
@ -137,4 +137,9 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter implements RecordTranslator {
|
||||
oldRec.getLongValue(V1_TYPEDEF_LAST_CHANGE_TIME_COL));
|
||||
return rec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
}
|
||||
|
@ -138,8 +138,8 @@ class TypedefDBAdapterV2 extends TypedefDBAdapter {
|
||||
Field[] keys =
|
||||
table.findRecords(new LongField(datatypeID.getValue()), V2_TYPEDEF_UNIVERSAL_DT_ID_COL);
|
||||
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
DBRecord record = table.getRecord(keys[i]);
|
||||
for (Field key : keys) {
|
||||
DBRecord record = table.getRecord(key);
|
||||
if (record.getLongValue(V2_TYPEDEF_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) {
|
||||
return record;
|
||||
}
|
||||
@ -147,4 +147,9 @@ class TypedefDBAdapterV2 extends TypedefDBAdapter {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRecordCount() {
|
||||
return table.getRecordCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import db.Field;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@ -312,7 +313,7 @@ class UnionDB extends CompositeDB implements UnionInternal {
|
||||
}
|
||||
finally {
|
||||
if (isResolveCacheOwner) {
|
||||
dataMgr.flushResolveQueue(true);
|
||||
dataMgr.processResolveQueue(true);
|
||||
}
|
||||
lock.release();
|
||||
}
|
||||
@ -722,12 +723,11 @@ class UnionDB extends CompositeDB implements UnionInternal {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dataType) {
|
||||
|
||||
protected boolean isEquivalent(DataType dataType, DataTypeConflictHandler handler) {
|
||||
if (dataType == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(dataType instanceof UnionInternal)) {
|
||||
if (!(dataType instanceof UnionInternal union)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -746,7 +746,14 @@ class UnionDB extends CompositeDB implements UnionInternal {
|
||||
|
||||
try {
|
||||
isEquivalent = false;
|
||||
UnionInternal union = (UnionInternal) dataType;
|
||||
|
||||
if (handler != null &&
|
||||
ConflictResult.USE_EXISTING == handler.resolveConflict(union, this)) {
|
||||
// treat this type as equivalent if existing type will be used
|
||||
isEquivalent = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getStoredPackingValue() != union.getStoredPackingValue() ||
|
||||
getStoredMinimumAlignment() != union.getStoredMinimumAlignment()) {
|
||||
// rely on component match instead of checking length
|
||||
@ -758,8 +765,11 @@ class UnionDB extends CompositeDB implements UnionInternal {
|
||||
if (myComps.length != otherComps.length) {
|
||||
return false;
|
||||
}
|
||||
if (handler != null) {
|
||||
handler = handler.getSubsequentHandler();
|
||||
}
|
||||
for (int i = 0; i < myComps.length; i++) {
|
||||
if (!myComps[i].isEquivalent(otherComps[i])) {
|
||||
if (!DataTypeComponentDB.isEquivalent(myComps[i], otherComps[i], handler)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -771,6 +781,11 @@ class UnionDB extends CompositeDB implements UnionInternal {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(DataType dt) {
|
||||
return isEquivalent(dt, null);
|
||||
}
|
||||
|
||||
private void shiftOrdinals(int ordinal, int deltaOrdinal) {
|
||||
for (int i = ordinal; i < components.size(); i++) {
|
||||
DataTypeComponentDB dtc = components.get(i);
|
||||
|
@ -33,7 +33,7 @@ public interface DBRecordAdapter {
|
||||
public RecordIterator getRecords() throws IOException;
|
||||
|
||||
/**
|
||||
* Get the number of records in table
|
||||
* Get the number of function definition datatype records
|
||||
* @return total record count
|
||||
*/
|
||||
public int getRecordCount();
|
||||
|
@ -59,6 +59,9 @@ public interface Category extends Comparable<Category> {
|
||||
* occurs. In other words, finds all data types whose name matches the given name once
|
||||
* any conflict suffixes have been removed from both the given name and the data types
|
||||
* that are being scanned.
|
||||
* <br>
|
||||
* NOTE: The {@code name} provided must not contain array or pointer decorations.
|
||||
*
|
||||
* @param name the name for which to get conflict related data types in this category. Note:
|
||||
* the name that is passed in will be normalized to its base name, so you may pass in names
|
||||
* with .conflict appended as a convenience.
|
||||
|
@ -49,7 +49,7 @@ public interface Composite extends DataType {
|
||||
|
||||
/**
|
||||
* Returns the component of this data type with the indicated ordinal.
|
||||
* @param ordinal the component's ordinal (zero based).
|
||||
* @param ordinal the component's ordinal (numbering starts at 0).
|
||||
* @return the data type component.
|
||||
* @throws IndexOutOfBoundsException if the ordinal is out of bounds
|
||||
*/
|
||||
@ -155,7 +155,7 @@ public interface Composite extends DataType {
|
||||
* Inserts a new datatype at the specified ordinal position in this composite.
|
||||
* <BR>Note: For an aligned structure the ordinal position will get adjusted
|
||||
* automatically to provide the proper alignment.
|
||||
* @param ordinal the ordinal where the new datatype is to be inserted.
|
||||
* @param ordinal the ordinal where the new datatype is to be inserted (numbering starts at 0).
|
||||
* @param dataType the datatype to insert.
|
||||
* @return the componentDataType created.
|
||||
* @throws IllegalArgumentException if the specified data type is not
|
||||
@ -171,7 +171,7 @@ public interface Composite extends DataType {
|
||||
* Inserts a new datatype at the specified ordinal position in this composite.
|
||||
* <BR>Note: For an aligned structure the ordinal position will get adjusted
|
||||
* automatically to provide the proper alignment.
|
||||
* @param ordinal the ordinal where the new datatype is to be inserted.
|
||||
* @param ordinal the ordinal where the new datatype is to be inserted (numbering starts at 0).
|
||||
* @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.
|
||||
@ -190,7 +190,7 @@ public interface Composite extends DataType {
|
||||
* Inserts a new datatype at the specified ordinal position in this composite.
|
||||
* <BR>Note: For an aligned structure the ordinal position will get adjusted
|
||||
* automatically to provide the proper alignment.
|
||||
* @param ordinal the ordinal where the new datatype is to be inserted.
|
||||
* @param ordinal the ordinal where the new datatype is to be inserted (numbering starts at 0).
|
||||
* @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.
|
||||
@ -211,7 +211,7 @@ public interface Composite extends DataType {
|
||||
* Deletes the component at the given ordinal position.
|
||||
* <BR>Note: Removal of bitfields from a structure with packing disabled will
|
||||
* not shift other components causing vacated bytes to revert to undefined filler.
|
||||
* @param ordinal the ordinal of the component to be deleted.
|
||||
* @param ordinal the ordinal of the component to be deleted (numbering starts at 0).
|
||||
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
|
||||
*/
|
||||
public void delete(int ordinal) throws IndexOutOfBoundsException;
|
||||
|
@ -17,50 +17,49 @@ package ghidra.program.model.data;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class DataTypeComparator implements Comparator<Object> {
|
||||
/**
|
||||
* {@link DataTypeComparator} provides the preferred named-based comparison of {@link DataType}
|
||||
* which utilizes the {@link DataTypeNameComparator} for a primary {@link DataType#getName() name}
|
||||
* comparison followed by sub-ordering on {@link DataTypeManager} name and {@link CategoryPath}.
|
||||
*/
|
||||
public class DataTypeComparator implements Comparator<DataType> {
|
||||
|
||||
public static DataTypeComparator INSTANCE = new DataTypeComparator();
|
||||
|
||||
@Override
|
||||
public int compare(Object o1, Object o2) {
|
||||
public int compare(DataType dt1, DataType dt2) {
|
||||
String name1 = dt1.getName();
|
||||
String name2 = dt2.getName();
|
||||
|
||||
if (o1 instanceof DataType && o2 instanceof DataType) {
|
||||
DataType dt1 = (DataType) o1;
|
||||
DataType dt2 = (DataType) o2;
|
||||
int nameCompare = DataTypeNameComparator.INSTANCE.compare(name1, name2);
|
||||
if (nameCompare == 0) {
|
||||
|
||||
String name1 = dt1.getName();
|
||||
String name2 = dt2.getName();
|
||||
DataTypeManager dtm1 = dt1.getDataTypeManager();
|
||||
String dtmName1 = dtm1 != null ? dtm1.getName() : null;
|
||||
|
||||
// if the names are the same, then sort by the path
|
||||
int nameResult = name1.compareToIgnoreCase(name2);
|
||||
if (nameResult != 0) {
|
||||
return nameResult;
|
||||
DataTypeManager dtm2 = dt2.getDataTypeManager();
|
||||
String dtmName2 = dtm2 != null ? dtm2.getName() : null;
|
||||
|
||||
if (dtm1 == null) {
|
||||
if (dtm2 != null) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (dtm2 == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
String dtmName1 = dt1.getDataTypeManager().getName();
|
||||
String dtmName2 = dt2.getDataTypeManager().getName();
|
||||
// Compare DataTypeManager names if datatypes have the same name
|
||||
int compare = dtmName1.compareTo(dtmName2);
|
||||
if (compare == 0) {
|
||||
|
||||
// if they have the same name, and are in the same DTM, then compare paths
|
||||
int dtmResult = dtmName1.compareToIgnoreCase(dtmName2);
|
||||
if (dtmResult != 0) {
|
||||
return dtmResult;
|
||||
// Compare category paths if they have the same name and DTM
|
||||
String catPath1 = dt1.getCategoryPath().getPath();
|
||||
String catPath2 = dt2.getCategoryPath().getPath();
|
||||
compare = catPath1.compareTo(catPath2);
|
||||
}
|
||||
|
||||
return dt1.getPathName().compareToIgnoreCase(dt2.getPathName());
|
||||
return compare;
|
||||
}
|
||||
// these cases are for lookups by string keys
|
||||
else if (o1 instanceof String && o2 instanceof DataType) {
|
||||
|
||||
DataType dt2 = (DataType) o2;
|
||||
String name2 = dt2.getName();
|
||||
|
||||
return ((String) o1).compareToIgnoreCase(name2);
|
||||
}
|
||||
else if (o1 instanceof DataType && o2 instanceof String) {
|
||||
DataType dt1 = (DataType) o1;
|
||||
String name1 = dt1.getName();
|
||||
|
||||
return name1.compareToIgnoreCase(((String) o2));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return nameCompare;
|
||||
}
|
||||
}
|
||||
|
@ -167,15 +167,19 @@ public interface DataTypeManager {
|
||||
/**
|
||||
* Begin searching at the root category for all data types with the
|
||||
* given name. Places all the data types in this data type manager
|
||||
* with the given name into the list.
|
||||
* @param name name of the data type
|
||||
* with the given name into the list. Presence of {@code .conflict}
|
||||
* extension will be ignored for both specified name and returned
|
||||
* results.
|
||||
* @param name name of the data type (wildcards are not supported and will be treated
|
||||
* as explicit search characters)
|
||||
* @param list list that will be populated with matching DataType objects
|
||||
*/
|
||||
public void findDataTypes(String name, List<DataType> list);
|
||||
|
||||
/**
|
||||
* Begin searching at the root category for all data types with names
|
||||
* that match the given name that may contain wildcards.
|
||||
* that match the given name that may contain wildcards using familiar globbing
|
||||
* characters '*' and '?'.
|
||||
* @param name name to match; may contain wildcards
|
||||
* @param list list that will be populated with matching DataType objects
|
||||
* @param caseSensitive true if the match is case sensitive
|
||||
|
@ -0,0 +1,102 @@
|
||||
/* ###
|
||||
* 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.*;
|
||||
|
||||
import ghidra.program.database.data.DataTypeUtilities;
|
||||
|
||||
/**
|
||||
* {@link DataTypeNameComparator} provides the preferred named-based comparison of {@link DataType}
|
||||
* which handles both some degree of case-insensity as well as proper grouping and ordering of
|
||||
* conflict datatypes.
|
||||
*/
|
||||
public class DataTypeNameComparator implements Comparator<String> {
|
||||
|
||||
public static final DataTypeNameComparator INSTANCE = new DataTypeNameComparator();
|
||||
|
||||
@Override
|
||||
public int compare(String dt1Name, String dt2Name) {
|
||||
|
||||
String name1 = DataTypeUtilities.getNameWithoutConflict(dt1Name);
|
||||
String name2 = DataTypeUtilities.getNameWithoutConflict(dt2Name);
|
||||
|
||||
int len1 = name1.length();
|
||||
int len2 = name2.length();
|
||||
|
||||
int len = Math.min(len1, len2); // overlapping length
|
||||
int baseNameLen = len; // Length of overlapping portion of base-name (no decorations)
|
||||
|
||||
// Case-insensitive compare of significant overlapping portion of name
|
||||
int baseCaseCompare = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c1 = name1.charAt(i);
|
||||
char c2 = name2.charAt(i);
|
||||
char lc1 = Character.toLowerCase(c1);
|
||||
char lc2 = Character.toLowerCase(c2);
|
||||
// first space treated as end of base-name
|
||||
if (lc1 == ' ') {
|
||||
if (lc2 == ' ') {
|
||||
baseNameLen = i;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (lc2 == ' ') {
|
||||
return 1;
|
||||
}
|
||||
if (lc1 != lc2) {
|
||||
return lc1 - lc2;
|
||||
}
|
||||
if (baseCaseCompare == 0) {
|
||||
baseCaseCompare = c1 - c2;
|
||||
}
|
||||
}
|
||||
|
||||
if (len1 > baseNameLen && name1.charAt(baseNameLen) != ' ') {
|
||||
return 1; // first name has longer base-name
|
||||
}
|
||||
|
||||
if (len2 > baseNameLen && name2.charAt(baseNameLen) != ' ') {
|
||||
return -1; // second name has longer base-name
|
||||
}
|
||||
|
||||
if (baseCaseCompare != 0) {
|
||||
return baseCaseCompare;
|
||||
}
|
||||
|
||||
// Same base-name, order by conflict
|
||||
int conflict1 = getConflictValue(dt1Name);
|
||||
int conflict2 = getConflictValue(dt2Name);
|
||||
if (conflict1 != conflict2) {
|
||||
return conflict1 - conflict2;
|
||||
}
|
||||
|
||||
return name1.compareTo(name2);
|
||||
}
|
||||
|
||||
private int getConflictValue(String dtName) {
|
||||
String conflict = DataTypeUtilities.getConflictString(dtName);
|
||||
if (conflict == null) {
|
||||
return -1;
|
||||
}
|
||||
if (conflict.length() == 0) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.parseInt(conflict);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/* ###
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* {@link DataTypeObjectComparator} provides the preferred named-based comparison of data types
|
||||
* using the {@link DataTypeNameComparator} allowing a mix of {@link DataType} and/or {@link String}
|
||||
* names to be compared.
|
||||
*/
|
||||
public class DataTypeObjectComparator implements Comparator<Object> {
|
||||
|
||||
public static DataTypeObjectComparator INSTANCE = new DataTypeObjectComparator();
|
||||
|
||||
/**
|
||||
* Compare two data type names
|
||||
* @param o1 the first {@link DataType} or {@link String} name to be compared.
|
||||
* @param o2 the second {@link DataType} or {@link String} name to be compared.
|
||||
* @return a negative integer, zero, or a positive integer as the
|
||||
* first argument is less than, equal to, or greater than the
|
||||
* second.
|
||||
* @throws IllegalArgumentException if object types other than {@link DataType} or
|
||||
* {@link String} are compared.
|
||||
*/
|
||||
@Override
|
||||
public int compare(Object o1, Object o2) {
|
||||
|
||||
String name1, name2;
|
||||
if (o1 instanceof DataType && o2 instanceof DataType) {
|
||||
DataType dt1 = (DataType) o1;
|
||||
name1 = dt1.getName();
|
||||
DataType dt2 = (DataType) o2;
|
||||
name2 = dt2.getName();
|
||||
}
|
||||
// these cases are for lookups by string keys
|
||||
else if (o1 instanceof String && o2 instanceof DataType) {
|
||||
name1 = (String) o1;
|
||||
DataType dt2 = (DataType) o2;
|
||||
name2 = dt2.getName();
|
||||
}
|
||||
else if (o1 instanceof DataType && o2 instanceof String) {
|
||||
DataType dt1 = (DataType) o1;
|
||||
name1 = dt1.getName();
|
||||
name2 = (String) o2;
|
||||
}
|
||||
else if (o1 instanceof String && o2 instanceof String) {
|
||||
name1 = (String) o1;
|
||||
name2 = (String) o2;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported comparison " +
|
||||
o1.getClass().getSimpleName() + " / " + o2.getClass().getSimpleName());
|
||||
}
|
||||
return DataTypeNameComparator.INSTANCE.compare(name1, name2);
|
||||
}
|
||||
}
|
@ -875,7 +875,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDataTypeIDs(LinkedList<Long> deletedIds, TaskMonitor monitor) {
|
||||
protected void deleteDataTypeIDs(LinkedList<Long> deletedIds) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ public interface Structure extends Composite {
|
||||
/**
|
||||
* Returns the component of this structure with the indicated ordinal.
|
||||
*
|
||||
* @param ordinal the ordinal of the component requested.
|
||||
* @param ordinal the ordinal of the component requested (numbering starts at 0).
|
||||
* @return the data type component.
|
||||
* @throws IndexOutOfBoundsException if the ordinal is out of bounds
|
||||
*/
|
||||
@ -71,7 +71,7 @@ public interface Structure extends Composite {
|
||||
* or null if not found.
|
||||
*/
|
||||
public DataTypeComponent getComponentContaining(int offset);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the first non-zero-length component that starts at the specified offset.
|
||||
* Note that one or more components may share the same offset when a bit-field or zero-length
|
||||
@ -102,7 +102,7 @@ public interface Structure extends Composite {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an ordered list of components that contain the byte at the specified offset.
|
||||
* Unlike {@link #getComponentAt(int)} and {@link #getComponentContaining(int)} this method will
|
||||
@ -151,7 +151,7 @@ public interface Structure extends Composite {
|
||||
* 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 of the component to be inserted.
|
||||
* @param ordinal the ordinal of the component to be inserted (numbering starts at 0).
|
||||
* @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.
|
||||
@ -305,7 +305,7 @@ public interface Structure extends Composite {
|
||||
* which may not result in such undefined components. In the case of a packed structure
|
||||
* clearing is always completed without backfill.
|
||||
*
|
||||
* @param ordinal the ordinal of the component to clear.
|
||||
* @param ordinal the ordinal of the component to clear (numbering starts at 0).
|
||||
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
|
||||
*/
|
||||
public void clearComponent(int ordinal) throws IndexOutOfBoundsException;
|
||||
@ -331,7 +331,7 @@ public interface Structure extends Composite {
|
||||
* NOTE: In general, it is not recommended that this method be used with non-packed
|
||||
* structures where the replaced component is a bit-field.
|
||||
*
|
||||
* @param ordinal the ordinal of the component to be replaced.
|
||||
* @param ordinal the ordinal of the component to be replaced (numbering starts at 0).
|
||||
* @param dataType the datatype to insert. If {@link DataType#DEFAULT} is specified for a packed
|
||||
* structure an {@link Undefined1DataType} will be used in its place. If {@link DataType#DEFAULT}
|
||||
* is specified for a non-packed structure this is equivelant to {@link #clearComponent(int)}, ignoring
|
||||
@ -368,7 +368,7 @@ public interface Structure extends Composite {
|
||||
* NOTE: In general, it is not recommended that this method be used with non-packed
|
||||
* structures where the replaced component is a bit-field.
|
||||
*
|
||||
* @param ordinal the ordinal of the component to be replaced.
|
||||
* @param ordinal the ordinal of the component to be replaced (numbering starts at 0).
|
||||
* @param dataType the datatype to insert. If {@link DataType#DEFAULT} is specified for a packed
|
||||
* structure an {@link Undefined1DataType} will be used in its place. If {@link DataType#DEFAULT}
|
||||
* is specified for a non-packed structure this is equivelant to {@link #clearComponent(int)}, ignoring
|
||||
|
@ -168,20 +168,28 @@ public class TypedefDataType extends GenericDataType implements TypeDef {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof TypeDef) {
|
||||
TypeDef td = (TypeDef) obj;
|
||||
if (isAutoNamed != td.isAutoNamed()) {
|
||||
return false;
|
||||
}
|
||||
if (!isAutoNamed && !DataTypeUtilities.equalsIgnoreConflict(getName(), td.getName())) {
|
||||
return false;
|
||||
}
|
||||
if (!hasSameTypeDefSettings(td)) {
|
||||
return false;
|
||||
}
|
||||
return DataTypeUtilities.isSameOrEquivalentDataType(getDataType(), td.getDataType());
|
||||
if (!(obj instanceof TypeDef td)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
if (isAutoNamed != td.isAutoNamed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAutoNamed && !DataTypeUtilities.equalsIgnoreConflict(getName(), td.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasSameTypeDefSettings(td)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataType otherDataType = td.getDataType();
|
||||
if (DataTypeUtilities.isSameDataType(dataType, otherDataType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return dataType.isEquivalent(otherDataType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -235,8 +243,7 @@ public class TypedefDataType extends GenericDataType implements TypeDef {
|
||||
}
|
||||
TypedefDataType newTypedef =
|
||||
new TypedefDataType(typedef.getCategoryPath(), typedef.getName(), typedef.getDataType(),
|
||||
typedef.getUniversalID(),
|
||||
typedef.getSourceArchive(), typedef.getLastChangeTime(),
|
||||
typedef.getUniversalID(), typedef.getSourceArchive(), typedef.getLastChangeTime(),
|
||||
typedef.getLastChangeTimeInSourceArchive(), dtm);
|
||||
copyTypeDefSettings(typedef, newTypedef, false);
|
||||
newTypedef.isAutoNamed = typedef.isAutoNamed();
|
||||
|
@ -33,7 +33,7 @@ public interface Union extends Composite {
|
||||
* 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 where the new datatype is to be inserted (numbering starts at 0).
|
||||
* @param baseDataType the bitfield base datatype (certain restrictions apply).
|
||||
* @param bitSize the declared bitfield size in bits. The effective bit size may be
|
||||
* adjusted based upon the specified baseDataType.
|
||||
|
@ -0,0 +1,265 @@
|
||||
/* ###
|
||||
* 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 static org.junit.Assert.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.program.model.data.*;
|
||||
|
||||
public class DataTypeDBReplaceTest extends AbstractGenericTest {
|
||||
|
||||
private DataTypeManagerDB dataMgr;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
dataMgr = new StandAloneDataTypeManager("dummyDTM");
|
||||
dataMgr.startTransaction("Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceHandlingPointers() throws Exception {
|
||||
|
||||
Structure s1 = new StructureDataType(new CategoryPath("/P1"), "MyStruct1", 0, dataMgr);
|
||||
s1.add(ByteDataType.dataType);
|
||||
Structure sDb1 = (Structure) dataMgr.resolve(s1, null);
|
||||
|
||||
Structure s2 = new StructureDataType(new CategoryPath("/P2"), "MyStruct2", 0, dataMgr);
|
||||
s2.add(new PointerDataType(s1)); // MyStruct1*
|
||||
Structure sDb2 = (Structure) dataMgr.resolve(s2, null);
|
||||
DataType ptrDb1 = sDb2.getComponent(0).getDataType(); // MyStruct1*
|
||||
|
||||
Structure s3 = new StructureDataType(new CategoryPath("/P3"), "MyStruct3", 0, dataMgr);
|
||||
s3.add(new PointerDataType(new PointerDataType(s2))); // MyStruct2**
|
||||
Structure sDb3 = (Structure) dataMgr.resolve(s3, null);
|
||||
Pointer ptrDb2a = (Pointer) sDb3.getComponent(0).getDataType(); // MyStruct2**
|
||||
Pointer ptrDb2b = (Pointer) ptrDb2a.getDataType(); // MyStruct2*
|
||||
|
||||
assertTrue(sDb3.isEquivalent(s3));
|
||||
|
||||
assertEquals(8, getDataTypeCount()); // include "undefined" type used during resolve
|
||||
|
||||
dataMgr.replaceDataType(sDb2, sDb1, false);
|
||||
|
||||
System.out.println("---");
|
||||
|
||||
assertTrue("Expected MyStruct2* to be replaced by MyStruct1*", ptrDb2b.isDeleted());
|
||||
assertFalse("Expected /P2/MyStruct2** to be moved/transformed to /P1/MyStruct1**",
|
||||
ptrDb2a.isDeleted());
|
||||
|
||||
// Pointer instance should have changed category as well as using MyStruct1*
|
||||
Pointer ptrPtr = (Pointer) sDb3.getComponent(0).getDataType(); // MyStruct1**
|
||||
assertTrue(ptrDb2a == ptrPtr);
|
||||
assertEquals("/P1/MyStruct1 * *", ptrPtr.getPathName());
|
||||
|
||||
// Existing MyStruct1* pointer instance should be used and MyStruct2* removed
|
||||
Pointer ptr = (Pointer) ptrPtr.getDataType(); // MyStruct1*
|
||||
assertTrue(ptrDb1 == ptr);
|
||||
assertEquals("/P1/MyStruct1 *", ptr.getPathName());
|
||||
|
||||
assertEquals(6, getDataTypeCount()); // include "undefined" type used during resolve
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceHandlingArrays() throws Exception {
|
||||
|
||||
Structure s1 = new StructureDataType(new CategoryPath("/P1"), "MyStruct1", 0, dataMgr);
|
||||
s1.add(ByteDataType.dataType);
|
||||
Structure sDb1 = (Structure) dataMgr.resolve(s1, null);
|
||||
|
||||
Structure s2 = new StructureDataType(new CategoryPath("/P2"), "MyStruct2", 0, dataMgr);
|
||||
s2.add(WordDataType.dataType);
|
||||
Structure sDb2 = (Structure) dataMgr.resolve(s2, null);
|
||||
|
||||
Structure s3 = new StructureDataType(new CategoryPath("/P3"), "MyStruct3", 0, dataMgr);
|
||||
s3.add(new ArrayDataType(s1, 3, -1)); // MyStruct1[3]
|
||||
s3.add(new ArrayDataType(s2, 2, -1)); // MyStruct2[2]
|
||||
s3.add(new ArrayDataType(new ArrayDataType(s2, 3, -1), 2, -1)); // MyStruct2[2][3]
|
||||
|
||||
Structure sDb3 = (Structure) dataMgr.resolve(s3, null);
|
||||
Array aDb1_3 = (Array) sDb3.getComponent(0).getDataType(); // 0: MyStruct1[3]
|
||||
Array aDb2_2 = (Array) sDb3.getComponent(1).getDataType(); // 1: MyStruct2[2]
|
||||
Array aDb2_3_2 = (Array) sDb3.getComponent(2).getDataType(); // 2: MyStruct2[2][3]
|
||||
Array aDb2_3 = (Array) aDb2_3_2.getDataType(); // MyStruct2[3]
|
||||
|
||||
//@formatter:off
|
||||
assertEquals("/P3/MyStruct3\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure MyStruct3 {\n" +
|
||||
" 0 MyStruct1[3] 3 \"\"\n" +
|
||||
" 3 MyStruct2[2] 4 \"\"\n" +
|
||||
" 7 MyStruct2[2][3] 12 \"\"\n" +
|
||||
"}\n" +
|
||||
"Size = 19 Actual Alignment = 1\n", sDb3.toString());
|
||||
//@formatter:on
|
||||
|
||||
assertTrue(sDb3.isEquivalent(s3));
|
||||
|
||||
assertEquals(9, getDataTypeCount()); // include "undefined" type used during resolve
|
||||
|
||||
dataMgr.replaceDataType(sDb2, sDb1, false);
|
||||
|
||||
System.out.println("---");
|
||||
|
||||
assertFalse("Expected no change", aDb1_3.isDeleted());
|
||||
assertFalse("Expected MyStruct2[2] to be moved/transformed to MyStruct1[2]",
|
||||
aDb2_2.isDeleted());
|
||||
assertFalse("Expected MyStruct2[3][2] to be moved/transformed to MyStruct1[3][2]",
|
||||
aDb2_3_2.isDeleted());
|
||||
assertTrue("Expected MyStruct2[3] to be replaced by MyStruct1[3]", aDb2_3.isDeleted());
|
||||
|
||||
DataTypeComponent[] definedComponents = sDb3.getDefinedComponents();
|
||||
assertEquals(3, definedComponents.length);
|
||||
|
||||
// Array instance should have changed category as well as using MyStruct1
|
||||
Array a1 = (Array) definedComponents[1].getDataType(); // MyStruct1[2]
|
||||
assertTrue(aDb2_2 == a1);
|
||||
assertEquals("/P1/MyStruct1[2]", a1.getPathName());
|
||||
|
||||
// Array instance should have changed category as well as using MyStruct1[3]
|
||||
Array a1a = (Array) definedComponents[2].getDataType(); // MyStruct1[3][2]
|
||||
assertTrue(aDb2_3_2 == a1a);
|
||||
assertEquals("/P1/MyStruct1[2][3]", a1a.getPathName());
|
||||
|
||||
// Existing MyStruct1[3] array instance should be used and MyStruct2[3] removed
|
||||
Array a1b = (Array) a1a.getDataType(); // MyStruct1[3]
|
||||
assertTrue(aDb1_3 == a1b);
|
||||
assertEquals("/P1/MyStruct1[3]", a1b.getPathName());
|
||||
|
||||
// Component placements should not change but sizes will
|
||||
//@formatter:off
|
||||
assertEquals("/P3/MyStruct3\n" +
|
||||
"pack(disabled)\n" +
|
||||
"Structure MyStruct3 {\n" +
|
||||
" 0 MyStruct1[3] 3 \"\"\n" +
|
||||
" 3 MyStruct1[2] 2 \"\"\n" +
|
||||
" 7 MyStruct1[2][3] 6 \"\"\n" +
|
||||
"}\n" +
|
||||
"Size = 19 Actual Alignment = 1\n", sDb3.toString());
|
||||
//@formatter:on
|
||||
|
||||
assertEquals(7, getDataTypeCount()); // include "undefined" type used during resolve
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceHandlingArraysPacked() throws Exception {
|
||||
|
||||
Structure s1 = new StructureDataType(new CategoryPath("/P1"), "MyStruct1", 0, dataMgr);
|
||||
s1.setPackingEnabled(true);
|
||||
s1.add(ByteDataType.dataType);
|
||||
Structure sDb1 = (Structure) dataMgr.resolve(s1, null);
|
||||
|
||||
Structure s2 = new StructureDataType(new CategoryPath("/P2"), "MyStruct2", 0, dataMgr);
|
||||
s2.setPackingEnabled(true);
|
||||
s2.add(WordDataType.dataType);
|
||||
Structure sDb2 = (Structure) dataMgr.resolve(s2, null);
|
||||
|
||||
Structure s3 = new StructureDataType(new CategoryPath("/P3"), "MyStruct3", 0, dataMgr);
|
||||
s3.setPackingEnabled(true);
|
||||
s3.add(new ArrayDataType(s1, 3, -1)); // MyStruct1[3]
|
||||
s3.add(new ArrayDataType(s2, 2, -1)); // MyStruct2[2]
|
||||
s3.add(new ArrayDataType(new ArrayDataType(s2, 3, -1), 2, -1)); // MyStruct2[2][3]
|
||||
|
||||
Structure sDb3 = (Structure) dataMgr.resolve(s3, null);
|
||||
Array aDb1_3 = (Array) sDb3.getComponent(0).getDataType(); // 0: MyStruct1[3]
|
||||
Array aDb2_2 = (Array) sDb3.getComponent(1).getDataType(); // 1: MyStruct2[2]
|
||||
Array aDb2_3_2 = (Array) sDb3.getComponent(2).getDataType(); // 2: MyStruct2[2][3]
|
||||
Array aDb2_3 = (Array) aDb2_3_2.getDataType(); // MyStruct2[3]
|
||||
|
||||
//@formatter:off
|
||||
assertEquals("/P3/MyStruct3\n" +
|
||||
"pack()\n" +
|
||||
"Structure MyStruct3 {\n" +
|
||||
" 0 MyStruct1[3] 3 \"\"\n" +
|
||||
" 4 MyStruct2[2] 4 \"\"\n" +
|
||||
" 8 MyStruct2[2][3] 12 \"\"\n" +
|
||||
"}\n" +
|
||||
"Size = 20 Actual Alignment = 2\n", sDb3.toString());
|
||||
//@formatter:on
|
||||
|
||||
assertTrue(sDb3.isEquivalent(s3));
|
||||
|
||||
assertEquals(9, getDataTypeCount()); // include "undefined" type used during resolve
|
||||
|
||||
dataMgr.replaceDataType(sDb2, sDb1, false);
|
||||
|
||||
assertFalse("Expected no change", aDb1_3.isDeleted());
|
||||
assertFalse("Expected MyStruct2[2] to be moved/transformed to MyStruct1[2]",
|
||||
aDb2_2.isDeleted());
|
||||
assertFalse("Expected MyStruct2[3][2] to be moved/transformed to MyStruct1[3][2]",
|
||||
aDb2_3_2.isDeleted());
|
||||
assertTrue("Expected MyStruct2[3] to be replaced by MyStruct1[3]", aDb2_3.isDeleted());
|
||||
|
||||
DataTypeComponent[] definedComponents = sDb3.getDefinedComponents();
|
||||
assertEquals(3, definedComponents.length);
|
||||
|
||||
// Array instance should have changed category as well as using MyStruct1
|
||||
Array a1 = (Array) definedComponents[1].getDataType(); // MyStruct1[2]
|
||||
assertTrue(aDb2_2 == a1);
|
||||
assertEquals("/P1/MyStruct1[2]", a1.getPathName());
|
||||
|
||||
// Array instance should have changed category as well as using MyStruct1[3]
|
||||
Array a1a = (Array) definedComponents[2].getDataType(); // MyStruct1[3][2]
|
||||
assertTrue(aDb2_3_2 == a1a);
|
||||
assertEquals("/P1/MyStruct1[2][3]", a1a.getPathName());
|
||||
|
||||
// Existing MyStruct1[3] array instance should be used and MyStruct2[3] removed
|
||||
Array a1b = (Array) a1a.getDataType(); // MyStruct1[3]
|
||||
assertTrue(aDb1_3 == a1b);
|
||||
assertEquals("/P1/MyStruct1[3]", a1b.getPathName());
|
||||
|
||||
// Structure should get repacked
|
||||
//@formatter:off
|
||||
assertEquals("/P3/MyStruct3\n" +
|
||||
"pack()\n" +
|
||||
"Structure MyStruct3 {\n" +
|
||||
" 0 MyStruct1[3] 3 \"\"\n" +
|
||||
" 3 MyStruct1[2] 2 \"\"\n" +
|
||||
" 5 MyStruct1[2][3] 6 \"\"\n" +
|
||||
"}\n" +
|
||||
"Size = 11 Actual Alignment = 1\n", sDb3.toString());
|
||||
//@formatter:on
|
||||
|
||||
assertEquals(7, getDataTypeCount()); // include "undefined" type used during resolve
|
||||
|
||||
}
|
||||
|
||||
private int getDataTypeCount() {
|
||||
// NOTE: the DataTypeManager.getAllDataTypes() method will not properly detect duplicate
|
||||
// datatypes if they occur due to the category-based collection which use named-based
|
||||
// maps.
|
||||
int cnt = 0;
|
||||
Iterator<DataType> allDataTypes = dataMgr.getAllDataTypes();
|
||||
while (allDataTypes.hasNext()) {
|
||||
allDataTypes.next();
|
||||
++cnt;
|
||||
}
|
||||
|
||||
// Compare count with actual record count to ensure both proper maps updates and
|
||||
// potential datatype duplication not reflected in count above.
|
||||
assertEquals("Incomplete datatype manager update", cnt, dataMgr.getDataTypeRecordCount());
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/* ###
|
||||
* 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.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
|
||||
public class DataTypeNameComparatorTest extends AbstractGTest {
|
||||
|
||||
@Test
|
||||
public void testDataTypeNameSort() {
|
||||
|
||||
String[] names = new String[] {
|
||||
//@formatter:off
|
||||
"int",
|
||||
"INT",
|
||||
"int *",
|
||||
"INT *",
|
||||
"int [2]",
|
||||
"int * *",
|
||||
"INT_PTR",
|
||||
"s1 *",
|
||||
"S1 *",
|
||||
"S1.conflict",
|
||||
"s1",
|
||||
"S1",
|
||||
"S1.conflict1",
|
||||
"S1.conflict10",
|
||||
"S1.conflict2",
|
||||
"s1.conflict *",
|
||||
"s1.conflict2 *",
|
||||
"s1.conflict10"
|
||||
//@formatter:on
|
||||
};
|
||||
|
||||
String[] sortedNames = new String[] {
|
||||
//@formatter:off
|
||||
"INT",
|
||||
"INT *",
|
||||
"int",
|
||||
"int *",
|
||||
"int * *",
|
||||
"int [2]",
|
||||
"INT_PTR",
|
||||
"S1",
|
||||
"S1 *",
|
||||
"S1.conflict",
|
||||
"S1.conflict1",
|
||||
"S1.conflict2",
|
||||
"S1.conflict10",
|
||||
"s1",
|
||||
"s1 *",
|
||||
"s1.conflict *",
|
||||
"s1.conflict2 *",
|
||||
"s1.conflict10"
|
||||
//@formatter:on
|
||||
};
|
||||
|
||||
Arrays.sort(names, DataTypeNameComparator.INSTANCE);
|
||||
|
||||
assertArraysEqualOrdered("Incorrect datatype name sort order", sortedNames, names);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user