GT-3434 - Updated AddEditDialog to allow users to create namespaces that

match the label name
This commit is contained in:
dragonmacher 2020-01-15 14:02:28 -05:00
parent d66027e27a
commit 7ad8505dcf
9 changed files with 386 additions and 200 deletions

View file

@ -36,10 +36,7 @@ import ghidra.util.exception.*;
* <b>Assumptions for creating namespaces from a path string: </b>
* <ul>
* <li>All elements of a namespace path should be namespace symbols and not other
* symbol types.
* <li>If the method takes an address, then the path can contain a function name provided
* the address is in the body of the function; otherwise
* the names must all be namespaces other than functions.
* symbol types.
* <li>Absolute paths can optionally start with the global namespace.
* <li>You can provide a relative path that will start at the given
* parent namespace (or global if there is no parent provided).
@ -129,43 +126,66 @@ public class NamespaceUtils {
return null;
}
/**
* Returns a list of all namespaces with the given name in the parent namespace
*
* @param program the program to search
* @param parent the parent namespace from which to find all namespaces with the given name;
* if null, the global namespace will be used
* @param namespaceName the name of the namespaces to retrieve
* @return a list of all namespaces that match the given name in the given parent namespace.
*/
public static List<Namespace> getNamespacesByName(Program program, Namespace parent,
String namespaceName) {
validate(program, parent);
List<Namespace> namespaceList = new ArrayList<>();
List<Symbol> symbols = program.getSymbolTable().getSymbols(namespaceName, parent);
for (Symbol symbol : symbols) {
if (symbol.getSymbolType().isNamespace()) {
namespaceList.add((Namespace) symbol.getObject());
}
}
return namespaceList;
}
/**
* Returns a list of namespaces that match the given path. The path can be
* relative to the given root namespace or absolute if the path begins with
* the global namespace name.
*
* <P>Note: this path must only contain Namespace names and no other symbol types.
*
* @param namespacePath the path to the desired namespace.
* @param rootNamespace the namespace to use as the root for relative paths. If null, the
* global namespace will be used.
* @param program the program to search.
* @return a list of namespaces that match the given path.
*
* @param program the program to search
* @param parent the namespace to use as the root for relative paths. If null, the
* global namespace will be used
* @param pathString the path to the desired namespace
* @return a list of namespaces that match the given path
*/
public static List<Namespace> getNamespaces(String namespacePath, Namespace rootNamespace,
Program program) {
public static List<Namespace> getNamespaceByPath(Program program, Namespace parent,
String pathString) {
validate(program, rootNamespace);
validate(program, parent);
rootNamespace = adjustForNullRootNamespace(rootNamespace, namespacePath, program);
parent = adjustForNullRootNamespace(parent, pathString, program);
SymbolPath path = new SymbolPath(rootNamespace.getSymbol());
if (namespacePath != null) {
path = path.append(new SymbolPath(namespacePath));
SymbolPath path = new SymbolPath(parent.getSymbol());
if (pathString != null) {
path = path.append(new SymbolPath(pathString));
}
List<String> namespaceNames = path.asList();
List<Namespace> namespaces = doGetNamespaces(namespaceNames, rootNamespace, program);
List<Namespace> namespaces = doGetNamespaces(namespaceNames, parent, program);
return namespaces;
}
private static List<Namespace> doGetNamespaces(List<String> namespaceNames,
Namespace rootNamespace, Program program) {
if (rootNamespace == null) {
rootNamespace = program.getGlobalNamespace();
Namespace root, Program program) {
if (root == null) {
root = program.getGlobalNamespace();
}
List<Namespace> parents = Arrays.asList(rootNamespace);
List<Namespace> parents = Arrays.asList(root);
for (String name : namespaceNames) {
List<Namespace> matches = getMatchingNamespaces(name, parents, program);
parents = matches;
@ -174,19 +194,19 @@ public class NamespaceUtils {
}
/**
* Returns a list all namespaces that have the given name in any of the given namespaces.
* Returns a list all namespaces that have the given name in any of the given namespaces
*
* @param childName the name of the namespaces to retrieve.
* @param parents a list of all namespaces to search for child namespaces with the given name.
* @param program the program to search.
* @return a list all namespaces that have the given name in any of the given namespaces.
* @param childName the name of the namespaces to retrieve
* @param parents a list of all namespaces to search for child namespaces with the given name
* @param program the program to search
* @return a list all namespaces that have the given name in any of the given namespaces
*/
public static List<Namespace> getMatchingNamespaces(String childName, List<Namespace> parents,
Program program) {
validate(program, parents);
List<Namespace> list = new ArrayList<>();
for (Namespace parent : parents) {
list.addAll(getNamespaces(parent, childName, program));
list.addAll(getNamespacesByName(program, parent, childName));
}
return list;
@ -250,27 +270,6 @@ public class NamespaceUtils {
return searchForAllSymbolsInAnyOfTheseNamespaces(parents, symbolPath.getName(), program);
}
/**
* Returns a list of all namespaces with the given name in the parent namespace.
* @param parent the parent namespace from which to find all namespaces with the given name.
* @param namespaceName the name of the namespaces to retrieve.
* @param program the program to search.
* @return a list of all namespaces that match the given name in
* the given parent namespace.
*/
public static List<Namespace> getNamespaces(Namespace parent, String namespaceName,
Program program) {
validate(program, parent);
List<Namespace> namespaceList = new ArrayList<>();
List<Symbol> symbols = program.getSymbolTable().getSymbols(namespaceName, parent);
for (Symbol symbol : symbols) {
if (symbol.getSymbolType().isNamespace()) {
namespaceList.add((Namespace) symbol.getObject());
}
}
return namespaceList;
}
/**
* Returns the first namespace with the given name and that is NOT a function that
* is within the parent namespace. (ie. the first namespace that is not tied to a program
@ -331,17 +330,22 @@ public class NamespaceUtils {
* and uses the given address to resolve functions with duplicate names. When
* resolving down the namespace path, a function that matches a name will only
* be used if the given address is contained in the body of that function.
* <P>
* The root namespace can be a function.
*
* <p>The root namespace can be a function.
*
* <p>If an address is passed, then the path can contain a function name provided the
* address is in the body of the function; otherwise the names must all be namespaces other
* than functions.
*
* @param namespacePath The namespace name or path string to be parsed.
* This value should not include a trailing symbol name, only namespace names.
* @param namespacePath The namespace name or path string to be parsed
* This value should not include a trailing symbol name, only namespace names
* @param rootNamespace The parent namespace under which the desired
* namespace or path resides. If this value is null, then the
* global namespace will be used.
* @param program The current program in which the desired namespace
* resides.
* @param address the address used to resolve possible functions with duplicate names.
* resides
* @param address the address used to resolve possible functions with duplicate names; may
* be null
* @param source the source of the namespace
* @return The namespace that matches the given path. This can be either an existing
* namespace or a newly created one.
@ -358,8 +362,8 @@ public class NamespaceUtils {
if (namespacePath == null) {
return rootNamespace;
}
SymbolPath path = new SymbolPath(namespacePath);
SymbolPath path = new SymbolPath(namespacePath);
List<String> namespacesList = path.asList();
SymbolTable symbolTable = program.getSymbolTable();
@ -382,40 +386,76 @@ public class NamespaceUtils {
}
/**
* Finds the namespace for the given symbolPath. Since this method
* takes an address, the symbolPath can contain a function name provided the address
* lives in the body of that function.
* Returns the existing Function at the given address if its {@link SymbolPath} matches the
* given path
*
* @param program the program from which to get the namespace.
* @param symbolPath the path of namespace names including the name of the desired namespace.
* @param address the address used to determine if any function namespaces can be used
* to resolve the path. For a function to be used, it must contain this address in it's body.
* @return the namespace represented by the given path, or null if no such namespace exists.
* @param program the program
* @param symbolPath the path of namespace
* @param address the address
* @return the namespace represented by the given path, or null if no such namespace exists
*/
public static Namespace getNamespace(Program program, SymbolPath symbolPath, Address address) {
if (address == null) {
if (symbolPath == null) {
return program.getGlobalNamespace();
}
List<Symbol> symbols = getSymbols(symbolPath, program);
for (Symbol symbol : symbols) {
if (symbol.getSymbolType() != SymbolType.FUNCTION &&
symbol.getSymbolType().isNamespace()) {
return (Namespace) symbol.getObject();
}
}
}
else if (symbolPath == null) {
public static Namespace getFunctionNamespaceAt(Program program, SymbolPath symbolPath,
Address address) {
if (symbolPath == null || address == null) {
return null;
}
else {
Symbol[] symbols = program.getSymbolTable().getSymbols(address);
for (Symbol symbol : symbols) {
symbol.getSymbolType();
if (symbol.getSymbolType() == SymbolType.FUNCTION &&
symbolPath.equals(new SymbolPath(symbol))) {
return (Function) symbol.getObject();
}
Symbol[] symbols = program.getSymbolTable().getSymbols(address);
for (Symbol symbol : symbols) {
if (symbol.getSymbolType() == SymbolType.FUNCTION &&
symbolPath.matchesPathOf(symbol)) {
return (Function) symbol.getObject();
}
}
return null;
}
/**
* Returns the existing Function containing the given address if its
* {@link SymbolPath} matches the given path
*
* @param program the program
* @param symbolPath the path of namespace
* @param address the address
* @return the namespace represented by the given path, or null if no such namespace exists
*/
public static Namespace getFunctionNamespaceContaining(Program program, SymbolPath symbolPath,
Address address) {
if (symbolPath == null || address == null) {
return null;
}
FunctionManager fm = program.getFunctionManager();
Function f = fm.getFunctionContaining(address);
if (f != null) {
if (symbolPath.matchesPathOf(f.getSymbol())) {
return f;
}
}
return null;
}
/**
* Finds the namespace for the given symbol path <b>that is not a function</b>
*
* @param program the program from which to get the namespace
* @param symbolPath the path of namespace names including the name of the desired namespace
* @return the namespace represented by the given path, or null if no such namespace exists or
* the namespace is a function
*/
public static Namespace getNonFunctionNamespace(Program program, SymbolPath symbolPath) {
if (symbolPath == null) {
return program.getGlobalNamespace();
}
List<Symbol> symbols = getSymbols(symbolPath, program);
for (Symbol symbol : symbols) {
if (symbol.getSymbolType() != SymbolType.FUNCTION &&
symbol.getSymbolType().isNamespace()) {
return (Namespace) symbol.getObject();
}
}
return null;
@ -483,11 +523,12 @@ public class NamespaceUtils {
/**
* Convert a namespace to a class by copying all namespace children into a newly created class
* and then removing the old namespace.
* and then removing the old namespace
*
* @param namespace namespace to be converted
* @return new class namespace
* @throws InvalidInputException if namespace was contained within a function and can not be
* converted to a class
* converted to a class
*/
public static GhidraClass convertNamespaceToClass(Namespace namespace)
throws InvalidInputException {
@ -530,7 +571,7 @@ public class NamespaceUtils {
"Namespace contained within Function may not be converted to a class: " + name);
}
// move everything from old namespace into new class namespce
// move everything from old namespace into new class namespace
try {
for (Symbol s : symbolTable.getSymbols(namespace)) {
s.setNamespace(classNamespace);

View file

@ -20,7 +20,6 @@ import java.util.*;
import ghidra.program.model.address.GlobalNamespace;
import ghidra.program.model.listing.Library;
import ghidra.program.model.symbol.*;
import ghidra.util.SystemUtilities;
/**
* A convenience object for parsing a namespace path to a symbol.
@ -190,6 +189,16 @@ public class SymbolPath implements Comparable<SymbolPath> {
return new SymbolPath(list);
}
/**
* Returns true if this path contains any path entry matching the given text
*
* @param text the text for which to search
* @return true if any path entry matches the given text
*/
public boolean containsPathEntry(String text) {
return asList().contains(text);
}
@Override
public int hashCode() {
final int prime = 31;
@ -211,15 +220,25 @@ public class SymbolPath implements Comparable<SymbolPath> {
return false;
}
SymbolPath other = (SymbolPath) obj;
if (!SystemUtilities.isEqual(parentPath, other.parentPath)) {
if (!Objects.equals(parentPath, other.parentPath)) {
return false;
}
if (!SystemUtilities.isEqual(symbolName, other.symbolName)) {
if (!Objects.equals(symbolName, other.symbolName)) {
return false;
}
return true;
}
/**
* A convenience method to check if the given symbol's symbol path matches this path
*
* @param s the symbol to check
* @return true if the symbol paths match
*/
public boolean matchesPathOf(Symbol s) {
return equals(new SymbolPath(s));
}
/**
* Returns a list of names of the symbols in the symbol path, starting with the name just
* below the global namespace.