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)
|
R2DecDecompiler::R2DecDecompiler(QObject *parent)
|
||||||
: Decompiler("r2dec", "r2dec", parent)
|
: Decompiler("r2dec", "r2dec", parent)
|
||||||
{
|
{
|
||||||
|
task = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool R2DecDecompiler::isAvailable()
|
bool R2DecDecompiler::isAvailable()
|
||||||
@ -58,47 +59,59 @@ bool R2DecDecompiler::isAvailable()
|
|||||||
return Core()->cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd"));
|
return Core()->cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd"));
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnotatedCode R2DecDecompiler::decompileAt(RVA addr)
|
void R2DecDecompiler::decompileAt(RVA addr)
|
||||||
{
|
{
|
||||||
AnnotatedCode code = {};
|
if (task) {
|
||||||
QString s;
|
return;
|
||||||
|
|
||||||
QJsonObject json = Core()->cmdj("pddj @ " + QString::number(addr)).object();
|
|
||||||
if (json.isEmpty()) {
|
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &line : json["log"].toArray()) {
|
task = new R2Task("pddj @ " + QString::number(addr));
|
||||||
if (!line.isString()) {
|
connect(task, &R2Task::finished, this, [this]() {
|
||||||
continue;
|
AnnotatedCode code = {};
|
||||||
}
|
QString s;
|
||||||
code.code.append(line.toString() + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto linesArray = json["lines"].toArray();
|
QJsonObject json = task->getResultJson().object();
|
||||||
for (const auto &line : linesArray) {
|
delete task;
|
||||||
QJsonObject lineObject = line.toObject();
|
task = nullptr;
|
||||||
if (lineObject.isEmpty()) {
|
if (json.isEmpty()) {
|
||||||
continue;
|
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()) {
|
for (const auto &line : json["log"].toArray()) {
|
||||||
if (!line.isString()) {
|
if (!line.isString()) {
|
||||||
continue;
|
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
|
#define DECOMPILER_H
|
||||||
|
|
||||||
#include "CutterCommon.h"
|
#include "CutterCommon.h"
|
||||||
|
#include "R2Task.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@ -54,19 +55,30 @@ public:
|
|||||||
Decompiler(const QString &id, const QString &name, QObject *parent = nullptr);
|
Decompiler(const QString &id, const QString &name, QObject *parent = nullptr);
|
||||||
virtual ~Decompiler() = default;
|
virtual ~Decompiler() = default;
|
||||||
|
|
||||||
QString getId() const { return id; }
|
QString getId() const { return id; }
|
||||||
QString getName() const { return name; }
|
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
|
class R2DecDecompiler: public Decompiler
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
R2Task *task;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit R2DecDecompiler(QObject *parent = nullptr);
|
explicit R2DecDecompiler(QObject *parent = nullptr);
|
||||||
AnnotatedCode decompileAt(RVA addr) override;
|
void decompileAt(RVA addr) override;
|
||||||
|
|
||||||
|
bool isRunning() override { return task != nullptr; }
|
||||||
|
|
||||||
static bool isAvailable();
|
static bool isAvailable();
|
||||||
};
|
};
|
||||||
|
@ -47,6 +47,11 @@ QString R2Task::getResult()
|
|||||||
return QString::fromUtf8(task->res);
|
return QString::fromUtf8(task->res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonDocument R2Task::getResultJson()
|
||||||
|
{
|
||||||
|
return Core()->parseJson(task->res, task->cmd);
|
||||||
|
}
|
||||||
|
|
||||||
const char *R2Task::getResultRaw()
|
const char *R2Task::getResultRaw()
|
||||||
{
|
{
|
||||||
return task->res;
|
return task->res;
|
||||||
|
@ -23,6 +23,7 @@ public:
|
|||||||
void joinTask();
|
void joinTask();
|
||||||
|
|
||||||
QString getResult();
|
QString getResult();
|
||||||
|
QJsonDocument getResultJson();
|
||||||
const char *getResultRaw();
|
const char *getResultRaw();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -41,13 +41,14 @@ PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) :
|
|||||||
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, &PseudocodeWidget::decompilationFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decompilers.size() <= 1) {
|
decompilerSelectionEnabled = decompilers.size() > 1;
|
||||||
ui->decompilerComboBox->setEnabled(false);
|
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
||||||
if (decompilers.isEmpty()) {
|
|
||||||
ui->textEdit->setPlainText(tr("No Decompiler available."));
|
if (decompilers.isEmpty()) {
|
||||||
}
|
ui->textEdit->setPlainText(tr("No Decompiler available."));
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(ui->decompilerComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &PseudocodeWidget::decompilerSelected);
|
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);
|
connect(mCtxMenu, &QMenu::triggered, this, &PseudocodeWidget::refreshPseudocode);
|
||||||
addActions(mCtxMenu->actions());
|
addActions(mCtxMenu->actions());
|
||||||
|
|
||||||
|
ui->progressLabel->setVisible(false);
|
||||||
doRefresh(RVA_INVALID);
|
doRefresh(RVA_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +76,7 @@ void PseudocodeWidget::doRefresh(RVA addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Decompiler *dec = Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString());
|
Decompiler *dec = Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString());
|
||||||
if (!dec) {
|
if (!dec || dec->isRunning()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +85,34 @@ void PseudocodeWidget::doRefresh(RVA addr)
|
|||||||
return;
|
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()) {
|
if (code.code.isEmpty()) {
|
||||||
ui->textEdit->setPlainText(tr("Cannot decompile at") + " " + RAddressString(
|
ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)"));
|
||||||
addr) + " " + tr("(Not a function?)"));
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
connectCursorPositionChanged(true);
|
connectCursorPositionChanged(true);
|
||||||
@ -96,11 +122,6 @@ void PseudocodeWidget::doRefresh(RVA addr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PseudocodeWidget::refreshPseudocode()
|
|
||||||
{
|
|
||||||
doRefresh(Core()->getOffset());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PseudocodeWidget::decompilerSelected()
|
void PseudocodeWidget::decompilerSelected()
|
||||||
{
|
{
|
||||||
Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString());
|
Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString());
|
||||||
|
@ -36,11 +36,13 @@ private slots:
|
|||||||
void decompilerSelected();
|
void decompilerSelected();
|
||||||
void cursorPositionChanged();
|
void cursorPositionChanged();
|
||||||
void seekChanged();
|
void seekChanged();
|
||||||
|
void decompilationFinished(AnnotatedCode code);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::PseudocodeWidget> ui;
|
std::unique_ptr<Ui::PseudocodeWidget> ui;
|
||||||
|
|
||||||
QSyntaxHighlighter *syntaxHighlighter;
|
QSyntaxHighlighter *syntaxHighlighter;
|
||||||
|
bool decompilerSelectionEnabled;
|
||||||
|
|
||||||
AnnotatedCode code;
|
AnnotatedCode code;
|
||||||
|
|
||||||
|
@ -49,6 +49,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
@ -70,7 +84,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="decompilerComboBox" />
|
<widget class="QComboBox" name="decompilerComboBox"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user