GP-356 refactor of EditFunctionSignatureDialog and the various

extensions.
This commit is contained in:
ghidra1
2021-02-25 18:33:00 -05:00
parent 1e737c7b62
commit bc8e56bd60
6 changed files with 778 additions and 527 deletions
@@ -18,25 +18,21 @@ package ghidra.app.plugin.core.datamgr.editor;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ComboBoxModel;
import javax.swing.JPanel;
import docking.ComponentProvider;
import docking.actions.DockingToolActions;
import docking.actions.SharedDockingActionPlaceholder;
import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.label.GLabel;
import ghidra.app.plugin.core.compositeeditor.*;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.plugin.core.function.EditFunctionSignatureDialog;
import ghidra.app.plugin.core.function.AbstractEditFunctionSignatureDialog;
import ghidra.framework.model.DomainObject;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.FunctionSignature;
import ghidra.program.model.listing.Program;
import ghidra.util.*;
import ghidra.util.exception.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
/**
* Manages program and archive data type editors.
@@ -238,8 +234,8 @@ public class DataTypeEditorManager
list.add(editor);
}
}
for (int i = 0; i < list.size(); i++) {
dismissEditor(list.get(i));
for (EditorProvider element : list) {
dismissEditor(element);
}
}
@@ -519,55 +515,10 @@ public class DataTypeEditorManager
editFunctionSignature(category, functionDefinition);
}
private void editFunctionSignature(final Category category,
final FunctionDefinition functionDefinition) {
Function function =
new UndefinedFunction(plugin.getProgram(), plugin.getProgram().getMinAddress()) {
@Override
public String getCallingConventionName() {
if (functionDefinition == null) {
return super.getCallingConventionName();
}
return functionDefinition.getGenericCallingConvention().toString();
}
@Override
public void setCallingConvention(String name) throws InvalidInputException {
// no-op; we handle this in the editor dialog
}
@Override
public void setInline(boolean isInline) {
// can't edit this from the DataTypeManager
}
@Override
public void setNoReturn(boolean hasNoReturn) {
// can't edit this from the DataTypeManager
}
@Override
public FunctionSignature getSignature() {
if (functionDefinition != null) {
return functionDefinition;
}
return super.getSignature();
}
@Override
public String getName() {
if (functionDefinition != null) {
return functionDefinition.getName();
}
return "newFunction";
}
};
// DT how do I do the same as the other creates.
private void editFunctionSignature(Category category, FunctionDefinition functionDefinition) {
PluginTool tool = plugin.getTool();
DTMEditFunctionSignatureDialog editSigDialog = new DTMEditFunctionSignatureDialog(
plugin.getTool(), "Edit Function Signature", function, category, functionDefinition);
plugin.getTool(), "Edit Function Signature", category, functionDefinition);
editSigDialog.setHelpLocation(
new HelpLocation("DataTypeManagerPlugin", "Function_Definition"));
tool.showDialog(editSigDialog);
@@ -577,64 +528,73 @@ public class DataTypeEditorManager
// Inner Classes
//==================================================================================================
private class DTMEditFunctionSignatureDialog extends EditFunctionSignatureDialog {
/**
* <code>DTMEditFunctionSignatureDialog</code> provides the ability to edit the
* function signature associated with a specific {@link FunctionDefinition}.
* Use of this editor requires the presence of the tool-based datatype manager service.
*/
private class DTMEditFunctionSignatureDialog extends AbstractEditFunctionSignatureDialog {
private final FunctionDefinition functionDefinition;
private final FunctionSignature oldSignature;
private final Category category;
private final FunctionDefinition functionDefinitionDataType;
DTMEditFunctionSignatureDialog(PluginTool pluginTool, String title, Function function,
Category category, FunctionDefinition functionDefinition) {
super(pluginTool, title, function);
DTMEditFunctionSignatureDialog(PluginTool pluginTool, String title, Category category,
FunctionDefinition functionDefinition) {
super(pluginTool, title, false, false, false);
this.functionDefinition = functionDefinition;
this.category = category;
this.functionDefinitionDataType = functionDefinition;
if (functionDefinitionDataType != null) {
setCallingConvention(
functionDefinitionDataType.getGenericCallingConvention().toString());
}
this.oldSignature = buildSignature();
}
@Override
protected void installCallingConventionWidget(JPanel parentPanel) {
callingConventionComboBox = new GhidraComboBox<>();
GenericCallingConvention[] values = GenericCallingConvention.values();
String[] choices = new String[values.length];
for (int i = 0; i < values.length; i++) {
choices[i] = values[i].toString();
}
setCallingConventionChoices(choices);
parentPanel.add(new GLabel("Calling Convention:"));
parentPanel.add(callingConventionComboBox);
}
@Override
protected void installInlineWidget(JPanel parentPanel) {
inlineCheckBox = new GCheckBox("Inline");
}
@Override
protected void installNoReturnWidget(JPanel parentPanel) {
noReturnCheckBox = new GCheckBox("No Return");
}
@Override
protected void installCallFixupWidget(JPanel parentPanel) {
// don't add this panel
}
@Override
protected void setCallingConvention(String callingConvention) {
ComboBoxModel<?> model = callingConventionComboBox.getModel();
int size = model.getSize();
for (int i = 0; i < size; i++) {
Object item = model.getElementAt(i);
if (item.equals(callingConvention)) {
callingConventionComboBox.setSelectedItem(callingConvention);
return;
private FunctionSignature buildSignature() {
if (functionDefinition != null) {
if (category.getDataTypeManager() != functionDefinition.getDataTypeManager()) {
throw new IllegalArgumentException(
"functionDefinition and category must have same Datatypemanager");
}
return functionDefinition;
}
return new FunctionDefinitionDataType("newFunction");
}
callingConventionComboBox.setSelectedItem(GenericCallingConvention.unknown);
@Override
protected String[] getSupportedCallFixupNames() {
return null; // Call fixup not supported on FunctionDefinition
}
@Override
protected String getCallFixupName() {
return null; // Call fixup not supported on FunctionDefinition
}
@Override
protected FunctionSignature getFunctionSignature() {
return oldSignature;
}
@Override
protected String getPrototypeString() {
return getFunctionSignature().getPrototypeString();
}
@Override
protected String getCallingConventionName() {
return getFunctionSignature().getGenericCallingConvention().toString();
}
@Override
protected List<String> getCallingConventionNames() {
GenericCallingConvention[] values = GenericCallingConvention.values();
List<String> choices = new ArrayList<>();
for (GenericCallingConvention value : values) {
choices.add(value.toString());
}
return choices;
}
@Override
protected DataTypeManager getDataTypeManager() {
return category.getDataTypeManager();
}
@Override
@@ -657,9 +617,9 @@ public class DataTypeEditorManager
GenericCallingConvention.getGenericCallingConvention(getCallingConvention());
newDefinition.setGenericCallingConvention(callingConvention);
DataTypeManager manager = category.getDataTypeManager();
DataTypeManager manager = getDataTypeManager();
SourceArchive sourceArchive = manager.getLocalSourceArchive();
if (functionDefinitionDataType == null) {
if (functionDefinition == null) {
newDefinition.setSourceArchive(sourceArchive);
newDefinition.setCategoryPath(category.getCategoryPath());
int id = manager.startTransaction("Create Function Definition");
@@ -669,14 +629,14 @@ public class DataTypeEditorManager
else {
int id = manager.startTransaction("Edit Function Definition");
try {
if (!functionDefinitionDataType.getName().equals(newDefinition.getName())) {
functionDefinitionDataType.setName(newDefinition.getName());
if (!functionDefinition.getName().equals(newDefinition.getName())) {
functionDefinition.setName(newDefinition.getName());
}
functionDefinitionDataType.setArguments(newDefinition.getArguments());
functionDefinitionDataType.setGenericCallingConvention(
functionDefinition.setArguments(newDefinition.getArguments());
functionDefinition.setGenericCallingConvention(
newDefinition.getGenericCallingConvention());
functionDefinitionDataType.setReturnType(newDefinition.getReturnType());
functionDefinitionDataType.setVarArgs(newDefinition.hasVarArgs());
functionDefinition.setReturnType(newDefinition.getReturnType());
functionDefinition.setVarArgs(newDefinition.hasVarArgs());
}
catch (InvalidNameException | DuplicateNameException e) {
// not sure why we are squashing this? ...assuming this can't happen
@@ -0,0 +1,435 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.function;
import java.awt.Component;
import java.awt.event.ItemEvent;
import java.util.List;
import javax.swing.*;
import docking.DialogComponentProvider;
import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.label.GDLabel;
import docking.widgets.label.GLabel;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.cparser.C.ParseException;
import ghidra.app.util.parser.FunctionSignatureParser;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.FunctionDefinitionDataType;
import ghidra.program.model.listing.FunctionSignature;
import ghidra.util.exception.CancelledException;
/**
* <code>EditFunctionSignatureDialog</code> provides an abstract implementation
* a function signature editor. Use of this editor requires the presence of the tool-based
* datatype manager service.
*/
public abstract class AbstractEditFunctionSignatureDialog extends DialogComponentProvider {
private static final String NONE_CHOICE = "-NONE-";
private static int SIGNATURE_COLUMNS = 60;
protected JLabel signatureLabel;
protected JTextField signatureField;
protected JComboBox<String> callingConventionComboBox;
protected JComboBox<String> callFixupComboBox;
protected JCheckBox inlineCheckBox;
protected JCheckBox noReturnCheckBox;
protected boolean allowInLine;
protected boolean allowNoReturn;
protected boolean allowCallFixup;
protected PluginTool tool;
// Due to delayed initialization and tests not actually displaying dialog
// we will track function info initialization
boolean initialized = false;
/**
* Abstract function signature editor
*
* @param tool A reference to the active tool.
* @param title The title of the dialog.
* @param allowInLine true if in-line attribute control should be included
* @param allowNoReturn true if no-return attribute control should be added
* @param allowCallFixup true if call-fixup choice should be added
*/
public AbstractEditFunctionSignatureDialog(PluginTool tool, String title, boolean allowInLine,
boolean allowNoReturn, boolean allowCallFixup) {
super(title, true, true, true, false);
this.tool = tool;
this.allowInLine = allowInLine;
this.allowNoReturn = allowNoReturn;
this.allowCallFixup = allowCallFixup;
addWorkPanel(buildMainPanel());
addOKButton();
addCancelButton();
setDefaultButton(okButton);
setRememberSize(true);
}
@Override
public JComponent getComponent() {
setFunctionInfo(); //delay update for after construction
return super.getComponent();
}
/**
* @return DataTypeManager associated with function or function definition
*/
protected abstract DataTypeManager getDataTypeManager();
/**
* @return optional initial function signature which can assist parse with
* identifying referenced datatypes within signature
*/
protected abstract FunctionSignature getFunctionSignature();
/**
* @return the initial signature string for the dialog
*/
protected abstract String getPrototypeString();
/**
* @return initial calling convention name
*/
protected abstract String getCallingConventionName();
/**
* @return list of acceptable calling convention names
*/
protected abstract List<String> getCallingConventionNames();
/**
* @return initial in-line attribute value
*/
protected boolean isInline() {
return false;
}
/**
* @return initial no-return attribute value
*/
protected boolean hasNoReturn() {
return false;
}
/**
* @return initial call-fixup name or null if n/a
*/
protected abstract String getCallFixupName();
/**
* @return array of allowed call fixup names or null
*/
protected abstract String[] getSupportedCallFixupNames();
/**
* Method must be invoked following construction to fetch function info
* and update components.
*/
private void setFunctionInfo() {
if (initialized) {
return;
}
initialized = true;
signatureField.setText(getPrototypeString());
setCallingConventionChoices();
callingConventionComboBox.setSelectedItem(getCallingConventionName());
if (allowInLine) {
inlineCheckBox.setSelected(isInline());
}
if (allowNoReturn) {
noReturnCheckBox.setSelected(hasNoReturn());
}
if (allowCallFixup) {
setCallFixupChoices();
String callFixupName = getCallFixupName();
if (callFixupName != null) {
callFixupComboBox.setSelectedItem(callFixupName);
}
}
}
private JPanel buildMainPanel() {
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 2));
mainPanel.add(buildSignaturePanel());
mainPanel.add(buildAttributePanel());
if (allowCallFixup) {
installCallFixupWidget(mainPanel);
}
return mainPanel;
}
private void installCallFixupWidget(JPanel parentPanel) {
JPanel callFixupPanel = buildCallFixupPanel();
parentPanel.add(callFixupPanel != null ? callFixupPanel : buildSpacerPanel());
}
private JPanel buildSignaturePanel() {
JPanel signaturePanel = new JPanel();
signaturePanel.setLayout(new BoxLayout(signaturePanel, BoxLayout.X_AXIS));
signatureField = new JTextField(SIGNATURE_COLUMNS);
signatureLabel = new GDLabel("Signature:");
signaturePanel.add(signatureLabel);
signaturePanel.add(signatureField);
signaturePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
return signaturePanel;
}
private Component buildSpacerPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.add(Box.createVerticalStrut(20));
return panel;
}
private JPanel buildAttributePanel() {
JPanel attributePanel = new JPanel();
attributePanel.setLayout(new BoxLayout(attributePanel, BoxLayout.X_AXIS));
attributePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
installCallingConventionWidget(attributePanel);
if (allowInLine) {
installInlineWidget(attributePanel);
}
if (allowNoReturn) {
installNoReturnWidget(attributePanel);
}
attributePanel.add(Box.createGlue());
return attributePanel;
}
private void installCallingConventionWidget(JPanel parentPanel) {
callingConventionComboBox = new GhidraComboBox<>();
parentPanel.add(new GLabel("Calling Convention:"));
parentPanel.add(callingConventionComboBox);
}
private void installInlineWidget(JPanel parentPanel) {
inlineCheckBox = new GCheckBox("Inline");
inlineCheckBox.addChangeListener(e -> {
if (inlineCheckBox.isSelected() && callFixupComboBox != null) {
callFixupComboBox.setSelectedItem(NONE_CHOICE);
}
});
parentPanel.add(inlineCheckBox);
}
private void installNoReturnWidget(JPanel parentPanel) {
noReturnCheckBox = new GCheckBox("No Return");
parentPanel.add(noReturnCheckBox);
}
private JPanel buildCallFixupPanel() {
if (allowCallFixup) {
return null;
}
JPanel callFixupPanel = new JPanel();
callFixupPanel.setLayout(new BoxLayout(callFixupPanel, BoxLayout.X_AXIS));
callFixupComboBox = new GhidraComboBox<>();
callFixupComboBox.addItemListener(e -> {
if (e.getStateChange() == ItemEvent.DESELECTED) {
return;
}
if (!NONE_CHOICE.equals(e.getItem())) {
inlineCheckBox.setSelected(false);
}
});
callFixupPanel.add(new GLabel("Call-Fixup:"));
callFixupPanel.add(callFixupComboBox);
callFixupPanel.add(Box.createGlue());
callFixupPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
return callFixupPanel;
}
/**
* @return plugin tool for which dialog was constructed
*/
protected PluginTool getTool() {
return tool;
}
private String getSignature() {
return signatureField.getText();
}
private void setCallingConventionChoices() {
callingConventionComboBox.removeAllItems();
for (String element : getCallingConventionNames()) {
callingConventionComboBox.addItem(element);
}
}
/**
* @return current calling convention selection from dialog
*/
protected String getCallingConvention() {
return (String) callingConventionComboBox.getSelectedItem();
}
/**
* @return current in-line attribute value from dialog
*/
protected boolean isInlineSelected() {
return inlineCheckBox != null ? inlineCheckBox.isSelected() : false;
}
/**
* @return current no-return attribute value from dialog
*/
protected boolean hasNoReturnSelected() {
return noReturnCheckBox != null ? noReturnCheckBox.isSelected() : false;
}
private void setCallFixupChoices() {
String[] callFixupNames = getSupportedCallFixupNames();
callFixupComboBox.addItem(NONE_CHOICE);
if (callFixupNames != null) {
for (String element : callFixupNames) {
callFixupComboBox.addItem(element);
}
}
}
/**
* @return current call fixup selection from dialog or null
*/
protected String getCallFixupSelection() {
if (callFixupComboBox != null) {
String callFixup = (String) callFixupComboBox.getSelectedItem();
if (callFixup != null && !NONE_CHOICE.equals(callFixup)) {
return callFixup;
}
}
return null;
}
/**
* This method gets called when the user clicks on the OK Button. The base
* class calls this method. This method will invoke {@link #applyChanges()}
* and close dialog if that method returns true. If false is returned, the
* {@link #applyChanges()} method should display a status message to indicate
* the failure.
*/
@Override
protected void okCallback() {
// only close the dialog if the user made valid changes
try {
if (applyChanges()) {
close();
}
}
catch (CancelledException e) {
// ignore - do not close
}
}
@Override
protected void cancelCallback() {
setStatusText("");
close();
}
/**
* Called when the user initiates changes that need to be applied to the
* underlying function or function definition
*
* @return true if applied successfully, otherwise false which will keep
* dialog displayed (a status message should bet set)
* @throws CancelledException if operation cancelled by user
*/
protected abstract boolean applyChanges() throws CancelledException;
/**
* Perform parse of current user-specified function signature (see {@link #getSignature()})
* and return valid {@link FunctionDefinitionDataType} if parse successful.
* @return function definition data type if parse successful, otherwise null
* @throws CancelledException if function signature entry cancelled
*/
protected final FunctionDefinitionDataType parseSignature() throws CancelledException {
setFunctionInfo(); // needed for testing which never shows dialog
FunctionSignatureParser parser = new FunctionSignatureParser(
getDataTypeManager(), tool.getService(DataTypeManagerService.class));
try {
// FIXME: Parser returns FunctionDefinition which only supports GenericCallingConventions
return parser.parse(getFunctionSignature(), getSignature());
}
catch (ParseException e) {
setStatusText("Invalid Signature: " + e.getMessage());
}
return null;
}
/**
* Determine if user-specified function signature has been modified from original
* @return true if modified signature has been entered, else false
*/
protected final boolean isSignatureChanged() {
return !getSignature().equals(getPrototypeString());
}
/**
* Determine if user has changed the selected calling convention from the original
* @return true if a change in the selected calling convention has been made
*/
protected final boolean isCallingConventionChanged() {
String current = getCallingConventionName();
if (current == null && this.getCallingConvention() == null) {
return false;
}
if (current == null && this.getCallingConvention().equals("default")) {
return false;
}
if (current == null && this.getCallingConvention().equals("unknown")) {
return false;
}
if (current == null) {
return true;
}
if (current.equals(getCallingConvention())) {
return false;
}
return true;
}
@Override
protected void dialogShown() {
signatureField.selectAll();
}
}
@@ -88,7 +88,7 @@ public class FunctionDataTypeHTMLRepresentation extends HTMLDataTypeRepresentati
GenericCallingConvention genericCallingConvention =
functionDefinition.getGenericCallingConvention();
String modifier = genericCallingConvention != GenericCallingConvention.unknown
? (" " + genericCallingConvention.name())
? (" " + genericCallingConvention.getDeclarationName())
: "";
return new TextLine(
HTMLUtilities.friendlyEncodeHTML(returnDataType.getDisplayName()) + modifier);
@@ -50,7 +50,7 @@ import ghidra.app.plugin.core.datamgr.actions.CreateTypeDefDialog;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.app.plugin.core.function.EditFunctionSignatureDialog;
import ghidra.app.plugin.core.function.AbstractEditFunctionSignatureDialog;
import ghidra.app.plugin.core.programtree.ProgramTreePlugin;
import ghidra.app.services.ProgramManager;
import ghidra.app.util.datatype.DataTypeSelectionEditor;
@@ -695,11 +695,9 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
DataType dt = iter.next();
listTwo.add(dt);
}
for (int i = 0; i < listOne.size(); i++) {
DataType dt = listOne.get(i);
for (DataType dt : listOne) {
boolean found = false;
for (int j = 0; j < listTwo.size(); j++) {
DataType dt2 = listTwo.get(j);
for (DataType dt2 : listTwo) {
if (dt.isEquivalent(dt2)) {
found = true;
break;
@@ -807,8 +805,8 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
assertTrue(action.isEnabledForContext(treeContext));
performAction(action, treeContext, false);
EditFunctionSignatureDialog dialog =
waitForDialogComponent(EditFunctionSignatureDialog.class);
AbstractEditFunctionSignatureDialog dialog =
waitForDialogComponent(AbstractEditFunctionSignatureDialog.class);
JTextField textField = (JTextField) getInstanceField("signatureField", dialog);
setText(textField, newSignature);
@@ -29,59 +29,12 @@ import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;
public class OverridePrototypeAction extends AbstractDecompilerAction {
public class ProtoOverrideDialog extends EditFunctionSignatureDialog {
private FunctionDefinition functionDefinition;
public FunctionDefinition getFunctionDefinition() {
return functionDefinition;
}
public ProtoOverrideDialog(PluginTool tool, Function func, String signature, String conv) {
super(tool, "Override Signature", func);
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionOverrideSignature"));
setSignature(signature);
setCallingConvention(conv);
}
/**
* This method gets called when the user clicks on the OK Button. The base
* class calls this method.
*/
@Override
protected void okCallback() {
// only close the dialog if the user made valid changes
if (parseFunctionDefinition()) {
close();
}
}
private boolean parseFunctionDefinition() {
functionDefinition = null;
try {
functionDefinition = parseSignature();
}
catch (CancelledException e) {
// ignore
}
if (functionDefinition == null) {
return false;
}
GenericCallingConvention convention =
GenericCallingConvention.guessFromName(getCallingConvention());
functionDefinition.setGenericCallingConvention(convention);
return true;
}
}
public OverridePrototypeAction() {
super("Override Signature");
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionOverrideSignature"));
@@ -183,11 +136,30 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
return null;
}
private String generateSignature(PcodeOp op, String name) {
private String generateSignature(PcodeOp op, String name, Function calledfunc) {
// TODO: If an override has already be placed-down it should probably be used
// for the initial signature. HighFunction does not make it easy to grab
// existing override prototype
if (calledfunc != null) {
SourceType signatureSource = calledfunc.getSignatureSource();
if (signatureSource == SourceType.DEFAULT || signatureSource == SourceType.ANALYSIS) {
calledfunc = null; // ignore
}
}
StringBuffer buf = new StringBuffer();
Varnode vn = op.getOutput();
DataType dt = null;
if (vn != null) {
if (calledfunc != null) {
dt = calledfunc.getReturnType();
if (Undefined.isUndefined(dt)) {
dt = null;
}
}
if (dt == null && vn != null) {
dt = vn.getHigh().getDataType();
}
if (dt != null) {
@@ -198,26 +170,48 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
}
buf.append(' ').append(name).append('(');
for (int i = 1; i < op.getNumInputs(); ++i) {
vn = op.getInput(i);
dt = null;
if (vn != null) {
dt = vn.getHigh().getDataType();
}
if (dt != null) {
buf.append(dt.getDisplayName());
}
else {
buf.append("BAD");
}
if (i != op.getNumInputs() - 1) {
buf.append(',');
int index = 1;
if (calledfunc != null) {
for (Parameter p : calledfunc.getParameters()) {
String dtName = getInputDataTypeName(op, index, p.getDataType());
if (index++ != 1) {
buf.append(", ");
}
buf.append(dtName);
if (p.getSource() != SourceType.DEFAULT) {
buf.append(' ');
buf.append(p.getName());
}
}
}
for (int i = index; i < op.getNumInputs(); ++i) {
if (i != 1) {
buf.append(", ");
}
buf.append(getInputDataTypeName(op, i, null));
}
buf.append(')');
return buf.toString();
}
private String getInputDataTypeName(PcodeOp op, int inIndex, DataType preferredDt) {
if (preferredDt != null && !Undefined.isUndefined(preferredDt)) {
return preferredDt.getDisplayName();
}
Varnode vn = op.getInput(inIndex);
DataType dt = null;
if (vn != null) {
dt = vn.getHigh().getDataType();
}
if (dt != null) {
return dt.getDisplayName();
}
return "BAD";
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
@@ -257,9 +251,10 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
conv = calledfunc.getCallingConventionName();
}
String signature = generateSignature(op, name);
String signature = generateSignature(op, name, calledfunc);
PluginTool tool = context.getTool();
ProtoOverrideDialog dialog = new ProtoOverrideDialog(tool, func, signature, conv);
ProtoOverrideDialog dialog =
new ProtoOverrideDialog(tool, calledfunc != null ? calledfunc : func, signature, conv);
tool.showDialog(dialog);
FunctionDefinition fdef = dialog.getFunctionDefinition();
if (fdef == null) {
@@ -279,4 +274,71 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
program.endTransaction(transaction, commit);
}
}
/**
* <code>ProtoOverrideDialog</code> provides the ability to edit the
* function signature associated with a specific function definition override
* at a sub-function callsite.
* Use of this editor requires the presence of the tool-based datatype manager service.
*/
private class ProtoOverrideDialog extends EditFunctionSignatureDialog {
private FunctionDefinition functionDefinition;
private final String initialSignature;
private final String initialConvention;
/**
* Construct signature override for called function
* @param tool active tool
* @param func function from which program access is achieved and supply of preferred
* datatypes when parsing signature
* @param signature initial prototype signature to be used
* @param conv initial calling convention
*/
public ProtoOverrideDialog(PluginTool tool, Function func, String signature, String conv) {
super(tool, "Override Signature", func, false, false, false);
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ActionOverrideSignature"));
this.initialSignature = signature;
this.initialConvention = conv;
}
@Override
protected String getPrototypeString() {
return initialSignature;
}
@Override
protected String getCallingConventionName() {
return initialConvention;
}
@Override
protected boolean applyChanges() throws CancelledException {
return parseFunctionDefinition();
}
private boolean parseFunctionDefinition() {
functionDefinition = null;
try {
functionDefinition = parseSignature();
}
catch (CancelledException e) {
// ignore
}
if (functionDefinition == null) {
return false;
}
GenericCallingConvention convention =
GenericCallingConvention.guessFromName(getCallingConvention());
functionDefinition.setGenericCallingConvention(convention);
return true;
}
public FunctionDefinition getFunctionDefinition() {
return functionDefinition;
}
}
}