2020-12-16 10:59:23 +00:00
|
|
|
#include "RizinGraphWidget.h"
|
|
|
|
#include "ui_RizinGraphWidget.h"
|
2020-07-16 08:05:10 +00:00
|
|
|
|
|
|
|
#include <QJsonValue>
|
|
|
|
#include <QJsonArray>
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
2020-12-16 10:59:23 +00:00
|
|
|
RizinGraphWidget::RizinGraphWidget(MainWindow *main)
|
2021-01-24 14:50:13 +00:00
|
|
|
: CutterDockWidget(main),
|
|
|
|
ui(new Ui::RizinGraphWidget),
|
|
|
|
graphView(new GenericRizinGraphView(this, main))
|
2020-07-16 08:05:10 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
ui->verticalLayout->addWidget(graphView);
|
2021-01-24 14:50:13 +00:00
|
|
|
connect(ui->refreshButton, &QPushButton::pressed, this, [this]() { graphView->refreshView(); });
|
|
|
|
struct GraphType
|
|
|
|
{
|
2020-07-16 08:05:10 +00:00
|
|
|
QChar commandChar;
|
|
|
|
QString label;
|
|
|
|
} types[] = {
|
2021-01-24 14:50:13 +00:00
|
|
|
{ 'a', tr("Data reference graph (aga)") },
|
|
|
|
{ 'A', tr("Global data references graph (agA)") },
|
2020-07-16 08:05:10 +00:00
|
|
|
// {'c', tr("c - Function callgraph")},
|
|
|
|
// {'C', tr("C - Global callgraph")},
|
|
|
|
// {'f', tr("f - Basic blocks function graph")},
|
2021-01-24 14:50:13 +00:00
|
|
|
{ 'i', tr("Imports graph (agi)") },
|
|
|
|
{ 'r', tr("References graph (agr)") },
|
|
|
|
{ 'R', tr("Global references graph (agR)") },
|
|
|
|
{ 'x', tr("Cross references graph (agx)") },
|
|
|
|
{ 'g', tr("Custom graph (agg)") },
|
|
|
|
{ ' ', tr("User command") },
|
2020-07-16 08:05:10 +00:00
|
|
|
};
|
|
|
|
for (auto &graphType : types) {
|
2020-07-19 19:00:05 +00:00
|
|
|
if (graphType.commandChar != ' ') {
|
|
|
|
ui->graphType->addItem(graphType.label, graphType.commandChar);
|
|
|
|
} else {
|
|
|
|
ui->graphType->addItem(graphType.label, QVariant());
|
|
|
|
}
|
2020-07-16 08:05:10 +00:00
|
|
|
}
|
2021-01-24 14:50:13 +00:00
|
|
|
connect<void (QComboBox::*)(int)>(ui->graphType, &QComboBox::currentIndexChanged, this,
|
|
|
|
&RizinGraphWidget::typeChanged);
|
|
|
|
connect(ui->customCommand, &QLineEdit::textEdited, this,
|
|
|
|
[this]() { graphView->setGraphCommand(ui->customCommand->text()); });
|
|
|
|
connect(ui->customCommand, &QLineEdit::returnPressed, this, [this]() {
|
2020-07-19 19:00:05 +00:00
|
|
|
graphView->setGraphCommand(ui->customCommand->text());
|
|
|
|
graphView->refreshView();
|
|
|
|
});
|
|
|
|
ui->customCommand->hide();
|
|
|
|
typeChanged();
|
2020-07-16 08:05:10 +00:00
|
|
|
}
|
|
|
|
|
2021-01-24 14:50:13 +00:00
|
|
|
RizinGraphWidget::~RizinGraphWidget() {}
|
2020-07-16 08:05:10 +00:00
|
|
|
|
2020-12-16 10:59:23 +00:00
|
|
|
void RizinGraphWidget::typeChanged()
|
2020-07-16 08:05:10 +00:00
|
|
|
{
|
|
|
|
auto currentData = ui->graphType->currentData();
|
|
|
|
if (currentData.isNull()) {
|
2020-07-19 19:00:05 +00:00
|
|
|
ui->customCommand->setVisible(true);
|
|
|
|
graphView->setGraphCommand(ui->customCommand->text());
|
|
|
|
ui->customCommand->setFocus();
|
2020-07-16 08:05:10 +00:00
|
|
|
} else {
|
2020-07-19 19:00:05 +00:00
|
|
|
ui->customCommand->setVisible(false);
|
2020-07-16 08:05:10 +00:00
|
|
|
auto command = QString("ag%1").arg(currentData.toChar());
|
|
|
|
graphView->setGraphCommand(command);
|
2020-07-19 19:00:05 +00:00
|
|
|
graphView->refreshView();
|
2020-07-16 08:05:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-16 10:59:23 +00:00
|
|
|
GenericRizinGraphView::GenericRizinGraphView(RizinGraphWidget *parent, MainWindow *main)
|
2021-01-24 14:50:13 +00:00
|
|
|
: SimpleTextGraphView(parent, main), refreshDeferrer(nullptr, this)
|
2020-07-16 08:05:10 +00:00
|
|
|
{
|
|
|
|
refreshDeferrer.registerFor(parent);
|
2021-01-24 14:50:13 +00:00
|
|
|
connect(&refreshDeferrer, &RefreshDeferrer::refreshNow, this,
|
|
|
|
&GenericRizinGraphView::refreshView);
|
2020-07-16 08:05:10 +00:00
|
|
|
}
|
|
|
|
|
2020-12-16 10:59:23 +00:00
|
|
|
void GenericRizinGraphView::setGraphCommand(QString cmd)
|
2020-07-16 08:05:10 +00:00
|
|
|
{
|
|
|
|
graphCommand = cmd;
|
|
|
|
}
|
|
|
|
|
2020-12-16 10:59:23 +00:00
|
|
|
void GenericRizinGraphView::refreshView()
|
2020-07-16 08:05:10 +00:00
|
|
|
{
|
|
|
|
if (!refreshDeferrer.attemptRefresh(nullptr)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SimpleTextGraphView::refreshView();
|
|
|
|
}
|
|
|
|
|
2020-12-16 10:59:23 +00:00
|
|
|
void GenericRizinGraphView::loadCurrentGraph()
|
2020-07-16 08:05:10 +00:00
|
|
|
{
|
|
|
|
blockContent.clear();
|
|
|
|
blocks.clear();
|
|
|
|
|
|
|
|
if (graphCommand.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-14 08:04:49 +00:00
|
|
|
CutterJson functionsDoc = Core()->cmdj(QString("%1j").arg(graphCommand));
|
|
|
|
auto nodes = functionsDoc["nodes"];
|
2020-07-16 08:05:10 +00:00
|
|
|
|
2022-03-14 08:04:49 +00:00
|
|
|
for (CutterJson block : nodes) {
|
|
|
|
uint64_t id = block["id"].toUt64();
|
2020-07-16 08:05:10 +00:00
|
|
|
|
|
|
|
QString content;
|
|
|
|
QString title = block["title"].toString();
|
|
|
|
QString body = block["body"].toString();
|
|
|
|
if (!title.isEmpty() && !body.isEmpty()) {
|
|
|
|
content = title + "/n" + body;
|
|
|
|
} else {
|
|
|
|
content = title + body;
|
|
|
|
}
|
|
|
|
|
2022-03-14 08:04:49 +00:00
|
|
|
auto edges = block["out_nodes"];
|
2020-07-16 08:05:10 +00:00
|
|
|
GraphLayout::GraphBlock layoutBlock;
|
|
|
|
layoutBlock.entry = id;
|
|
|
|
for (auto edge : edges) {
|
2022-03-14 08:04:49 +00:00
|
|
|
auto targetId = edge.toUt64();
|
2020-07-16 08:05:10 +00:00
|
|
|
layoutBlock.edges.emplace_back(targetId);
|
|
|
|
}
|
|
|
|
|
|
|
|
addBlock(std::move(layoutBlock), content);
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanupEdges(blocks);
|
|
|
|
|
|
|
|
computeGraphPlacement();
|
|
|
|
|
|
|
|
if (graphCommand != lastShownCommand) {
|
|
|
|
selectedBlock = NO_BLOCK_SELECTED;
|
|
|
|
lastShownCommand = graphCommand;
|
|
|
|
center();
|
|
|
|
}
|
|
|
|
}
|