mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-18 10:56:11 +00:00
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:
parent
ec22b01086
commit
691de14853
@ -59,6 +59,12 @@ Show 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
|
||||
----------------------------------------------
|
||||
**Description:** Create a new instance of the Disassembly widget in order to view one or multiple locations at the same time.
|
||||
|
@ -812,13 +812,13 @@ int CutterCore::sizeofDataMeta(RVA addr)
|
||||
void CutterCore::setComment(RVA addr, const QString &cmt)
|
||||
{
|
||||
cmdRawAt(QString("CCu base64:%1").arg(QString(cmt.toLocal8Bit().toBase64())), addr);
|
||||
emit commentsChanged();
|
||||
emit commentsChanged(addr);
|
||||
}
|
||||
|
||||
void CutterCore::delComment(RVA addr)
|
||||
{
|
||||
cmdRawAt("CC-", addr);
|
||||
emit commentsChanged();
|
||||
emit commentsChanged(addr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -647,7 +647,7 @@ signals:
|
||||
void varsChanged();
|
||||
void functionsChanged();
|
||||
void flagsChanged();
|
||||
void commentsChanged();
|
||||
void commentsChanged(RVA addr);
|
||||
void registersChanged();
|
||||
void instructionChanged(RVA offset);
|
||||
void breakpointsChanged(RVA offset);
|
||||
|
@ -143,6 +143,7 @@ void MainWindow::initUI()
|
||||
disassemblyContextMenuExtensions = 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->actionExtraDisassembly, &QAction::triggered, this, &MainWindow::addExtraDisassembly);
|
||||
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
|
||||
@ -347,7 +348,6 @@ void MainWindow::initToolBar()
|
||||
void MainWindow::initDocks()
|
||||
{
|
||||
dockWidgets.reserve(20);
|
||||
decompilerDock = new DecompilerWidget(this);
|
||||
consoleDock = new ConsoleWidget(this);
|
||||
|
||||
overviewDock = new OverviewWidget(this);
|
||||
@ -415,7 +415,6 @@ void MainWindow::initDocks()
|
||||
dashboardDock,
|
||||
nullptr,
|
||||
functionsDock,
|
||||
decompilerDock,
|
||||
overviewDock,
|
||||
nullptr,
|
||||
searchDock,
|
||||
@ -423,7 +422,7 @@ void MainWindow::initDocks()
|
||||
typesDock,
|
||||
nullptr,
|
||||
};
|
||||
ui->menuWindows->insertActions(ui->actionExtraDisassembly, makeActionList(windowDocks));
|
||||
ui->menuWindows->insertActions(ui->actionExtraDecompiler, makeActionList(windowDocks));
|
||||
QList<CutterDockWidget *> windowDocks2 = {
|
||||
consoleDock,
|
||||
commentsDock,
|
||||
@ -475,6 +474,12 @@ void MainWindow::addExtraDisassembly()
|
||||
addExtraWidget(extraDock);
|
||||
}
|
||||
|
||||
void MainWindow::addExtraDecompiler()
|
||||
{
|
||||
auto *extraDock = new DecompilerWidget(this);
|
||||
addExtraWidget(extraDock);
|
||||
}
|
||||
|
||||
void MainWindow::addExtraWidget(CutterDockWidget *extraDock)
|
||||
{
|
||||
extraDock->setTransient(true);
|
||||
@ -820,7 +825,6 @@ void MainWindow::restoreDocks()
|
||||
splitDockWidget(functionsDock, overviewDock, Qt::Vertical);
|
||||
|
||||
// main area
|
||||
tabifyDockWidget(dashboardDock, decompilerDock);
|
||||
tabifyDockWidget(dashboardDock, entrypointDock);
|
||||
tabifyDockWidget(dashboardDock, flagsDock);
|
||||
tabifyDockWidget(dashboardDock, stringsDock);
|
||||
@ -989,7 +993,7 @@ QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address, AddressTypeHi
|
||||
createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph);
|
||||
}
|
||||
createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump);
|
||||
|
||||
createAddNewWidgetAction(tr("New Decompiler"), MemoryWidgetType::Decompiler);
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
@ -168,6 +168,7 @@ private slots:
|
||||
void addExtraGraph();
|
||||
void addExtraHexdump();
|
||||
void addExtraDisassembly();
|
||||
void addExtraDecompiler();
|
||||
|
||||
void on_actionRefresh_Panels_triggered();
|
||||
|
||||
@ -236,7 +237,6 @@ private:
|
||||
|
||||
QList<CutterDockWidget *> dockWidgets;
|
||||
QList<CutterDockWidget *> pluginDocks;
|
||||
DecompilerWidget *decompilerDock = nullptr;
|
||||
OverviewWidget *overviewDock = nullptr;
|
||||
QAction *actionOverview = nullptr;
|
||||
EntrypointWidget *entrypointDock = nullptr;
|
||||
|
@ -162,6 +162,7 @@
|
||||
<string>Debug...</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="actionExtraDecompiler"/>
|
||||
<addaction name="actionExtraDisassembly"/>
|
||||
<addaction name="actionExtraGraph"/>
|
||||
<addaction name="actionExtraHexdump"/>
|
||||
@ -756,6 +757,11 @@
|
||||
<string>Add Hexdump</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExtraDecompiler">
|
||||
<property name="text">
|
||||
<string>Add Decompiler</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExtraDisassembly">
|
||||
<property name="text">
|
||||
<string>Add Disassembly</string>
|
||||
|
@ -147,7 +147,7 @@ private:
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "common/SelectionHighlight.h"
|
||||
#include "common/Decompiler.h"
|
||||
#include "common/CutterSeekable.h"
|
||||
#include "core/MainWindow.h"
|
||||
|
||||
#include <QTextEdit>
|
||||
#include <QPlainTextEdit>
|
||||
@ -22,7 +23,8 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
|
||||
MemoryDockWidget(MemoryWidgetType::Decompiler, main),
|
||||
mCtxMenu(new DecompilerContextMenu(this, main)),
|
||||
ui(new Ui::DecompilerWidget),
|
||||
decompilerWasBusy(false),
|
||||
decompilerBusy(false),
|
||||
seekFromCursor(false),
|
||||
scrollerHorizontal(0),
|
||||
scrollerVertical(0),
|
||||
previousFunctionAddr(RVA_INVALID),
|
||||
@ -31,6 +33,11 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
|
||||
&r_annotated_code_free)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setObjectName(main
|
||||
? main->getUniqueObjectName(tr("Decompiler"))
|
||||
: tr("Decompiler"));
|
||||
updateWindowTitle();
|
||||
|
||||
syntaxHighlighter = Config()->createSyntaxHighlighter(ui->textEdit->document());
|
||||
// Event filter to intercept double click and right click in the textbox
|
||||
ui->textEdit->viewport()->installEventFilter(this);
|
||||
@ -43,33 +50,21 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
|
||||
connect(Core(), &CutterCore::registersChanged, this, &DecompilerWidget::highlightPC);
|
||||
connect(mCtxMenu, &DecompilerContextMenu::copy, this, &DecompilerWidget::copy);
|
||||
|
||||
connect(ui->refreshButton, &QAbstractButton::clicked, this, [this]() {
|
||||
doRefresh();
|
||||
});
|
||||
refreshDeferrer = createRefreshDeferrer([this]() {
|
||||
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 selectedDecompilerId = Config()->getSelectedDecompiler();
|
||||
QString selectedDecompilerId = Config()->getSelectedDecompiler();
|
||||
if (selectedDecompilerId.isEmpty()) {
|
||||
// If no decompiler was previously chosen. set r2ghidra as default decompiler
|
||||
selectedDecompilerId = "r2ghidra";
|
||||
}
|
||||
for (auto dec : decompilers) {
|
||||
for (Decompiler *dec : decompilers) {
|
||||
ui->decompilerComboBox->addItem(dec->getName(), dec->getId());
|
||||
if (dec->getId() == selectedDecompilerId) {
|
||||
ui->decompilerComboBox->setCurrentIndex(ui->decompilerComboBox->count() - 1);
|
||||
}
|
||||
connect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
|
||||
}
|
||||
decompilerSelectionEnabled = decompilers.size() > 1;
|
||||
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
||||
@ -80,26 +75,28 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
|
||||
connect(ui->decompilerComboBox,
|
||||
static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
&DecompilerWidget::decompilerSelected);
|
||||
connectCursorPositionChanged(false);
|
||||
connect(Core(), &CutterCore::seekChanged, this, &DecompilerWidget::seekChanged);
|
||||
connectCursorPositionChanged(true);
|
||||
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DecompilerWidget::seekChanged);
|
||||
ui->textEdit->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->textEdit, &QWidget::customContextMenuRequested,
|
||||
this, &DecompilerWidget::showDecompilerContextMenu);
|
||||
|
||||
connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::updateBreakpoints);
|
||||
mCtxMenu->addSeparator();
|
||||
mCtxMenu->addAction(&syncAction);
|
||||
addActions(mCtxMenu->actions());
|
||||
|
||||
ui->progressLabel->setVisible(false);
|
||||
doRefresh(RVA_INVALID);
|
||||
doRefresh();
|
||||
|
||||
connect(Core(), &CutterCore::refreshAll, this, &DecompilerWidget::doAutoRefresh);
|
||||
connect(Core(), &CutterCore::functionRenamed, this, &DecompilerWidget::doAutoRefresh);
|
||||
connect(Core(), &CutterCore::varsChanged, this, &DecompilerWidget::doAutoRefresh);
|
||||
connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doAutoRefresh);
|
||||
connect(Core(), &CutterCore::flagsChanged, this, &DecompilerWidget::doAutoRefresh);
|
||||
connect(Core(), &CutterCore::commentsChanged, this, &DecompilerWidget::doAutoRefresh);
|
||||
connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::doAutoRefresh);
|
||||
connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doAutoRefresh);
|
||||
connect(Core(), &CutterCore::refreshAll, this, &DecompilerWidget::doRefresh);
|
||||
connect(Core(), &CutterCore::functionRenamed, this, &DecompilerWidget::doRefresh);
|
||||
connect(Core(), &CutterCore::varsChanged, this, &DecompilerWidget::doRefresh);
|
||||
connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doRefresh);
|
||||
connect(Core(), &CutterCore::flagsChanged, this, &DecompilerWidget::doRefresh);
|
||||
connect(Core(), &CutterCore::commentsChanged, this, &DecompilerWidget::refreshIfChanged);
|
||||
connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::refreshIfChanged);
|
||||
connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doRefresh);
|
||||
|
||||
// Esc to seek backward
|
||||
QAction *seekPrevAction = new QAction(this);
|
||||
@ -116,31 +113,6 @@ Decompiler *DecompilerWidget::getCurrentDecompiler()
|
||||
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)
|
||||
{
|
||||
size_t closestPos = SIZE_MAX;
|
||||
@ -180,8 +152,11 @@ size_t DecompilerWidget::positionForOffset(ut64 offset)
|
||||
return closestPos;
|
||||
}
|
||||
|
||||
void DecompilerWidget::updateBreakpoints()
|
||||
void DecompilerWidget::updateBreakpoints(RVA addr)
|
||||
{
|
||||
if (!addressInRange(addr)) {
|
||||
return;
|
||||
}
|
||||
setInfoForBreakpoints();
|
||||
QTextCursor cursor = ui->textEdit->textCursor();
|
||||
cursor.select(QTextCursor::Document);
|
||||
@ -195,8 +170,9 @@ void DecompilerWidget::updateBreakpoints()
|
||||
|
||||
void DecompilerWidget::setInfoForBreakpoints()
|
||||
{
|
||||
if (mCtxMenu->getIsTogglingBreakpoints())
|
||||
if (mCtxMenu->getIsTogglingBreakpoints()) {
|
||||
return;
|
||||
}
|
||||
// Get the range of the line
|
||||
QTextCursor cursorForLine = ui->textEdit->textCursor();
|
||||
cursorForLine.movePosition(QTextCursor::StartOfLine);
|
||||
@ -224,7 +200,7 @@ void DecompilerWidget::gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size
|
||||
mCtxMenu->setFirstOffsetInLine(firstOffset);
|
||||
QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);
|
||||
QVector<RVA> offsetList;
|
||||
for (auto bpOffset : functionBreakpoints) {
|
||||
for (RVA bpOffset : functionBreakpoints) {
|
||||
size_t pos = positionForOffset(bpOffset);
|
||||
if (startPos <= pos && pos <= endPos) {
|
||||
offsetList.push_back(bpOffset);
|
||||
@ -234,8 +210,16 @@ void DecompilerWidget::gatherBreakpointInfo(RAnnotatedCode &codeDecompiled, size
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
@ -246,26 +230,36 @@ void DecompilerWidget::doRefresh(RVA addr)
|
||||
if (!dec) {
|
||||
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()) {
|
||||
decompilerWasBusy = true;
|
||||
return;
|
||||
}
|
||||
if (addr == RVA_INVALID) {
|
||||
ui->textEdit->setPlainText(tr("Click Refresh to generate Decompiler from current offset."));
|
||||
if (!decompilerBusy) {
|
||||
connect(dec, &Decompiler::finished, this, &DecompilerWidget::doRefresh);
|
||||
}
|
||||
return;
|
||||
}
|
||||
disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::doRefresh);
|
||||
// Clear all selections since we just refreshed
|
||||
ui->textEdit->setExtraSelections({});
|
||||
previousFunctionAddr = decompiledFunctionAddr;
|
||||
decompiledFunctionAddr = Core()->getFunctionStart(addr);
|
||||
mCtxMenu->setDecompiledFunctionAddress(decompiledFunctionAddr);
|
||||
dec->decompileAt(addr);
|
||||
if (dec->isRunning()) {
|
||||
ui->progressLabel->setVisible(true);
|
||||
ui->decompilerComboBox->setEnabled(false);
|
||||
updateRefreshButton();
|
||||
updateWindowTitle();
|
||||
if (decompiledFunctionAddr == RVA_INVALID) {
|
||||
// No function was found, so making the progress label invisible and enabling
|
||||
// the decompiler selection combo box as we are not waiting for any decompilation to finish.
|
||||
ui->progressLabel->setVisible(false);
|
||||
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;
|
||||
}
|
||||
mCtxMenu->setDecompiledFunctionAddress(decompiledFunctionAddr);
|
||||
connect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
|
||||
decompilerBusy = true;
|
||||
dec->decompileAt(addr);
|
||||
}
|
||||
|
||||
void DecompilerWidget::refreshDecompiler()
|
||||
@ -296,26 +290,43 @@ void DecompilerWidget::decompilationFinished(RAnnotatedCode *codeDecompiled)
|
||||
|
||||
ui->progressLabel->setVisible(false);
|
||||
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
||||
updateRefreshButton();
|
||||
|
||||
mCtxMenu->setAnnotationHere(nullptr);
|
||||
this->code.reset(codeDecompiled);
|
||||
|
||||
Decompiler *dec = getCurrentDecompiler();
|
||||
QObject::disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
|
||||
decompilerBusy = false;
|
||||
|
||||
QString codeString = QString::fromUtf8(this->code->code);
|
||||
if (codeString.isEmpty()) {
|
||||
connectCursorPositionChanged(false);
|
||||
ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)"));
|
||||
connectCursorPositionChanged(true);
|
||||
lowestOffsetInCode = RVA_MAX;
|
||||
highestOffsetInCode = 0;
|
||||
return;
|
||||
} else {
|
||||
connectCursorPositionChanged(true);
|
||||
ui->textEdit->setPlainText(codeString);
|
||||
connectCursorPositionChanged(false);
|
||||
ui->textEdit->setPlainText(codeString);
|
||||
connectCursorPositionChanged(true);
|
||||
updateCursorPosition();
|
||||
highlightPC();
|
||||
highlightBreakpoints();
|
||||
}
|
||||
|
||||
if (decompilerWasBusy) {
|
||||
decompilerWasBusy = false;
|
||||
doAutoRefresh();
|
||||
lowestOffsetInCode = RVA_MAX;
|
||||
highestOffsetInCode = 0;
|
||||
void *iter;
|
||||
r_vector_foreach(&code->annotations, iter) {
|
||||
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) {
|
||||
@ -344,16 +355,14 @@ void DecompilerWidget::setAnnotationsAtCursor(size_t pos)
|
||||
void DecompilerWidget::decompilerSelected()
|
||||
{
|
||||
Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString());
|
||||
if (autoRefreshEnabled) {
|
||||
doRefresh();
|
||||
}
|
||||
doRefresh();
|
||||
}
|
||||
|
||||
void DecompilerWidget::connectCursorPositionChanged(bool disconnect)
|
||||
void DecompilerWidget::connectCursorPositionChanged(bool connectPositionChange)
|
||||
{
|
||||
if (disconnect) {
|
||||
QObject::disconnect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this,
|
||||
&DecompilerWidget::cursorPositionChanged);
|
||||
if (!connectPositionChange) {
|
||||
disconnect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this,
|
||||
&DecompilerWidget::cursorPositionChanged);
|
||||
} else {
|
||||
connect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this,
|
||||
&DecompilerWidget::cursorPositionChanged);
|
||||
@ -372,9 +381,9 @@ void DecompilerWidget::cursorPositionChanged()
|
||||
setInfoForBreakpoints();
|
||||
|
||||
RVA offset = offsetForPosition(pos);
|
||||
if (offset != RVA_INVALID && offset != Core()->getOffset()) {
|
||||
if (offset != RVA_INVALID && offset != seekable->getOffset()) {
|
||||
seekFromCursor = true;
|
||||
Core()->seek(offset);
|
||||
seekable->seek(offset);
|
||||
mCtxMenu->setOffset(offset);
|
||||
seekFromCursor = false;
|
||||
}
|
||||
@ -386,30 +395,28 @@ void DecompilerWidget::seekChanged()
|
||||
if (seekFromCursor) {
|
||||
return;
|
||||
}
|
||||
if (autoRefreshEnabled) {
|
||||
auto fcnAddr = Core()->getFunctionStart(Core()->getOffset());
|
||||
if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {
|
||||
doRefresh();
|
||||
return;
|
||||
}
|
||||
RVA fcnAddr = Core()->getFunctionStart(seekable->getOffset());
|
||||
if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {
|
||||
doRefresh();
|
||||
return;
|
||||
}
|
||||
updateCursorPosition();
|
||||
}
|
||||
|
||||
void DecompilerWidget::updateCursorPosition()
|
||||
{
|
||||
RVA offset = Core()->getOffset();
|
||||
RVA offset = seekable->getOffset();
|
||||
size_t pos = positionForOffset(offset);
|
||||
if (pos == SIZE_MAX) {
|
||||
return;
|
||||
}
|
||||
mCtxMenu->setOffset(offset);
|
||||
connectCursorPositionChanged(true);
|
||||
connectCursorPositionChanged(false);
|
||||
QTextCursor cursor = ui->textEdit->textCursor();
|
||||
cursor.setPosition(pos);
|
||||
ui->textEdit->setTextCursor(cursor);
|
||||
updateSelection();
|
||||
connectCursorPositionChanged(false);
|
||||
connectCursorPositionChanged(true);
|
||||
}
|
||||
|
||||
void DecompilerWidget::setupFonts()
|
||||
@ -422,7 +429,7 @@ void DecompilerWidget::updateSelection()
|
||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||
|
||||
// Highlight the current line
|
||||
auto cursor = ui->textEdit->textCursor();
|
||||
QTextCursor cursor = ui->textEdit->textCursor();
|
||||
extraSelections.append(createLineHighlightSelection(cursor));
|
||||
|
||||
// Highlight all the words in the document same as the current one
|
||||
@ -438,7 +445,14 @@ void DecompilerWidget::updateSelection()
|
||||
|
||||
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()
|
||||
@ -501,7 +515,7 @@ void DecompilerWidget::highlightBreakpoints()
|
||||
|
||||
QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);
|
||||
QTextCursor cursor;
|
||||
for (auto &bp : functionBreakpoints) {
|
||||
for (RVA &bp : functionBreakpoints) {
|
||||
if (bp == RVA_INVALID) {
|
||||
continue;;
|
||||
}
|
||||
@ -539,3 +553,11 @@ void DecompilerWidget::copy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DecompilerWidget::addressInRange(RVA addr)
|
||||
{
|
||||
if (lowestOffsetInCode <= addr && addr <= highestOffsetInCode) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ private slots:
|
||||
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.
|
||||
@ -62,36 +62,45 @@ private:
|
||||
|
||||
QSyntaxHighlighter *syntaxHighlighter;
|
||||
bool decompilerSelectionEnabled;
|
||||
bool autoRefreshEnabled;
|
||||
|
||||
/**
|
||||
* True if doRefresh() was called, but the decompiler was still running.
|
||||
* This means, after the decompiler has finished, it should be refreshed immediately.
|
||||
* True if the selected decompiler is currently running a decompilation for this widget. Once the decompilation
|
||||
* is over, this should be set to false.
|
||||
*/
|
||||
bool decompilerWasBusy;
|
||||
bool decompilerBusy;
|
||||
|
||||
bool seekFromCursor;
|
||||
int scrollerHorizontal;
|
||||
int scrollerVertical;
|
||||
RVA previousFunctionAddr;
|
||||
RVA decompiledFunctionAddr;
|
||||
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();
|
||||
|
||||
/**
|
||||
* @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);
|
||||
/**
|
||||
* @brief Calls the function doRefresh() if auto-refresh is enabled.
|
||||
*/
|
||||
void doAutoRefresh();
|
||||
void refreshIfChanged(RVA addr);
|
||||
/**
|
||||
* @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.
|
||||
@ -102,15 +111,14 @@ private:
|
||||
*
|
||||
* @param addr Specified offset/offset in sync.
|
||||
*/
|
||||
void doRefresh(RVA addr = Core()->getOffset());
|
||||
void updateRefreshButton();
|
||||
void doRefresh();
|
||||
/**
|
||||
* @brief Update fonts
|
||||
*/
|
||||
void setupFonts();
|
||||
/**
|
||||
* @brief Update highlights in the text widget.
|
||||
*
|
||||
*
|
||||
* These include respective highlights for:
|
||||
* - Line under cursor
|
||||
* - Word under cursor
|
||||
@ -120,13 +128,13 @@ private:
|
||||
/**
|
||||
* @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.
|
||||
* 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
|
||||
* to the position specified by this offset (found using positionForOffset() )
|
||||
@ -167,7 +175,7 @@ private:
|
||||
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.
|
||||
*/
|
||||
void highlightBreakpoints();
|
||||
@ -205,7 +213,7 @@ private:
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
@ -217,6 +225,13 @@ private:
|
||||
* @param pos Position of cursor in the decompiled code.
|
||||
*/
|
||||
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
|
||||
|
@ -42,20 +42,6 @@
|
||||
</item>
|
||||
<item>
|
||||
<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>
|
||||
<layout class="QHBoxLayout" name="progressLayout">
|
||||
<property name="leftMargin">
|
||||
|
@ -42,7 +42,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
// Signals that require a refresh all
|
||||
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(), SIGNAL(flagsChanged()), this, SLOT(refreshView()));
|
||||
connect(Core(), SIGNAL(varsChanged()), this, SLOT(refreshView()));
|
||||
|
@ -48,14 +48,13 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main)
|
||||
setObjectName(main
|
||||
? main->getUniqueObjectName(getWidgetType())
|
||||
: getWidgetType());
|
||||
updateWindowTitle();
|
||||
|
||||
topOffset = bottomOffset = RVA_INVALID;
|
||||
cursorLineOffset = 0;
|
||||
cursorCharOffset = 0;
|
||||
seekFromCursor = false;
|
||||
|
||||
setWindowTitle(getWindowTitle());
|
||||
|
||||
// Instantiate the window layout
|
||||
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(functionsChanged()), this, SLOT(refreshDisasm()));
|
||||
connect(Core(), &CutterCore::functionRenamed, this, [this]() {refreshDisasm();});
|
||||
|
@ -26,6 +26,7 @@ HexdumpWidget::HexdumpWidget(MainWindow *main) :
|
||||
setObjectName(main
|
||||
? main->getUniqueObjectName(getWidgetType())
|
||||
: getWidgetType());
|
||||
updateWindowTitle();
|
||||
|
||||
ui->copyMD5->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"
|
||||
"}");
|
||||
|
||||
setWindowTitle(getWindowTitle());
|
||||
|
||||
refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) {
|
||||
refresh(offset ? *offset : RVA_INVALID);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user