Merge remote-tracking branch

'origin/GP-5694-dragonmacher-dtm-get-data-type' (Closes #8157)
This commit is contained in:
Ryan Kurtz 2025-05-20 09:54:15 -04:00
commit 795d92cb1a
15 changed files with 251 additions and 66 deletions

View file

@ -25,6 +25,7 @@ import ghidra.app.plugin.core.datamgr.archive.DefaultDataTypeArchiveService;
import ghidra.app.services.DataTypeManagerService;
import ghidra.program.model.data.*;
import ghidra.util.HelpLocation;
import ghidra.util.task.TaskMonitor;
// FIXME!! TESTING
public class DefaultDataTypeManagerService extends DefaultDataTypeArchiveService
@ -64,6 +65,26 @@ public class DefaultDataTypeManagerService extends DefaultDataTypeArchiveService
throw new UnsupportedOperationException();
}
@Override
public List<DataType> getDataTypesByPath(DataTypePath path) {
throw new UnsupportedOperationException();
}
@Override
public DataType getProgramDataTypeByPath(DataTypePath path) {
throw new UnsupportedOperationException();
}
@Override
public DataType promptForDataType(String filterText) {
throw new UnsupportedOperationException();
}
@Override
public List<DataType> findDataTypes(String name, TaskMonitor monitor) {
throw new UnsupportedOperationException();
}
@Override
public DataType getDataType(TreePath selectedTreeNode) {
if (selectedTreeNode == null) {

View file

@ -84,7 +84,7 @@ import ghidra.util.task.TaskMonitor;
description = "Provides the window for managing and categorizing dataTypes. " +
"The datatype display shows all built-in datatypes, datatypes in the " +
"current program, and datatypes in all open archives.",
servicesProvided = { DataTypeManagerService.class, DataTypeArchiveService.class }
servicesProvided = { DataTypeManagerService.class, DataTypeQueryService.class, DataTypeArchiveService.class }
)
//@formatter:on
public class DataTypeManagerPlugin extends ProgramPlugin
@ -519,6 +519,59 @@ public class DataTypeManagerPlugin extends ProgramPlugin
@Override
public DataType getDataType(String filterText) {
return promptForDataType(filterText);
}
@Override
public List<DataType> findDataTypes(String dtName, TaskMonitor monitor) {
List<DataType> results = new ArrayList<>();
DataTypeManager[] managers = getDataTypeManagers();
// we put the program's data types at the front of the list so clients can tell if the
// types we have found already exist in the program
DataTypeManager pdtm = getProgramDataTypeManager();
pdtm.findDataTypes(dtName, results);
for (DataTypeManager manager : managers) {
if (!(manager instanceof ProgramDataTypeManager)) {
manager.findDataTypes(dtName, results);
}
}
return results;
}
@Override
public List<DataType> getDataTypesByPath(DataTypePath path) {
List<DataType> results = new ArrayList<>();
DataTypeManager[] managers = getDataTypeManagers();
for (DataTypeManager manager : managers) {
DataType dt = manager.getDataType(path);
if (dt == null) {
continue;
}
if (manager instanceof ProgramDataTypeManager) {
// we put the program's data type at the front of the list so clients can tell if
// the types we have found already exist in the program
results.add(0, dt);
}
else {
results.add(dt);
}
}
return results;
}
@Override
public DataType getProgramDataTypeByPath(DataTypePath path) {
DataTypeManager pdtm = getProgramDataTypeManager();
if (pdtm == null) {
return null;
}
return pdtm.getDataType(path);
}
@Override
public DataType promptForDataType(String filterText) {
DataTypeChooserDialog dialog = new DataTypeChooserDialog(this);
if (!StringUtils.isBlank(filterText)) {
dialog.showPrepopulatedDialog(tool, filterText);

View file

@ -4,9 +4,9 @@
* 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.
@ -31,8 +31,7 @@ import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
/**
* A service that manages a set of data type archives, allowing re-use of already open
* archives.
* A service that manages a set of data type archives, allowing re-use of already open archives.
*/
//@formatter:off
@ServiceInfo(
@ -95,12 +94,12 @@ public interface DataTypeArchiveService {
* Opens the specified project-located data type archive.
*
* @param domainFile archive file located in the current project
* @param monitor {@link TaskMonitor} to display progess during the opening
* @param monitor {@link TaskMonitor} to display progress during the opening
* @return the data type archive
* @throws IOException if an i/o error occurs opening the data type archive
* @throws DuplicateIdException if another archive with the same ID is already open
* @throws VersionException
* @throws CancelledException
* @throws VersionException if there is a version exception
* @throws CancelledException if the user cancels
*/
public DataTypeManager openArchive(DomainFile domainFile, TaskMonitor monitor)
throws VersionException, CancelledException, IOException, DuplicateIdException;
@ -128,5 +127,4 @@ public interface DataTypeArchiveService {
public Archive openArchive(File file, boolean acquireWriteLock)
throws IOException, DuplicateIdException;
}

View file

@ -4,9 +4,9 @@
* 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.
@ -17,11 +17,16 @@ package ghidra.app.services;
import java.util.List;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*;
import ghidra.util.task.TaskMonitor;
/**
* Simplified datatype service interface to provide query capabilities
* to a set of open datatype managers
* Simplified datatype service interface to provide query capabilities to a set of open datatype
* managers.
*
* @see DataTypeUtilities
* @see DataTypeManagerService
*/
public interface DataTypeQueryService {
@ -36,16 +41,59 @@ public interface DataTypeQueryService {
public List<DataType> getSortedDataTypeList();
/**
* Obtain the preferred datatype which corresponds to the specified
* datatype specified by filterText. A tool-based service provider
* may prompt the user to select a datatype if more than one possibility
* exists.
* This method simply calls {@link #promptForDataType(String)}
* @deprecated use {@link #promptForDataType(String)}
*/
@SuppressWarnings("javadoc")
@Deprecated(since = "11.5", forRemoval = true)
public DataType getDataType(String filterText);
/**
* Prompts the user for a data type. The optional filter text will be used to filter the tree
* of available types.
*
* @param filterText If not null, this text filters the visible data types to only show those
* that start with the given text
* @return the preferred data type (e.g., chosen by the user) or null if no match found
* or selection was cancelled by user.
*/
public DataType getDataType(String filterText);
public DataType promptForDataType(String filterText);
/**
* Finds all data types matching the given name. This method will search all open data type
* archives.
* <p>
* Unlike {@link DataTypeManagerService#findDataTypes(String, TaskMonitor)}, this method will
* not return {@code .conflict} data types. If you need those types, then you must call each
* data type manager directly.
* <p>
* In the list of types returned, the program data type manager's types will be in the list
* before types from other archives.
*
* @param name the data type name to find
* @param monitor the task monitor
* @return the data types
* @see DataTypeManagerService#getDataTypeManagers()
*/
public List<DataType> findDataTypes(String name, TaskMonitor monitor);
/**
* Get the data type for the given data type path.
* <p>
* This method will check each open data type manager for a data type that matches the path.
* <p>
* If a type is in the program data type manager, then it will be first in the returned list.
*
* @param path the path
* @return the data type
*/
public List<DataType> getDataTypesByPath(DataTypePath path);
/**
* Get the data type for the given data type path from the program's data type manager.
* @param path the path
* @return the data type; null if the type does not exist
*/
public DataType getProgramDataTypeByPath(DataTypePath path);
}

View file

@ -379,17 +379,13 @@ 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()) {
return null;
}
//use the datatype that exists in the root category,
//otherwise just pick the first one...
// use the datatype that exists in the root category, otherwise just pick the first one
DataType anyDt = null;
DataType preferredDataType = null;
for (DataType existingDT : list) {

View file

@ -4,9 +4,9 @@
* 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.
@ -28,6 +28,7 @@ import ghidra.program.model.listing.FunctionSignature;
import ghidra.util.data.DataTypeParser;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Class for parsing function signatures. This class attempts to be much more
@ -353,9 +354,14 @@ public class FunctionSignatureParser {
@Override
public DataType getDataType(String filterText) {
return promptForDataType(filterText);
}
@Override
public DataType promptForDataType(String filterText) {
DataType dt = dtCache.get(filterText);
if (dt == null) {
dt = service.getDataType(filterText);
dt = service.promptForDataType(filterText);
if (dt != null) {
dtCache.put(filterText, dt);
}
@ -363,5 +369,19 @@ public class FunctionSignatureParser {
return dt;
}
@Override
public List<DataType> getDataTypesByPath(DataTypePath path) {
return service.getDataTypesByPath(path);
}
@Override
public DataType getProgramDataTypeByPath(DataTypePath path) {
return service.getProgramDataTypeByPath(path);
}
@Override
public List<DataType> findDataTypes(String name, TaskMonitor monitor) {
return service.findDataTypes(name, monitor);
}
}
}

View file

@ -4,9 +4,9 @@
* 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.
@ -379,7 +379,6 @@ public class DataTypeParser {
// give up and ask the user
return proptUserForType(baseName);
}
private DataType proptUserForType(String baseName) throws CancelledException {
@ -388,7 +387,7 @@ public class DataTypeParser {
return null;
}
DataType dt = dataTypeManagerService.getDataType(baseName);
DataType dt = dataTypeManagerService.promptForDataType(baseName);
if (dt == null) {
throw new CancelledException();
}
@ -483,7 +482,8 @@ public class DataTypeParser {
continue;
}
char n = nextIndex + 1 < dataTypeString.length() ? dataTypeString.charAt(nextIndex + 1) : '\0';
char n = nextIndex + 1 < dataTypeString.length() ? dataTypeString.charAt(nextIndex + 1)
: '\0';
if (c == ':' && n == ':') {
nextIndex += 2;
continue;

View file

@ -4,9 +4,9 @@
* 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.
@ -181,8 +181,8 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
List<DataType> dts = changeListener.getFavoriteDts();
boolean found = false;
for (int i = 0; i < dts.size(); i++) {
if (dts.get(i).getName().equals("PascalUnicode")) {
for (DataType dt : dts) {
if (dt.getName().equals("PascalUnicode")) {
found = true;
break;
}
@ -198,8 +198,8 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
waitForSwing();
dts = changeListener.getFavoriteDts();
for (int i = 0; i < dts.size(); i++) {
if (dts.get(i).getName().equals("MBCString")) {
for (DataType dt : dts) {
if (dt.getName().equals("MBCString")) {
Assert.fail("Should not have found MBCString as a favorite!");
}
}
@ -338,7 +338,7 @@ public class FavoritesAndMiscTest extends AbstractGhidraHeadedIntegrationTest {
program.flushEvents();
waitForSwing();
runSwing(() -> plugin.getDataType("ArrayStruct"), false);
runSwing(() -> plugin.promptForDataType("ArrayStruct"), false);
DataTypeChooserDialog d = waitForDialogComponent(DataTypeChooserDialog.class);

View file

@ -4,9 +4,9 @@
* 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.
@ -66,6 +66,11 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration
@Override
public DataType getDataType(String filterText) {
return promptForDataType(filterText);
}
@Override
public DataType promptForDataType(String filterText) {
// method only called if no results or multiple results were found.
// Tool based implementation will prompt user, test will pick last one
ArrayList<DataType> list = new ArrayList<>();

View file

@ -26,6 +26,7 @@ import ghidra.program.database.ProgramDB;
import ghidra.program.model.data.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.InvalidNameException;
import ghidra.util.Msg;
import ghidra.util.task.TaskMonitor;
public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest {
@ -98,11 +99,17 @@ public class DataManagerTest extends AbstractGhidraHeadedIntegrationTest {
dataMgr.resolve(new EnumDataType(s3.getCategoryPath(), "Enum", 2), null);
dataMgr.resolve(new EnumDataType(s3.getCategoryPath(), "Enum", 2), null);
ArrayList<DataType> list = new ArrayList<DataType>();
dataMgr.findDataTypes("Enum", list);
dataMgr.resolve(new EnumDataType(s3.getCategoryPath(), "zEnum", 2), null);
dataMgr.resolve(new EnumDataType(s3.getCategoryPath(), "zEnum2", 2), null);
List<DataType> list = new ArrayList<DataType>();
dataMgr.findDataTypes("Enum", list);
assertEquals(3, list.size());
list.clear();
dataMgr.findDataTypes("Enum1", list);
Msg.debug(this, "dts: " + list);
Category c1 = root.createCategory("c1");
dataMgr.resolve(new EnumDataType(c1.getCategoryPath(), "Enum", 2), null);

View file

@ -54,6 +54,26 @@ public class TestDoubleDataTypeManagerService implements DataTypeManagerService
throw new UnsupportedOperationException();
}
@Override
public DataType promptForDataType(String filterText) {
throw new UnsupportedOperationException();
}
@Override
public List<DataType> getDataTypesByPath(DataTypePath path) {
throw new UnsupportedOperationException();
}
@Override
public DataType getProgramDataTypeByPath(DataTypePath path) {
throw new UnsupportedOperationException();
}
@Override
public List<DataType> findDataTypes(String name, TaskMonitor monitor) {
throw new UnsupportedOperationException();
}
@Override
public DataTypeManager getBuiltInDataTypesManager() {
throw new UnsupportedOperationException();

View file

@ -1,13 +1,12 @@
/* ###
* 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.
@ -16,11 +15,19 @@
*/
package ghidra.framework.plugintool;
import generic.json.Json;
public class ServiceInterfaceImplementationPair {
public Class<?> interfaceClass;
public Object provider;
public ServiceInterfaceImplementationPair(Class<?> interfaceClass, Object provider) {
this.interfaceClass = interfaceClass;
this.provider = provider;
}
@Override
public String toString() {
return Json.toString(this);
}
}

View file

@ -25,6 +25,8 @@ import java.util.regex.Pattern;
import javax.help.UnsupportedOperationException;
import org.apache.commons.lang3.StringUtils;
import db.*;
import db.util.ErrorHandler;
import generic.jar.ResourceFile;
@ -2098,32 +2100,37 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
@Override
public void findDataTypes(String name, List<DataType> list) {
if (name == null || name.length() == 0) {
if (StringUtils.isBlank(name)) {
return;
}
if (name.equals(DataType.DEFAULT.getName())) {
list.add(DataType.DEFAULT);
return;
}
// ignore .conflict in both name and result matches
lock.acquire();
try {
buildSortedDataTypeList();
// Use exemplar datatype in root category without .conflict to position at start
// of possible matches
name = DataTypeUtilities.getNameWithoutConflict(name);
String baseName = DataTypeUtilities.getNameWithoutConflict(name);
DataType compareDataType =
new TypedefDataType(CategoryPath.ROOT, name, DataType.DEFAULT, this);
new TypedefDataType(CategoryPath.ROOT, baseName, DataType.DEFAULT, this);
int index = Collections.binarySearch(sortedDataTypes, compareDataType, nameComparator);
if (index < 0) {
// this allows us to find foo.conflict types
index = -index - 1;
}
// add all matches to list
while (index < sortedDataTypes.size()) {
DataType dt = sortedDataTypes.get(index);
if (!name.equals(DataTypeUtilities.getNameWithoutConflict(dt, false))) {
break;
String baseDtName = DataTypeUtilities.getNameWithoutConflict(dt, false);
if (!baseName.equals(baseDtName)) {
break; // not foo or foo.conflict
}
list.add(dt);
++index;
}
@ -2143,9 +2150,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
list.add(DataType.DEFAULT);
return;
}
if (monitor == null) {
monitor = TaskMonitor.DUMMY;
}
monitor = TaskMonitor.dummyIfNull(monitor);
Pattern regexp = UserSearchUtils.createSearchPattern(name, caseSensitive);
lock.acquire();
try {

View file

@ -4,9 +4,9 @@
* 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.
@ -698,7 +698,7 @@ public class DataTypeUtilities {
* @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
* enabled and preferred over match on actual namespace. This is relevant for
* class structure searching.
* @return preferred datatype match if found
*/

View file

@ -169,11 +169,10 @@ public interface DataTypeManager {
public Iterator<FunctionDefinition> getAllFunctionDefinitions();
/**
* 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. Presence of {@code .conflict}
* extension will be ignored for both specified name and returned
* results.
* 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. The presence of
* {@code .conflict} extension will be ignored and thus included in the 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
@ -181,9 +180,13 @@ public interface DataTypeManager {
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 using familiar globbing
* characters '*' and '?'.
* Begin searching at the root category for all data types with names that match the given name
* that may contain wildcards using familiar globbing characters '*' and '?'.
* <p>
* Unlike {@link #findDataTypes(String, List)}, data types with a {@code .conflict} extension
* will not be included in the results of this method unless they explicitly match the provided
* name.
*
* @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
@ -421,7 +424,7 @@ public interface DataTypeManager {
* transaction is ended.
* <P>
* NOTE: Use of rollback ({@code commit=false} should be avoided unless absolutely
* neccessary since it will incur overhead to revert changes and may rollback multiple
* necessary since it will incur overhead to revert changes and may rollback multiple
* concurrent transactions if they exist.
* <P>
* NOTE: If this manager is part of a larger {@link DomainObject} its transactions may become
@ -630,7 +633,7 @@ public interface DataTypeManager {
public SourceArchive getLocalSourceArchive();
/**
* Change the given data type and its dependencies so thier source archive is set to
* Change the given data type and its dependencies so their source archive is set to
* given archive. Only those data types not already associated with a source archive
* will be changed.
*