mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-23 01:28:38 +08:00
GP-6790 - Function Signature Editor - Added support for changing the namespace
This commit is contained in:
@@ -327,6 +327,10 @@
|
||||
|
||||
<P>This text field can be used to change the name of the function.</P>
|
||||
|
||||
<H3>Namespace</H3>
|
||||
|
||||
<P>This combo box and browse button can be use to change the function namespace.</P>
|
||||
|
||||
<H3>Calling Convention</H3>
|
||||
|
||||
<P>This field is a combobox that allows you to choose a calling convention from the list of
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 28 KiB |
+14
-10
@@ -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.
|
||||
@@ -21,6 +21,7 @@ import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.PrototypeModel;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
@@ -40,19 +41,18 @@ class FunctionData extends FunctionDataView {
|
||||
|
||||
boolean checkStorage = false;
|
||||
if (canCustomizeStorage()) {
|
||||
// if (!originalFunctionData.canCustomizeStorage()) {
|
||||
// // switched to using custom storage
|
||||
// return true;
|
||||
// }
|
||||
checkStorage = true;
|
||||
}
|
||||
|
||||
if (!returnInfo.getFormalDataType()
|
||||
.equals(originalFunctionData.returnInfo.getFormalDataType())) {
|
||||
DataType returnType = returnInfo.getFormalDataType();
|
||||
DataType originalReturnType = originalFunctionData.returnInfo.getFormalDataType();
|
||||
if (!returnType.equals(originalReturnType)) {
|
||||
return true;
|
||||
}
|
||||
if (checkStorage &&
|
||||
!returnInfo.getStorage().equals(originalFunctionData.returnInfo.getStorage())) {
|
||||
|
||||
VariableStorage returnStorage = returnInfo.getStorage();
|
||||
VariableStorage originalReturnStorage = originalFunctionData.returnInfo.getStorage();
|
||||
if (checkStorage && !returnStorage.equals(originalReturnStorage)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -162,6 +162,10 @@ class FunctionData extends FunctionDataView {
|
||||
this.name = n;
|
||||
}
|
||||
|
||||
void setNamespace(Namespace ns) {
|
||||
this.namespace = ns;
|
||||
}
|
||||
|
||||
void setInline(boolean enable) {
|
||||
this.isInLine = enable;
|
||||
}
|
||||
|
||||
+12
-3
@@ -20,6 +20,7 @@ import java.util.*;
|
||||
import ghidra.program.model.data.VoidDataType;
|
||||
import ghidra.program.model.lang.PrototypeModel;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SymbolUtilities;
|
||||
|
||||
/**
|
||||
@@ -30,6 +31,7 @@ class FunctionDataView {
|
||||
|
||||
Function function;
|
||||
|
||||
Namespace namespace;
|
||||
String name;
|
||||
boolean hasVarArgs;
|
||||
ParamInfo returnInfo;
|
||||
@@ -47,7 +49,8 @@ class FunctionDataView {
|
||||
*/
|
||||
FunctionDataView(Function function) {
|
||||
this.function = function;
|
||||
this.name = function.getName();
|
||||
name = function.getName();
|
||||
namespace = function.getParentNamespace();
|
||||
allowCustomStorage = function.hasCustomVariableStorage();
|
||||
hasVarArgs = function.hasVarArgs();
|
||||
isInLine = function.isInline();
|
||||
@@ -63,6 +66,7 @@ class FunctionDataView {
|
||||
*/
|
||||
FunctionDataView(FunctionDataView otherFunctionData) {
|
||||
name = otherFunctionData.name;
|
||||
namespace = otherFunctionData.namespace;
|
||||
hasVarArgs = otherFunctionData.hasVarArgs;
|
||||
returnInfo = otherFunctionData.returnInfo.copy();
|
||||
for (ParamInfo p : otherFunctionData.parameters) {
|
||||
@@ -82,6 +86,7 @@ class FunctionDataView {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(name, otherFunctionData.name) ||
|
||||
!Objects.equals(namespace, otherFunctionData.namespace) ||
|
||||
!Objects.equals(callingConventionName, otherFunctionData.callingConventionName) ||
|
||||
hasVarArgs != otherFunctionData.hasVarArgs ||
|
||||
parameters.size() != otherFunctionData.parameters.size() ||
|
||||
@@ -174,7 +179,7 @@ class FunctionDataView {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public Program getProgram() {
|
||||
Program getProgram() {
|
||||
return function.getProgram();
|
||||
}
|
||||
|
||||
@@ -190,10 +195,14 @@ class FunctionDataView {
|
||||
return parameters.size();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
Namespace getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
String getNameString() {
|
||||
return name.length() == 0 ? SymbolUtilities.getDefaultFunctionName(function.getEntryPoint())
|
||||
: name;
|
||||
|
||||
+220
-16
@@ -17,8 +17,7 @@ package ghidra.app.plugin.core.function.editor;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.EventObject;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -33,25 +32,29 @@ import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.*;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.button.BrowseButton;
|
||||
import docking.widgets.button.GButton;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GLabel;
|
||||
import docking.widgets.table.*;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.app.util.ToolTipUtils;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.cparser.C.CParserUtils;
|
||||
import ghidra.app.util.cparser.C.ParseException;
|
||||
import ghidra.app.util.viewer.field.ListingColors.FunctionColors;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.VoidDataType;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.VariableStorage;
|
||||
import ghidra.program.model.symbol.ExternalLocation;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
import resources.Icons;
|
||||
@@ -68,6 +71,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
private GTable parameterTable;
|
||||
|
||||
private JTextField nameField;
|
||||
private GhidraComboBox<NamespaceWrapper> namespaceChoices;
|
||||
private JCheckBox varArgsCheckBox;
|
||||
private DataTypeManagerService service;
|
||||
private JCheckBox inLineCheckBox;
|
||||
@@ -167,7 +171,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
protected void okCallback() {
|
||||
if (model.isInParsingMode()) {
|
||||
try {
|
||||
model.parseSignatureFieldText();
|
||||
doParse();
|
||||
}
|
||||
catch (Exception e) {
|
||||
handleParseException(e);
|
||||
@@ -212,6 +216,32 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
super.close();
|
||||
}
|
||||
|
||||
FunctionSignatureTextField getSignatureField() {
|
||||
return signatureTextField;
|
||||
}
|
||||
|
||||
void triggerSignatureParsing() {
|
||||
try {
|
||||
doParse();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (!handleParseException(ex)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doParse() throws CancelledException, ParseException {
|
||||
model.parseSignatureFieldText();
|
||||
|
||||
Namespace ns = model.getNamespace();
|
||||
rebuildNamespaces(ns);
|
||||
}
|
||||
|
||||
Namespace getSelectedNamesapce() {
|
||||
return namespaceChoices.getSelectedItem().getNamespace();
|
||||
}
|
||||
|
||||
private JComponent buildMainPanel(boolean hasOptionalSignatureCommit) {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
@@ -224,7 +254,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
private JComponent buildCenterPanel(boolean hasOptionalSignatureCommit) {
|
||||
centerPanel = new JPanel(new BorderLayout());
|
||||
centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
|
||||
centerPanel.add(buildAttributePanel(), BorderLayout.NORTH);
|
||||
centerPanel.add(createAttributePanel(), BorderLayout.NORTH);
|
||||
centerPanel.add(buildTable(), BorderLayout.CENTER);
|
||||
centerPanel.add(buildBottomPanel(hasOptionalSignatureCommit), BorderLayout.SOUTH);
|
||||
centerPanel.getAccessibleContext().setAccessibleName("Function Attributes");
|
||||
@@ -342,7 +372,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
signatureTextField.setActionListener(e -> {
|
||||
try {
|
||||
if (model.isInParsingMode()) {
|
||||
model.parseSignatureFieldText();
|
||||
doParse();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -361,7 +391,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
|
||||
ActionListener tabListener = e -> {
|
||||
try {
|
||||
model.parseSignatureFieldText();
|
||||
doParse();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (!handleParseException(ex)) {
|
||||
@@ -411,16 +441,21 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
return result == OptionDialog.OPTION_TWO; // Option 2 is to abort
|
||||
}
|
||||
|
||||
private Component buildAttributePanel() {
|
||||
private Component createAttributePanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 15, 15));
|
||||
|
||||
JPanel leftPanel = new JPanel(new PairLayout(4, 8));
|
||||
leftPanel.add(new GLabel("Function Name:"));
|
||||
leftPanel.add(createNameField());
|
||||
leftPanel.add(new GLabel("Calling Convention"));
|
||||
|
||||
leftPanel.add(new GLabel("Namespace:"));
|
||||
leftPanel.add(createNamespacePanel());
|
||||
|
||||
leftPanel.add(new GLabel("Calling Convention:"));
|
||||
leftPanel.add(createCallingConventionCombo());
|
||||
leftPanel.setBorder(BorderFactory.createEmptyBorder(14, 0, 0, 10));
|
||||
|
||||
leftPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10));
|
||||
leftPanel.getAccessibleContext().setAccessibleName("Function");
|
||||
panel.add(leftPanel, BorderLayout.CENTER);
|
||||
panel.add(buildTogglePanel(), BorderLayout.EAST);
|
||||
@@ -428,6 +463,133 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component createNamespacePanel() {
|
||||
|
||||
namespaceChoices = new GhidraComboBox<>();
|
||||
namespaceChoices.setName("NamespaceComboBox");
|
||||
namespaceChoices.addItemListener(e -> {
|
||||
NamespaceWrapper wrapper = (NamespaceWrapper) e.getItem();
|
||||
Namespace ns = wrapper.getNamespace();
|
||||
model.setNamespace(ns);
|
||||
});
|
||||
|
||||
initNamespaces();
|
||||
selectNamespace();
|
||||
|
||||
JPanel nsPanel = new JPanel();
|
||||
nsPanel.setLayout(new BoxLayout(nsPanel, BoxLayout.LINE_AXIS));
|
||||
Component browsePanel = createBrowseButton();
|
||||
nsPanel.add(namespaceChoices);
|
||||
nsPanel.add(Box.createHorizontalStrut(5));
|
||||
nsPanel.add(browsePanel);
|
||||
return nsPanel;
|
||||
}
|
||||
|
||||
private void selectNamespace() {
|
||||
Function function = model.getFunction();
|
||||
Symbol symbol = function.getSymbol();
|
||||
Namespace ns = symbol.getParentNamespace();
|
||||
namespaceChoices.setSelectedItem(new NamespaceWrapper(ns));
|
||||
}
|
||||
|
||||
private Component createBrowseButton() {
|
||||
JButton browseButton = new BrowseButton();
|
||||
browseButton.setToolTipText("Choose Namespace");
|
||||
browseButton.addActionListener(e -> showNamespaceChooser());
|
||||
return browseButton;
|
||||
}
|
||||
|
||||
private void showNamespaceChooser() {
|
||||
Function function = model.getFunction();
|
||||
Program program = function.getProgram();
|
||||
NamespaceChooserDialog dialog = new NamespaceChooserDialog();
|
||||
Namespace namespace = dialog.getNamespace(program);
|
||||
if (namespace != null) {
|
||||
rebuildNamespaces(namespace);
|
||||
return;
|
||||
}
|
||||
|
||||
String nsText = dialog.getNamespaceText();
|
||||
if (StringUtils.isBlank(nsText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Namespace newNamespace = createNamespace(nsText);
|
||||
rebuildNamespaces(newNamespace);
|
||||
}
|
||||
|
||||
private void rebuildNamespaces(Namespace namespace) {
|
||||
if (namespace == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Function function = model.getFunction();
|
||||
Program program = function.getProgram();
|
||||
NamespaceCache.add(program, namespace);
|
||||
initNamespaces();
|
||||
namespaceChoices.setSelectedItem(new NamespaceWrapper(namespace));
|
||||
}
|
||||
|
||||
private Namespace createNamespace(String nsText) {
|
||||
|
||||
Program p = model.getProgram();
|
||||
return p.withTransaction("Create Namespace", () -> {
|
||||
Namespace globalNs = p.getGlobalNamespace();
|
||||
try {
|
||||
return NamespaceUtils.createNamespaceHierarchy(nsText, globalNs, p,
|
||||
SourceType.USER_DEFINED);
|
||||
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
setStatusText("Invalid Namespace name: " + nsText);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initNamespaces() {
|
||||
namespaceChoices.removeAllItems();
|
||||
|
||||
for (Namespace namespace : getSelectableNamespaces()) {
|
||||
namespaceChoices.addItem(new NamespaceWrapper(namespace));
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<Namespace> getSelectableNamespaces() {
|
||||
SequencedSet<Namespace> namespaces = new LinkedHashSet<>();
|
||||
addGlobalNamespace(namespaces);
|
||||
addCurrentNamespace(namespaces);
|
||||
addRecentNamespaces(namespaces);
|
||||
return namespaces;
|
||||
}
|
||||
|
||||
private void addRecentNamespaces(SequencedSet<Namespace> namespaces) {
|
||||
Program program = model.getProgram();
|
||||
List<Namespace> recentNamespaces = NamespaceCache.get(program);
|
||||
if (recentNamespaces == null) {
|
||||
return;
|
||||
}
|
||||
for (Namespace namespace : recentNamespaces) {
|
||||
if (!namespaces.contains(namespace)) {
|
||||
namespaces.add(namespace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addGlobalNamespace(SequencedSet<Namespace> namespaces) {
|
||||
Program program = model.getProgram();
|
||||
Namespace globalNamespace = program.getGlobalNamespace();
|
||||
if (!namespaces.contains(globalNamespace)) {
|
||||
namespaces.add(globalNamespace);
|
||||
}
|
||||
}
|
||||
|
||||
private void addCurrentNamespace(SequencedSet<Namespace> namespaces) {
|
||||
Function function = model.getFunction();
|
||||
Namespace ns = function.getParentNamespace();
|
||||
namespaces.add(ns);
|
||||
}
|
||||
|
||||
private Component buildTogglePanel() {
|
||||
JPanel panel = new JPanel(new PairLayout());
|
||||
varArgsCheckBox = new GCheckBox("Varargs");
|
||||
@@ -753,8 +915,9 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
}
|
||||
|
||||
if (!model.hasValidName()) {
|
||||
signatureTextField.setError(model.getFunctionNameStartPosition(),
|
||||
model.getNameString().length());
|
||||
int pos = model.getFunctionNameStartPosition();
|
||||
int len = model.getNameString().length();
|
||||
signatureTextField.setError(pos, len);
|
||||
}
|
||||
if (caretPosition < preview.length()) {
|
||||
signatureTextField.setCaretPosition(caretPosition);
|
||||
@@ -771,6 +934,47 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Inner Classes
|
||||
//=================================================================================================
|
||||
|
||||
private class NamespaceWrapper {
|
||||
private Namespace namespace;
|
||||
|
||||
NamespaceWrapper(Namespace namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
Namespace getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return namespace.getName(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == this) {
|
||||
return true;
|
||||
}
|
||||
if (object == null) {
|
||||
return false;
|
||||
}
|
||||
if (object.getClass() == getClass()) {
|
||||
NamespaceWrapper w = (NamespaceWrapper) object;
|
||||
return namespace.equals(w.namespace);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return namespace.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private class ParameterDataTypeCellRenderer extends GTableCellRenderer {
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
@@ -1133,7 +1337,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
||||
|
||||
if (!processEvent(e)) {
|
||||
try {
|
||||
model.parseSignatureFieldText();
|
||||
doParse();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
handleParseException(ex);
|
||||
|
||||
+56
-7
@@ -18,8 +18,11 @@ package ghidra.app.plugin.core.function.editor;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.app.util.cparser.C.ParseException;
|
||||
import ghidra.app.util.parser.FunctionSignatureParser;
|
||||
import ghidra.app.util.parser.FunctionSignatureParser.FsParseResult;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
@@ -61,7 +64,7 @@ public class FunctionEditorModel {
|
||||
this.dataTypeManagerService = service;
|
||||
this.function = function;
|
||||
this.program = function.getProgram();
|
||||
functionData = new FunctionData(function);
|
||||
this.functionData = new FunctionData(function);
|
||||
this.originalFunctionData = new FunctionDataView(functionData);
|
||||
validate();
|
||||
}
|
||||
@@ -118,6 +121,7 @@ public class FunctionEditorModel {
|
||||
"Signature transformed due to auto-params and/or forced-indirect storage change";
|
||||
isSignatureTransformed = false; // one-shot message
|
||||
}
|
||||
|
||||
isValid =
|
||||
hasValidName() && hasValidReturnType() && hasValidReturnStorage() && hasValidParams();
|
||||
hasSignificantParameterChanges = false;
|
||||
@@ -256,7 +260,7 @@ public class FunctionEditorModel {
|
||||
* {@link VariableUtilities#checkVariableConflict(List, Variable, VariableStorage, VariableConflictHandler)}
|
||||
* @param conflicts parameters whose storage conflicts
|
||||
* @return return false to indicate conflicts have not been resolved and additional checks
|
||||
* should be disconctinued.
|
||||
* should be discontinued.
|
||||
* @see VariableConflictHandler
|
||||
*/
|
||||
private boolean handleConflicts(List<Variable> conflicts) {
|
||||
@@ -405,6 +409,18 @@ public class FunctionEditorModel {
|
||||
notifyDataChanged();
|
||||
}
|
||||
|
||||
Namespace getNamespace() {
|
||||
return functionData.getNamespace();
|
||||
}
|
||||
|
||||
void setNamespace(Namespace ns) {
|
||||
if (getNamespace().equals(ns)) {
|
||||
return;
|
||||
}
|
||||
functionData.setNamespace(ns);
|
||||
notifyDataChanged();
|
||||
}
|
||||
|
||||
String getCallingConventionName() {
|
||||
return functionData.getCallingConventionName();
|
||||
}
|
||||
@@ -742,9 +758,12 @@ public class FunctionEditorModel {
|
||||
if (b == canUseCustomStorage()) {
|
||||
return;
|
||||
}
|
||||
|
||||
functionData.setUseCustomStorage(b);
|
||||
isSignatureTransformed = !functionData.getFunctionSignatureText()
|
||||
.equals(originalFunctionData.getFunctionSignatureText());
|
||||
|
||||
String signatureText = functionData.getFunctionSignatureText();
|
||||
String originalSignatureText = originalFunctionData.getFunctionSignatureText();
|
||||
isSignatureTransformed = !signatureText.equals(originalSignatureText);
|
||||
notifyDataChanged();
|
||||
}
|
||||
|
||||
@@ -779,6 +798,11 @@ public class FunctionEditorModel {
|
||||
function.setName(name, SourceType.USER_DEFINED);
|
||||
}
|
||||
|
||||
Namespace namespace = functionData.getNamespace();
|
||||
if (!namespace.equals(function.getParentNamespace())) {
|
||||
function.setParentNamespace(namespace);
|
||||
}
|
||||
|
||||
boolean isInline = functionData.isInline();
|
||||
if (function.isInline() != isInline) {
|
||||
function.setInline(isInline);
|
||||
@@ -905,7 +929,7 @@ public class FunctionEditorModel {
|
||||
return dt1.getLength() == dt2.getLength();
|
||||
}
|
||||
|
||||
public void setFunctionData(FunctionDefinitionDataType functionDefinition) {
|
||||
public void setFunctionData(Namespace ns, FunctionDefinitionDataType functionDefinition) {
|
||||
|
||||
setName(functionDefinition.getName());
|
||||
|
||||
@@ -941,6 +965,8 @@ public class FunctionEditorModel {
|
||||
|
||||
functionData.updateParameterAndReturnStorage();
|
||||
|
||||
functionData.setNamespace(ns);
|
||||
|
||||
notifyDataChanged();
|
||||
}
|
||||
|
||||
@@ -1010,7 +1036,9 @@ public class FunctionEditorModel {
|
||||
void parseSignatureFieldText() throws ParseException, CancelledException {
|
||||
FunctionSignatureParser parser =
|
||||
new FunctionSignatureParser(program.getDataTypeManager(), dataTypeManagerService);
|
||||
FunctionDefinitionDataType f = parser.parse(getFunctionSignature(), signatureFieldText);
|
||||
FsParseResult result =
|
||||
parser.parseWithNamespace(getFunctionSignature(), signatureFieldText);
|
||||
FunctionDefinitionDataType f = result.functionDefinition();
|
||||
|
||||
// Preserve calling convention and noreturn flag from current model
|
||||
f.setNoReturn(functionData.hasNoReturn());
|
||||
@@ -1021,10 +1049,31 @@ public class FunctionEditorModel {
|
||||
// ignore
|
||||
}
|
||||
|
||||
setFunctionData(f);
|
||||
Namespace ns = createNamespace(result.namespace());
|
||||
setFunctionData(ns, f);
|
||||
isInParsingMode = false;
|
||||
}
|
||||
|
||||
private Namespace createNamespace(SymbolPath path) throws ParseException {
|
||||
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String nsText = path.getPath();
|
||||
return program.withTransaction("Create Namespace", () -> {
|
||||
Namespace globalNs = program.getGlobalNamespace();
|
||||
try {
|
||||
return NamespaceUtils.createNamespaceHierarchy(nsText, globalNs, program,
|
||||
SourceType.USER_DEFINED);
|
||||
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new ParseException("Invalid Namespace name: " + nsText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int getFunctionNameStartPosition() {
|
||||
return getFormalReturnType().getName().length() + 1;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
|
||||
/**
|
||||
@@ -575,8 +576,7 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
|
||||
// the number of columns determines the default width of the add/edit label dialog
|
||||
labelNameChoices.setColumns(20);
|
||||
labelNameChoices.setName("label.name.choices");
|
||||
GhidraComboBox<NamespaceWrapper> comboBox = new GhidraComboBox<>();
|
||||
namespaceChoices = comboBox;
|
||||
namespaceChoices = new GhidraComboBox<>();
|
||||
|
||||
primaryCheckBox = new GCheckBox("Primary");
|
||||
primaryCheckBox.setMnemonic('P');
|
||||
@@ -636,12 +636,40 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
|
||||
|
||||
private void showNamespaceChooser() {
|
||||
NamespaceChooserDialog dialog = new NamespaceChooserDialog();
|
||||
Namespace namespace = dialog.getNameSpace(program);
|
||||
Namespace namespace = dialog.getNamespace(program);
|
||||
if (namespace != null) {
|
||||
NamespaceCache.add(program, namespace);
|
||||
initNamespaces();
|
||||
namespaceChoices.setSelectedItem(new NamespaceWrapper(namespace));
|
||||
}
|
||||
|
||||
String nsText = dialog.getNamespaceText();
|
||||
if (StringUtils.isBlank(nsText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Namespace newNamespace = createNamespace(nsText);
|
||||
if (newNamespace != null) {
|
||||
NamespaceCache.add(program, newNamespace);
|
||||
initNamespaces();
|
||||
namespaceChoices.setSelectedItem(new NamespaceWrapper(newNamespace));
|
||||
}
|
||||
}
|
||||
|
||||
private Namespace createNamespace(String nsText) {
|
||||
|
||||
return program.withTransaction("Create Namespace", () -> {
|
||||
Namespace globalNs = program.getGlobalNamespace();
|
||||
try {
|
||||
return NamespaceUtils.createNamespaceHierarchy(nsText, globalNs, program,
|
||||
SourceType.USER_DEFINED);
|
||||
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
setStatusText("Invalid Namespace name: " + nsText);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
|
||||
@@ -23,6 +23,9 @@ import ghidra.util.datastruct.LRUSet;
|
||||
|
||||
/**
|
||||
* Static class for remember the last few namespaces used for a program.
|
||||
*
|
||||
* <p>Note: This class is not currently multi-threaded. Accesses are expected to be on the Swing
|
||||
* thread.
|
||||
*/
|
||||
public class NamespaceCache {
|
||||
public static final int MAX_RECENTS = 10;
|
||||
|
||||
@@ -48,7 +48,11 @@ public class NamespaceChooserDialog extends DialogComponentProvider {
|
||||
addCancelButton();
|
||||
}
|
||||
|
||||
public Namespace getNameSpace(Program program) {
|
||||
public void setText(String text) {
|
||||
dropDownField.setText(text);
|
||||
}
|
||||
|
||||
public Namespace getNamespace(Program program) {
|
||||
List<Namespace> namespaces = gatherNamespaces(program);
|
||||
if (namespaces == null) {
|
||||
// user cancelled while gathering namespaces
|
||||
@@ -59,6 +63,10 @@ public class NamespaceChooserDialog extends DialogComponentProvider {
|
||||
return chosenNamespace;
|
||||
}
|
||||
|
||||
public String getNamespaceText() {
|
||||
return dropDownField.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
chosenNamespace = dropDownField.getSelectedValue();
|
||||
@@ -68,6 +76,7 @@ public class NamespaceChooserDialog extends DialogComponentProvider {
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
chosenNamespace = null;
|
||||
dropDownField.setText("");
|
||||
close();
|
||||
}
|
||||
|
||||
@@ -82,6 +91,7 @@ public class NamespaceChooserDialog extends DialogComponentProvider {
|
||||
panel.add(new JLabel("Namespace: "));
|
||||
|
||||
dropDownField = new DropDownSelectionTextField<>(namespaceModel);
|
||||
dropDownField.setShowMatchingListOnEmptyText(true);
|
||||
panel.add(dropDownField);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
return panel;
|
||||
|
||||
+29
-7
@@ -21,6 +21,7 @@ import java.util.regex.Pattern;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.app.services.DataTypeQueryService;
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.app.util.cparser.C.ParseException;
|
||||
import ghidra.program.database.data.DataTypeUtilities;
|
||||
import ghidra.program.model.data.*;
|
||||
@@ -56,7 +57,8 @@ public class FunctionSignatureParser {
|
||||
private DataTypeParser dataTypeParser;
|
||||
private Map<String, DataType> dtMap = new HashMap<>();
|
||||
|
||||
private Map<String, String> nameMap = new HashMap<>();
|
||||
/** Stores parameter names that required name fixup for parsing to work correctly */
|
||||
private Map<String, String> replacedNameMap = new HashMap<>();
|
||||
private DataTypeManager destDataTypeManager;
|
||||
private ParserDataTypeManagerService dtmService;
|
||||
|
||||
@@ -96,8 +98,16 @@ public class FunctionSignatureParser {
|
||||
*/
|
||||
public FunctionDefinitionDataType parse(FunctionSignature originalSignature,
|
||||
String signatureText) throws ParseException, CancelledException {
|
||||
|
||||
FsParseResult result = parseWithNamespace(originalSignature, signatureText);
|
||||
return result.functionDefinition();
|
||||
}
|
||||
|
||||
public FsParseResult parseWithNamespace(FunctionSignature originalSignature,
|
||||
String signatureText) throws ParseException, CancelledException {
|
||||
|
||||
dtMap.clear();
|
||||
nameMap.clear();
|
||||
replacedNameMap.clear();
|
||||
if (dtmService != null) {
|
||||
dtmService.clearCache(); // clear datatype selection cache
|
||||
}
|
||||
@@ -108,14 +118,18 @@ public class FunctionSignatureParser {
|
||||
}
|
||||
|
||||
String functionName = extractFunctionName(signatureText);
|
||||
SymbolPath path = new SymbolPath(functionName);
|
||||
SymbolPath nsPath = path.getParent();
|
||||
String name = path.getName();
|
||||
|
||||
FunctionDefinitionDataType function =
|
||||
new FunctionDefinitionDataType(functionName, destDataTypeManager);
|
||||
new FunctionDefinitionDataType(name, destDataTypeManager);
|
||||
|
||||
function.setReturnType(extractReturnType(signatureText));
|
||||
function.setArguments(extractArguments(signatureText));
|
||||
function.setVarArgs(hasVarArgs(signatureText));
|
||||
|
||||
return function;
|
||||
return new FsParseResult(nsPath, function);
|
||||
}
|
||||
|
||||
private void initDataTypeMap(FunctionSignature signature) {
|
||||
@@ -247,7 +261,7 @@ public class FunctionSignatureParser {
|
||||
if (canParseName(name)) {
|
||||
return text;
|
||||
}
|
||||
nameMap.put(replacementName, name);
|
||||
replacedNameMap.put(replacementName, name);
|
||||
return substitute(text, name, replacementName);
|
||||
}
|
||||
|
||||
@@ -299,8 +313,8 @@ public class FunctionSignatureParser {
|
||||
}
|
||||
|
||||
private String resolveName(String name) throws ParseException {
|
||||
if (nameMap.containsKey(name)) {
|
||||
return nameMap.get(name);
|
||||
if (replacedNameMap.containsKey(name)) {
|
||||
return replacedNameMap.get(name);
|
||||
}
|
||||
if (!canParseName(name)) {
|
||||
throw new ParseException("Can't parse name: " + name);
|
||||
@@ -320,6 +334,14 @@ public class FunctionSignatureParser {
|
||||
return !StringUtils.containsAny(text, "()<>,");
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple object to hold data for the results of parsing the function signature text
|
||||
* @param namespace the namespace; may be null
|
||||
* @param functionDefinition the function definition; will not be null
|
||||
*/
|
||||
public record FsParseResult(SymbolPath namespace,
|
||||
FunctionDefinitionDataType functionDefinition) {}
|
||||
|
||||
/**
|
||||
* Provides a simple caching datatype manager service wrapper.<br>
|
||||
* Implementation intended for use with {@link FunctionSignatureParser}
|
||||
|
||||
+188
-10
@@ -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.
|
||||
@@ -15,34 +15,36 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.function.editor;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.DropDownSelectionTextField;
|
||||
import docking.widgets.button.BrowseButton;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.app.cmd.function.DeleteFunctionCmd;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||
import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.test.*;
|
||||
|
||||
public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
public FunctionEditorDialogTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
private TestEnv env;
|
||||
private PluginTool tool;
|
||||
private AddressFactory addrFactory;
|
||||
@@ -64,10 +66,11 @@ public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTes
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
closeAllWindows();
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Tests that an invalid parameter type entry will generate the proper error message
|
||||
* shown in the status box, and NOT present the user with a stack trace.
|
||||
*/
|
||||
@@ -93,10 +96,180 @@ public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTes
|
||||
assertTrue(dialog.getStatusText().contains("Invalid data type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNamespace() throws Exception {
|
||||
|
||||
createFunctionAtEntry();
|
||||
|
||||
String newNamespaceName = "NewNamespace";
|
||||
Namespace newNs = createNamespace(newNamespaceName);
|
||||
|
||||
FunctionEditorDialog dialog = editFunction();
|
||||
|
||||
pickNamespaceFromComboBox(dialog, newNs);
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForBusyTool(tool);
|
||||
|
||||
Function f = getFunction("0x1006420");
|
||||
Namespace actualNamespace = f.getParentNamespace();
|
||||
assertEquals(newNs, actualNamespace);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNamespace_Browse_CreateNew() throws Exception {
|
||||
|
||||
createFunctionAtEntry();
|
||||
|
||||
String newNamespace = "NonExistingNamespace";
|
||||
|
||||
FunctionEditorDialog dialog = editFunction();
|
||||
|
||||
setNamespaceUsingNsChooserDilaog(dialog, newNamespace);
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForBusyTool(tool);
|
||||
|
||||
Function f = getFunction("0x1006420");
|
||||
Namespace actualNamespace = f.getParentNamespace();
|
||||
assertEquals(newNamespace, actualNamespace.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNamespace_Browse_CreateNew_NamespacePath() throws Exception {
|
||||
|
||||
createFunctionAtEntry();
|
||||
|
||||
String newNamespacePath = "Foo::Bar::NonExistingNamespace";
|
||||
|
||||
FunctionEditorDialog dialog = editFunction();
|
||||
|
||||
setNamespaceUsingNsChooserDilaog(dialog, newNamespacePath);
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForBusyTool(tool);
|
||||
|
||||
Function f = getFunction("0x1006420");
|
||||
Namespace actualNamespace = f.getParentNamespace();
|
||||
SymbolPath expectedPath = new SymbolPath(newNamespacePath);
|
||||
SymbolPath actualPath = new SymbolPath(actualNamespace.getPathList(true));
|
||||
assertEquals(expectedPath, actualPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetNamespace_ViaTextEditor() throws Exception {
|
||||
|
||||
createFunctionAtEntry();
|
||||
|
||||
String newNamespacePath = "Foo::Bar";
|
||||
|
||||
FunctionEditorDialog dialog = editFunction();
|
||||
|
||||
setNamespaceUsingTextEditor(dialog, newNamespacePath);
|
||||
assertNamespaceNotVisibleInEditorAfterParsing(dialog);
|
||||
assertNamespaceComboBoxIsShowingNamespace(dialog, newNamespacePath);
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForBusyTool(tool);
|
||||
|
||||
Function f = getFunction("0x1006420");
|
||||
Namespace actualNamespace = f.getParentNamespace();
|
||||
SymbolPath expectedPath = new SymbolPath(newNamespacePath);
|
||||
SymbolPath actualPath = new SymbolPath(actualNamespace.getPathList(true));
|
||||
assertEquals(expectedPath, actualPath);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void assertNamespaceComboBoxIsShowingNamespace(FunctionEditorDialog dialog,
|
||||
String expectedNsPath) {
|
||||
|
||||
Namespace actualNs = runSwing(() -> dialog.getSelectedNamesapce());
|
||||
assertEquals(expectedNsPath, actualNs.toString());
|
||||
}
|
||||
|
||||
private void assertNamespaceNotVisibleInEditorAfterParsing(FunctionEditorDialog dialog) {
|
||||
FunctionSignatureTextField field = dialog.getSignatureField();
|
||||
String signature = runSwing(() -> field.getText());
|
||||
assertFalse("Namespace should not be visible in the editor after parsing",
|
||||
signature.contains("::"));
|
||||
}
|
||||
|
||||
private void setNamespaceUsingTextEditor(FunctionEditorDialog dialog, String ns) {
|
||||
|
||||
FunctionSignatureTextField field = dialog.getSignatureField();
|
||||
String signature = runSwing(() -> field.getText());
|
||||
|
||||
// Insert namespace in front of name. Format:
|
||||
// undefined entry (void)
|
||||
int paren = signature.indexOf('(');
|
||||
int spaceBeforeParen = paren - 1;
|
||||
int space = signature.lastIndexOf(' ', spaceBeforeParen - 1);
|
||||
String beginning = signature.substring(0, space + 1);
|
||||
String end = signature.substring(space + 1);
|
||||
String updated = beginning + ns + "::" + end;
|
||||
|
||||
setText(field, updated);
|
||||
|
||||
runSwing(() -> dialog.triggerSignatureParsing());
|
||||
}
|
||||
|
||||
private void pickNamespaceFromComboBox(FunctionEditorDialog dialog, Namespace ns) {
|
||||
|
||||
GhidraComboBox<?> combo =
|
||||
(GhidraComboBox<?>) findComponentByName(dialog, "NamespaceComboBox");
|
||||
|
||||
int index = indexOf(combo, ns);
|
||||
if (index < 0) {
|
||||
fail("Could not find namespace in combo box: " + ns);
|
||||
}
|
||||
runSwing(() -> combo.setSelectedIndex(index));
|
||||
}
|
||||
|
||||
private int indexOf(GhidraComboBox<?> combo, Namespace ns) {
|
||||
return runSwing(() -> {
|
||||
|
||||
String nsName = ns.getName();
|
||||
ComboBoxModel<?> model = combo.getModel();
|
||||
int n = model.getSize();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Object element = model.getElementAt(i);
|
||||
String elementText = element.toString();
|
||||
if (elementText.equals(nsName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
|
||||
private void setNamespaceUsingNsChooserDilaog(FunctionEditorDialog dialog,
|
||||
String newNamespace) {
|
||||
|
||||
AbstractButton button = findButtonByName(dialog, BrowseButton.NAME);
|
||||
pressButton(button, false);
|
||||
|
||||
NamespaceChooserDialog nsDialog = waitForDialogComponent(NamespaceChooserDialog.class);
|
||||
runSwing(() -> nsDialog.setText(newNamespace));
|
||||
|
||||
pressButtonByText(nsDialog, "OK");
|
||||
waitForBusyTool(tool);
|
||||
}
|
||||
|
||||
private Namespace createNamespace(String newNamespace) {
|
||||
|
||||
Namespace newNs = tx(program, () -> {
|
||||
return NamespaceUtils.createNamespaceHierarchy(newNamespace, null, program,
|
||||
SourceType.USER_DEFINED);
|
||||
});
|
||||
|
||||
runSwing(() -> NamespaceCache.add(program, newNs));
|
||||
|
||||
return newNs;
|
||||
}
|
||||
|
||||
private void setEditorText(TableCellEditor cellEditor, String text) {
|
||||
DropDownSelectionTextField<?> textField = getDataTypeEditor(cellEditor);
|
||||
setText(textField, text);
|
||||
@@ -106,7 +279,7 @@ public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTes
|
||||
|
||||
private FunctionEditorDialog editFunction() {
|
||||
performAction(editFunction, cb.getProvider(), false);
|
||||
return waitForDialogComponent(null, FunctionEditorDialog.class, DEFAULT_WINDOW_TIMEOUT);
|
||||
return waitForDialogComponent(FunctionEditorDialog.class);
|
||||
}
|
||||
|
||||
private void finishEditing(final TableCellEditor cellEditor) {
|
||||
@@ -150,6 +323,11 @@ public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTes
|
||||
addrFactory = program.getAddressFactory();
|
||||
}
|
||||
|
||||
private Function getFunction(String addr) {
|
||||
FunctionManager fm = program.getFunctionManager();
|
||||
return fm.getFunctionAt(addr(addr));
|
||||
}
|
||||
|
||||
private void createFunctionAtEntry() {
|
||||
FunctionManager fm = program.getFunctionManager();
|
||||
Function f = fm.getFunctionAt(addr("0x1006420"));
|
||||
|
||||
+1
@@ -132,6 +132,7 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration
|
||||
public void testExtractFunctionName() throws Exception {
|
||||
assertEquals("bob", parser.extractFunctionName("void bob(int a)"));
|
||||
assertEquals("bob", parser.extractFunctionName("void bob (int a)"));
|
||||
assertEquals("Foo::Bar::bob", parser.extractFunctionName("void Foo::Bar::bob (int a)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+2
-5
@@ -22,7 +22,6 @@ import java.util.List;
|
||||
import org.junit.*;
|
||||
|
||||
import generic.test.AbstractGuiTest;
|
||||
import ghidra.app.services.DataTypeManagerService;
|
||||
import ghidra.app.util.cparser.C.ParseException;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
@@ -40,7 +39,6 @@ public class FunctionEditorModelTest extends AbstractGuiTest {
|
||||
private volatile boolean dataChangeCalled;
|
||||
private Structure bigStruct;
|
||||
private ProgramDB program;
|
||||
private DataTypeManagerService service;
|
||||
private volatile boolean tableRowsChanged;
|
||||
|
||||
class MyModelChangeListener implements ModelChangeListener {
|
||||
@@ -1612,9 +1610,9 @@ public class FunctionEditorModelTest extends AbstractGuiTest {
|
||||
assertEquals("R9D:4", storage.toString());
|
||||
|
||||
model.setUseCustomizeStorage(false);
|
||||
// no change to 'this', return ptr consumed and unfortunately
|
||||
// no change to 'this', return pointer consumed and unfortunately
|
||||
// injected before custom 'this' param
|
||||
// TODO: should we be removing 'this' param if not __thiscall ?
|
||||
// Note: should we be removing 'this' param if not __thiscall ?
|
||||
|
||||
assertTrue(model.getReturnType().isEquivalent(new PointerDataType(bigStruct)));
|
||||
assertTrue(model.getFormalReturnType().isEquivalent(bigStruct));
|
||||
@@ -1843,7 +1841,6 @@ public class FunctionEditorModelTest extends AbstractGuiTest {
|
||||
|
||||
model.setUseCustomizeStorage(true);
|
||||
VariableStorage paramStorage1 = model.getParameters().get(0).getStorage();
|
||||
VariableStorage paramStorage2 = model.getParameters().get(1).getStorage();
|
||||
VariableStorage paramStorage3 = model.getParameters().get(2).getStorage();
|
||||
|
||||
model.setSignatureFieldText("int joe(int e, int c, int f, int g)");
|
||||
|
||||
+10
-4
@@ -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.
|
||||
@@ -27,6 +27,7 @@ import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.VariableStorage;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.UndefinedFunction;
|
||||
@@ -99,7 +100,10 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
|
||||
if (useCustom) {
|
||||
// Force custom storage
|
||||
model.setUseCustomizeStorage(true);
|
||||
model.setFunctionData(buildSignature(hf));
|
||||
Function function = hf.getFunction();
|
||||
Namespace ns = function.getParentNamespace();
|
||||
FunctionDefinitionDataType signature = buildSignature(hf);
|
||||
model.setFunctionData(ns, signature);
|
||||
model.setReturnStorage(functionPrototype.getReturnStorage());
|
||||
parameters = model.getParameters();
|
||||
for (int i = 0; i < decompParamCnt; i++) {
|
||||
@@ -169,7 +173,9 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
|
||||
if (function.getEntryPoint().equals(hf.getFunction().getEntryPoint())) {
|
||||
|
||||
if (function.getSignatureSource() == SourceType.DEFAULT) {
|
||||
model.setFunctionData(buildSignature(hf));
|
||||
Namespace ns = function.getParentNamespace();
|
||||
FunctionDefinitionDataType signature = buildSignature(hf);
|
||||
model.setFunctionData(ns, signature);
|
||||
verifyDynamicEditorModel(hf, model);
|
||||
}
|
||||
else if (function.getReturnType() == DataType.DEFAULT) {
|
||||
|
||||
@@ -145,8 +145,8 @@ public class FcgLevel implements Comparable<FcgLevel> {
|
||||
/**
|
||||
* Returns the child level of this level. The child of a level has the same direction
|
||||
* as this level, with a distance of one more than this level.
|
||||
*
|
||||
* @param the direction of the child
|
||||
*
|
||||
* @param newDirection the direction of the child
|
||||
* @return returns the child level of this level
|
||||
* @throws IllegalArgumentException if this is the source level, which is row 1
|
||||
*/
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ import ghidra.util.Msg;
|
||||
*
|
||||
* <P>This class is handed a group of edges to processes. In this group there are vertices that
|
||||
* do not need to be arranged, referred to as the {@code existing} vertices. This
|
||||
* classes uses {@link VertexCollection} to find and store the new vertices that need
|
||||
* classes uses {@link FcgExpandingVertexCollection} to find and store the new vertices that need
|
||||
* to be arranged.
|
||||
*/
|
||||
public class BowTieExpandVerticesJob extends AbstractGraphTransitionJob<FcgVertex, FcgEdge> {
|
||||
|
||||
+3
-3
@@ -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,7 +31,7 @@ import util.CollectionUtils;
|
||||
* A container to house all newly added vertices (those being arranged) and the sources, or
|
||||
* 'from' vertices, of the new vertices.
|
||||
*
|
||||
* <P>This offers exiting vertices and new vertices pre-sorted by position in the graph in
|
||||
* <P>This offers existing vertices and new vertices pre-sorted by position in the graph in
|
||||
* order to minimize edge crossings. Specifically, the new vertices will be sorted
|
||||
* by the level of the parent and then the x-value of the parent so that the
|
||||
* immediate parent level will be preferred, with the x-value dictating where to place
|
||||
|
||||
@@ -131,6 +131,12 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public E getSelectedItem() {
|
||||
return (E) super.getSelectedItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text in combobox's editor text component
|
||||
* @return the text in combobox's editor text component
|
||||
|
||||
+7
-2
@@ -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.
|
||||
@@ -129,4 +129,9 @@ public class GlobalNamespace implements Namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ public class LabelMgrPluginScreenShots extends GhidraScreenShotGenerator {
|
||||
public void testChooseNamespace() {
|
||||
runSwingLater(() -> {
|
||||
NamespaceChooserDialog dialog = new NamespaceChooserDialog();
|
||||
dialog.getNameSpace(program);
|
||||
dialog.getNamespace(program);
|
||||
});
|
||||
waitForDialogComponent(NamespaceChooserDialog.class);
|
||||
captureDialog();
|
||||
|
||||
Reference in New Issue
Block a user