Global variable actions and Show in action and refactoring (#2295)

* Show in action for global variables and functions
* Copy address of global variable or function referenced by the cursor selection
* Rename global variable
This commit is contained in:
NIRMAL MANOJ C 2020-07-30 14:11:23 +05:30
parent eef9baa300
commit 37fc01478f
6 changed files with 229 additions and 32 deletions

View File

@ -927,12 +927,19 @@ void MainWindow::showMemoryWidget(MemoryWidgetType type)
memoryDockWidget->raiseMemoryWidget();
}
QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address)
QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address, AddressTypeHint addressType)
{
QMenu *menu = new QMenu(parent);
// Memory dock widgets
for (auto &dock : dockWidgets) {
if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {
if (memoryWidget->getType() == MemoryWidgetType::Graph
|| memoryWidget->getType() == MemoryWidgetType::Decompiler)
{
if (addressType == AddressTypeHint::Data) {
continue;
}
}
QAction *action = new QAction(memoryWidget->windowTitle(), menu);
connect(action, &QAction::triggered, this, [memoryWidget, address]() {
memoryWidget->getSeekable()->seek(address);
@ -965,7 +972,9 @@ QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address)
menu->addAction(action);
};
createAddNewWidgetAction(tr("New disassembly"), MemoryWidgetType::Disassembly);
createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph);
if (addressType != AddressTypeHint::Data) {
createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph);
}
createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump);
return menu;

View File

@ -121,8 +121,8 @@ public:
QString getUniqueObjectName(const QString &widgetType) const;
void showMemoryWidget();
void showMemoryWidget(MemoryWidgetType type);
QMenu *createShowInMenu(QWidget *parent, RVA address);
enum class AddressTypeHint { Function, Data, Unknown };
QMenu *createShowInMenu(QWidget *parent, RVA address, AddressTypeHint addressType = AddressTypeHint::Unknown);
void setCurrentMemoryWidget(MemoryDockWidget* memoryWidget);
MemoryDockWidget* getLastMemoryWidget();

View File

