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);
}
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();
void setSelectedDecompiler(const QString &id);
bool getDecompilerAutoRefreshEnabled();
void setDecompilerAutoRefreshEnabled(bool enabled);
signals:
void fontsUpdated();
void colorsUpdated();

View File

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

View File

@ -41,14 +41,29 @@ private slots:
private:
std::unique_ptr<Ui::PseudocodeWidget> ui;
RefreshDeferrer *refreshDeferrer;
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.
*/
bool decompilerWasBusy;
RVA decompiledFunctionAddr;
AnnotatedCode code;
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 updateSelection();
void connectCursorPositionChanged(bool disconnect);

View File

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