mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-29 06:29:30 +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>
|
<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>
|
<H3>Calling Convention</H3>
|
||||||
|
|
||||||
<P>This field is a combobox that allows you to choose a calling convention from the list of
|
<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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* 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.CompilerSpec;
|
||||||
import ghidra.program.model.lang.PrototypeModel;
|
import ghidra.program.model.lang.PrototypeModel;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.symbol.Namespace;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.exception.InvalidInputException;
|
import ghidra.util.exception.InvalidInputException;
|
||||||
|
|
||||||
@@ -40,19 +41,18 @@ class FunctionData extends FunctionDataView {
|
|||||||
|
|
||||||
boolean checkStorage = false;
|
boolean checkStorage = false;
|
||||||
if (canCustomizeStorage()) {
|
if (canCustomizeStorage()) {
|
||||||
// if (!originalFunctionData.canCustomizeStorage()) {
|
|
||||||
// // switched to using custom storage
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
checkStorage = true;
|
checkStorage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!returnInfo.getFormalDataType()
|
DataType returnType = returnInfo.getFormalDataType();
|
||||||
.equals(originalFunctionData.returnInfo.getFormalDataType())) {
|
DataType originalReturnType = originalFunctionData.returnInfo.getFormalDataType();
|
||||||
|
if (!returnType.equals(originalReturnType)) {
|
||||||
return true;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +162,10 @@ class FunctionData extends FunctionDataView {
|
|||||||
this.name = n;
|
this.name = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setNamespace(Namespace ns) {
|
||||||
|
this.namespace = ns;
|
||||||
|
}
|
||||||
|
|
||||||
void setInline(boolean enable) {
|
void setInline(boolean enable) {
|
||||||
this.isInLine = enable;
|
this.isInLine = enable;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-3
@@ -20,6 +20,7 @@ import java.util.*;
|
|||||||
import ghidra.program.model.data.VoidDataType;
|
import ghidra.program.model.data.VoidDataType;
|
||||||
import ghidra.program.model.lang.PrototypeModel;
|
import ghidra.program.model.lang.PrototypeModel;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.symbol.Namespace;
|
||||||
import ghidra.program.model.symbol.SymbolUtilities;
|
import ghidra.program.model.symbol.SymbolUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,6 +31,7 @@ class FunctionDataView {
|
|||||||
|
|
||||||
Function function;
|
Function function;
|
||||||
|
|
||||||
|
Namespace namespace;
|
||||||
String name;
|
String name;
|
||||||
boolean hasVarArgs;
|
boolean hasVarArgs;
|
||||||
ParamInfo returnInfo;
|
ParamInfo returnInfo;
|
||||||
@@ -47,7 +49,8 @@ class FunctionDataView {
|
|||||||
*/
|
*/
|
||||||
FunctionDataView(Function function) {
|
FunctionDataView(Function function) {
|
||||||
this.function = function;
|
this.function = function;
|
||||||
this.name = function.getName();
|
name = function.getName();
|
||||||
|
namespace = function.getParentNamespace();
|
||||||
allowCustomStorage = function.hasCustomVariableStorage();
|
allowCustomStorage = function.hasCustomVariableStorage();
|
||||||
hasVarArgs = function.hasVarArgs();
|
hasVarArgs = function.hasVarArgs();
|
||||||
isInLine = function.isInline();
|
isInLine = function.isInline();
|
||||||
@@ -63,6 +66,7 @@ class FunctionDataView {
|
|||||||
*/
|
*/
|
||||||
FunctionDataView(FunctionDataView otherFunctionData) {
|
FunctionDataView(FunctionDataView otherFunctionData) {
|
||||||
name = otherFunctionData.name;
|
name = otherFunctionData.name;
|
||||||
|
namespace = otherFunctionData.namespace;
|
||||||
hasVarArgs = otherFunctionData.hasVarArgs;
|
hasVarArgs = otherFunctionData.hasVarArgs;
|
||||||
returnInfo = otherFunctionData.returnInfo.copy();
|
returnInfo = otherFunctionData.returnInfo.copy();
|
||||||
for (ParamInfo p : otherFunctionData.parameters) {
|
for (ParamInfo p : otherFunctionData.parameters) {
|
||||||
@@ -82,6 +86,7 @@ class FunctionDataView {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Objects.equals(name, otherFunctionData.name) ||
|
if (!Objects.equals(name, otherFunctionData.name) ||
|
||||||
|
!Objects.equals(namespace, otherFunctionData.namespace) ||
|
||||||
!Objects.equals(callingConventionName, otherFunctionData.callingConventionName) ||
|
!Objects.equals(callingConventionName, otherFunctionData.callingConventionName) ||
|
||||||
hasVarArgs != otherFunctionData.hasVarArgs ||
|
hasVarArgs != otherFunctionData.hasVarArgs ||
|
||||||
parameters.size() != otherFunctionData.parameters.size() ||
|
parameters.size() != otherFunctionData.parameters.size() ||
|
||||||
@@ -174,7 +179,7 @@ class FunctionDataView {
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program getProgram() {
|
Program getProgram() {
|
||||||
return function.getProgram();
|
return function.getProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,10 +195,14 @@ class FunctionDataView {
|
|||||||
return parameters.size();
|
return parameters.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Namespace getNamespace() {
|
||||||
|
return namespace;
|
||||||
|
}
|
||||||
|
|
||||||
String getNameString() {
|
String getNameString() {
|
||||||
return name.length() == 0 ? SymbolUtilities.getDefaultFunctionName(function.getEntryPoint())
|
return name.length() == 0 ? SymbolUtilities.getDefaultFunctionName(function.getEntryPoint())
|
||||||
: name;
|
: name;
|
||||||
|
|||||||
+220
-16
@@ -17,8 +17,7 @@ package ghidra.app.plugin.core.function.editor;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.EventObject;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@@ -33,25 +32,29 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
|
import docking.widgets.button.BrowseButton;
|
||||||
import docking.widgets.button.GButton;
|
import docking.widgets.button.GButton;
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
|
import docking.widgets.combobox.GhidraComboBox;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
import generic.theme.GThemeDefaults.Colors;
|
import generic.theme.GThemeDefaults.Colors;
|
||||||
import generic.util.WindowUtilities;
|
import generic.util.WindowUtilities;
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
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.CParserUtils;
|
||||||
|
import ghidra.app.util.cparser.C.ParseException;
|
||||||
import ghidra.app.util.viewer.field.ListingColors.FunctionColors;
|
import ghidra.app.util.viewer.field.ListingColors.FunctionColors;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
import ghidra.program.model.data.VoidDataType;
|
import ghidra.program.model.data.VoidDataType;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.listing.VariableStorage;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.model.symbol.ExternalLocation;
|
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.exception.InvalidInputException;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
import ghidra.util.layout.VerticalLayout;
|
import ghidra.util.layout.VerticalLayout;
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
@@ -68,6 +71,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
private GTable parameterTable;
|
private GTable parameterTable;
|
||||||
|
|
||||||
private JTextField nameField;
|
private JTextField nameField;
|
||||||
|
private GhidraComboBox<NamespaceWrapper> namespaceChoices;
|
||||||
private JCheckBox varArgsCheckBox;
|
private JCheckBox varArgsCheckBox;
|
||||||
private DataTypeManagerService service;
|
private DataTypeManagerService service;
|
||||||
private JCheckBox inLineCheckBox;
|
private JCheckBox inLineCheckBox;
|
||||||
@@ -167,7 +171,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
protected void okCallback() {
|
protected void okCallback() {
|
||||||
if (model.isInParsingMode()) {
|
if (model.isInParsingMode()) {
|
||||||
try {
|
try {
|
||||||
model.parseSignatureFieldText();
|
doParse();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
handleParseException(e);
|
handleParseException(e);
|
||||||
@@ -212,6 +216,32 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
super.close();
|
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) {
|
private JComponent buildMainPanel(boolean hasOptionalSignatureCommit) {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
@@ -224,7 +254,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
private JComponent buildCenterPanel(boolean hasOptionalSignatureCommit) {
|
private JComponent buildCenterPanel(boolean hasOptionalSignatureCommit) {
|
||||||
centerPanel = new JPanel(new BorderLayout());
|
centerPanel = new JPanel(new BorderLayout());
|
||||||
centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
|
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(buildTable(), BorderLayout.CENTER);
|
||||||
centerPanel.add(buildBottomPanel(hasOptionalSignatureCommit), BorderLayout.SOUTH);
|
centerPanel.add(buildBottomPanel(hasOptionalSignatureCommit), BorderLayout.SOUTH);
|
||||||
centerPanel.getAccessibleContext().setAccessibleName("Function Attributes");
|
centerPanel.getAccessibleContext().setAccessibleName("Function Attributes");
|
||||||
@@ -342,7 +372,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
signatureTextField.setActionListener(e -> {
|
signatureTextField.setActionListener(e -> {
|
||||||
try {
|
try {
|
||||||
if (model.isInParsingMode()) {
|
if (model.isInParsingMode()) {
|
||||||
model.parseSignatureFieldText();
|
doParse();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,7 +391,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
|
|
||||||
ActionListener tabListener = e -> {
|
ActionListener tabListener = e -> {
|
||||||
try {
|
try {
|
||||||
model.parseSignatureFieldText();
|
doParse();
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
if (!handleParseException(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
|
return result == OptionDialog.OPTION_TWO; // Option 2 is to abort
|
||||||
}
|
}
|
||||||
|
|
||||||
private Component buildAttributePanel() {
|
private Component createAttributePanel() {
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 15, 15));
|
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 15, 15));
|
||||||
|
|
||||||
JPanel leftPanel = new JPanel(new PairLayout(4, 8));
|
JPanel leftPanel = new JPanel(new PairLayout(4, 8));
|
||||||
leftPanel.add(new GLabel("Function Name:"));
|
leftPanel.add(new GLabel("Function Name:"));
|
||||||
leftPanel.add(createNameField());
|
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.add(createCallingConventionCombo());
|
||||||
leftPanel.setBorder(BorderFactory.createEmptyBorder(14, 0, 0, 10));
|
|
||||||
|
leftPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 10));
|
||||||
leftPanel.getAccessibleContext().setAccessibleName("Function");
|
leftPanel.getAccessibleContext().setAccessibleName("Function");
|
||||||
panel.add(leftPanel, BorderLayout.CENTER);
|
panel.add(leftPanel, BorderLayout.CENTER);
|
||||||
panel.add(buildTogglePanel(), BorderLayout.EAST);
|
panel.add(buildTogglePanel(), BorderLayout.EAST);
|
||||||
@@ -428,6 +463,133 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
return panel;
|
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() {
|
private Component buildTogglePanel() {
|
||||||
JPanel panel = new JPanel(new PairLayout());
|
JPanel panel = new JPanel(new PairLayout());
|
||||||
varArgsCheckBox = new GCheckBox("Varargs");
|
varArgsCheckBox = new GCheckBox("Varargs");
|
||||||
@@ -753,8 +915,9 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!model.hasValidName()) {
|
if (!model.hasValidName()) {
|
||||||
signatureTextField.setError(model.getFunctionNameStartPosition(),
|
int pos = model.getFunctionNameStartPosition();
|
||||||
model.getNameString().length());
|
int len = model.getNameString().length();
|
||||||
|
signatureTextField.setError(pos, len);
|
||||||
}
|
}
|
||||||
if (caretPosition < preview.length()) {
|
if (caretPosition < preview.length()) {
|
||||||
signatureTextField.setCaretPosition(caretPosition);
|
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 {
|
private class ParameterDataTypeCellRenderer extends GTableCellRenderer {
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||||
@@ -1133,7 +1337,7 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
|
|||||||
|
|
||||||
if (!processEvent(e)) {
|
if (!processEvent(e)) {
|
||||||
try {
|
try {
|
||||||
model.parseSignatureFieldText();
|
doParse();
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
handleParseException(ex);
|
handleParseException(ex);
|
||||||
|
|||||||
+56
-7
@@ -18,8 +18,11 @@ package ghidra.app.plugin.core.function.editor;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
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.cparser.C.ParseException;
|
||||||
import ghidra.app.util.parser.FunctionSignatureParser;
|
import ghidra.app.util.parser.FunctionSignatureParser;
|
||||||
|
import ghidra.app.util.parser.FunctionSignatureParser.FsParseResult;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
@@ -61,7 +64,7 @@ public class FunctionEditorModel {
|
|||||||
this.dataTypeManagerService = service;
|
this.dataTypeManagerService = service;
|
||||||
this.function = function;
|
this.function = function;
|
||||||
this.program = function.getProgram();
|
this.program = function.getProgram();
|
||||||
functionData = new FunctionData(function);
|
this.functionData = new FunctionData(function);
|
||||||
this.originalFunctionData = new FunctionDataView(functionData);
|
this.originalFunctionData = new FunctionDataView(functionData);
|
||||||
validate();
|
validate();
|
||||||
}
|
}
|
||||||
@@ -118,6 +121,7 @@ public class FunctionEditorModel {
|
|||||||
"Signature transformed due to auto-params and/or forced-indirect storage change";
|
"Signature transformed due to auto-params and/or forced-indirect storage change";
|
||||||
isSignatureTransformed = false; // one-shot message
|
isSignatureTransformed = false; // one-shot message
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid =
|
isValid =
|
||||||
hasValidName() && hasValidReturnType() && hasValidReturnStorage() && hasValidParams();
|
hasValidName() && hasValidReturnType() && hasValidReturnStorage() && hasValidParams();
|
||||||
hasSignificantParameterChanges = false;
|
hasSignificantParameterChanges = false;
|
||||||
@@ -256,7 +260,7 @@ public class FunctionEditorModel {
|
|||||||
* {@link VariableUtilities#checkVariableConflict(List, Variable, VariableStorage, VariableConflictHandler)}
|
* {@link VariableUtilities#checkVariableConflict(List, Variable, VariableStorage, VariableConflictHandler)}
|
||||||
* @param conflicts parameters whose storage conflicts
|
* @param conflicts parameters whose storage conflicts
|
||||||
* @return return false to indicate conflicts have not been resolved and additional checks
|
* @return return false to indicate conflicts have not been resolved and additional checks
|
||||||
* should be disconctinued.
|
* should be discontinued.
|
||||||
* @see VariableConflictHandler
|
* @see VariableConflictHandler
|
||||||
*/
|
*/
|
||||||
private boolean handleConflicts(List<Variable> conflicts) {
|
private boolean handleConflicts(List<Variable> conflicts) {
|
||||||
@@ -405,6 +409,18 @@ public class FunctionEditorModel {
|
|||||||
notifyDataChanged();
|
notifyDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Namespace getNamespace() {
|
||||||
|
return functionData.getNamespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNamespace(Namespace ns) {
|
||||||
|
if (getNamespace().equals(ns)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
functionData.setNamespace(ns);
|
||||||
|
notifyDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
String getCallingConventionName() {
|
String getCallingConventionName() {
|
||||||
return functionData.getCallingConventionName();
|
return functionData.getCallingConventionName();
|
||||||
}
|
}
|
||||||
@@ -742,9 +758,12 @@ public class FunctionEditorModel {
|
|||||||
if (b == canUseCustomStorage()) {
|
if (b == canUseCustomStorage()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
functionData.setUseCustomStorage(b);
|
functionData.setUseCustomStorage(b);
|
||||||
isSignatureTransformed = !functionData.getFunctionSignatureText()
|
|
||||||
.equals(originalFunctionData.getFunctionSignatureText());
|
String signatureText = functionData.getFunctionSignatureText();
|
||||||
|
String originalSignatureText = originalFunctionData.getFunctionSignatureText();
|
||||||
|
isSignatureTransformed = !signatureText.equals(originalSignatureText);
|
||||||
notifyDataChanged();
|
notifyDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -779,6 +798,11 @@ public class FunctionEditorModel {
|
|||||||
function.setName(name, SourceType.USER_DEFINED);
|
function.setName(name, SourceType.USER_DEFINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Namespace namespace = functionData.getNamespace();
|
||||||
|
if (!namespace.equals(function.getParentNamespace())) {
|
||||||
|
function.setParentNamespace(namespace);
|
||||||
|
}
|
||||||
|
|
||||||
boolean isInline = functionData.isInline();
|
boolean isInline = functionData.isInline();
|
||||||
if (function.isInline() != isInline) {
|
if (function.isInline() != isInline) {
|
||||||
function.setInline(isInline);
|
function.setInline(isInline);
|
||||||
@@ -905,7 +929,7 @@ public class FunctionEditorModel {
|
|||||||
return dt1.getLength() == dt2.getLength();
|
return dt1.getLength() == dt2.getLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFunctionData(FunctionDefinitionDataType functionDefinition) {
|
public void setFunctionData(Namespace ns, FunctionDefinitionDataType functionDefinition) {
|
||||||
|
|
||||||
setName(functionDefinition.getName());
|
setName(functionDefinition.getName());
|
||||||
|
|
||||||
@@ -941,6 +965,8 @@ public class FunctionEditorModel {
|
|||||||
|
|
||||||
functionData.updateParameterAndReturnStorage();
|
functionData.updateParameterAndReturnStorage();
|
||||||
|
|
||||||
|
functionData.setNamespace(ns);
|
||||||
|
|
||||||
notifyDataChanged();
|
notifyDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1010,7 +1036,9 @@ public class FunctionEditorModel {
|
|||||||
void parseSignatureFieldText() throws ParseException, CancelledException {
|
void parseSignatureFieldText() throws ParseException, CancelledException {
|
||||||
FunctionSignatureParser parser =
|
FunctionSignatureParser parser =
|
||||||
new FunctionSignatureParser(program.getDataTypeManager(), dataTypeManagerService);
|
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
|
// Preserve calling convention and noreturn flag from current model
|
||||||
f.setNoReturn(functionData.hasNoReturn());
|
f.setNoReturn(functionData.hasNoReturn());
|
||||||
@@ -1021,10 +1049,31 @@ public class FunctionEditorModel {
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
setFunctionData(f);
|
Namespace ns = createNamespace(result.namespace());
|
||||||
|
setFunctionData(ns, f);
|
||||||
isInParsingMode = false;
|
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() {
|
int getFunctionNameStartPosition() {
|
||||||
return getFormalReturnType().getName().length() + 1;
|
return getFormalReturnType().getName().length() + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import ghidra.program.model.listing.*;
|
|||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
|
import ghidra.util.exception.InvalidInputException;
|
||||||
import ghidra.util.layout.VerticalLayout;
|
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
|
// the number of columns determines the default width of the add/edit label dialog
|
||||||
labelNameChoices.setColumns(20);
|
labelNameChoices.setColumns(20);
|
||||||
labelNameChoices.setName("label.name.choices");
|
labelNameChoices.setName("label.name.choices");
|
||||||
GhidraComboBox<NamespaceWrapper> comboBox = new GhidraComboBox<>();
|
namespaceChoices = new GhidraComboBox<>();
|
||||||
namespaceChoices = comboBox;
|
|
||||||
|
|
||||||
primaryCheckBox = new GCheckBox("Primary");
|
primaryCheckBox = new GCheckBox("Primary");
|
||||||
primaryCheckBox.setMnemonic('P');
|
primaryCheckBox.setMnemonic('P');
|
||||||
@@ -636,12 +636,40 @@ public class AddEditDialog extends ReusableDialogComponentProvider {
|
|||||||
|
|
||||||
private void showNamespaceChooser() {
|
private void showNamespaceChooser() {
|
||||||
NamespaceChooserDialog dialog = new NamespaceChooserDialog();
|
NamespaceChooserDialog dialog = new NamespaceChooserDialog();
|
||||||
Namespace namespace = dialog.getNameSpace(program);
|
Namespace namespace = dialog.getNamespace(program);
|
||||||
if (namespace != null) {
|
if (namespace != null) {
|
||||||
NamespaceCache.add(program, namespace);
|
NamespaceCache.add(program, namespace);
|
||||||
initNamespaces();
|
initNamespaces();
|
||||||
namespaceChoices.setSelectedItem(new NamespaceWrapper(namespace));
|
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() {
|
private void addListeners() {
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import ghidra.util.datastruct.LRUSet;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Static class for remember the last few namespaces used for a program.
|
* 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 class NamespaceCache {
|
||||||
public static final int MAX_RECENTS = 10;
|
public static final int MAX_RECENTS = 10;
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ public class NamespaceChooserDialog extends DialogComponentProvider {
|
|||||||
addCancelButton();
|
addCancelButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Namespace getNameSpace(Program program) {
|
public void setText(String text) {
|
||||||
|
dropDownField.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Namespace getNamespace(Program program) {
|
||||||
List<Namespace> namespaces = gatherNamespaces(program);
|
List<Namespace> namespaces = gatherNamespaces(program);
|
||||||
if (namespaces == null) {
|
if (namespaces == null) {
|
||||||
// user cancelled while gathering namespaces
|
// user cancelled while gathering namespaces
|
||||||
@@ -59,6 +63,10 @@ public class NamespaceChooserDialog extends DialogComponentProvider {
|
|||||||
return chosenNamespace;
|
return chosenNamespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getNamespaceText() {
|
||||||
|
return dropDownField.getText();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void okCallback() {
|
protected void okCallback() {
|
||||||
chosenNamespace = dropDownField.getSelectedValue();
|
chosenNamespace = dropDownField.getSelectedValue();
|
||||||
@@ -68,6 +76,7 @@ public class NamespaceChooserDialog extends DialogComponentProvider {
|
|||||||
@Override
|
@Override
|
||||||
protected void cancelCallback() {
|
protected void cancelCallback() {
|
||||||
chosenNamespace = null;
|
chosenNamespace = null;
|
||||||
|
dropDownField.setText("");
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +91,7 @@ public class NamespaceChooserDialog extends DialogComponentProvider {
|
|||||||
panel.add(new JLabel("Namespace: "));
|
panel.add(new JLabel("Namespace: "));
|
||||||
|
|
||||||
dropDownField = new DropDownSelectionTextField<>(namespaceModel);
|
dropDownField = new DropDownSelectionTextField<>(namespaceModel);
|
||||||
|
dropDownField.setShowMatchingListOnEmptyText(true);
|
||||||
panel.add(dropDownField);
|
panel.add(dropDownField);
|
||||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
return panel;
|
return panel;
|
||||||
|
|||||||
+29
-7
@@ -21,6 +21,7 @@ import java.util.regex.Pattern;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ghidra.app.services.DataTypeQueryService;
|
import ghidra.app.services.DataTypeQueryService;
|
||||||
|
import ghidra.app.util.SymbolPath;
|
||||||
import ghidra.app.util.cparser.C.ParseException;
|
import ghidra.app.util.cparser.C.ParseException;
|
||||||
import ghidra.program.database.data.DataTypeUtilities;
|
import ghidra.program.database.data.DataTypeUtilities;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
@@ -56,7 +57,8 @@ public class FunctionSignatureParser {
|
|||||||
private DataTypeParser dataTypeParser;
|
private DataTypeParser dataTypeParser;
|
||||||
private Map<String, DataType> dtMap = new HashMap<>();
|
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 DataTypeManager destDataTypeManager;
|
||||||
private ParserDataTypeManagerService dtmService;
|
private ParserDataTypeManagerService dtmService;
|
||||||
|
|
||||||
@@ -96,8 +98,16 @@ public class FunctionSignatureParser {
|
|||||||
*/
|
*/
|
||||||
public FunctionDefinitionDataType parse(FunctionSignature originalSignature,
|
public FunctionDefinitionDataType parse(FunctionSignature originalSignature,
|
||||||
String signatureText) throws ParseException, CancelledException {
|
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();
|
dtMap.clear();
|
||||||
nameMap.clear();
|
replacedNameMap.clear();
|
||||||
if (dtmService != null) {
|
if (dtmService != null) {
|
||||||
dtmService.clearCache(); // clear datatype selection cache
|
dtmService.clearCache(); // clear datatype selection cache
|
||||||
}
|
}
|
||||||
@@ -108,14 +118,18 @@ public class FunctionSignatureParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String functionName = extractFunctionName(signatureText);
|
String functionName = extractFunctionName(signatureText);
|
||||||
|
SymbolPath path = new SymbolPath(functionName);
|
||||||
|
SymbolPath nsPath = path.getParent();
|
||||||
|
String name = path.getName();
|
||||||
|
|
||||||
FunctionDefinitionDataType function =
|
FunctionDefinitionDataType function =
|
||||||
new FunctionDefinitionDataType(functionName, destDataTypeManager);
|
new FunctionDefinitionDataType(name, destDataTypeManager);
|
||||||
|
|
||||||
function.setReturnType(extractReturnType(signatureText));
|
function.setReturnType(extractReturnType(signatureText));
|
||||||
function.setArguments(extractArguments(signatureText));
|
function.setArguments(extractArguments(signatureText));
|
||||||
function.setVarArgs(hasVarArgs(signatureText));
|
function.setVarArgs(hasVarArgs(signatureText));
|
||||||
|
|
||||||
return function;
|
return new FsParseResult(nsPath, function);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDataTypeMap(FunctionSignature signature) {
|
private void initDataTypeMap(FunctionSignature signature) {
|
||||||
@@ -247,7 +261,7 @@ public class FunctionSignatureParser {
|
|||||||
if (canParseName(name)) {
|
if (canParseName(name)) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
nameMap.put(replacementName, name);
|
replacedNameMap.put(replacementName, name);
|
||||||
return substitute(text, name, replacementName);
|
return substitute(text, name, replacementName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,8 +313,8 @@ public class FunctionSignatureParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String resolveName(String name) throws ParseException {
|
private String resolveName(String name) throws ParseException {
|
||||||
if (nameMap.containsKey(name)) {
|
if (replacedNameMap.containsKey(name)) {
|
||||||
return nameMap.get(name);
|
return replacedNameMap.get(name);
|
||||||
}
|
}
|
||||||
if (!canParseName(name)) {
|
if (!canParseName(name)) {
|
||||||
throw new ParseException("Can't parse name: " + name);
|
throw new ParseException("Can't parse name: " + name);
|
||||||
@@ -320,6 +334,14 @@ public class FunctionSignatureParser {
|
|||||||
return !StringUtils.containsAny(text, "()<>,");
|
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>
|
* Provides a simple caching datatype manager service wrapper.<br>
|
||||||
* Implementation intended for use with {@link FunctionSignatureParser}
|
* Implementation intended for use with {@link FunctionSignatureParser}
|
||||||
|
|||||||
+188
-10
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -15,34 +15,36 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.function.editor;
|
package ghidra.app.plugin.core.function.editor;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.ComboBoxModel;
|
||||||
import javax.swing.table.TableCellEditor;
|
import javax.swing.table.TableCellEditor;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.widgets.DropDownSelectionTextField;
|
import docking.widgets.DropDownSelectionTextField;
|
||||||
|
import docking.widgets.button.BrowseButton;
|
||||||
|
import docking.widgets.combobox.GhidraComboBox;
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
import ghidra.app.cmd.function.DeleteFunctionCmd;
|
import ghidra.app.cmd.function.DeleteFunctionCmd;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||||
import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin;
|
import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
|
import ghidra.app.util.*;
|
||||||
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
import ghidra.app.util.datatype.DataTypeSelectionEditor;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.address.AddressFactory;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
import ghidra.program.model.symbol.Namespace;
|
||||||
|
import ghidra.program.model.symbol.SourceType;
|
||||||
import ghidra.test.*;
|
import ghidra.test.*;
|
||||||
|
|
||||||
public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
public FunctionEditorDialogTest() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TestEnv env;
|
private TestEnv env;
|
||||||
private PluginTool tool;
|
private PluginTool tool;
|
||||||
private AddressFactory addrFactory;
|
private AddressFactory addrFactory;
|
||||||
@@ -64,10 +66,11 @@ public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTes
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
|
closeAllWindows();
|
||||||
env.dispose();
|
env.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Tests that an invalid parameter type entry will generate the proper error message
|
* 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.
|
* 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"));
|
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 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) {
|
private void setEditorText(TableCellEditor cellEditor, String text) {
|
||||||
DropDownSelectionTextField<?> textField = getDataTypeEditor(cellEditor);
|
DropDownSelectionTextField<?> textField = getDataTypeEditor(cellEditor);
|
||||||
setText(textField, text);
|
setText(textField, text);
|
||||||
@@ -106,7 +279,7 @@ public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTes
|
|||||||
|
|
||||||
private FunctionEditorDialog editFunction() {
|
private FunctionEditorDialog editFunction() {
|
||||||
performAction(editFunction, cb.getProvider(), false);
|
performAction(editFunction, cb.getProvider(), false);
|
||||||
return waitForDialogComponent(null, FunctionEditorDialog.class, DEFAULT_WINDOW_TIMEOUT);
|
return waitForDialogComponent(FunctionEditorDialog.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finishEditing(final TableCellEditor cellEditor) {
|
private void finishEditing(final TableCellEditor cellEditor) {
|
||||||
@@ -150,6 +323,11 @@ public class FunctionEditorDialogTest extends AbstractGhidraHeadedIntegrationTes
|
|||||||
addrFactory = program.getAddressFactory();
|
addrFactory = program.getAddressFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Function getFunction(String addr) {
|
||||||
|
FunctionManager fm = program.getFunctionManager();
|
||||||
|
return fm.getFunctionAt(addr(addr));
|
||||||
|
}
|
||||||
|
|
||||||
private void createFunctionAtEntry() {
|
private void createFunctionAtEntry() {
|
||||||
FunctionManager fm = program.getFunctionManager();
|
FunctionManager fm = program.getFunctionManager();
|
||||||
Function f = fm.getFunctionAt(addr("0x1006420"));
|
Function f = fm.getFunctionAt(addr("0x1006420"));
|
||||||
|
|||||||
+1
@@ -132,6 +132,7 @@ public class FunctionSignatureParserTest extends AbstractGhidraHeadedIntegration
|
|||||||
public void testExtractFunctionName() throws Exception {
|
public void testExtractFunctionName() throws Exception {
|
||||||
assertEquals("bob", parser.extractFunctionName("void bob(int a)"));
|
assertEquals("bob", parser.extractFunctionName("void bob(int a)"));
|
||||||
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
|
@Test
|
||||||
|
|||||||
+2
-5
@@ -22,7 +22,6 @@ import java.util.List;
|
|||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import generic.test.AbstractGuiTest;
|
import generic.test.AbstractGuiTest;
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
|
||||||
import ghidra.app.util.cparser.C.ParseException;
|
import ghidra.app.util.cparser.C.ParseException;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
@@ -40,7 +39,6 @@ public class FunctionEditorModelTest extends AbstractGuiTest {
|
|||||||
private volatile boolean dataChangeCalled;
|
private volatile boolean dataChangeCalled;
|
||||||
private Structure bigStruct;
|
private Structure bigStruct;
|
||||||
private ProgramDB program;
|
private ProgramDB program;
|
||||||
private DataTypeManagerService service;
|
|
||||||
private volatile boolean tableRowsChanged;
|
private volatile boolean tableRowsChanged;
|
||||||
|
|
||||||
class MyModelChangeListener implements ModelChangeListener {
|
class MyModelChangeListener implements ModelChangeListener {
|
||||||
@@ -1612,9 +1610,9 @@ public class FunctionEditorModelTest extends AbstractGuiTest {
|
|||||||
assertEquals("R9D:4", storage.toString());
|
assertEquals("R9D:4", storage.toString());
|
||||||
|
|
||||||
model.setUseCustomizeStorage(false);
|
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
|
// 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.getReturnType().isEquivalent(new PointerDataType(bigStruct)));
|
||||||
assertTrue(model.getFormalReturnType().isEquivalent(bigStruct));
|
assertTrue(model.getFormalReturnType().isEquivalent(bigStruct));
|
||||||
@@ -1843,7 +1841,6 @@ public class FunctionEditorModelTest extends AbstractGuiTest {
|
|||||||
|
|
||||||
model.setUseCustomizeStorage(true);
|
model.setUseCustomizeStorage(true);
|
||||||
VariableStorage paramStorage1 = model.getParameters().get(0).getStorage();
|
VariableStorage paramStorage1 = model.getParameters().get(0).getStorage();
|
||||||
VariableStorage paramStorage2 = model.getParameters().get(1).getStorage();
|
|
||||||
VariableStorage paramStorage3 = model.getParameters().get(2).getStorage();
|
VariableStorage paramStorage3 = model.getParameters().get(2).getStorage();
|
||||||
|
|
||||||
model.setSignatureFieldText("int joe(int e, int c, int f, int g)");
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* 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.Function;
|
||||||
import ghidra.program.model.listing.VariableStorage;
|
import ghidra.program.model.listing.VariableStorage;
|
||||||
import ghidra.program.model.pcode.*;
|
import ghidra.program.model.pcode.*;
|
||||||
|
import ghidra.program.model.symbol.Namespace;
|
||||||
import ghidra.program.model.symbol.SourceType;
|
import ghidra.program.model.symbol.SourceType;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.UndefinedFunction;
|
import ghidra.util.UndefinedFunction;
|
||||||
@@ -99,7 +100,10 @@ public class SpecifyCPrototypeAction extends AbstractDecompilerAction {
|
|||||||
if (useCustom) {
|
if (useCustom) {
|
||||||
// Force custom storage
|
// Force custom storage
|
||||||
model.setUseCustomizeStorage(true);
|
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());
|
model.setReturnStorage(functionPrototype.getReturnStorage());
|
||||||
parameters = model.getParameters();
|
parameters = model.getParameters();
|
||||||
for (int i = 0; i < decompParamCnt; i++) {
|
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.getEntryPoint().equals(hf.getFunction().getEntryPoint())) {
|
||||||
|
|
||||||
if (function.getSignatureSource() == SourceType.DEFAULT) {
|
if (function.getSignatureSource() == SourceType.DEFAULT) {
|
||||||
model.setFunctionData(buildSignature(hf));
|
Namespace ns = function.getParentNamespace();
|
||||||
|
FunctionDefinitionDataType signature = buildSignature(hf);
|
||||||
|
model.setFunctionData(ns, signature);
|
||||||
verifyDynamicEditorModel(hf, model);
|
verifyDynamicEditorModel(hf, model);
|
||||||
}
|
}
|
||||||
else if (function.getReturnType() == DataType.DEFAULT) {
|
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
|
* 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.
|
* 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
|
* @return returns the child level of this level
|
||||||
* @throws IllegalArgumentException if this is the source level, which is row 1
|
* @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
|
* <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
|
* 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.
|
* to be arranged.
|
||||||
*/
|
*/
|
||||||
public class BowTieExpandVerticesJob extends AbstractGraphTransitionJob<FcgVertex, FcgEdge> {
|
public class BowTieExpandVerticesJob extends AbstractGraphTransitionJob<FcgVertex, FcgEdge> {
|
||||||
|
|||||||
+3
-3
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* 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
|
* A container to house all newly added vertices (those being arranged) and the sources, or
|
||||||
* 'from' vertices, of the new vertices.
|
* '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
|
* 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
|
* 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
|
* 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
|
* Returns the text in combobox's editor text component
|
||||||
* @return 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -129,4 +129,9 @@ public class GlobalNamespace implements Namespace {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getClass().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -79,7 +79,7 @@ public class LabelMgrPluginScreenShots extends GhidraScreenShotGenerator {
|
|||||||
public void testChooseNamespace() {
|
public void testChooseNamespace() {
|
||||||
runSwingLater(() -> {
|
runSwingLater(() -> {
|
||||||
NamespaceChooserDialog dialog = new NamespaceChooserDialog();
|
NamespaceChooserDialog dialog = new NamespaceChooserDialog();
|
||||||
dialog.getNameSpace(program);
|
dialog.getNamespace(program);
|
||||||
});
|
});
|
||||||
waitForDialogComponent(NamespaceChooserDialog.class);
|
waitForDialogComponent(NamespaceChooserDialog.class);
|
||||||
captureDialog();
|
captureDialog();
|
||||||
|
|||||||
Reference in New Issue
Block a user