Synchronized Decompiler and Enabling Multiple Decompiler Widgets (#2402)

* Sync/Unsync decompiler widgets.

* Add multiple decompiler widgets with its own decompiler and functions.

* updateWindowTitle() in widgets for decompiler, disassembly, and hexdump.
This commit is contained in:
NIRMAL MANOJ C 2020-08-29 10:45:47 +05:30 committed by GitHub
parent ec22b01086
commit 691de14853
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 185 additions and 148 deletions

View File

@ -59,6 +59,12 @@ Show Types
**Steps:** Windows -> Types **Steps:** Windows -> Types
Add a new instance of the Decompiler Widget
----------------------------------------------
**Description:** Create a new instance of the Decompiler widget in order to view multiple decompiled functions using multiple supported decompilers.
**Steps:** Windows -> Add Decompiler
Add a new instance of the Disassembly Widget Add a new instance of the Disassembly Widget
---------------------------------------------- ----------------------------------------------
**Description:** Create a new instance of the Disassembly widget in order to view one or multiple locations at the same time. **Description:** Create a new instance of the Disassembly widget in order to view one or multiple locations at the same time.

View File

@ -812,13 +812,13 @@ int CutterCore::sizeofDataMeta(RVA addr)
void CutterCore::setComment(RVA addr, const QString &cmt) void CutterCore::setComment(RVA addr, const QString &cmt)
{ {
cmdRawAt(QString("CCu base64:%1").arg(QString(cmt.toLocal8Bit().toBase64())), addr); cmdRawAt(QString("CCu base64:%1").arg(QString(cmt.toLocal8Bit().toBase64())), addr);
emit commentsChanged(); emit commentsChanged(addr);
} }
void CutterCore::delComment(RVA addr) void CutterCore::delComment(RVA addr)
{ {
cmdRawAt("CC-", addr); cmdRawAt("CC-", addr);
emit commentsChanged(); emit commentsChanged(addr);
} }
/** /**

View File

@ -647,7 +647,7 @@ signals:
void varsChanged(); void varsChanged();
void functionsChanged(); void functionsChanged();
void flagsChanged(); void flagsChanged();
void commentsChanged(); void commentsChanged(RVA addr);
void registersChanged(); void registersChanged();
void instructionChanged(RVA offset); void instructionChanged(RVA offset);
void breakpointsChanged(RVA offset); void breakpointsChanged(RVA offset);

View File

@ -143,6 +143,7 @@ void MainWindow::initUI()
disassemblyContextMenuExtensions = new QMenu(tr("Plugins"), this); disassemblyContextMenuExtensions = new QMenu(tr("Plugins"), this);
addressableContextMenuExtensions = new QMenu(tr("Plugins"), this); addressableContextMenuExtensions = new QMenu(tr("Plugins"), this);
connect(ui->actionExtraDecompiler, &QAction::triggered, this, &MainWindow::addExtraDecompiler);
connect(ui->actionExtraGraph, &QAction::triggered, this, &MainWindow::addExtraGraph); connect(ui->actionExtraGraph, &QAction::triggered, this, &MainWindow::addExtraGraph);
connect(ui->actionExtraDisassembly, &QAction::triggered, this, &MainWindow::addExtraDisassembly); connect(ui->actionExtraDisassembly, &QAction::triggered, this, &MainWindow::addExtraDisassembly);
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump); connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
@ -347,7 +348,6 @@ void MainWindow::initToolBar()
void MainWindow::initDocks() void MainWindow::initDocks()
{ {
dockWidgets.reserve(20); dockWidgets.reserve(20);
decompilerDock = new DecompilerWidget(this);
consoleDock = new ConsoleWidget(this); consoleDock = new ConsoleWidget(this);
overviewDock = new OverviewWidget(this); overviewDock = new OverviewWidget(this);
@ -415,7 +415,6 @@ void MainWindow::initDocks()
dashboardDock, dashboardDock,
nullptr, nullptr,
functionsDock, functionsDock,
decompilerDock,
overviewDock, overviewDock,
nullptr, nullptr,
searchDock, searchDock,
@ -423,7 +422,7 @@ void MainWindow::initDocks()
typesDock, typesDock,
nullptr, nullptr,
}; };
ui->menuWindows->insertActions(ui->actionExtraDisassembly, makeActionList(windowDocks)); ui->menuWindows->insertActions(ui->actionExtraDecompiler, makeActionList(windowDocks));
QList<CutterDockWidget *> windowDocks2 = { QList<CutterDockWidget *> windowDocks2 = {
consoleDock, consoleDock,
commentsDock, commentsDock,
@ -475,6 +474,12 @@ void MainWindow::addExtraDisassembly()
addExtraWidget(extraDock); addExtraWidget(extraDock);
} }
void MainWindow::addExtraDecompiler()
{
auto *extraDock = new DecompilerWidget(this);
addExtraWidget(extraDock);
}
void MainWindow::addExtraWidget(CutterDockWidget *extraDock) void MainWindow::addExtraWidget(CutterDockWidget *extraDock)
{ {
extraDock->setTransient(true); extraDock->setTransient(true);
@ -820,7 +825,6 @@ void MainWindow::restoreDocks()
splitDockWidget(functionsDock, overviewDock, Qt::Vertical); splitDockWidget(functionsDock, overviewDock, Qt::Vertical);
// main area // main area
tabifyDockWidget(dashboardDock, decompilerDock);
tabifyDockWidget(dashboardDock, entrypointDock); tabifyDockWidget(dashboardDock, entrypointDock);
tabifyDockWidget(dashboardDock, flagsDock); tabifyDockWidget(dashboardDock, flagsDock);
tabifyDockWidget(dashboardDock, stringsDock); tabifyDockWidget(dashboardDock, stringsDock);
@ -989,7 +993,7 @@ QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address, AddressTypeHi
createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph); createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph);
} }
createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump); createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump);
createAddNewWidgetAction(tr("New Decompiler"), MemoryWidgetType::Decompiler);
return menu; return menu;
} }

View File

@ -168,6 +168,7 @@ private slots:
void addExtraGraph(); void addExtraGraph();
void addExtraHexdump(); void addExtraHexdump();
void addExtraDisassembly(); void addExtraDisassembly();
void addExtraDecompiler();
void on_actionRefresh_Panels_triggered(); void on_actionRefresh_Panels_triggered();
@ -236,7 +237,6 @@ private:
QList<CutterDockWidget *> dockWidgets; QList<CutterDockWidget *> dockWidgets;
QList<CutterDockWidget *> pluginDocks; QList<CutterDockWidget *> pluginDocks;
DecompilerWidget *decompilerDock = nullptr;
OverviewWidget *overviewDock = nullptr; OverviewWidget *overviewDock = nullptr;
QAction *actionOverview = nullptr; QAction *actionOverview = nullptr;
EntrypointWidget *entrypointDock = nullptr; EntrypointWidget *entrypointDock = nullptr;

View File

@ -162,6 +162,7 @@
<string>Debug...</string> <string>Debug...</string>
</property> </property>
</widget> </widget>
<addaction name="actionExtraDecompiler"/>
<addaction name="actionExtraDisassembly"/> <addaction name="actionExtraDisassembly"/>
<addaction name="actionExtraGraph"/> <addaction name="actionExtraGraph"/>
<addaction name="actionExtraHexdump"/> <addaction name="actionExtraHexdump"/>
@ -756,6 +757,11 @@
<string>Add Hexdump</string> <string>Add Hexdump</string>
</property> </property>
</action> </action>
<action name="actionExtraDecompiler">
<property name="text">
<string>Add Decompiler</string>
</property>
</action>
<action name="actionExtraDisassembly"> <action name="actionExtraDisassembly">
<property name="text"> <property name="text">
<string>Add Disassembly</string> <string>Add Disassembly</string>

View File

@ -147,7 +147,7 @@ private:
/** /**
* @brief Updates targeted "Show in" menu. * @brief Updates targeted "Show in" menu.
* *
* Removes all actions from the existing targeted "show in" menu. If annotationHere * 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 * 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. * type of this item in the targeted "Show in" menu.

View File

@ -8,6 +8,7 @@
#include "common/SelectionHighlight.h" #include "common/SelectionHighlight.h"
#include "common/Decompiler.h" #include "common/Decompiler.h"
#include "common/CutterSeekable.h" #include "common/CutterSeekable.h"
#include "core/MainWindow.h"
#include <QTextEdit> #include <QTextEdit>
#include <QPlainTextEdit> #include <QPlainTextEdit>
@ -22,7 +23,8 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
MemoryDockWidget(MemoryWidgetType::Decompiler, main), MemoryDockWidget(MemoryWidgetType::Decompiler, main),
mCtxMenu(new DecompilerContextMenu(this, main)), mCtxMenu(new DecompilerContextMenu(this, main)),
ui(new Ui::DecompilerWidget), ui(new Ui::DecompilerWidget),
decompilerWasBusy(false), decompilerBusy(false),
seekFromCursor(false),
scrollerHorizontal(0), scrollerHorizontal(0),
scrollerVertical(0), scrollerVertical(0),
previousFunctionAddr(RVA_INVALID), previousFunctionAddr(RVA_INVALID),
@ -31,6 +33,11 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
&r_annotated_code_free) &r_annotated_code_free)
{ {
ui->setupUi(this); ui->setupUi(this);
setObjectName(main
? main->getUniqueObjectName(tr("Decompiler"))
: tr("Decompiler"));
updateWindowTitle();
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 click and right click in the textbox
ui->textEdit->viewport()->installEventFilter(this); ui->textEdit->viewport()->installEventFilter(this);
@ -43,33 +50,21 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(Core(), &CutterCore::registersChanged, this, &DecompilerWidget::highlightPC); connect(Core(), &CutterCore::registersChanged, this, &DecompilerWidget::highlightPC);
connect(mCtxMenu, &DecompilerContextMenu::copy, this, &DecompilerWidget::copy); connect(mCtxMenu, &DecompilerContextMenu::copy, this, &DecompilerWidget::copy);
connect(ui->refreshButton, &QAbstractButton::clicked, this, [this]() {
doRefresh();
});
refreshDeferrer = createRefreshDeferrer([this]() { refreshDeferrer = createRefreshDeferrer([this]() {
doRefresh(); doRefresh();
}); });
autoRefreshEnabled = Config()->getDecompilerAutoRefreshEnabled();
ui->autoRefreshCheckBox->setChecked(autoRefreshEnabled);
setAutoRefresh(autoRefreshEnabled);
connect(ui->autoRefreshCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
setAutoRefresh(state == Qt::Checked);
Config()->setDecompilerAutoRefreshEnabled(autoRefreshEnabled);
doAutoRefresh();
});
auto decompilers = Core()->getDecompilers(); auto decompilers = Core()->getDecompilers();
auto selectedDecompilerId = Config()->getSelectedDecompiler(); QString selectedDecompilerId = Config()->getSelectedDecompiler();
if (selectedDecompilerId.isEmpty()) { if (selectedDecompilerId.isEmpty()) {
// If no decompiler was previously chosen. set r2ghidra as default decompiler // If no decompiler was previously chosen. set r2ghidra as default decompiler
selectedDecompilerId = "r2ghidra"; selectedDecompilerId = "r2ghidra";
} }
for (auto dec : decompilers) { for (Decompiler *dec : decompilers) {
ui->decompilerComboBox->addItem(dec->getName(), dec->getId()); ui->decompilerComboBox->addItem(dec->getName(), dec->getId());
if (dec->getId() == selectedDecompilerId) { if (dec->getId() == selectedDecompilerId) {
ui->decompilerComboBox->setCurrentIndex(ui->decompilerComboBox->count() - 1); ui->decompilerComboBox->setCurrentIndex(ui->decompilerComboBox->count() - 1);
} }
connect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
} }
decompilerSelectionEnabled = decompilers.size() > 1; decompilerSelectionEnabled = decompilers.size() > 1;
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled); ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
@ -80,26 +75,28 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
connect(ui->decompilerComboBox, connect(ui->decompilerComboBox,
static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&DecompilerWidget::decompilerSelected); &DecompilerWidget::decompilerSelected);
connectCursorPositionChanged(false); connectCursorPositionChanged(true);
connect(Core(), &CutterCore::seekChanged, this, &DecompilerWidget::seekChanged); connect(seekable, &CutterSeekable::seekableSeekChanged, 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::showDecompilerContextMenu); this, &DecompilerWidget::showDecompilerContextMenu);
connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::updateBreakpoints); connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::updateBreakpoints);
mCtxMenu->addSeparator();
mCtxMenu->addAction(&syncAction);
addActions(mCtxMenu->actions()); addActions(mCtxMenu->actions());
ui->progressLabel->setVisible(false); ui->progressLabel->setVisible(false);
doRefresh(RVA_INVALID); doRefresh();
connect(Core(), &CutterCore::refreshAll, this, &DecompilerWidget::doAutoRefresh); connect(Core(), &CutterCore::refreshAll, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::functionRenamed, this, &DecompilerWidget::doAutoRefresh); connect(Core(), &CutterCore::functionRenamed, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::varsChanged, this, &DecompilerWidget::doAutoRefresh); connect(Core(), &CutterCore::varsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doAutoRefresh); connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::flagsChanged, this, &DecompilerWidget::doAutoRefresh); connect(Core(), &CutterCore::flagsChanged, this, &DecompilerWidget::doRefresh);
connect(Core(), &CutterCore::commentsChanged, this, &DecompilerWidget::doAutoRefresh); connect(Core(), &CutterCore::commentsChanged, this, &DecompilerWidget::refreshIfChanged);
connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::doAutoRefresh); connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::refreshIfChanged);
connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doAutoRefresh); connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doRefresh);
// Esc to seek backward // Esc to seek backward
QAction *seekPrevAction = new QAction(this); QAction *seekPrevAction = new QAction(this);
@ -116,31 +113,6 @@ Decompiler *DecompilerWidget::getCurrentDecompiler()
return Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString()); return Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString());
} }
void DecompilerWidget::setAutoRefresh(bool enabled)
{
autoRefreshEnabled = enabled;
updateRefreshButton();
}
void DecompilerWidget::doAutoRefresh()
{
if (!autoRefreshEnabled) {
return;
}
doRefresh();
}
void DecompilerWidget::updateRefreshButton()
{
Decompiler *dec = getCurrentDecompiler();
ui->refreshButton->setEnabled(!autoRefreshEnabled && dec && !dec->isRunning());
if (dec && dec->isRunning() && dec->isCancelable()) {
ui->refreshButton->setText(tr("Cancel"));
} else {
ui->refreshButton->setText(tr("Refresh"));
}
}
ut64 DecompilerWidget::offsetForPosition(size_t pos) ut64 DecompilerWidget::offsetForPosition(size_t pos)
{ {
size_t closestPos = SIZE_MAX; size_t closestPos = SIZE_MAX;
@ -180,8 +152,11 @@ size_t DecompilerWidget::positionForOffset(ut64 offset)
return closestPos; return closestPos;
} }
void DecompilerWidget::updateBreakpoints() void DecompilerWidget::updateBreakpoints(RVA addr)
{ {
if (!addressInRange(addr)) {
return;
}
setInfoForBreakpoints(); setInfoForBreakpoints();
QTextCursor cursor = ui->textEdit->textCursor(); QTextCursor cursor = ui->textEdit->textCursor();
cursor.select(QTextCursor::Document); cursor.select(QTextCursor::Document);
@ -195,8 +170,9 @@ void DecompilerWidget::updateBreakpoints()
void DecompilerWidget::setInfoForBreakpoints() void DecompilerWidget::setInfoForBreakpoints()
{ {
if (mCtxMenu->getIsTogglingBreakpoints()) if (mCtxMenu->getIsTogglingBreakpoints()) {
return; return;
}
// Get the range of the line // Get the range of the line
QTextCursor cursorForLine = ui->textEdit->textCursor(); QTextCursor cursorForLine = ui->textEdit->textCursor();
cursorForLine.movePosition(QTextCursor::StartOfLine); cursorForLine.movePosition(QTextCursor::StartOfLine);
@ -224,7 +200,7 @@ void DecompilerWidget::gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size
mCtxMenu->setFirstOffsetInLine(firstOffset); mCtxMenu->setFirstOffsetInLine(firstOffset);
QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr); QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);
QVector<RVA> offsetList; QVector<RVA> offsetList;
for (auto bpOffset : functionBreakpoints) { for (RVA bpOffset : functionBreakpoints) {
size_t pos = positionForOffset(bpOffset); size_t pos = positionForOffset(bpOffset);
if (startPos <= pos && pos <= endPos) { if (startPos <= pos && pos <= endPos) {
offsetList.push_back(bpOffset); offsetList.push_back(bpOffset);
@ -234,8 +210,16 @@ void DecompilerWidget::gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size
mCtxMenu->setAvailableBreakpoints(offsetList); mCtxMenu->setAvailableBreakpoints(offsetList);
} }
void DecompilerWidget::doRefresh(RVA addr) void DecompilerWidget::refreshIfChanged(RVA addr)
{ {
if (addressInRange(addr)) {
doRefresh();
}
}
void DecompilerWidget::doRefresh()
{
RVA addr = seekable->getOffset();
if (!refreshDeferrer->attemptRefresh(nullptr)) { if (!refreshDeferrer->attemptRefresh(nullptr)) {
return; return;
} }
@ -246,26 +230,36 @@ void DecompilerWidget::doRefresh(RVA addr)
if (!dec) { if (!dec) {
return; return;
} }
// Disabling decompiler selection combo box and making progress label visible ahead of decompilation.
ui->progressLabel->setVisible(true);
ui->decompilerComboBox->setEnabled(false);
if (dec->isRunning()) { if (dec->isRunning()) {
decompilerWasBusy = true; if (!decompilerBusy) {
return; connect(dec, &Decompiler::finished, this, &DecompilerWidget::doRefresh);
} }
if (addr == RVA_INVALID) {
ui->textEdit->setPlainText(tr("Click Refresh to generate Decompiler from current offset."));
return; return;
} }
disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::doRefresh);
// Clear all selections since we just refreshed // Clear all selections since we just refreshed
ui->textEdit->setExtraSelections({}); ui->textEdit->setExtraSelections({});
previousFunctionAddr = decompiledFunctionAddr; previousFunctionAddr = decompiledFunctionAddr;
decompiledFunctionAddr = Core()->getFunctionStart(addr); decompiledFunctionAddr = Core()->getFunctionStart(addr);
mCtxMenu->setDecompiledFunctionAddress(decompiledFunctionAddr); updateWindowTitle();
dec->decompileAt(addr); if (decompiledFunctionAddr == RVA_INVALID) {
if (dec->isRunning()) { // No function was found, so making the progress label invisible and enabling
ui->progressLabel->setVisible(true); // the decompiler selection combo box as we are not waiting for any decompilation to finish.
ui->decompilerComboBox->setEnabled(false); ui->progressLabel->setVisible(false);
updateRefreshButton(); ui->decompilerComboBox->setEnabled(true);
connectCursorPositionChanged(false);
ui->textEdit->setPlainText(
tr("No function found at this offset. Seek to a function or define one in order to decompile it."));
connectCursorPositionChanged(true);
return; return;
} }
mCtxMenu->setDecompiledFunctionAddress(decompiledFunctionAddr);
connect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
decompilerBusy = true;
dec->decompileAt(addr);
} }
void DecompilerWidget::refreshDecompiler() void DecompilerWidget::refreshDecompiler()
@ -296,26 +290,43 @@ void DecompilerWidget::decompilationFinished(RAnnotatedCode *codeDecompiled)
ui->progressLabel->setVisible(false); ui->progressLabel->setVisible(false);
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled); ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
updateRefreshButton();
mCtxMenu->setAnnotationHere(nullptr); mCtxMenu->setAnnotationHere(nullptr);
this->code.reset(codeDecompiled); this->code.reset(codeDecompiled);
Decompiler *dec = getCurrentDecompiler();
QObject::disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
decompilerBusy = false;
QString codeString = QString::fromUtf8(this->code->code); QString codeString = QString::fromUtf8(this->code->code);
if (codeString.isEmpty()) { if (codeString.isEmpty()) {
connectCursorPositionChanged(false);
ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)")); ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)"));
connectCursorPositionChanged(true);
lowestOffsetInCode = RVA_MAX;
highestOffsetInCode = 0;
return; return;
} else { } else {
connectCursorPositionChanged(true);
ui->textEdit->setPlainText(codeString);
connectCursorPositionChanged(false); connectCursorPositionChanged(false);
ui->textEdit->setPlainText(codeString);
connectCursorPositionChanged(true);
updateCursorPosition(); updateCursorPosition();
highlightPC(); highlightPC();
highlightBreakpoints(); highlightBreakpoints();
} lowestOffsetInCode = RVA_MAX;
highestOffsetInCode = 0;
if (decompilerWasBusy) { void *iter;
decompilerWasBusy = false; r_vector_foreach(&code->annotations, iter) {
doAutoRefresh(); RCodeAnnotation *annotation = (RCodeAnnotation *)iter;
if (annotation->type == R_CODE_ANNOTATION_TYPE_OFFSET) {
if (lowestOffsetInCode > annotation->offset.offset) {
lowestOffsetInCode = annotation->offset.offset;
}
if (highestOffsetInCode < annotation->offset.offset) {
highestOffsetInCode = annotation->offset.offset;
}
}
}
} }
if (isDisplayReset) { if (isDisplayReset) {
@ -344,16 +355,14 @@ void DecompilerWidget::setAnnotationsAtCursor(size_t pos)
void DecompilerWidget::decompilerSelected() void DecompilerWidget::decompilerSelected()
{ {
Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString()); Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString());
if (autoRefreshEnabled) { doRefresh();
doRefresh();
}
} }
void DecompilerWidget::connectCursorPositionChanged(bool disconnect) void DecompilerWidget::connectCursorPositionChanged(bool connectPositionChange)
{ {
if (disconnect) { if (!connectPositionChange) {
QObject::disconnect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this, disconnect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this,
&DecompilerWidget::cursorPositionChanged); &DecompilerWidget::cursorPositionChanged);
} else { } else {
connect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this, connect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this,
&DecompilerWidget::cursorPositionChanged); &DecompilerWidget::cursorPositionChanged);
@ -372,9 +381,9 @@ void DecompilerWidget::cursorPositionChanged()
setInfoForBreakpoints(); setInfoForBreakpoints();
RVA offset = offsetForPosition(pos); RVA offset = offsetForPosition(pos);
if (offset != RVA_INVALID && offset != Core()->getOffset()) { if (offset != RVA_INVALID && offset != seekable->getOffset()) {
seekFromCursor = true; seekFromCursor = true;
Core()->seek(offset); seekable->seek(offset);
mCtxMenu->setOffset(offset); mCtxMenu->setOffset(offset);
seekFromCursor = false; seekFromCursor = false;
} }
@ -386,30 +395,28 @@ void DecompilerWidget::seekChanged()
if (seekFromCursor) { if (seekFromCursor) {
return; return;
} }
if (autoRefreshEnabled) { RVA fcnAddr = Core()->getFunctionStart(seekable->getOffset());
auto fcnAddr = Core()->getFunctionStart(Core()->getOffset()); if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {
if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) { doRefresh();
doRefresh(); return;
return;
}
} }
updateCursorPosition(); updateCursorPosition();
} }
void DecompilerWidget::updateCursorPosition() void DecompilerWidget::updateCursorPosition()
{ {
RVA offset = Core()->getOffset(); RVA offset = seekable->getOffset();
size_t pos = positionForOffset(offset); size_t pos = positionForOffset(offset);
if (pos == SIZE_MAX) { if (pos == SIZE_MAX) {
return; return;
} }
mCtxMenu->setOffset(offset); mCtxMenu->setOffset(offset);
connectCursorPositionChanged(true); connectCursorPositionChanged(false);
QTextCursor cursor = ui->textEdit->textCursor(); QTextCursor cursor = ui->textEdit->textCursor();
cursor.setPosition(pos); cursor.setPosition(pos);
ui->textEdit->setTextCursor(cursor); ui->textEdit->setTextCursor(cursor);
updateSelection(); updateSelection();
connectCursorPositionChanged(false); connectCursorPositionChanged(true);
} }
void DecompilerWidget::setupFonts() void DecompilerWidget::setupFonts()
@ -422,7 +429,7 @@ void DecompilerWidget::updateSelection()
QList<QTextEdit::ExtraSelection> extraSelections; QList<QTextEdit::ExtraSelection> extraSelections;
// Highlight the current line // Highlight the current line
auto cursor = ui->textEdit->textCursor(); QTextCursor cursor = ui->textEdit->textCursor();
extraSelections.append(createLineHighlightSelection(cursor)); extraSelections.append(createLineHighlightSelection(cursor));
// Highlight all the words in the document same as the current one // Highlight all the words in the document same as the current one
@ -438,7 +445,14 @@ void DecompilerWidget::updateSelection()
QString DecompilerWidget::getWindowTitle() const QString DecompilerWidget::getWindowTitle() const
{ {
return tr("Decompiler"); RAnalFunction *fcn = Core()->functionAt(decompiledFunctionAddr);
QString windowTitle = tr("Decompiler");
if (fcn != NULL) {
windowTitle += " (" + QString(fcn->name) + ")";
} else {
windowTitle += " (Empty)";
}
return windowTitle;
} }
void DecompilerWidget::fontsUpdatedSlot() void DecompilerWidget::fontsUpdatedSlot()
@ -501,7 +515,7 @@ void DecompilerWidget::highlightBreakpoints()
QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr); QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);
QTextCursor cursor; QTextCursor cursor;
for (auto &bp : functionBreakpoints) { for (RVA &bp : functionBreakpoints) {
if (bp == RVA_INVALID) { if (bp == RVA_INVALID) {
continue;; continue;;
} }
@ -539,3 +553,11 @@ void DecompilerWidget::copy()
} }
} }
} }
bool DecompilerWidget::addressInRange(RVA addr)
{
if (lowestOffsetInCode <= addr && addr <= highestOffsetInCode) {
return true;
}
return false;
}

View File

@ -47,7 +47,7 @@ private slots:
void cursorPositionChanged(); void cursorPositionChanged();
/** /**
* @brief When the synced seek is changed, this refreshes the decompiler widget if needed. * @brief When the synced seek is changed, this refreshes the decompiler widget if needed.
* *
* Decompiler widget is not refreshed in the following two cases * Decompiler widget is not refreshed in the following two cases
* - Seek changed to an offset contained in the decompiled function. * - Seek changed to an offset contained in the decompiled function.
* - Auto-refresh is disabled. * - Auto-refresh is disabled.
@ -62,36 +62,45 @@ private:
QSyntaxHighlighter *syntaxHighlighter; QSyntaxHighlighter *syntaxHighlighter;
bool decompilerSelectionEnabled; bool decompilerSelectionEnabled;
bool autoRefreshEnabled;
/** /**
* True if doRefresh() was called, but the decompiler was still running. * True if the selected decompiler is currently running a decompilation for this widget. Once the decompilation
* This means, after the decompiler has finished, it should be refreshed immediately. * is over, this should be set to false.
*/ */
bool decompilerWasBusy; bool decompilerBusy;
bool seekFromCursor;
int scrollerHorizontal; int scrollerHorizontal;
int scrollerVertical; int scrollerVertical;
RVA previousFunctionAddr; RVA previousFunctionAddr;
RVA decompiledFunctionAddr; RVA decompiledFunctionAddr;
std::unique_ptr<RAnnotatedCode, void (*)(RAnnotatedCode *)> code; std::unique_ptr<RAnnotatedCode, void (*)(RAnnotatedCode *)> code;
bool seekFromCursor = false;
/**
* Specifies the lowest offset of instructions among all the instructions in the decompiled function.
*/
RVA lowestOffsetInCode;
/**
* Specifies the highest offset of instructions among all the instructions in the decompiled function.
*/
RVA highestOffsetInCode;
/**
* @brief Gets the current decompiler selected by the user.
*
* @return A pointer to the currently selected decompiler
*/
Decompiler *getCurrentDecompiler(); Decompiler *getCurrentDecompiler();
/** /**
* @brief Enable/Disable auto refresh as per the specified boolean value * @brief Calls the function doRefresh() if the address specified is a part of the decompiled function.
* *
* @param enabled * @param addr Address at which a change occurred.
*/ */
void setAutoRefresh(bool enabled); void refreshIfChanged(RVA addr);
/**
* @brief Calls the function doRefresh() if auto-refresh is enabled.
*/
void doAutoRefresh();
/** /**
* @brief Refreshes the decompiler. * @brief Refreshes the decompiler.
* *
* - This does the following if the specified offset is valid * - This does the following if the specified offset is valid
* - Decompile function that contains the specified offset. * - Decompile function that contains the specified offset.
* - Clears all selections stored for highlighting purposes. * - Clears all selections stored for highlighting purposes.
@ -102,15 +111,14 @@ private:
* *
* @param addr Specified offset/offset in sync. * @param addr Specified offset/offset in sync.
*/ */
void doRefresh(RVA addr = Core()->getOffset()); void doRefresh();
void updateRefreshButton();
/** /**
* @brief Update fonts * @brief Update fonts
*/ */
void setupFonts(); void setupFonts();
/** /**
* @brief Update highlights in the text widget. * @brief Update highlights in the text widget.
* *
* These include respective highlights for: * These include respective highlights for:
* - Line under cursor * - Line under cursor
* - Word under cursor * - Word under cursor
@ -120,13 +128,13 @@ private:
/** /**
* @brief Connect/Disconnect SIGNAL-SLOT connection that deals with changes in cursor position. * @brief Connect/Disconnect SIGNAL-SLOT connection that deals with changes in cursor position.
* *
* If the argument is true, then disconnect the SIGNAL-SLOT connection * If the argument is true, then connect the SIGNAL-SLOT connection
* that changes the view as cursor position gets changed in the text widget. * that changes the view as cursor position gets changed in the text widget.
* Otherwise, connect the corresponding signal with slot. * Otherwise, disconnect the corresponding signal with slot.
* *
* @param disconnect * @param connectPositionChange
*/ */
void connectCursorPositionChanged(bool disconnect); void connectCursorPositionChanged(bool connectPositionChange);
/** /**
* @brief Find the current global offset in sync and update cursor * @brief Find the current global offset in sync and update cursor
* to the position specified by this offset (found using positionForOffset() ) * to the position specified by this offset (found using positionForOffset() )
@ -167,7 +175,7 @@ private:
bool colorLine(QTextEdit::ExtraSelection extraSelection); bool colorLine(QTextEdit::ExtraSelection extraSelection);
/** /**
* @brief This function responsible to highlight all the breakpoints in the decompiler view. * @brief This function is responsible for highlighting all the breakpoints in the decompiler view.
* It will also run when a breakpoint is added, removed or modified. * It will also run when a breakpoint is added, removed or modified.
*/ */
void highlightBreakpoints(); void highlightBreakpoints();
@ -205,7 +213,7 @@ private:
/** /**
* @brief Updates the view when breakpoints are changed * @brief Updates the view when breakpoints are changed
*/ */
void updateBreakpoints(); void updateBreakpoints(RVA addr);
/** /**
* @brief Set information about the breakpoints on the line in the context menu * @brief Set information about the breakpoints on the line in the context menu
*/ */
@ -217,6 +225,13 @@ private:
* @param pos Position of cursor in the decompiled code. * @param pos Position of cursor in the decompiled code.
*/ */
void setAnnotationsAtCursor(size_t pos); void setAnnotationsAtCursor(size_t pos);
/**
* @brief Checks if the specified address is a part of the decompiled function.
*
* @param addr An offset in the binary.
* @return True if the specified is a part of the decompiled function, False otherwise.
*/
bool addressInRange(RVA addr);
}; };
#endif // DECOMPILERWIDGET_H #endif // DECOMPILERWIDGET_H

