GP-1994 refined datatype search and add ability to specify a program's

preferred root-namespace category node
This commit is contained in:
ghidra1 2022-05-06 15:11:39 -04:00
parent 4b600847eb
commit d7fc209657
12 changed files with 734 additions and 179 deletions

View File

@ -46,7 +46,8 @@ import ghidra.program.util.ChangeManager;
import ghidra.program.util.ProgramChangeRecord;
import ghidra.trace.database.DBTrace;
import ghidra.trace.database.listing.*;
import ghidra.trace.database.memory.*;
import ghidra.trace.database.memory.DBTraceMemoryRegisterSpace;
import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.database.symbol.DBTraceFunctionSymbolView;
import ghidra.trace.model.Trace.*;
import ghidra.trace.model.TraceAddressSnapRange;
@ -1071,6 +1072,18 @@ public class DBTraceProgramView implements TraceProgramView {
throw new UnsupportedOperationException();
}
@Override
public CategoryPath getPreferredRootNamespaceCategoryPath() {
// TODO: not yet implemented
return null;
}
@Override
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
// TODO: not yet implemented
throw new UnsupportedOperationException();
}
@Override
public String getExecutablePath() {
return trace.getExecutablePath();

View File

@ -26,6 +26,7 @@ import ghidra.framework.store.LockException;
import ghidra.program.database.IntRangeMap;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.Varnode;
@ -141,6 +142,16 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
view.setCompiler(compiler);
}
@Override
public CategoryPath getPreferredRootNamespaceCategoryPath() {
return view.getPreferredRootNamespaceCategoryPath();
}
@Override
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
view.setPreferredRootNamespaceCategoryPath(categoryPath);
}
@Override
public String getExecutablePath() {
return view.getExecutablePath();

View File

@ -342,6 +342,9 @@ public class DemangledDataType extends DemangledType {
static DataType findDataType(DataTypeManager dataTypeManager, Demangled namespace,
String dtName) {
// TODO: add support for use of Program.getPreferredRootNamespaceCategoryPath when
// searching for datatypes
List<DataType> list = new ArrayList<>();
dataTypeManager.findDataTypes(dtName, list);
if (list.isEmpty()) {

View File

@ -0,0 +1,201 @@
/* ###
* 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 org.junit.*;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.GhidraClass;
import ghidra.program.model.symbol.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
public class DataTypeUtilitiesFindTest extends AbstractGhidraHeadedIntegrationTest {
private ProgramDB program;
private DataTypeManagerDB dataMgr;
private SymbolTable symTab;
private GhidraClass a;
private Namespace ab;
private GhidraClass aba;
private Namespace abab;
private GhidraClass ababa;
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
dataMgr = program.getDataTypeManager();
symTab = program.getSymbolTable();
program.startTransaction("Test");
a = symTab.createClass(program.getGlobalNamespace(), "A", SourceType.USER_DEFINED);
ab = symTab.createNameSpace(a, "B", SourceType.USER_DEFINED); // A::B
aba = symTab.createClass(ab, "A", SourceType.USER_DEFINED); // A::B::A
abab = symTab.createNameSpace(aba, "B", SourceType.USER_DEFINED); // A::B::A::B
ababa = symTab.createClass(abab, "A", SourceType.USER_DEFINED); // A::B::A::B::A
StructureDataType structA = new StructureDataType("A", 0);
dataMgr.resolve(structA, null);
CategoryPath cp = new CategoryPath("/x/A");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
cp = new CategoryPath("/x/A/B");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
cp = new CategoryPath("/x/A/B/A");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
cp = new CategoryPath("/x/A/B/A/B");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
cp = new CategoryPath("/x/A/B/A/B/A");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
cp = new CategoryPath("/y/A");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
cp = new CategoryPath("/y/A/B");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
cp = new CategoryPath("/y/A/B/A");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
cp = new CategoryPath("/y/A/B/A/B");
structA.setCategoryPath(cp);
// omit struct fro category
cp = new CategoryPath("/y/A/B/A/B/A");
structA.setCategoryPath(cp);
dataMgr.resolve(structA, null);
}
@After
public void tearDown() throws Exception {
program.release(this);
}
private void assertPath(DataType dt, String path) {
assertNotNull(dt);
assertEquals(path, dt.getPathName());
}
@Test
public void testFindDataType() {
DataType dt = DataTypeUtilities.findDataType(dataMgr, program.getGlobalNamespace(), "A",
Structure.class);
assertPath(dt, "/A");
dt = DataTypeUtilities.findDataType(dataMgr, null, "A", null);
assertPath(dt, "/A");
dt = DataTypeUtilities.findDataType(dataMgr, ab, "A", Structure.class);
assertPath(dt, "/x/A/B/A");
dt = DataTypeUtilities.findDataType(dataMgr, aba, "A", Structure.class);
assertPath(dt, "/x/A/B/A/A");
program.setPreferredRootNamespaceCategoryPath("/y");
dt = DataTypeUtilities.findDataType(dataMgr, program.getGlobalNamespace(), "A",
Structure.class);
assertPath(dt, "/A");
dt = DataTypeUtilities.findDataType(dataMgr, null, "A", null);
assertPath(dt, "/A");
dt = DataTypeUtilities.findDataType(dataMgr, ab, "A", Structure.class);
assertPath(dt, "/y/A/B/A");
dt = DataTypeUtilities.findDataType(dataMgr, aba, "A", Structure.class);
assertPath(dt, "/y/A/B/A/A");
}
@Test
public void findExistingClassStruct() {
// NOTE: search gives preference to class structure found in parent-namespace
DataType dt = DataTypeUtilities.findExistingClassStruct(dataMgr, a);
assertPath(dt, "/A");
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, aba); // A::B::A
assertPath(dt, "/x/A/B/A");
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, ababa); // A::B::A::B::A
assertPath(dt, "/x/A/B/A/B/A");
program.setPreferredRootNamespaceCategoryPath("/y");
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, a);
assertPath(dt, "/y/A/A"); // not found in parent /y
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, aba); // A::B::A
assertPath(dt, "/y/A/B/A");
dt = DataTypeUtilities.findExistingClassStruct(dataMgr, ababa); // A::B::A::B::A
assertPath(dt, "/y/A/B/A/B/A/A"); // not found in parent /y/A/B/A/B
}
@Test
public void findNamespaceQualifiedDataType() {
DataType dt =
DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A", Structure.class);
assertPath(dt, "/A");
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A", null);
assertPath(dt, "/A");
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A::A", Structure.class);
assertPath(dt, "/x/A/A");
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A::B::A", Structure.class);
assertPath(dt, "/x/A/B/A");
program.setPreferredRootNamespaceCategoryPath("/y");
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A", Structure.class);
assertPath(dt, "/A");
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A", null);
assertPath(dt, "/A");
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A::A", Structure.class);
assertPath(dt, "/y/A/A");
dt = DataTypeUtilities.findNamespaceQualifiedDataType(dataMgr, "A::B::A", Structure.class);
assertPath(dt, "/y/A/B/A");
}
}

View File

@ -60,8 +60,12 @@ public abstract class DomainObjectAdapter implements DomainObject {
private ArrayList<Object> consumers;
protected Map<String, String> metadata = new LinkedHashMap<String, String>();
// A flag indicating whether the domain object has changed. Any methods of this domain object
// which cause its state to change must set this flag to true
// FIXME: (see GP-2003) "changed" flag is improperly manipulated by various methods.
// In general, comitted transactions will trigger all valid cases of setting flag to true,
// there may be a few cases where setting it to false may be appropriate. Without a transation
// it's unclear why it should ever need to get set true.
// A flag indicating whether the domain object has changed.
protected boolean changed = false;
// a flag indicating that this object is temporary

View File

@ -214,9 +214,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
return changeSet;
}
/**
* @see db.util.ErrorHandler#dbError(java.io.IOException)
*/
@Override
public void dbError(IOException e) {
fatalErrorOccurred = true;
@ -238,9 +235,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
return names;
}
/**
* @see ghidra.framework.model.DomainObject#getOptions(java.lang.String)
*/
@Override
public Options getOptions(String propertyListName) {
return new SubOptions(options, propertyListName, propertyListName + Options.DELIMITER);
@ -259,25 +253,16 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
options.performAlterations(propertyAlterations);
}
/**
* @see ghidra.framework.model.DomainObject#canLock()
*/
@Override
public boolean canLock() {
return transactionMgr.getCurrentTransaction() == null && !closed;
}
/**
* @see ghidra.framework.model.DomainObject#isLocked()
*/
@Override
public boolean isLocked() {
return transactionMgr.isLocked();
}
/**
* @see ghidra.framework.model.DomainObject#lock(String)
*/
@Override
public boolean lock(String reason) {
return transactionMgr.lock(reason);
@ -308,17 +293,11 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
return transactionMgr.lockForSnapshot(this, hasProgress, title);
}
/**
* @see ghidra.framework.model.DomainObject#forceLock(boolean, String)
*/
@Override
public void forceLock(boolean rollback, String reason) {
transactionMgr.forceLock(rollback, reason);
}
/**
* @see ghidra.framework.model.DomainObject#unlock()
*/
@Override
public void unlock() {
transactionMgr.unlock();
@ -336,9 +315,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
return startTransaction(description, null);
}
/**
* @see ghidra.framework.model.UndoableDomainObject#startTransaction(java.lang.String)
*/
@Override
public int startTransaction(String description, AbortedTransactionListener listener) {
int id = -1;
@ -359,9 +335,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
return id;
}
/**
* @see ghidra.framework.model.UndoableDomainObject#endTransaction(int, boolean)
*/
@Override
public void endTransaction(int transactionID, boolean commit) {
transactionMgr.endTransaction(this, transactionID, commit, true);
@ -395,65 +368,41 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
return transactionMgr.getUndoStackDepth();
}
/**
* @see ghidra.framework.model.Undoable#canRedo()
*/
@Override
public boolean canRedo() {
return transactionMgr.canRedo();
}
/**
* @see ghidra.framework.model.Undoable#canUndo()
*/
@Override
public boolean canUndo() {
return transactionMgr.canUndo();
}
/**
* @see ghidra.framework.model.Undoable#getRedoName()
*/
@Override
public String getRedoName() {
return transactionMgr.getRedoName();
}
/**
* @see ghidra.framework.model.Undoable#getUndoName()
*/
@Override
public String getUndoName() {
return transactionMgr.getUndoName();
}
/**
* @see ghidra.framework.model.UndoableDomainObject#getCurrentTransaction()
*/
@Override
public Transaction getCurrentTransaction() {
return transactionMgr.getCurrentTransaction();
}
/**
* @see ghidra.framework.model.Undoable#redo()
*/
@Override
public void redo() throws IOException {
transactionMgr.redo();
}
/**
* @see ghidra.framework.model.Undoable#undo()
*/
@Override
public void undo() throws IOException {
transactionMgr.undo();
}
/**
* @see ghidra.framework.model.DomainObject#isChanged()
*/
@Override
public boolean isChanged() {
if (dbh == null) {
@ -484,9 +433,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
return true;
}
/**
* @see ghidra.framework.model.Undoable#clearUndo()
*/
@Override
public void clearUndo() {
clearUndo(true);
@ -500,9 +446,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
options.clearCache();
}
/**
* @see ghidra.framework.model.DomainObject#canSave()
*/
@Override
public synchronized boolean canSave() {
DomainFile df = getDomainFile();
@ -512,9 +455,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
return dbh.canUpdate();
}
/**
* @see ghidra.framework.model.DomainObject#save(java.lang.String, ghidra.util.task.TaskMonitor)
*/
@Override
public void save(String comment, TaskMonitor monitor) throws IOException, CancelledException {
if (!canSave()) {
@ -554,9 +494,6 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
}
}
/**
* @see ghidra.framework.model.DomainObject#saveToPackedFile(java.io.File, ghidra.util.task.TaskMonitor)
*/
@Override
public void saveToPackedFile(File outputFile, TaskMonitor monitor)
throws IOException, CancelledException {
@ -620,17 +557,11 @@ public abstract class DomainObjectAdapterDB extends DomainObjectAdapter
}
}
/**
* @see ghidra.framework.model.DomainObject#isClosed()
*/
@Override
public boolean isClosed() {
return closed;
}
/**
* @see ghidra.framework.model.UndoableDomainObject#hasTerminatedTransaction()
*/
@Override
public boolean hasTerminatedTransaction() {
return transactionMgr.hasTerminatedTransaction();

View File

@ -19,6 +19,8 @@ import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import db.DBConstants;
import db.DBHandle;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
@ -46,6 +48,7 @@ import ghidra.program.database.reloc.RelocationManager;
import ghidra.program.database.symbol.*;
import ghidra.program.database.util.AddressSetPropertyMapDB;
import ghidra.program.model.address.*;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock;
@ -208,6 +211,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
private Map<String, AddressSetPropertyMapDB> addrSetPropertyMap = new HashMap<>();
private Map<String, IntRangeMapDB> intRangePropertyMap = new HashMap<>();
// cached program information properties
private static final String PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY_PATHNAME =
PROGRAM_INFO + "." + PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY;
private CategoryPath preferredRootNamespaceCategory; // value of PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY
/**
* Constructs a new ProgramDB
* @param name the name of the program
@ -247,7 +255,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
listing = new ListingDB();
changeSet = new ProgramDBChangeSet(addrMap, NUM_UNDOS);
initManagers(CREATE, TaskMonitor.DUMMY);
propertiesCreate();
createProgramInformationOptions();
programUserData = new ProgramUserDataDB(this);
endTransaction(id, true);
clearUndo(false);
@ -369,7 +377,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
changed = true;
}
propertiesRestore();
registerProgramInformationOptions();
restoreProgramInformationOptions();
recordChanges = true;
endTransaction(id, true);
clearUndo(false);
@ -433,25 +442,59 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
recordChanges = true;
}
private void propertiesRestore() {
private void registerProgramInformationOptions() {
Options pl = getOptions(PROGRAM_INFO);
boolean origChangeState = changed;
pl.registerOption(EXECUTABLE_PATH, UNKNOWN, null, "Original import path of program image");
pl.registerOption(EXECUTABLE_FORMAT, UNKNOWN, null, "Original program image format");
pl.registerOption(CREATED_WITH_GHIDRA_VERSION, "3.0 or earlier", null,
"Version of Ghidra used to create this program.");
pl.registerOption(DATE_CREATED, JANUARY_1_1970, null, "Date this program was created");
changed = origChangeState;
pl.registerOption(PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY, "", null,
"Preferred data type category path of root namespace");
}
private void propertiesCreate() {
private void createProgramInformationOptions() {
registerProgramInformationOptions();
Options pl = getOptions(PROGRAM_INFO);
boolean origChangeState = changed;
pl.setString(EXECUTABLE_PATH, UNKNOWN);
pl.setString(EXECUTABLE_FORMAT, UNKNOWN);
pl.setString(CREATED_WITH_GHIDRA_VERSION, Application.getApplicationVersion());
pl.setDate(DATE_CREATED, new Date());
changed = origChangeState;
}
private void restoreProgramInformationOptions() {
Options pl = getOptions(PROGRAM_INFO);
updatePreferredRootNamespaceCategory(
pl.getString(PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY, null));
}
protected boolean propertyChanged(String propertyName, Object oldValue, Object newValue) {
if (propertyName.equals(PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY_PATHNAME)) {
String path = (String) newValue;
if (!updatePreferredRootNamespaceCategory(path)) {
return false;
}
}
super.propertyChanged(propertyName, oldValue, newValue);
return true;
}
private boolean updatePreferredRootNamespaceCategory(String path) {
if (path != null) {
path = path.trim();
}
preferredRootNamespaceCategory = null;
if (!StringUtils.isBlank(path)) {
try {
preferredRootNamespaceCategory = new CategoryPath(path);
}
catch (Exception e) {
// ignore invalid path
Msg.error(this, "Ignoring invalid preferred root namespace category path: " + path);
return false;
}
}
return true;
}
void setProgramUserData(ProgramUserDataDB programUserData) {
@ -556,7 +599,17 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setCompiler(String compiler) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(COMPILER, compiler);
changed = true;
}
@Override
public CategoryPath getPreferredRootNamespaceCategoryPath() {
return preferredRootNamespaceCategory;
}
@Override
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY, categoryPath);
}
@Override
@ -571,7 +624,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setExecutablePath(String path) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(EXECUTABLE_PATH, path);
changed = true;
}
@Override
@ -591,7 +643,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setExecutableFormat(String format) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(EXECUTABLE_FORMAT, format);
changed = true;
}
@Override
@ -611,7 +662,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setExecutableMD5(String md5) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(EXECUTABLE_MD5, md5);
changed = true;
}
@Override
@ -631,7 +681,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
public void setExecutableSHA256(String sha256) {
Options pl = getOptions(PROGRAM_INFO);
pl.setString(EXECUTABLE_SHA256, sha256);
changed = true;
}
@Override
@ -1738,6 +1787,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
for (int i = 0; i < NUM_MANAGERS; i++) {
managers[i].invalidateCache(all);
}
restoreProgramInformationOptions();
installExtensions(); // Reload any extensions
}
catch (IOException e) {

View File

@ -19,11 +19,11 @@ import java.util.*;
import java.util.regex.Pattern;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.SymbolPathParser;
import ghidra.docking.settings.Settings;
import ghidra.program.model.address.GlobalNamespace;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.Library;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
@ -354,11 +354,37 @@ public class DataTypeUtilities {
: new CategoryPath(baseCategory, categoryPathParts);
}
/**
* Find the structure data type which corresponds to the specified class namespace
* within the specified data type manager.
* The structure must utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the full class namespace.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
* @param dataTypeManager data type manager which should be searched.
* @param classNamespace class namespace
* @return existing structure which resides within matching category.
*/
public static Structure findExistingClassStruct(DataTypeManager dataTypeManager, GhidraClass classNamespace) {
Structure dt = findPreferredDataType(dataTypeManager, classNamespace,
classNamespace.getName(), Structure.class, true);
if (dt != null) {
return dt;
}
final String[] namespacePaths = getRelativeCategoryPaths(classNamespace);
return findDataType(dataTypeManager, classNamespace.getName(), Structure.class,
categoryPath -> getCategoryMatchType(categoryPath, namespacePaths, true));
}
/**
* Attempt to find the data type whose dtName and specified namespace match a stored data type
* within the specified dataTypeManager. The best match will be returned. The namespace will be
* used in checking data type parent categories, however if no type corresponds to the namespace
* another type whose name matches may be returned.
* within the specified dataTypeManager. The first match which satisfies the category path
* requirement will be returned. If a non-root namespace is specified the datatype's trailing
* category path must match the specified namespace path.
*
* @param dataTypeManager data type manager
* @param namespace namespace associated with dtName (null indicates no namespace constraint)
@ -366,18 +392,26 @@ public class DataTypeUtilities {
* @param classConstraint optional data type interface constraint (e.g., Structure), or null
* @return best matching data type
*/
public static DataType findDataType(DataTypeManager dataTypeManager, Namespace namespace,
String dtName, Class<? extends DataType> classConstraint) {
public static <T extends DataType> T findDataType(DataTypeManager dataTypeManager,
Namespace namespace, String dtName, Class<T> classConstraint) {
T dt =
findPreferredDataType(dataTypeManager, namespace, dtName, classConstraint, false);
if (dt != null) {
return dt;
}
final String[] namespacePaths = getRelativeCategoryPaths(namespace);
return findDataType(dataTypeManager, dtName, classConstraint,
categoryPath -> hasPreferredNamespaceCategory(categoryPath, namespace));
categoryPath -> getCategoryMatchType(categoryPath, namespacePaths, false));
}
/**
* Attempt to find the data type whose dtNameWithNamespace match a stored data type within the
* specified dataTypeManager. The best match will be returned. The namespace will be used in
* checking data type parent categories, however if no type corresponds to the namespace another
* type whose name matches may be returned. NOTE: name parsing assumes :: delimiter and can be
* thrown off if name include template information which could contain namespaces.
* specified dataTypeManager. The namespace will be used in checking data type parent categories.
* NOTE: name parsing assumes :: namespace delimiter which can be thrown off if name includes
* template information which could contain namespaces (see {@link SymbolPathParser#parse(String)}).
*
* @param dataTypeManager data type manager
* @param dtNameWithNamespace name of data type qualified with namespace (e.g.,
@ -385,14 +419,34 @@ public class DataTypeUtilities {
* @param classConstraint optional data type interface constraint (e.g., Structure), or null
* @return best matching data type
*/
public static DataType findNamespaceQualifiedDataType(DataTypeManager dataTypeManager,
String dtNameWithNamespace, Class<? extends DataType> classConstraint) {
public static <T extends DataType> T findNamespaceQualifiedDataType(
DataTypeManager dataTypeManager,
String dtNameWithNamespace, Class<T> classConstraint) {
String[] splitName = dtNameWithNamespace.split(Namespace.DELIMITER);
String dtName = splitName[splitName.length - 1];
List<String> pathList = SymbolPathParser.parse(dtNameWithNamespace);
int nameIndex = pathList.size() - 1;
String dtName = pathList.get(nameIndex);
CategoryPath rootPath = getPreferredRootNamespaceCategoryPath(dataTypeManager);
if (rootPath != null) {
List<String> namespacePath = pathList.subList(0, nameIndex);
T dt = getAssignableDataType(dataTypeManager, rootPath, namespacePath, dtName,
classConstraint);
if (dt != null) {
return dt;
}
}
// generate namespace path with / instead of :: separators
StringBuilder buf = new StringBuilder();
for (int i = 0; i < nameIndex; i++) {
buf.append(CategoryPath.DELIMITER_STRING);
buf.append(pathList.get(i));
}
final String namespacePath = buf.toString(); // root path will have empty string
return findDataType(dataTypeManager, dtName, classConstraint,
dataType -> hasPreferredNamespaceCategory(dataType, splitName));
categoryPath -> getCategoryMatchType(categoryPath, namespacePath));
}
/**
@ -410,40 +464,92 @@ public class DataTypeUtilities {
return cPrimitiveNameMap.get(dataTypeName);
}
private static boolean hasPreferredNamespaceCategory(DataType dataType,
String[] splitDataTypeName) {
// last element of split array is data type name and is ignored here
if (splitDataTypeName.length == 1) {
return true;
private static final int NAMESPACE_PATH_INDEX = 0;
private static final int PARENT_NAMESPACE_PATH_INDEX = 1;
/**
* Get relative/partial category paths which corresponds to a specified namespace.
* Any {@link Library} namespace will be ignored and treated like the global namespace
* when generating a related category path. An empty string will be returned for the
* global namespace.
* @param namespace data type namespace
* @return partial two-element array with category path for namespace [NAMESPACE_PATH_INDEX]
* and parent-namespace [PARENT_NAMESPACE_PATH_INDEX].
* A null is returned if namespace is null or the root/global namespace.
*/
private static String[] getRelativeCategoryPaths(Namespace namespace) {
if (namespace == null || namespace.isGlobal() || namespace.isLibrary()) {
return null;
}
CategoryPath categoryPath = dataType.getCategoryPath();
int index = splitDataTypeName.length - 2;
while (index >= 0) {
if (categoryPath.equals(CategoryPath.ROOT) ||
!categoryPath.getName().equals(splitDataTypeName[index])) {
return false;
String[] paths = new String[2];
StringBuilder buf = new StringBuilder();
for (String n : namespace.getParentNamespace().getPathList(true)) {
buf.append(CategoryPath.DELIMITER_STRING);
buf.append(n);
}
categoryPath = categoryPath.getParent();
--index;
}
return true;
paths[PARENT_NAMESPACE_PATH_INDEX] = buf.toString();
buf.append(CategoryPath.DELIMITER_STRING);
buf.append(namespace.getName());
paths[NAMESPACE_PATH_INDEX] = buf.toString();
return paths;
}
private static boolean hasPreferredNamespaceCategory(DataType dataType, Namespace namespace) {
if (namespace == null) {
return true;
private enum CategoryMatchType {
NONE, SECONDARY, PREFERRED;
}
CategoryPath categoryPath = dataType.getCategoryPath();
Namespace ns = namespace;
while (!(ns instanceof GlobalNamespace) && !(ns instanceof Library)) {
if (categoryPath.equals(CategoryPath.ROOT) ||
!categoryPath.getName().equals(ns.getName())) {
return false;
/**
* Namespace category matcher. Only those datatypes contained within a catgeory
* whose trailing category path matches the specified namespacePath will be considered
* a possible match. If the namespacePath is empty array all category paths will
* be considered a match with preference given to the root category.
* @param categoryPath datatype category path
* @param namespacePath namespace path
* @return {@link CategoryMatchType#PREFERRED} if namespace match found, {@link CategoryMatchType#SECONDARY}
* if no namespace constraint specified else {@link CategoryMatchType#NONE} if namespace constraint not
* satisfied.
*/
private static CategoryMatchType getCategoryMatchType(CategoryPath categoryPath,
String namespacePath) {
if (namespacePath.length() == 0) {
// root or unspecified namespace - prefer root category
return categoryPath.isRoot() ? CategoryMatchType.PREFERRED : CategoryMatchType.SECONDARY;
}
categoryPath = categoryPath.getParent();
ns = ns.getParentNamespace();
String path = categoryPath.getPath();
return path.endsWith(namespacePath) ? CategoryMatchType.PREFERRED : CategoryMatchType.NONE;
}
return true;
/**
* Namespace category matcher.
* @param categoryPath datatype category path
* @param namespacePaths namespace paths constraint or null for no namespace. This value should
* be obtained from the {@link #getRelativeCategoryPaths(Namespace)} method.
* @param parentNamespacePreferred if true matching on parent namespace is
* enabled and preferred over match on actual namespace. This is used for
* class structure searching.
* @return {@link CategoryMatchType#PREFERRED} is returned if parentNamespacePreferred is true
* and category path matches on parent-namespace or parentNamespacePreferred is false
* and category path matches on namespace. {@link CategoryMatchType#SECONDARY} is returned
* if parentNamespacePreferred is true and category path matches on namespace. Otherwise
* {@link CategoryMatchType#NONE} is returned.
*/
private static CategoryMatchType getCategoryMatchType(CategoryPath categoryPath,
String[] namespacePaths, boolean parentNamespacePreferred) {
if (namespacePaths == null) {
// root or unspecified namespace - prefer root category
return categoryPath.isRoot() ? CategoryMatchType.PREFERRED : CategoryMatchType.SECONDARY;
}
String path = categoryPath.getPath();
if (parentNamespacePreferred &&
path.endsWith(namespacePaths[PARENT_NAMESPACE_PATH_INDEX])) {
return CategoryMatchType.PREFERRED;
}
if (path.endsWith(namespacePaths[NAMESPACE_PATH_INDEX])) {
return parentNamespacePreferred ? CategoryMatchType.SECONDARY
: CategoryMatchType.PREFERRED;
}
return CategoryMatchType.NONE;
}
/**
@ -451,38 +557,170 @@ public class DataTypeUtilities {
* preferred namespace.
*/
private static interface NamespaceMatcher {
boolean isNamespaceCategoryMatch(DataType dataType);
/**
* Score category path match.
* @param path category path
* @return path match type
*/
CategoryMatchType getMatchType(CategoryPath path);
}
private static DataType findDataType(DataTypeManager dataTypeManager, String dtName,
Class<? extends DataType> classConstraint, NamespaceMatcher preferredCategoryMatcher) {
private static CategoryPath getPreferredRootNamespaceCategoryPath(
DataTypeManager dataTypeManager) {
if (!(dataTypeManager instanceof ProgramBasedDataTypeManager)) {
return null;
}
ProgramBasedDataTypeManager pdtm = (ProgramBasedDataTypeManager) dataTypeManager;
Program p = pdtm.getProgram();
return p.getPreferredRootNamespaceCategoryPath();
}
/**
* Get the specified datatype by full path and return only if its type corresponds to class
* constraint if specified.
* @param <T> A standard interface which extends {@link DataType} (e.g., {@link Structure}).
* @param dataTypeManager datatype manager to query
* @param rootPath root category path
* @param namespacePath an optional namespace path to be checked under rootPath.
* If null or empty the rootPath will be checked for dtName.
* @param dtName datatype name
* @param classConstraint datatype class constraint (optional, may be null)
* @return datatype which corresponds to specified path or null if not found
*/
private static <T extends DataType> T getAssignableDataType(DataTypeManager dataTypeManager,
CategoryPath rootPath, List<String> namespacePath, String dtName,
Class<? extends DataType> classConstraint) {
Category category = dataTypeManager.getCategory(rootPath);
if (category == null) {
return null;
}
if (namespacePath == null || namespacePath.isEmpty()) {
return getAssignableDataType(category, dtName, classConstraint);
}
CategoryPath categoryPath = new CategoryPath(rootPath, namespacePath);
category = dataTypeManager.getCategory(categoryPath);
if (category == null) {
return null;
}
return getAssignableDataType(category, dtName, classConstraint);
}
/**
* Get the specified datatype by name and category and return only if its type
* corresponds to an class constraint if specified.
* @param <T> A standard interface which extends {@link DataType} (e.g., {@link Structure}).
* @param category datatype category to query
* @param dtName datatype name
* @param classConstraint datatype class constraint (optional, may be null)
* @return datatype which corresponds to specified path or null if not found
*/
@SuppressWarnings("unchecked")
private static <T extends DataType> T getAssignableDataType(Category category, String dtName,
Class<? extends DataType> classConstraint) {
DataType dt = category.getDataType(dtName);
if (dt != null &&
(classConstraint == null || classConstraint.isAssignableFrom(dt.getClass()))) {
return (T) dt;
}
return null;
}
/**
* Perform a preferred category namespace qualified datatype search using
* category path supplied by {@link Program#getPreferredRootNamespaceCategoryPath()}.
* Any {@link Library} namespace will be ignored and treated like the global namespace
* when generating a related category path. This method only applies to
* {@link ProgramBasedDataTypeManager} and will always return null for other
* datatype managers.
* @param dataTypeManager datatype manager
* @param namespace namespace constraint or null for no namespace.
* @param dtName datatype name
* @param classConstraint type of datatype by its interface class (e.g., {@link Structure}).
* @param parentNamespacePreferred if true matching on parent namespace is
* enabled and preferred over match on actual namespace. This is relavent for
* class structure searching.
* @return preferred datatype match if found
*/
private static <T extends DataType> T findPreferredDataType(DataTypeManager dataTypeManager,
Namespace namespace, String dtName, Class<T> classConstraint,
boolean parentNamespacePreferred) {
CategoryPath rootPath = getPreferredRootNamespaceCategoryPath(dataTypeManager);
if (rootPath == null) {
return null;
}
if (namespace == null || namespace.isGlobal() || namespace.isLibrary()) {
return getAssignableDataType(dataTypeManager, rootPath, null, dtName, classConstraint);
}
if (parentNamespacePreferred) {
T dt = getAssignableDataType(dataTypeManager, rootPath,
namespace.getParentNamespace().getPathList(true), dtName, classConstraint);
if (dt != null) {
return dt;
}
}
return getAssignableDataType(dataTypeManager, rootPath, namespace.getPathList(true), dtName,
classConstraint);
}
/**
* Compare datatype category path lengths for sorting shortest path first.
* Tie-breaker based on path name sort.
* Rationale is to provide some deterministic datatype selection behavior and
* to allow duplicates within a hierarchical orgainzation to prefer the short
* path to reduce bad namespace matches.
*/
private static final Comparator<DataType> DATATYPE_CATEGORY_PATH_LENGTH_COMPARATOR =
(DataType dt1, DataType dt2) -> {
String catPath1 = dt1.getCategoryPath().getPath();
String catPath2 = dt2.getCategoryPath().getPath();
int cmp = catPath1.length() - catPath2.length();
if (cmp == 0) {
cmp = catPath1.compareTo(catPath2);
}
return cmp;
};
/**
* Perform a namespace qualified datatype search.
* @param dataTypeManager datatype manager
* @param dtName datatype name
* @param classConstraint type of datatype by its interface class (e.g., {@link Structure}).
* @param categoryMatcher responsible for evaluating the category path
* for a possible match with a namespace constraint.
* @return The first {@link CategoryMatchType#PREFERRED} match will be
* returned if found. If none are {@link CategoryMatchType#PREFERRED}, the first
* {@link CategoryMatchType#SECONDARY} match will be returned. Otherwise null is returned.
*/
@SuppressWarnings("unchecked")
private static <T extends DataType> T findDataType(DataTypeManager dataTypeManager,
String dtName, Class<T> classConstraint, NamespaceMatcher categoryMatcher) {
ArrayList<DataType> list = new ArrayList<>();
dataTypeManager.findDataTypes(dtName, list);
Collections.sort(list, DATATYPE_CATEGORY_PATH_LENGTH_COMPARATOR);
if (!list.isEmpty()) {
//use the datatype that exists in the root category,
//otherwise just pick the first one...
DataType anyDt = null;
DataType preferredDataType = null;
T secondaryMatch = null;
for (DataType existingDT : list) {
if (classConstraint != null &&
!classConstraint.isAssignableFrom(existingDT.getClass())) {
continue;
}
if (preferredCategoryMatcher == null) {
if (existingDT.getCategoryPath().equals(CategoryPath.ROOT)) {
return existingDT;
CategoryMatchType matchType =
categoryMatcher.getMatchType(existingDT.getCategoryPath());
if (matchType == CategoryMatchType.PREFERRED) {
return (T) existingDT; // preferred match
}
else if (secondaryMatch == null && matchType == CategoryMatchType.SECONDARY) {
secondaryMatch = (T) existingDT;
}
}
if (preferredCategoryMatcher.isNamespaceCategoryMatch(existingDT)) {
preferredDataType = existingDT;
}
// If all else fails return any matching name for backward compatibility
anyDt = existingDT;
}
if (preferredDataType != null) {
return preferredDataType;
}
return anyDt;
return secondaryMatch;
}
return null;
}

View File

@ -19,6 +19,7 @@ import java.util.Date;
import ghidra.framework.store.LockException;
import ghidra.program.database.IntRangeMap;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
@ -60,6 +61,9 @@ public interface Program extends DataTypeManagerDomainObject {
public static final String DATE_CREATED = "Date Created";
/** Name of ghidra version property */
public static final String CREATED_WITH_GHIDRA_VERSION = "Created With Ghidra Version";
/** Name of ghidra preferred root namespace category property */
public static final String PREFERRED_ROOT_NAMESPACE_CATEGORY_PROPERTY =
"Preferred Root Namespace Category";
/** Creation date to ask for analysis */
public static final String ANALYSIS_START_DATE = "2007-Jan-01";
/** Format string of analysis date */
@ -160,6 +164,37 @@ public interface Program extends DataTypeManagerDomainObject {
*/
public void setCompiler(String compiler);
/**
* Gets the preferred root data type category path which corresponds
* to the global namespace of a namespace-based storage area. Preference
* will be given to this category when searching for data types
* within a specific namespace.
*
* This setting corresponds to the Program Information option
* <i>"Preferred Root Namespace Category</i>. See {@link DataTypeUtilities}
* and its various find methods for its usage details.
*
* @return data type category path for root namespace or null if not set or is invalid.
*/
public CategoryPath getPreferredRootNamespaceCategoryPath();
/**
* Sets the preferred data type category path which corresponds
* to the root of a namespace hierarchy storage area. Preference
* will be given to this category when searching for data types
* within a specific namespace.
*
* This setting corresponds to the Program Information option
* <i>"Preferred Root Namespace Category</i>. See {@link DataTypeUtilities}
* and its various find methods for its usage details.
*
* @param categoryPath data type category path for root namespace or null
* to clear option. The specified path must be absolute and start with "/"
* and must not end with one (e.g., <i>/ClassDataTypes</i>). An invalid
* path setting will be ignored.
*/
public void setPreferredRootNamespaceCategoryPath(String categoryPath);
/**
* Gets the path to the program's executable file.
* For example, <code>C:\Temp\test.exe</code>.

View File

@ -43,7 +43,7 @@ public class VariableUtilities {
/**
* Get a precedence value for the specified variable.
* This value can be used to assist with LocalVariable.compareTo(Variable var)
* @param var
* @param var function variable
* @return numeric precedence
*/
public static int getPrecedence(Variable var) {
@ -76,8 +76,8 @@ public class VariableUtilities {
* Compare storage varnodes for two lists of variables. No check is done to ensure that
* storage is considered good/valid (i.e., BAD_STORAGE, UNASSIGNED_STORAGE and VOID_STORAGE
* all have an empty varnode list and would be considered a match)
* @param vars
* @param otherVars
* @param vars function variables
* @param otherVars other function variables
* @return true if the exact sequence of variable storage varnodes matches across two lists of variables.
*/
public static boolean storageMatches(List<Variable> vars, List<Variable> otherVars) {
@ -96,8 +96,8 @@ public class VariableUtilities {
* Compare storage varnodes for two lists of variables. No check is done to ensure that
* storage is considered good/valid (i.e., BAD_STORAGE, UNASSIGNED_STORAGE and VOID_STORAGE
* all have an empty varnode list and would be considered a match)
* @param vars
* @param otherVars
* @param vars function variables
* @param otherVars other function variables
* @return true if the exact sequence of variable storage varnodes matches across two lists of variables.
*/
public static boolean storageMatches(List<? extends Variable> vars, Variable... otherVars) {
@ -116,8 +116,8 @@ public class VariableUtilities {
/**
* Compare two variables without using the instance specific compareTo method.
* @param v1
* @param v2
* @param v1 a function variable
* @param v2 another function variable
* @return a negative value if v1 &lt; v2, 0 if equal, and
* positive if v1 &gt; v2
*/
@ -162,8 +162,8 @@ public class VariableUtilities {
/**
* Determine the appropriate data type for an automatic parameter
* @param function
* @param returnDataType
* @param function function whose auto param datatype is to be determined
* @param returnDataType function's return datatype
* @param storage variable storage for an auto-parameter (isAutoStorage should be true)
* @return auto-parameter data type
*/
@ -174,7 +174,7 @@ public class VariableUtilities {
if (autoParameterType == AutoParameterType.THIS) {
DataType classStruct = findOrCreateClassStruct(function);
if (classStruct == null) {
classStruct = DataType.VOID;
classStruct = VoidDataType.dataType;
}
return getPointer(function.getProgram(), classStruct, storage.size());
}
@ -197,7 +197,7 @@ public class VariableUtilities {
* @param storage variable storage whose size must match the specified data type size
* @param dataType a datatype checked using {@link #checkDataType(DataType, boolean, int, Program)}
* @param allowSizeMismatch if true size mismatch will be ignore
* @throws InvalidInputException
* @throws InvalidInputException if specified storage is not suitable for datatype
*/
public static void checkStorage(VariableStorage storage, DataType dataType,
boolean allowSizeMismatch) throws InvalidInputException {
@ -212,7 +212,7 @@ public class VariableUtilities {
* @param dataType a datatype checked using {@link #checkDataType(DataType, boolean, int, Program)}
* @param allowSizeMismatch if true size mismatch will be ignore
* @return original storage or resized storage with the correct size.
* @throws InvalidInputException
* @throws InvalidInputException if specified storage is not suitable for datatype
*/
public static VariableStorage checkStorage(Function function, VariableStorage storage,
DataType dataType, boolean allowSizeMismatch) throws InvalidInputException {
@ -363,10 +363,10 @@ public class VariableUtilities {
* Perform resize variable storage to desired newSize. This method has limited ability to grow
* storage if current storage does not have a stack component or if other space constraints
* are exceeded.
* @param curStorage
* @param dataType
* @param curStorage current variable storage
* @param dataType variable datatype
* @param alignStack if false no attempt is made to align stack usage for big-endian
* @param function
* @param function function which corresponds to resized variable storage
* @return resize storage
* @throws InvalidInputException if unable to resize storage to specified size.
*/
@ -572,10 +572,10 @@ public class VariableUtilities {
/**
* Check for variable storage conflict and optionally remove conflicting variables.
* @param function
* @param function function which corresponds to specified variable
* @param var existing function variable or null for new variable
* @param newStorage new/updated variable storage
* @param deleteConflictingVariables
* @param deleteConflictingVariables if true function's conflicting variables may be deleted
* @throws VariableSizeException if deleteConflictingVariables is false and another variable conflicts
*/
public static void checkVariableConflict(Function function, Variable var,
@ -616,9 +616,9 @@ public class VariableUtilities {
/**
* Check for variable storage conflict and optionally remove conflicting variables.
* @param existingVariables variables to check (may contain null entries)
* @param var
* @param newStorage
* @throws VariableSizeException
* @param var function variable
* @param conflictHandler variable conflict handler
* @param newStorage variable storage
* @throws VariableSizeException if another variable conflicts
*/
public static void checkVariableConflict(List<? extends Variable> existingVariables,
@ -685,7 +685,7 @@ public class VariableUtilities {
/**
* Determine the minimum stack offset for parameters
* @param function
* @param function function whose stack use is to be examined
* @return stack parameter offset or null if it could not be determined
*/
public static Integer getBaseStackParamOffset(Function function) {
@ -712,7 +712,8 @@ public class VariableUtilities {
/**
* Generate a suitable 'this' parameter for the specified function
* @param function
* @param function function for which a <code>this</code> parameter is to be generated
* @param convention function calling convention
* @return this parameter or null of calling convention is not a 'thiscall'
* or some other error prevents it
* @deprecated should rely on auto-param instead - try not to use this method which may be eliminated
@ -769,10 +770,18 @@ public class VariableUtilities {
/**
* Find the structure data type which corresponds to the specified class namespace
* within the specified data type manager.
*
* The preferred structure will utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the class name.
* While a new empty structure may be returned, it will not be added to the program's data type
* manager.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
*
* If a match is not found an empty placeholder structure will be instantiated
* and returned. A newly instantiated structure will not be added to the data type manager
* and may refer to a non-existing category path which corresponds to the class-parent's
* namespace.
*
* @param classNamespace class namespace
* @param dataTypeManager data type manager which should be searched and whose
* data organization should be used.
@ -789,9 +798,19 @@ public class VariableUtilities {
/**
* Find the structure data type which corresponds to the specified function's class namespace
* within the function's program. One will be instantiated if not found.
* within the function's program.
*
* The preferred structure will utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the class name.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
*
* If a match is not found an empty placeholder structure will be instantiated
* and returned. A newly instantiated structure will not be added to the data type manager
* and may refer to a non-existing category path which corresponds to the class-parent's
* namespace.
*
* @param function function's whose class namespace is the basis for the structure
* @return new or existing structure whose name matches the function's class namespace or
* null if function not contained within a class namespace.
@ -807,9 +826,14 @@ public class VariableUtilities {
/**
* Find the structure data type which corresponds to the specified class namespace
* within the specified data type manager. .
* within the specified data type manager.
*
* The preferred structure will utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the class name.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
*
* @param classNamespace class namespace
* @param dataTypeManager data type manager which should be searched.
* @return existing structure whose name matches the specified class namespace
@ -817,15 +841,19 @@ public class VariableUtilities {
*/
public static Structure findExistingClassStruct(GhidraClass classNamespace,
DataTypeManager dataTypeManager) {
return (Structure) DataTypeUtilities.findDataType(dataTypeManager,
classNamespace.getParentNamespace(), classNamespace.getName(), Structure.class);
return DataTypeUtilities.findExistingClassStruct(dataTypeManager, classNamespace);
}
/**
* Find the structure data type which corresponds to the specified function's class namespace
* within the function's program.
*
* The preferred structure will utilize a namespace-based category path, however,
* the match criteria can be fuzzy and relies primarily on the class name.
* A properly named class structure must reside within a category whose trailing
* path either matches the class namespace or the class-parent's namespace.
* Preference is given to it residing within the class-parent's namespace.
*
* @param func the function.
* @return existing structure whose name matches the specified function's class namespace
* or null if not found.

View File

@ -15,6 +15,8 @@
*/
package ghidra.program.model.symbol;
import java.util.*;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.CircularDependencyException;
import ghidra.util.exception.DuplicateNameException;
@ -24,6 +26,7 @@ import ghidra.util.exception.InvalidInputException;
* The Namespace interface
*/
public interface Namespace {
static final long GLOBAL_NAMESPACE_ID = 0;
/**
* The delimiter that is used to separate namespace nodes in a namespace
@ -63,6 +66,24 @@ public interface Namespace {
*/
public String getName(boolean includeNamespacePath);
/**
* Get the namespace path as a list of namespace names.
* @param omitLibrary if true Library name (if applicable) will be
* omitted from returned list and treated same as global namespace.
* @return namespace path list or empty list for global namespace
*/
public default List<String> getPathList(boolean omitLibrary) {
if (isGlobal()) {
return Collections.emptyList();
}
ArrayDeque<String> list = new ArrayDeque<>();
for (Namespace n = this; !n.isGlobal() && !(omitLibrary && n.isLibrary()); n =
n.getParentNamespace()) {
list.addFirst(n.getName());
}
return List.copyOf(list);
}
/**
* Return the namespace id
* @return the namespace id
@ -96,11 +117,20 @@ public interface Namespace {
throws DuplicateNameException, InvalidInputException, CircularDependencyException;
/**
* Return true if this is the global namespace;
* @return true if this is the global namespace;
* Return true if this is the global namespace
* @return true if this is the global namespace
*/
public default boolean isGlobal() {
return getID() == GLOBAL_NAMESPACE_ID;
}
/**
* Return true if this is a library
* @return true if this is a library
*/
public default boolean isLibrary() {
Symbol s = getSymbol();
return s != null && s.getSymbolType() == SymbolType.LIBRARY;
}
}

View File

@ -26,6 +26,7 @@ import ghidra.program.database.IntRangeMap;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
@ -376,6 +377,16 @@ public class ProgramTestDouble implements Program {
throw new UnsupportedOperationException();
}
@Override
public CategoryPath getPreferredRootNamespaceCategoryPath() {
return null;
}
@Override
public void setPreferredRootNamespaceCategoryPath(String categoryPath) {
throw new UnsupportedOperationException();
}
@Override
public String getExecutablePath() {
throw new UnsupportedOperationException();