Decompiler Documentation + Clean up (#2374)

This commit is contained in:
NIRMAL MANOJ C 2020-08-15 13:31:27 +05:30
parent 1c86f54c95
commit b7d1059a1b
4 changed files with 206 additions and 75 deletions

View File

@ -17,11 +17,11 @@
DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow) DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow)
: QMenu(parent), : QMenu(parent),
mainWindow(mainWindow),
curHighlightedWord(QString()), curHighlightedWord(QString()),
offset(0), offset(0),
decompiledFunctionAddress(RVA_INVALID), decompiledFunctionAddress(RVA_INVALID),
isTogglingBreakpoints(false), isTogglingBreakpoints(false),
mainWindow(mainWindow),
annotationHere(nullptr), annotationHere(nullptr),
actionCopy(tr("Copy"), this), actionCopy(tr("Copy"), this),
actionCopyInstructionAddress(tr("Copy instruction address (<address>)"), this), actionCopyInstructionAddress(tr("Copy instruction address (<address>)"), this),
@ -48,11 +48,11 @@ DecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWi
setActionAddComment(); setActionAddComment();
setActionDeleteComment(); setActionDeleteComment();
setActionXRefs();
setActionRenameThingHere(); setActionRenameThingHere();
setActionDeleteName(); setActionDeleteName();
setActionXRefs();
setActionEditFunctionVariables(); setActionEditFunctionVariables();
addSeparator(); addSeparator();
@ -73,34 +73,37 @@ DecompilerContextMenu::~DecompilerContextMenu()
void DecompilerContextMenu::setAnnotationHere(RCodeAnnotation *annotation) void DecompilerContextMenu::setAnnotationHere(RCodeAnnotation *annotation)
{ {
this->annotationHere = annotation; annotationHere = annotation;
} }
void DecompilerContextMenu::setCurHighlightedWord(QString word) void DecompilerContextMenu::setCurHighlightedWord(QString word)
{ {
this->curHighlightedWord = word; curHighlightedWord = word;
} }
void DecompilerContextMenu::setOffset(RVA offset) void DecompilerContextMenu::setOffset(RVA newOffset)
{ {
this->offset = offset; offset = newOffset;
// this->actionSetFunctionVarTypes.setVisible(true);
} }
void DecompilerContextMenu::setDecompiledFunctionAddress(RVA functionAddr) void DecompilerContextMenu::setDecompiledFunctionAddress(RVA functionAddr)
{ {
this->decompiledFunctionAddress = functionAddr; decompiledFunctionAddress = functionAddr;
} }
void DecompilerContextMenu::setFirstOffsetInLine(RVA firstOffset) void DecompilerContextMenu::setFirstOffsetInLine(RVA firstOffset)
{ {
this->firstOffsetInLine = firstOffset; firstOffsetInLine = firstOffset;
}
RVA DecompilerContextMenu::getFirstOffsetInLine()
{
return firstOffsetInLine;
} }
void DecompilerContextMenu::setAvailableBreakpoints(QVector<RVA> offsetList) void DecompilerContextMenu::setAvailableBreakpoints(QVector<RVA> offsetList)
{ {
this->availableBreakpoints = offsetList; availableBreakpoints = offsetList;
} }
void DecompilerContextMenu::setupBreakpointsInLineMenu() void DecompilerContextMenu::setupBreakpointsInLineMenu()
@ -130,12 +133,12 @@ void DecompilerContextMenu::setShortcutContextInActions(QMenu *menu)
void DecompilerContextMenu::setIsTogglingBreakpoints(bool isToggling) void DecompilerContextMenu::setIsTogglingBreakpoints(bool isToggling)
{ {
this->isTogglingBreakpoints = isToggling; isTogglingBreakpoints = isToggling;
} }
bool DecompilerContextMenu::getIsTogglingBreakpoints() bool DecompilerContextMenu::getIsTogglingBreakpoints()
{ {
return this->isTogglingBreakpoints; return isTogglingBreakpoints;
} }
void DecompilerContextMenu::aboutToHideSlot() void DecompilerContextMenu::aboutToHideSlot()
@ -169,7 +172,6 @@ void DecompilerContextMenu::aboutToShowSlot()
actionDeleteComment.setVisible(false); actionDeleteComment.setVisible(false);
} }
setupBreakpointsInLineMenu(); setupBreakpointsInLineMenu();
// Only show debug options if we are currently debugging // Only show debug options if we are currently debugging
@ -184,7 +186,6 @@ void DecompilerContextMenu::aboutToShowSlot()
} else { } else {
actionToggleBreakpoint.setText(tr("Remove all breakpoints in line")); actionToggleBreakpoint.setText(tr("Remove all breakpoints in line"));
} }
if (numberOfBreakpoints > 1) { if (numberOfBreakpoints > 1) {
actionAdvancedBreakpoint.setMenu(breakpointsInLineMenu); actionAdvancedBreakpoint.setMenu(breakpointsInLineMenu);
} else { } else {
@ -198,7 +199,7 @@ void DecompilerContextMenu::aboutToShowSlot()
if (!annotationHere if (!annotationHere
|| annotationHere->type == || annotationHere->type ==
R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) { // To be considered as invalid R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) { // If constant, don't show rename and targeted show-in
actionRenameThingHere.setVisible(false); actionRenameThingHere.setVisible(false);
copySeparator->setVisible(false); copySeparator->setVisible(false);
} else { } else {
@ -219,7 +220,7 @@ void DecompilerContextMenu::aboutToShowSlot()
} }
actionCopyInstructionAddress.setText(tr("Copy instruction address (%1)").arg(RAddressString( actionCopyInstructionAddress.setText(tr("Copy instruction address (%1)").arg(RAddressString(
offset))); offset)));
if (annotationHere && r_annotation_is_reference(annotationHere)) { if (isReference()) {
actionCopyReferenceAddress.setVisible(true); actionCopyReferenceAddress.setVisible(true);
RVA referenceAddr = annotationHere->reference.offset; RVA referenceAddr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(Core()->core()->flags, referenceAddr); RFlagItem *flagDetails = r_flag_get_i(Core()->core()->flags, referenceAddr);
@ -258,7 +259,6 @@ void DecompilerContextMenu::aboutToShowSlot()
// Set up actions // Set up actions
void DecompilerContextMenu::setActionCopy() // Set all three copy actions void DecompilerContextMenu::setActionCopy() // Set all three copy actions
{ {
connect(&actionCopy, &QAction::triggered, this, &DecompilerContextMenu::actionCopyTriggered); connect(&actionCopy, &QAction::triggered, this, &DecompilerContextMenu::actionCopyTriggered);
@ -386,7 +386,7 @@ void DecompilerContextMenu::actionDeleteCommentTriggered()
void DecompilerContextMenu::actionRenameThingHereTriggered() void DecompilerContextMenu::actionRenameThingHereTriggered()
{ {
if (!annotationHere) { if (!annotationHere || annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) {
return; return;
} }
RCoreLocked core = Core()->core(); RCoreLocked core = Core()->core();
@ -410,7 +410,6 @@ void DecompilerContextMenu::actionRenameThingHereTriggered()
Core()->renameFunction(func_addr, newName); Core()->renameFunction(func_addr, newName);
} }
} }
} else if (type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) { } else if (type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) {
RVA var_addr = annotationHere->reference.offset; RVA var_addr = annotationHere->reference.offset;
RFlagItem *flagDetails = r_flag_get_i(core->flags, var_addr); RFlagItem *flagDetails = r_flag_get_i(core->flags, var_addr);
@ -467,7 +466,7 @@ void DecompilerContextMenu::actionEditFunctionVariablesTriggered()
void DecompilerContextMenu::actionXRefsTriggered() void DecompilerContextMenu::actionXRefsTriggered()
{ {
if (!annotationHere || !r_annotation_is_reference(annotationHere)) { if (!isReference()) {
return; return;
} }
XrefsDialog dialog(mainWindow, nullptr); XrefsDialog dialog(mainWindow, nullptr);
@ -551,9 +550,7 @@ void DecompilerContextMenu::updateTargetMenuActions()
} }
showTargetMenuActions.clear(); showTargetMenuActions.clear();
RCoreLocked core = Core()->core(); RCoreLocked core = Core()->core();
if (annotationHere && (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE if (isReference()) {
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE
|| annotationHere->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME)) {
QString name; QString name;
QMenu *menu; QMenu *menu;
if (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE if (annotationHere->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE
@ -580,6 +577,11 @@ void DecompilerContextMenu::updateTargetMenuActions()
} }
} }
bool DecompilerContextMenu::isReference()
{
return (annotationHere && r_annotation_is_reference(annotationHere));
}
bool DecompilerContextMenu::isFunctionVariable() bool DecompilerContextMenu::isFunctionVariable()
{ {
return (annotationHere && r_annotation_is_variable(annotationHere)); return (annotationHere && r_annotation_is_variable(annotationHere));

View File

@ -7,6 +7,8 @@
#include <r_util/r_annotated_code.h> #include <r_util/r_annotated_code.h>
class MainWindow;
class DecompilerContextMenu : public QMenu class DecompilerContextMenu : public QMenu
{ {
Q_OBJECT Q_OBJECT
@ -17,18 +19,18 @@ public:
bool getIsTogglingBreakpoints(); bool getIsTogglingBreakpoints();
void setAnnotationHere(RCodeAnnotation *annotation); void setAnnotationHere(RCodeAnnotation *annotation);
RVA getFirstOffsetInLine();
signals: signals:
void copy(); void copy();
public slots: public slots:
void setCurHighlightedWord(QString word); void setCurHighlightedWord(QString word);
void setOffset(RVA offset); void setOffset(RVA newOffset);
void setDecompiledFunctionAddress(RVA functionAddr); void setDecompiledFunctionAddress(RVA functionAddr);
void setFirstOffsetInLine(RVA firstOffset); void setFirstOffsetInLine(RVA firstOffset);
void setAvailableBreakpoints(QVector<RVA> offsetList); void setAvailableBreakpoints(QVector<RVA> offsetList);
private slots: private slots:
void aboutToShowSlot(); void aboutToShowSlot();
void aboutToHideSlot(); void aboutToHideSlot();
@ -55,16 +57,30 @@ private slots:
private: private:
// Private variables // Private variables
MainWindow *mainWindow;
QString curHighlightedWord; QString curHighlightedWord;
RVA offset; RVA offset;
RVA decompiledFunctionAddress; RVA decompiledFunctionAddress;
/**
* Lowest offset among all offsets present in the line under cursor in the decompiler widget.
*/
RVA firstOffsetInLine; RVA firstOffsetInLine;
/**
* When the actionToggleBreakpoint has been triggered, and it hasn't finished executing,
* the value of this variable will be true, otherwise false
*/
bool isTogglingBreakpoints; bool isTogglingBreakpoints;
/**
* List of the offsets of all the breakpoints (enabled and disabled) that are present in the line under cursor.
*/
QVector<RVA> availableBreakpoints; QVector<RVA> availableBreakpoints;
MainWindow *mainWindow; /**
* Context-related annotation for the data under cursor in the decompiler widget.
* If such an annotation doesn't exist, its value is nullptr.
*/
RCodeAnnotation *annotationHere; RCodeAnnotation *annotationHere;
// Actions and menus in the context menu
QAction actionCopy; QAction actionCopy;
QAction actionCopyInstructionAddress; QAction actionCopyInstructionAddress;
QAction actionCopyReferenceAddress; QAction actionCopyReferenceAddress;
@ -94,6 +110,12 @@ private:
QAction actionSetPC; QAction actionSetPC;
// Private Functions // Private Functions
/**
* @brief Sets the shortcut context in all the actions contained
* in the specified QMenu to Qt::WidgetWithChildrenShortcut.
*
* @param menu - QMenu specified
*/
void setShortcutContextInActions(QMenu *menu); void setShortcutContextInActions(QMenu *menu);
void setupBreakpointsInLineMenu(); void setupBreakpointsInLineMenu();
void setIsTogglingBreakpoints(bool isToggling); void setIsTogglingBreakpoints(bool isToggling);
@ -123,9 +145,35 @@ private:
void addBreakpointMenu(); void addBreakpointMenu();
void addDebugMenu(); void addDebugMenu();
/**
* @brief Updates targeted "Show in" menu.
*
* Removes all actions from the existing targeted "show in" menu. If annotationHere
* represents an item that has an address assigned to it, insert actions compatible with the
* type of this item in the targeted "Show in" menu.
*/
void updateTargetMenuActions(); void updateTargetMenuActions();
/**
* @brief Check if annotationHere is a reference (function name,
* global variable, constant variable with an address).
*
* @return True if annotationHere is a reference, otherwise false.
*/
bool isReference();
/**
* @brief Check if annotationHere is a function variable
* (local variable or function parameter).
*
* @return True if annotationHere is a function variable, otherwise false.
*/
bool isFunctionVariable(); bool isFunctionVariable();
/**
* @brief Check if the function variable annotated by annotationHere is
* present in radare2.
*
* @return True if the variable is present, otherwise false
*/
bool variablePresentInR2(); bool variablePresentInR2();
}; };

View File

@ -31,10 +31,8 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
&r_annotated_code_free) &r_annotated_code_free)
{ {
ui->setupUi(this); ui->setupUi(this);
syntaxHighlighter = Config()->createSyntaxHighlighter(ui->textEdit->document()); syntaxHighlighter = Config()->createSyntaxHighlighter(ui->textEdit->document());
// Event filter to intercept double click and right click in the textbox
// Event filter to intercept double clicks in the textbox
ui->textEdit->viewport()->installEventFilter(this); ui->textEdit->viewport()->installEventFilter(this);
setupFonts(); setupFonts();
@ -48,11 +46,9 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(ui->refreshButton, &QAbstractButton::clicked, this, [this]() { connect(ui->refreshButton, &QAbstractButton::clicked, this, [this]() {
doRefresh(); doRefresh();
}); });
refreshDeferrer = createRefreshDeferrer([this]() { refreshDeferrer = createRefreshDeferrer([this]() {
doRefresh(); doRefresh();
}); });
autoRefreshEnabled = Config()->getDecompilerAutoRefreshEnabled(); autoRefreshEnabled = Config()->getDecompilerAutoRefreshEnabled();
ui->autoRefreshCheckBox->setChecked(autoRefreshEnabled); ui->autoRefreshCheckBox->setChecked(autoRefreshEnabled);
setAutoRefresh(autoRefreshEnabled); setAutoRefresh(autoRefreshEnabled);
@ -75,10 +71,8 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
} }
connect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished); connect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
} }
decompilerSelectionEnabled = decompilers.size() > 1; decompilerSelectionEnabled = decompilers.size() > 1;
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled); ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
if (decompilers.isEmpty()) { if (decompilers.isEmpty()) {
ui->textEdit->setPlainText(tr("No Decompiler available.")); ui->textEdit->setPlainText(tr("No Decompiler available."));
} }
@ -90,7 +84,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(Core(), &CutterCore::seekChanged, this, &DecompilerWidget::seekChanged); connect(Core(), &CutterCore::seekChanged, this, &DecompilerWidget::seekChanged);
ui->textEdit->setContextMenuPolicy(Qt::CustomContextMenu); ui->textEdit->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->textEdit, &QWidget::customContextMenuRequested, connect(ui->textEdit, &QWidget::customContextMenuRequested,
this, &DecompilerWidget::showDisasContextMenu); this, &DecompilerWidget::showDecompilerContextMenu);
connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::updateBreakpoints); connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::updateBreakpoints);
addActions(mCtxMenu->actions()); addActions(mCtxMenu->actions());
@ -147,13 +141,13 @@ void DecompilerWidget::updateRefreshButton()
} }
} }
static ut64 offsetForPosition(RAnnotatedCode &codeDecompiled, size_t pos) ut64 DecompilerWidget::offsetForPosition(size_t pos)
{ {
size_t closestPos = SIZE_MAX; size_t closestPos = SIZE_MAX;
ut64 closestOffset = UT64_MAX; ut64 closestOffset = mCtxMenu->getFirstOffsetInLine();
void *annotationi; void *iter;
r_vector_foreach(&codeDecompiled.annotations, annotationi) { r_vector_foreach(&code->annotations, iter) {
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi; RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET || annotation->start > pos if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET || annotation->start > pos
|| annotation->end <= pos) { || annotation->end <= pos) {
continue; continue;
@ -167,13 +161,13 @@ static ut64 offsetForPosition(RAnnotatedCode &codeDecompiled, size_t pos)
return closestOffset; return closestOffset;
} }
static size_t positionForOffset(RAnnotatedCode &codeDecompiled, ut64 offset) size_t DecompilerWidget::positionForOffset(ut64 offset)
{ {
size_t closestPos = SIZE_MAX; size_t closestPos = SIZE_MAX;
ut64 closestOffset = UT64_MAX; ut64 closestOffset = UT64_MAX;
void *annotationi; void *iter;
r_vector_foreach(&codeDecompiled.annotations, annotationi) { r_vector_foreach(&code->annotations, iter) {
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi; RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET || annotation->offset.offset > offset) { if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET || annotation->offset.offset > offset) {
continue; continue;
} }
@ -216,13 +210,13 @@ void DecompilerWidget::gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size
size_t endPos) size_t endPos)
{ {
RVA firstOffset = RVA_MAX; RVA firstOffset = RVA_MAX;
void *annotationi; void *iter;
r_vector_foreach(&codeDecompiled.annotations, annotationi) { r_vector_foreach(&codeDecompiled.annotations, iter) {
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi; RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET) { if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET) {
continue; continue;
} }
if ((startPos <= annotation->start && annotation->start < endPos) || (startPos <= annotation->end if ((startPos <= annotation->start && annotation->start < endPos) || (startPos < annotation->end
&& annotation->end < endPos)) { && annotation->end < endPos)) {
firstOffset = (annotation->offset.offset < firstOffset) ? annotation->offset.offset : firstOffset; firstOffset = (annotation->offset.offset < firstOffset) ? annotation->offset.offset : firstOffset;
} }
@ -231,7 +225,7 @@ void DecompilerWidget::gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size
QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr); QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);
QVector<RVA> offsetList; QVector<RVA> offsetList;
for (auto bpOffset : functionBreakpoints) { for (auto bpOffset : functionBreakpoints) {
size_t pos = positionForOffset(*code, bpOffset); size_t pos = positionForOffset(bpOffset);
if (startPos <= pos && pos <= endPos) { if (startPos <= pos && pos <= endPos) {
offsetList.push_back(bpOffset); offsetList.push_back(bpOffset);
} }
@ -245,26 +239,21 @@ void DecompilerWidget::doRefresh(RVA addr)
if (!refreshDeferrer->attemptRefresh(nullptr)) { if (!refreshDeferrer->attemptRefresh(nullptr)) {
return; return;
} }
if (ui->decompilerComboBox->currentIndex() < 0) { if (ui->decompilerComboBox->currentIndex() < 0) {
return; return;
} }
Decompiler *dec = getCurrentDecompiler(); Decompiler *dec = getCurrentDecompiler();
if (!dec) { if (!dec) {
return; return;
} }
if (dec->isRunning()) { if (dec->isRunning()) {
decompilerWasBusy = true; decompilerWasBusy = true;
return; return;
} }
if (addr == RVA_INVALID) { if (addr == RVA_INVALID) {
ui->textEdit->setPlainText(tr("Click Refresh to generate Decompiler from current offset.")); ui->textEdit->setPlainText(tr("Click Refresh to generate Decompiler from current offset."));
return; return;
} }
// Clear all selections since we just refreshed // Clear all selections since we just refreshed
ui->textEdit->setExtraSelections({}); ui->textEdit->setExtraSelections({});
previousFunctionAddr = decompiledFunctionAddr; previousFunctionAddr = decompiledFunctionAddr;
@ -287,11 +276,10 @@ void DecompilerWidget::refreshDecompiler()
QTextCursor DecompilerWidget::getCursorForAddress(RVA addr) QTextCursor DecompilerWidget::getCursorForAddress(RVA addr)
{ {
size_t pos = positionForOffset(*code, addr); size_t pos = positionForOffset(addr);
if (pos == SIZE_MAX || pos == 0) { if (pos == SIZE_MAX || pos == 0) {
return QTextCursor(); return QTextCursor();
} }
QTextCursor cursor = ui->textEdit->textCursor(); QTextCursor cursor = ui->textEdit->textCursor();
cursor.setPosition(pos); cursor.setPosition(pos);
return cursor; return cursor;
@ -339,9 +327,9 @@ void DecompilerWidget::decompilationFinished(RAnnotatedCode *codeDecompiled)
void DecompilerWidget::setAnnotationsAtCursor(size_t pos) void DecompilerWidget::setAnnotationsAtCursor(size_t pos)
{ {
RCodeAnnotation *annotationAtPos = nullptr; RCodeAnnotation *annotationAtPos = nullptr;
void *annotationi; void *iter;
r_vector_foreach(&this->code->annotations, annotationi) { r_vector_foreach(&this->code->annotations, iter) {
RCodeAnnotation *annotation = (RCodeAnnotation *)annotationi; RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type == R_CODE_ANNOTATION_TYPE_OFFSET || if (annotation->type == R_CODE_ANNOTATION_TYPE_OFFSET ||
annotation->type == R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT || annotation->type == R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT ||
annotation->start > pos || annotation->end <= pos) { annotation->start > pos || annotation->end <= pos) {
@ -381,10 +369,9 @@ void DecompilerWidget::cursorPositionChanged()
size_t pos = ui->textEdit->textCursor().position(); size_t pos = ui->textEdit->textCursor().position();
setAnnotationsAtCursor(pos); setAnnotationsAtCursor(pos);
setInfoForBreakpoints(); setInfoForBreakpoints();
RVA offset = offsetForPosition(*code, pos); RVA offset = offsetForPosition(pos);
if (offset != RVA_INVALID && offset != Core()->getOffset()) { if (offset != RVA_INVALID && offset != Core()->getOffset()) {
seekFromCursor = true; seekFromCursor = true;
Core()->seek(offset); Core()->seek(offset);
@ -399,7 +386,6 @@ void DecompilerWidget::seekChanged()
if (seekFromCursor) { if (seekFromCursor) {
return; return;
} }
if (autoRefreshEnabled) { if (autoRefreshEnabled) {
auto fcnAddr = Core()->getFunctionStart(Core()->getOffset()); auto fcnAddr = Core()->getFunctionStart(Core()->getOffset());
if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) { if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {
@ -407,14 +393,13 @@ void DecompilerWidget::seekChanged()
return; return;
} }
} }
updateCursorPosition(); updateCursorPosition();
} }
void DecompilerWidget::updateCursorPosition() void DecompilerWidget::updateCursorPosition()
{ {
RVA offset = Core()->getOffset(); RVA offset = Core()->getOffset();
size_t pos = positionForOffset(*code, offset); size_t pos = positionForOffset(offset);
if (pos == SIZE_MAX) { if (pos == SIZE_MAX) {
return; return;
} }
@ -465,7 +450,7 @@ void DecompilerWidget::colorsUpdatedSlot()
{ {
} }
void DecompilerWidget::showDisasContextMenu(const QPoint &pt) void DecompilerWidget::showDecompilerContextMenu(const QPoint &pt)
{ {
mCtxMenu->exec(ui->textEdit->mapToGlobal(pt)); mCtxMenu->exec(ui->textEdit->mapToGlobal(pt));
} }
@ -473,7 +458,7 @@ void DecompilerWidget::showDisasContextMenu(const QPoint &pt)
void DecompilerWidget::seekToReference() void DecompilerWidget::seekToReference()
{ {
size_t pos = ui->textEdit->textCursor().position(); size_t pos = ui->textEdit->textCursor().position();
RVA offset = offsetForPosition(*code, pos); RVA offset = offsetForPosition(pos);
seekable->seekToReference(offset); seekable->seekToReference(offset);
} }
@ -520,7 +505,6 @@ void DecompilerWidget::highlightBreakpoints()
if (bp == RVA_INVALID) { if (bp == RVA_INVALID) {
continue;; continue;;
} }
cursor = getCursorForAddress(bp); cursor = getCursorForAddress(bp);
if (!cursor.isNull()) { if (!cursor.isNull()) {
// Use a Block formatting since these lines are not updated frequently as selections and PC // Use a Block formatting since these lines are not updated frequently as selections and PC

View File

@ -28,16 +28,30 @@ public:
explicit DecompilerWidget(MainWindow *main); explicit DecompilerWidget(MainWindow *main);
~DecompilerWidget(); ~DecompilerWidget();
public slots: public slots:
void showDisasContextMenu(const QPoint &pt); void showDecompilerContextMenu(const QPoint &pt);
void highlightPC(); void highlightPC();
private slots: private slots:
/**
* @brief Copy to clipboard what's needed depending on the state of text widget.
*
* @note If something is selected in the text widget, copy selection.
* If something is highlighted, copy highlighted word.
* Otherwise, copy the line under cursor.
*/
void copy(); void copy();
void fontsUpdatedSlot(); void fontsUpdatedSlot();
void colorsUpdatedSlot(); void colorsUpdatedSlot();
void refreshDecompiler(); void refreshDecompiler();
void decompilerSelected(); void decompilerSelected();
void cursorPositionChanged(); void cursorPositionChanged();
/**
* @brief When the synced seek is changed, this refreshes the decompiler widget if needed.
*
* Decompiler widget is not refreshed in the following two cases
* - Seek changed to an offset contained in the decompiled function.
* - Auto-refresh is disabled.
*/
void seekChanged(); void seekChanged();
void decompilationFinished(RAnnotatedCode *code); void decompilationFinished(RAnnotatedCode *code);
@ -51,7 +65,7 @@ private:
bool autoRefreshEnabled; bool autoRefreshEnabled;
/** /**
* True if doRefresh() was called, but the decompiler was still running * True if doRefresh() was called, but the decompiler was still running.
* This means, after the decompiler has finished, it should be refreshed immediately. * This means, after the decompiler has finished, it should be refreshed immediately.
*/ */
bool decompilerWasBusy; bool decompilerWasBusy;
@ -65,16 +79,71 @@ private:
Decompiler *getCurrentDecompiler(); Decompiler *getCurrentDecompiler();
/**
* @brief Enable/Disable auto refresh as per the specified boolean value
*
* @param enabled
*/
void setAutoRefresh(bool enabled); void setAutoRefresh(bool enabled);
/**
* @brief Calls the function doRefresh() if auto-refresh is enabled.
*/
void doAutoRefresh(); void doAutoRefresh();
/**
* @brief Refreshes the decompiler.
*
* - This does the following if the specified offset is valid
* - Decompile function that contains the specified offset.
* - Clears all selections stored for highlighting purposes.
* - Reset previousFunctionAddr with the current function's address
* and decompiledFunctionAddr with the address of the function that
* was decompiled.
* - If the offset is invalid, error message is shown in the text widget.
*
* @param addr Specified offset/offset in sync.
*/
void doRefresh(RVA addr = Core()->getOffset()); void doRefresh(RVA addr = Core()->getOffset());
void updateRefreshButton(); void updateRefreshButton();
/**
* @brief Update fonts
*/
void setupFonts(); void setupFonts();
/**
* @brief Update highlights in the text widget.
*
* These include respective highlights for:
* - Line under cursor
* - Word under cursor
* - Program Counter(PC) while debugging
*/
void updateSelection(); void updateSelection();
/**
* @brief Connect/Disconnect SIGNAL-SLOT connection that deals with changes in cursor position.
*
* If the argument is true, then disconnect the SIGNAL-SLOT connection
* that changes the view as cursor position gets changed in the text widget.
* Otherwise, connect the corresponding signal with slot.
*
* @param disconnect
*/
void connectCursorPositionChanged(bool disconnect); void connectCursorPositionChanged(bool disconnect);
/**
* @brief Find the current global offset in sync and update cursor
* to the position specified by this offset (found using positionForOffset() )
*/
void updateCursorPosition(); void updateCursorPosition();
QString getWindowTitle() const override; QString getWindowTitle() const override;
/**
* @brief Event filter that intercept the following events:
* 1. Double click
* 2. Right click
*
* @param obj
* @param event
* @return
*/
bool eventFilter(QObject *obj, QEvent *event) override; bool eventFilter(QObject *obj, QEvent *event) override;
/** /**
@ -104,21 +173,49 @@ private:
void highlightBreakpoints(); void highlightBreakpoints();
/** /**
* @brief Finds the earliest offset and breakpoints within the specified range [startPos, endPos] * @brief Finds the earliest offset and breakpoints within the specified range [startPos, endPos]
* in the specified RAnnotatedCode * in the specified RAnnotatedCode.
* *
* This function is supposed to be used for finding the earliest offset and breakpoints within the specified range * This function is supposed to be used for finding the earliest offset and breakpoints within the specified range
* [startPos, endPos]. This will set the value of the variables 'RVA firstOffsetInLine' and 'QVector<RVA> availableBreakpoints' in * [startPos, endPos]. This will set the value of the variables 'RVA firstOffsetInLine' and 'QVector<RVA> availableBreakpoints' in
* this->mCtxMenu. * the context menu.
* *
* @param codeDecompiled - A reference to the RAnnotatedCode for the function that is decompiled. * @param codeDecompiled - A reference to the RAnnotatedCode for the function that is decompiled.
* @param startPos - Position of the start of the range(inclusive). * @param startPos - Position of the start of the range(inclusive).
* @param endPos - Position of the end of the range(inclusive). * @param endPos - Position of the end of the range(inclusive).
*/ */
void gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size_t startPos, size_t endPos); void gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size_t startPos, size_t endPos);
/**
* @brief Finds the offset that's closest to the specified position in the decompiled code.
*
* @note If no annotations that covers the specified position is found, the first offset in the line
* containing specified position will be returned
*
* @param pos - Position of the decompiled code.
* @return Offset for the specified position/first offset in line.
*/
ut64 offsetForPosition(size_t pos);
/**
* @brief Find the start position of the annotation with the offset that's closest to
* the specified offset
*
* @param offset
* @return Position found or SIZE_MAX
*/
size_t positionForOffset(ut64 offset);
/**
* @brief Updates the view when breakpoints are changed
*/
void updateBreakpoints(); void updateBreakpoints();
/**
* @brief Set information about the breakpoints on the line in the context menu
*/
void setInfoForBreakpoints(); void setInfoForBreakpoints();
/**
* @brief Find the context-related annotation covering the specified position.
* If found, set the variable annotationHere in the decompiler context menu.
*
* @param pos Position of cursor in the decompiled code.
*/
void setAnnotationsAtCursor(size_t pos); void setAnnotationsAtCursor(size_t pos);
}; };