View File

@ -42,20 +42,6 @@
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="autoRefreshCheckBox">
<property name="text">
<string>Auto Refresh</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="refreshButton">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="progressLayout"> <layout class="QHBoxLayout" name="progressLayout">
<property name="leftMargin"> <property name="leftMargin">

View File

@ -42,7 +42,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
auto *layout = new QVBoxLayout(this); auto *layout = new QVBoxLayout(this);
// Signals that require a refresh all // Signals that require a refresh all
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshView())); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshView()));
connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshView())); connect(Core(), &CutterCore::commentsChanged, this, &DisassemblerGraphView::refreshView);
connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView); connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView);
connect(Core(), SIGNAL(flagsChanged()), this, SLOT(refreshView())); connect(Core(), SIGNAL(flagsChanged()), this, SLOT(refreshView()));
connect(Core(), SIGNAL(varsChanged()), this, SLOT(refreshView())); connect(Core(), SIGNAL(varsChanged()), this, SLOT(refreshView()));

View File

@ -48,14 +48,13 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main)
setObjectName(main setObjectName(main
? main->getUniqueObjectName(getWidgetType()) ? main->getUniqueObjectName(getWidgetType())
: getWidgetType()); : getWidgetType());
updateWindowTitle();
topOffset = bottomOffset = RVA_INVALID; topOffset = bottomOffset = RVA_INVALID;
cursorLineOffset = 0; cursorLineOffset = 0;
cursorCharOffset = 0; cursorCharOffset = 0;
seekFromCursor = false; seekFromCursor = false;
setWindowTitle(getWindowTitle());
// Instantiate the window layout // Instantiate the window layout
auto *splitter = new QSplitter; auto *splitter = new QSplitter;
@ -145,7 +144,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main)
} }
}); });
connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshDisasm())); connect(Core(), &CutterCore::commentsChanged, this, [this]() {refreshDisasm();});
connect(Core(), SIGNAL(flagsChanged()), this, SLOT(refreshDisasm())); connect(Core(), SIGNAL(flagsChanged()), this, SLOT(refreshDisasm()));
connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshDisasm())); connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshDisasm()));
connect(Core(), &CutterCore::functionRenamed, this, [this]() {refreshDisasm();}); connect(Core(), &CutterCore::functionRenamed, this, [this]() {refreshDisasm();});

View File

@ -26,6 +26,7 @@ HexdumpWidget::HexdumpWidget(MainWindow *main) :
setObjectName(main setObjectName(main
? main->getUniqueObjectName(getWidgetType()) ? main->getUniqueObjectName(getWidgetType())
: getWidgetType()); : getWidgetType());
updateWindowTitle();
ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg")); ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg"));
ui->copySHA1->setIcon(QIcon(":/img/icons/copy.svg")); ui->copySHA1->setIcon(QIcon(":/img/icons/copy.svg"));
@ -76,8 +77,6 @@ HexdumpWidget::HexdumpWidget(MainWindow *main) :
" border-color : #3daee9" " border-color : #3daee9"
"}"); "}");
setWindowTitle(getWindowTitle());
refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) { refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) {
refresh(offset ? *offset : RVA_INVALID); refresh(offset ? *offset : RVA_INVALID);
}); });