Decompiler Auto-Refresh (#1724)

* Implement Decompiler Auto-Refresh
This commit is contained in:
Florian Märkl 2019-09-01 11:06:54 +02:00 committed by Itay Cohen
parent 81f7169379
commit 86473e3465
5 changed files with 126 additions and 17 deletions

View File

@ -652,3 +652,13 @@ void Configuration::setSelectedDecompiler(const QString &id)
{ {
s.setValue("selectedDecompiler", id); s.setValue("selectedDecompiler", id);
} }
bool Configuration::getDecompilerAutoRefreshEnabled()
{
return s.value("decompilerAutoRefresh", true).toBool();
}
void Configuration::setDecompilerAutoRefreshEnabled(bool enabled)
{
s.setValue("decompilerAutoRefresh", enabled);
}

View File

@ -154,6 +154,9 @@ public:
QString getSelectedDecompiler(); QString getSelectedDecompiler();
void setSelectedDecompiler(const QString &id); void setSelectedDecompiler(const QString &id);
bool getDecompilerAutoRefreshEnabled();
void setDecompilerAutoRefreshEnabled(bool enabled);
signals: signals:
void fontsUpdated(); void fontsUpdated();
void colorsUpdated(); void colorsUpdated();

View File

@ -29,9 +29,24 @@ PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated()));
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(colorsUpdatedSlot()));
// TODO Use RefreshDeferrer and remove the refresh button decompiledFunctionAddr = RVA_INVALID;
decompilerWasBusy = false;
connect(ui->refreshButton, &QAbstractButton::clicked, this, [this]() { connect(ui->refreshButton, &QAbstractButton::clicked, this, [this]() {
doRefresh(Core()->getOffset()); 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 decompilers = Core()->getDecompilers();
@ -64,19 +79,66 @@ PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
ui->progressLabel->setVisible(false); ui->progressLabel->setVisible(false);
doRefresh(RVA_INVALID); doRefresh(RVA_INVALID);
connect(Core(), &CutterCore::refreshAll, this, &PseudocodeWidget::doAutoRefresh);
connect(Core(), &CutterCore::functionRenamed, this, &PseudocodeWidget::doAutoRefresh);
connect(Core(), &CutterCore::varsChanged, this, &PseudocodeWidget::doAutoRefresh);
connect(Core(), &CutterCore::functionsChanged, this, &PseudocodeWidget::doAutoRefresh);
connect(Core(), &CutterCore::flagsChanged, this, &PseudocodeWidget::doAutoRefresh);
connect(Core(), &CutterCore::commentsChanged, this, &PseudocodeWidget::doAutoRefresh);
connect(Core(), &CutterCore::instructionChanged, this, &PseudocodeWidget::doAutoRefresh);
connect(Core(), &CutterCore::refreshCodeViews, this, &PseudocodeWidget::doAutoRefresh);
} }
PseudocodeWidget::~PseudocodeWidget() = default; PseudocodeWidget::~PseudocodeWidget() = default;
Decompiler *PseudocodeWidget::getCurrentDecompiler()
{
return Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString());
}
void PseudocodeWidget::setAutoRefresh(bool enabled)
{
autoRefreshEnabled = enabled;
updateRefreshButton();
}
void PseudocodeWidget::doAutoRefresh()
{
if (!autoRefreshEnabled) {
return;
}
doRefresh();
}
void PseudocodeWidget::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"));
}
}
void PseudocodeWidget::doRefresh(RVA addr) void PseudocodeWidget::doRefresh(RVA addr)
{ {
if (!refreshDeferrer->attemptRefresh(nullptr)) {
return;
}
if (ui->decompilerComboBox->currentIndex() < 0) { if (ui->decompilerComboBox->currentIndex() < 0) {
return; return;
} }
Decompiler *dec = Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString()); Decompiler *dec = getCurrentDecompiler();
if (!dec || dec->isRunning()) { if (!dec) {
return;
}
if (dec->isRunning()) {
decompilerWasBusy = true;
return; return;
} }
@ -89,26 +151,21 @@ void PseudocodeWidget::doRefresh(RVA addr)
if (dec->isRunning()) { if (dec->isRunning()) {
ui->progressLabel->setVisible(true); ui->progressLabel->setVisible(true);
ui->decompilerComboBox->setEnabled(false); ui->decompilerComboBox->setEnabled(false);
if (dec->isCancelable()) { updateRefreshButton();
ui->refreshButton->setText(tr("Cancel"));
} else {
ui->refreshButton->setEnabled(false);
}
return; return;
} }
} }
void PseudocodeWidget::refreshPseudocode() void PseudocodeWidget::refreshPseudocode()
{ {
doRefresh(Core()->getOffset()); doRefresh();
} }
void PseudocodeWidget::decompilationFinished(AnnotatedCode code) void PseudocodeWidget::decompilationFinished(AnnotatedCode code)
{ {
ui->progressLabel->setVisible(false); ui->progressLabel->setVisible(false);
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled); ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
ui->refreshButton->setText(tr("Refresh")); updateRefreshButton();
ui->refreshButton->setEnabled(true);
this->code = code; this->code = code;
if (code.code.isEmpty()) { if (code.code.isEmpty()) {
@ -118,13 +175,21 @@ void PseudocodeWidget::decompilationFinished(AnnotatedCode code)
connectCursorPositionChanged(true); connectCursorPositionChanged(true);
ui->textEdit->setPlainText(code.code); ui->textEdit->setPlainText(code.code);
connectCursorPositionChanged(false); connectCursorPositionChanged(false);
seekChanged(); updateCursorPosition();
}
if (decompilerWasBusy) {
decompilerWasBusy = false;
doAutoRefresh();
} }
} }
void PseudocodeWidget::decompilerSelected() void PseudocodeWidget::decompilerSelected()
{ {
Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString()); Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString());
if (autoRefreshEnabled) {
doRefresh();
}
} }
void PseudocodeWidget::connectCursorPositionChanged(bool disconnect) void PseudocodeWidget::connectCursorPositionChanged(bool disconnect)
@ -154,6 +219,15 @@ void PseudocodeWidget::seekChanged()
if (seekFromCursor) { if (seekFromCursor) {
return; return;
} }
if (autoRefreshEnabled) {
auto fcnAddr = Core()->getFunctionStart(Core()->getOffset());
if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {
doRefresh();
return;
}
}
updateCursorPosition(); updateCursorPosition();
} }
@ -212,5 +286,5 @@ void PseudocodeWidget::colorsUpdatedSlot()
void PseudocodeWidget::showDisasContextMenu(const QPoint &pt) void PseudocodeWidget::showDisasContextMenu(const QPoint &pt)
{ {
mCtxMenu->exec(ui->textEdit->mapToGlobal(pt)); mCtxMenu->exec(ui->textEdit->mapToGlobal(pt));
doRefresh(Core()->getOffset()); doRefresh();
} }

View File

@ -41,14 +41,29 @@ private slots:
private: private:
std::unique_ptr<Ui::PseudocodeWidget> ui; std::unique_ptr<Ui::PseudocodeWidget> ui;
RefreshDeferrer *refreshDeferrer;
QSyntaxHighlighter *syntaxHighlighter; QSyntaxHighlighter *syntaxHighlighter;
bool decompilerSelectionEnabled; 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.
*/
bool decompilerWasBusy;
RVA decompiledFunctionAddr;
AnnotatedCode code; AnnotatedCode code;
bool seekFromCursor = false; bool seekFromCursor = false;
void doRefresh(RVA addr); Decompiler *getCurrentDecompiler();
void setAutoRefresh(bool enabled);
void doAutoRefresh();
void doRefresh(RVA addr = Core()->getOffset());
void updateRefreshButton();
void setupFonts(); void setupFonts();
void updateSelection(); void updateSelection();
void connectCursorPositionChanged(bool disconnect); void connectCursorPositionChanged(bool disconnect);

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>555</width>
<height>300</height> <height>393</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -42,6 +42,13 @@
</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> <item>
<widget class="QPushButton" name="refreshButton"> <widget class="QPushButton" name="refreshButton">
<property name="text"> <property name="text">