mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-18 19:06:10 +00:00
Make Decompilation asynchronous (#1721)
This commit is contained in:
parent
d49caae37b
commit
76b9354673
@ -51,6 +51,7 @@ Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent)
|
||||
R2DecDecompiler::R2DecDecompiler(QObject *parent)
|
||||
: Decompiler("r2dec", "r2dec", parent)
|
||||
{
|
||||
task = nullptr;
|
||||
}
|
||||
|
||||
bool R2DecDecompiler::isAvailable()
|
||||
@ -58,47 +59,59 @@ bool R2DecDecompiler::isAvailable()
|
||||
return Core()->cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd"));
|
||||
}
|
||||
|
||||
AnnotatedCode R2DecDecompiler::decompileAt(RVA addr)
|
||||
void R2DecDecompiler::decompileAt(RVA addr)
|
||||
{
|
||||
AnnotatedCode code = {};
|
||||
QString s;
|
||||
|
||||
QJsonObject json = Core()->cmdj("pddj @ " + QString::number(addr)).object();
|
||||
if (json.isEmpty()) {
|
||||
return code;
|
||||
if (task) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &line : json["log"].toArray()) {
|
||||
if (!line.isString()) {
|
||||
continue;
|
||||
}
|
||||
code.code.append(line.toString() + "\n");
|
||||
}
|
||||
task = new R2Task("pddj @ " + QString::number(addr));
|
||||
connect(task, &R2Task::finished, this, [this]() {
|
||||
AnnotatedCode code = {};
|
||||
QString s;
|
||||
|
||||
auto linesArray = json["lines"].toArray();
|
||||
for (const auto &line : linesArray) {
|
||||
QJsonObject lineObject = line.toObject();
|
||||
if (lineObject.isEmpty()) {
|
||||
continue;
|
||||
QJsonObject json = task->getResultJson().object();
|
||||
delete task;
|
||||
task = nullptr;
|
||||
if (json.isEmpty()) {
|
||||
code.code = tr("Failed to parse JSON from r2dec");
|
||||
emit finished(code);
|
||||
return;
|
||||
}
|
||||
CodeAnnotation annotation = {};
|
||||
annotation.type = CodeAnnotation::Type::Offset;
|
||||
annotation.start = code.code.length();
|
||||
code.code.append(lineObject["str"].toString() + "\n");
|
||||
annotation.end = code.code.length();
|
||||
bool ok;
|
||||
annotation.offset.offset = lineObject["offset"].toVariant().toULongLong(&ok);
|
||||
if (ok) {
|
||||
code.annotations.push_back(annotation);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &line : json["errors"].toArray()) {
|
||||
if (!line.isString()) {
|
||||
continue;
|
||||
for (const auto &line : json["log"].toArray()) {
|
||||
if (!line.isString()) {
|
||||
continue;
|
||||
}
|
||||
code.code.append(line.toString() + "\n");
|
||||
}
|
||||
code.code.append(line.toString() + "\n");
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
auto linesArray = json["lines"].toArray();
|
||||
for (const auto &line : linesArray) {
|
||||
QJsonObject lineObject = line.toObject();
|
||||
if (lineObject.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
CodeAnnotation annotation = {};
|
||||
annotation.type = CodeAnnotation::Type::Offset;
|
||||
annotation.start = code.code.length();
|
||||
code.code.append(lineObject["str"].toString() + "\n");
|
||||
annotation.end = code.code.length();
|
||||
bool ok;
|
||||
annotation.offset.offset = lineObject["offset"].toVariant().toULongLong(&ok);
|
||||
if (ok) {
|
||||
code.annotations.push_back(annotation);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &line : json["errors"].toArray()) {
|
||||
if (!line.isString()) {
|
||||
continue;
|
||||
}
|
||||
code.code.append(line.toString() + "\n");
|
||||
}
|
||||
|
||||
emit finished(code);
|
||||
});
|
||||
task->startTask();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define DECOMPILER_H
|
||||
|
||||
#include "CutterCommon.h"
|
||||
#include "R2Task.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
@ -54,19 +55,30 @@ public:
|
||||
Decompiler(const QString &id, const QString &name, QObject *parent = nullptr);
|
||||
virtual ~Decompiler() = default;
|
||||
|
||||
QString getId() const { return id; }
|
||||
QString getName() const { return name; }
|
||||
QString getId() const { return id; }
|
||||
QString getName() const { return name; }
|
||||
virtual bool isRunning() { return false; }
|
||||
virtual bool isCancelable() { return false; }
|
||||
|
||||
virtual AnnotatedCode decompileAt(RVA addr) =0;
|
||||
virtual void decompileAt(RVA addr) =0;
|
||||
virtual void cancel() {}
|
||||
|
||||
signals:
|
||||
void finished(AnnotatedCode code);
|
||||
};
|
||||
|
||||
class R2DecDecompiler: public Decompiler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
R2Task *task;
|
||||
|
||||
public:
|
||||
explicit R2DecDecompiler(QObject *parent = nullptr);
|
||||
AnnotatedCode decompileAt(RVA addr) override;
|
||||
void decompileAt(RVA addr) override;
|
||||
|
||||
bool isRunning() override { return task != nullptr; }
|
||||
|
||||
static bool isAvailable();
|
||||
};
|
||||
|
@ -47,6 +47,11 @@ QString R2Task::getResult()
|
||||
return QString::fromUtf8(task->res);
|
||||
}
|
||||
|
||||
QJsonDocument R2Task::getResultJson()
|
||||
{
|
||||
return Core()->parseJson(task->res, task->cmd);
|
||||
}
|
||||
|
||||
const char *R2Task::getResultRaw()
|
||||
{
|
||||
return task->res;
|
||||
|
@ -23,6 +23,7 @@ public:
|
||||
void joinTask();
|
||||
|
||||
QString getResult();
|
||||
QJsonDocument getResultJson();
|
||||
const char *getResultRaw();
|
||||
|
||||
signals:
|
||||
|
@ -41,13 +41,14 @@ PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
|
||||
if (dec->getId() == selectedDecompilerId) {
|
||||
ui->decompilerComboBox->setCurrentIndex(ui->decompilerComboBox->count() - 1);
|
||||
}
|
||||
connect(dec, &Decompiler::finished, this, &PseudocodeWidget::decompilationFinished);
|
||||
}
|
||||
|
||||
if(decompilers.size() <= 1) {
|
||||
ui->decompilerComboBox->setEnabled(false);
|
||||
if (decompilers.isEmpty()) {
|
||||
ui->textEdit->setPlainText(tr("No Decompiler available."));
|
||||
}
|
||||
decompilerSelectionEnabled = decompilers.size() > 1;
|
||||
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
||||
|
||||
if (decompilers.isEmpty()) {
|
||||
ui->textEdit->setPlainText(tr("No Decompiler available."));
|
||||
}
|
||||
|
||||
connect(ui->decompilerComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &PseudocodeWidget::decompilerSelected);
|
||||
@ -61,6 +62,7 @@ PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
|
||||
connect(mCtxMenu, &QMenu::triggered, this, &PseudocodeWidget::refreshPseudocode);
|
||||
addActions(mCtxMenu->actions());
|
||||
|
||||
ui->progressLabel->setVisible(false);
|
||||
doRefresh(RVA_INVALID);
|
||||
}
|
||||
|
||||
@ -74,7 +76,7 @@ void PseudocodeWidget::doRefresh(RVA addr)
|
||||
}
|
||||
|
||||
Decompiler *dec = Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString());
|
||||
if (!dec) {
|
||||
if (!dec || dec->isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -83,10 +85,34 @@ void PseudocodeWidget::doRefresh(RVA addr)
|
||||
return;
|
||||
}
|
||||
|
||||
code = dec->decompileAt(addr);
|
||||
dec->decompileAt(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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PseudocodeWidget::refreshPseudocode()
|
||||
{
|
||||
doRefresh(Core()->getOffset());
|
||||
}
|
||||
|
||||
void PseudocodeWidget::decompilationFinished(AnnotatedCode code)
|
||||
{
|
||||
ui->progressLabel->setVisible(false);
|
||||
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
||||
ui->refreshButton->setText(tr("Refresh"));
|
||||
ui->refreshButton->setEnabled(true);
|
||||
|
||||
this->code = code;
|
||||
if (code.code.isEmpty()) {
|
||||
ui->textEdit->setPlainText(tr("Cannot decompile at") + " " + RAddressString(
|
||||
addr) + " " + tr("(Not a function?)"));
|
||||
ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)"));
|
||||
return;
|
||||
} else {
|
||||
connectCursorPositionChanged(true);
|
||||
@ -96,11 +122,6 @@ void PseudocodeWidget::doRefresh(RVA addr)
|
||||
}
|
||||
}
|
||||
|
||||
void PseudocodeWidget::refreshPseudocode()
|
||||
{
|
||||
doRefresh(Core()->getOffset());
|
||||
}
|
||||
|
||||
void PseudocodeWidget::decompilerSelected()
|
||||
{
|
||||
Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString());
|
||||
|
@ -36,11 +36,13 @@ private slots:
|
||||
void decompilerSelected();
|
||||
void cursorPositionChanged();
|
||||
void seekChanged();
|
||||
void decompilationFinished(AnnotatedCode code);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::PseudocodeWidget> ui;
|
||||
|
||||
QSyntaxHighlighter *syntaxHighlighter;
|
||||
bool decompilerSelectionEnabled;
|
||||
|
||||
AnnotatedCode code;
|
||||
|
||||
|
@ -49,6 +49,20 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="progressLayout">
|
||||
<property name="leftMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="progressLabel">
|
||||
<property name="text">
|
||||
<string>Decompiling...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@ -70,7 +84,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="decompilerComboBox" />
|
||||
<widget class="QComboBox" name="decompilerComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
Loading…
Reference in New Issue
Block a user