@ -3,6 +3,7 @@
#include "MainWindow.h"
#include "dialogs/BreakpointsDialog.h"
#include "dialogs/CommentsDialog.h"
#include "common/Configuration.h"
#include <QtCore>
#include <QShortcut>
@ -14,27 +15,36 @@
DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow)
: QMenu(parent),
curHighlightedWord(QString()),
offset(0),
isTogglingBreakpoints(false),
mainWindow(mainWindow),
annotationHere(nullptr),
actionCopy(tr("Copy"), this),
actionCopyInstructionAddress(tr("Copy instruction address (<address>)"), this),
actionCopyReferenceAddress(tr("Copy address of [flag] (<address>)"), this),
actionShowInSubmenu(tr("Show in"), this),
actionAddComment(tr("Add Comment"), this),
actionDeleteComment(tr("Delete comment"), this),
actionRenameThingHere(tr("Rename function at cursor"), this),
actionDeleteName(tr("Delete <name>"), this),
actionToggleBreakpoint(tr("Add/remove breakpoint"), this),
actionAdvancedBreakpoint(tr("Advanced breakpoint"), this),
breakpointsInLineMenu(new QMenu(this)),
actionContinueUntil(tr("Continue until line"), this),
actionSetPC(tr("Set PC"), this)
{
setActionCopy();
setActionCopy(); // Sets all three copy actions
addSeparator();
setActionShowInSubmenu();
copySeparator = addSeparator();
setActionAddComment();
setActionDeleteComment();
setActionRenameThingHere();
setActionDeleteName();
addSeparator();
addBreakpointMenu();
@ -57,6 +67,11 @@ void DecompilerContextMenu::setAnnotationHere(RCodeAnnotation *annotation)
this->annotationHere = annotation;
}
void DecompilerContextMenu::setCurHighlightedWord(QString word)
{
this->curHighlightedWord = word;
}
void DecompilerContextMenu::setOffset(RVA offset)
{
this->offset = offset;
@ -86,11 +101,6 @@ void DecompilerContextMenu::setupBreakpointsInLineMenu()
}
}
void DecompilerContextMenu::setCanCopy(bool enabled)
{
actionCopy.setVisible(enabled);
}
void DecompilerContextMenu::setShortcutContextInActions(QMenu *menu)
{
for (QAction *action : menu->actions()) {
@ -117,11 +127,14 @@ bool DecompilerContextMenu::getIsTogglingBreakpoints()
void DecompilerContextMenu::aboutToHideSlot()
{
actionAddComment.setVisible(true);
actionRenameThingHere.setVisible(true);
actionDeleteName.setVisible(false);
}
void DecompilerContextMenu::aboutToShowSlot()
{
if (this->firstOffsetInLine != RVA_MAX) {
actionShowInSubmenu.setVisible(true);
QString comment = Core()->cmdRawAt("CC.", this->firstOffsetInLine);
actionAddComment.setVisible(true);
if (comment.isEmpty()) {
@ -132,6 +145,7 @@ void DecompilerContextMenu::aboutToShowSlot()
actionAddComment.setText(tr("Edit Comment"));
}
} else {
actionShowInSubmenu.setVisible(false);
actionAddComment.setVisible(false);
actionDeleteComment.setVisible(false);
}
@ -163,22 +177,82 @@ void DecompilerContextMenu::aboutToShowSlot()
QString progCounterName = Core()->getRegisterName("PC").toUpper();
actionSetPC.setText(tr("Set %1 here").arg(progCounterName));
if (!annotationHere) { // To be considered as invalid
if (!annotationHere
|| annotationHere->type ==
R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) { // To be considered as invalid
actionRenameThingHere.setVisible(false);
copySeparator->setVisible(false);
} else {
actionRenameThingHere.setVisible(true);
actionRenameThingHere.setText(tr("Rename function %1").arg(QString(
annotationHere->function_name.name)));
copySeparator->setVisible(true);
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
actionRenameThingHere.setVisible(true);
actionRenameThingHere.setText(tr("Rename function %1").arg(QString(
annotationHere->reference.name)));
}
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) {
RFlagItem *flagDetails = r_flag_get_i(Core()->core()->flags, annotationHere->reference.offset);
if (flagDetails) {
actionRenameThingHere.setText(tr("Rename %1").arg(QString(flagDetails->name)));
actionDeleteName.setText(tr("Remove %1").arg(QString(flagDetails->name)));
actionDeleteName.setVisible(true);
} else {
actionRenameThingHere.setText(tr("Add name to %1").arg(curHighlightedWord));
}
}
}
actionCopyInstructionAddress.setText(tr("Copy instruction address (%1)").arg(RAddressString(
offset)));
bool isReference = false;
if (annotationHere) {
isReference = (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME);
}
if (isReference) {
actionCopyReferenceAddress.setVisible(true);
RVA referenceAddr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(Core()->core()->flags, referenceAddr);
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
actionCopyReferenceAddress.setText(tr("Copy address of %1 (%2)").arg
(QString(annotationHere->reference.name), RAddressString(referenceAddr)));
} else if (flagDetails) {
actionCopyReferenceAddress.setText(tr("Copy address of %1 (%2)").arg
(flagDetails->name, RAddressString(referenceAddr)));
} else {
actionCopyReferenceAddress.setText(tr("Copy address (%1)").arg(RAddressString(referenceAddr)));
}
} else {
actionCopyReferenceAddress.setVisible(false);
}
if (actionShowInSubmenu.menu() != nullptr) {
actionShowInSubmenu.menu()->deleteLater();
}
actionShowInSubmenu.setMenu(mainWindow->createShowInMenu(this, offset));
updateTargetMenuActions();
}
// Set up actions
void DecompilerContextMenu::setActionCopy()
void DecompilerContextMenu::setActionCopy() // Set all three copy actions
{
connect(&actionCopy, &QAction::triggered, this, &DecompilerContextMenu::actionCopyTriggered);
addAction(&actionCopy);
actionCopy.setShortcut(QKeySequence::Copy);
connect(&actionCopyInstructionAddress, &QAction::triggered, this,
&DecompilerContextMenu::actionCopyInstructionAddressTriggered);
addAction(&actionCopyInstructionAddress);
connect(&actionCopyReferenceAddress, &QAction::triggered, this,
&DecompilerContextMenu::actionCopyReferenceAddressTriggered);
addAction(&actionCopyReferenceAddress);
actionCopyReferenceAddress.setShortcut({Qt::CTRL + Qt::SHIFT + Qt::Key_C});
}
void DecompilerContextMenu::setActionShowInSubmenu()
{
addAction(&actionShowInSubmenu);
}
void DecompilerContextMenu::setActionAddComment()
@ -198,12 +272,20 @@ void DecompilerContextMenu::setActionDeleteComment()
void DecompilerContextMenu::setActionRenameThingHere()
{
actionRenameThingHere.setShortcut({Qt::SHIFT + Qt::Key_N});
actionRenameThingHere.setShortcut({Qt::Key_N});
connect(&actionRenameThingHere, &QAction::triggered, this,
&DecompilerContextMenu::actionRenameThingHereTriggered);
addAction(&actionRenameThingHere);
}
void DecompilerContextMenu::setActionDeleteName()
{
connect(&actionDeleteName, &QAction::triggered, this,
&DecompilerContextMenu::actionDeleteNameTriggered);
addAction(&actionDeleteName);
actionDeleteName.setVisible(false);
}
void DecompilerContextMenu::setActionToggleBreakpoint()
{
connect(&actionToggleBreakpoint, &QAction::triggered, this,
@ -236,6 +318,18 @@ void DecompilerContextMenu::actionCopyTriggered()
emit copy();
}
void DecompilerContextMenu::actionCopyInstructionAddressTriggered()
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(RAddressString(offset));
}
void DecompilerContextMenu::actionCopyReferenceAddressTriggered()
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(RAddressString(annotationHere->reference.offset));
}
void DecompilerContextMenu::actionAddCommentTriggered()
{
CommentsDialog::addOrEditComment(this->firstOffsetInLine, this);
@ -251,29 +345,53 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
if (!annotationHere) {
return;
}
RCoreLocked core = Core()->core();
bool ok;
auto type = annotationHere->type;
if (type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
QString currentName(annotationHere->function_name.name);
RVA func_addr = annotationHere->function_name.offset;
QString currentName(annotationHere->reference.name);
RVA func_addr = annotationHere->reference.offset;
RAnalFunction *func = Core()->functionAt(func_addr);
if (func == NULL) {
QString function_name = QInputDialog::getText(this, tr("Define this function at %2").arg(RAddressString(func_addr)),
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
QString function_name = QInputDialog::getText(this,
tr("Define this function at %2").arg(RAddressString(func_addr)),
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
if (ok && !function_name.isEmpty()) {
Core()->createFunctionAt(func_addr, function_name);
}
} else {
QString newName = QInputDialog::getText(this, tr("Rename function %2").arg(currentName),
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
tr("Function name:"), QLineEdit::Normal, currentName, &ok);
if (ok && !newName.isEmpty()) {
Core()->renameFunction(func_addr, newName);
Core()->renameFunction(func_addr, newName);
}
}
} else if (type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) {
RVA var_addr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(core->flags, var_addr);
if (flagDetails) {
QString newName = QInputDialog::getText(this, tr("Rename %2").arg(flagDetails->name),
tr("Enter name"), QLineEdit::Normal, flagDetails->name, &ok);
if (ok && !newName.isEmpty()) {
Core()->renameFlag(flagDetails->name, newName);
}
} else {
QString newName = QInputDialog::getText(this, tr("Add name to %2").arg(curHighlightedWord),
tr("Enter name"), QLineEdit::Normal, curHighlightedWord, &ok);
if (ok && !newName.isEmpty()) {
Core()->addFlag(var_addr, newName, 1);
}
}
}
}
void DecompilerContextMenu::actionDeleteNameTriggered()
{
Core()->delFlag(annotationHere->reference.offset);
}
void DecompilerContextMenu::actionToggleBreakpointTriggered()
{
if (!this->availableBreakpoints.isEmpty()) {
@ -335,3 +453,43 @@ void DecompilerContextMenu::addDebugMenu()
setActionSetPC();
debugMenu->addAction(&actionSetPC);
}
void DecompilerContextMenu::updateTargetMenuActions()
{
for (auto action : showTargetMenuActions) {
removeAction(action);
auto menu = action->menu();
if (menu) {
menu->deleteLater();
}
action->deleteLater();
}
showTargetMenuActions.clear();
RCoreLocked core = Core()->core();
if (annotationHere && (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME)) {
QString name;
QMenu *menu;
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) {
menu = mainWindow->createShowInMenu(this, annotationHere->reference.offset, MainWindow::AddressTypeHint::Data);
RVA var_addr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(core->flags, var_addr);
if (flagDetails) {
name = tr("Show %1 in").arg(flagDetails->name);
} else {
name = tr("Show %1 in").arg(RAddressString(annotationHere->reference.offset));
}
} else if (annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
menu = mainWindow->createShowInMenu(this, annotationHere->reference.offset,
MainWindow::AddressTypeHint::Function);
name = tr("%1 (%2)").arg(QString(annotationHere->reference.name),
RAddressString(annotationHere->reference.offset));
}
auto action = new QAction(name, this);
showTargetMenuActions.append(action);
action->setMenu(menu);
insertActions(copySeparator, showTargetMenuActions);
}
}

View File

@ -22,8 +22,8 @@ signals:
void copy();
public slots:
void setCurHighlightedWord(QString word);
void setOffset(RVA offset);
void setCanCopy(bool enabled);
void setFirstOffsetInLine(RVA firstOffset);
void setAvailableBreakpoints(QVector<RVA> offsetList);
@ -33,11 +33,14 @@ private slots:
void aboutToHideSlot();
void actionCopyTriggered();
void actionCopyInstructionAddressTriggered();
void actionCopyReferenceAddressTriggered();
void actionAddCommentTriggered();
void actionDeleteCommentTriggered();
void actionRenameThingHereTriggered();
void actionDeleteNameTriggered();
void actionToggleBreakpointTriggered();
void actionAdvancedBreakpointTriggered();
@ -47,6 +50,7 @@ private slots:
private:
// Private variables
QString curHighlightedWord;
RVA offset;
RVA firstOffsetInLine;
bool isTogglingBreakpoints;
@ -56,12 +60,18 @@ private:
RCodeAnnotation *annotationHere;
QAction actionCopy;
QAction actionCopyInstructionAddress;
QAction actionCopyReferenceAddress;
QAction *copySeparator;
QAction actionShowInSubmenu;
QList<QAction *> showTargetMenuActions;
QAction actionAddComment;
QAction actionDeleteComment;
QAction actionRenameThingHere;
QAction actionDeleteName;
QMenu *breakpointMenu;
QAction actionToggleBreakpoint;
@ -81,10 +91,13 @@ private:
// Set actions
void setActionCopy();
void setActionShowInSubmenu();
void setActionAddComment();
void setActionDeleteComment();
void setActionRenameThingHere();
void setActionDeleteName();
void setActionToggleBreakpoint();
void setActionAdvancedBreakpoint();
@ -113,6 +126,7 @@ private:
// QVector<ThingUsedHere> getThingUsedHere(RVA offset);
// void updateTargetMenuActions(const QVector<ThingUsedHere> &targets);
void updateTargetMenuActions();
};
#endif // DECOMPILERCONTEXTMENU_H

View File

@ -12,6 +12,7 @@
#include <QTextEdit>
#include <QPlainTextEdit>
#include <QTextBlock>
#include <QClipboard>
#include <QObject>
#include <QTextBlockUserData>
@ -35,7 +36,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(Config(), &Configuration::fontsUpdated, this, &DecompilerWidget::fontsUpdatedSlot);
connect(Config(), &Configuration::colorsUpdated, this, &DecompilerWidget::colorsUpdatedSlot);
connect(Core(), &CutterCore::registersChanged, this, &DecompilerWidget::highlightPC);
connect(mCtxMenu, &DecompilerContextMenu::copy, ui->textEdit, &QPlainTextEdit::copy);
connect(mCtxMenu, &DecompilerContextMenu::copy, this, &DecompilerWidget::copy);
decompiledFunctionAddr = RVA_INVALID;
decompilerWasBusy = false;
@ -87,8 +88,6 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(ui->textEdit, &QWidget::customContextMenuRequested,
this, &DecompilerWidget::showDisasContextMenu);
// refresh the widget when an action in this menu is triggered
connect(mCtxMenu, &QMenu::triggered, this, &DecompilerWidget::refreshDecompiler);
connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::setInfoForBreakpoints);
addActions(mCtxMenu->actions());
@ -344,7 +343,6 @@ void DecompilerWidget::connectCursorPositionChanged(bool disconnect)
void DecompilerWidget::cursorPositionChanged()
{
mCtxMenu->setCanCopy(ui->textEdit->textCursor().hasSelection());
// Do not perform seeks along with the cursor while selecting multiple lines
if (!ui->textEdit->textCursor().selectedText().isEmpty()) {
return;
@ -414,6 +412,7 @@ void DecompilerWidget::updateSelection()
// Highlight all the words in the document same as the current one
cursor.select(QTextCursor::WordUnderCursor);
QString searchString = cursor.selectedText();
mCtxMenu->setCurHighlightedWord(searchString);
extraSelections.append(createSameWordsSelections(ui->textEdit, searchString));
ui->textEdit->setExtraSelections(extraSelections);
@ -438,7 +437,6 @@ void DecompilerWidget::colorsUpdatedSlot()
void DecompilerWidget::showDisasContextMenu(const QPoint &pt)
{
mCtxMenu->exec(ui->textEdit->mapToGlobal(pt));
doRefresh();
}
void DecompilerWidget::seekToReference()
@ -460,7 +458,7 @@ bool DecompilerWidget::eventFilter(QObject *obj, QEvent *event)
if (event->type() == QEvent::MouseButtonPress
&& (obj == ui->textEdit || obj == ui->textEdit->viewport())) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::RightButton) {
if (mouseEvent->button() == Qt::RightButton && !ui->textEdit->textCursor().hasSelection()) {
ui->textEdit->setTextCursor(ui->textEdit->cursorForPosition(mouseEvent->pos()));
return true;
}
@ -509,3 +507,20 @@ bool DecompilerWidget::colorLine(QTextEdit::ExtraSelection extraSelection)
ui->textEdit->setExtraSelections(extraSelections);
return true;
}
void DecompilerWidget::copy()
{
if (ui->textEdit->textCursor().hasSelection()) {
ui->textEdit->copy();
} else {
QTextCursor cursor = ui->textEdit->textCursor();
QClipboard *clipboard = QApplication::clipboard();
cursor.select(QTextCursor::WordUnderCursor);
if (!cursor.selectedText().isEmpty()) {
clipboard->setText(cursor.selectedText());
} else {
cursor.select(QTextCursor::LineUnderCursor);
clipboard->setText(cursor.selectedText());
}
}
}

View File

@ -32,6 +32,7 @@ public slots:
void highlightPC();
private slots:
void copy();
void fontsUpdatedSlot();
void colorsUpdatedSlot();
void refreshDecompiler();