mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-18 18:38:51 +00:00
First implementation of new disassembly widget
This commit is contained in:
parent
21e3944feb
commit
33dca54176
@ -20,6 +20,7 @@ Q_GLOBAL_STATIC(CutterCore, uniqueInstance)
|
||||
namespace RJsonKey {
|
||||
R_JSON_KEY(addr);
|
||||
R_JSON_KEY(addr_end);
|
||||
R_JSON_KEY(arrow);
|
||||
R_JSON_KEY(baddr);
|
||||
R_JSON_KEY(bind);
|
||||
R_JSON_KEY(blocks);
|
||||
@ -2708,6 +2709,7 @@ QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
|
||||
DisassemblyLine line;
|
||||
line.offset = object[RJsonKey::offset].toVariant().toULongLong();
|
||||
line.text = ansiEscapeToHtml(object[RJsonKey::text].toString());
|
||||
line.arrow = object[RJsonKey::arrow].toVariant().toULongLong();
|
||||
r << line;
|
||||
}
|
||||
|
||||
|
@ -186,6 +186,7 @@ struct RAsmPluginDescription {
|
||||
struct DisassemblyLine {
|
||||
RVA offset;
|
||||
QString text;
|
||||
RVA arrow;
|
||||
};
|
||||
|
||||
struct BinClassBaseClassDescription {
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextBlockUserData>
|
||||
#include <QPainter>
|
||||
#include <QSplitter>
|
||||
|
||||
|
||||
class DisassemblyTextBlockUserData: public QTextBlockUserData
|
||||
@ -52,13 +54,28 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
|
||||
|
||||
setWindowTitle(getWindowTitle());
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
// Instantiate the window layout
|
||||
auto *splitter = new QSplitter;
|
||||
|
||||
// Setup the left frame that contains breakpoints and jumps
|
||||
leftPanel = new DisassemblyLeftPanel(this);
|
||||
splitter->addWidget(leftPanel);
|
||||
|
||||
// Setup the disassembly content
|
||||
auto *layout = new QHBoxLayout;
|
||||
layout->addWidget(mDisasTextEdit);
|
||||
layout->setMargin(0);
|
||||
mDisasScrollArea->viewport()->setLayout(layout);
|
||||
mDisasScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
splitter->addWidget(mDisasScrollArea);
|
||||
|
||||
// Set current widget to the splitted layout we just created
|
||||
setWidget(splitter);
|
||||
|
||||
setWidget(mDisasScrollArea);
|
||||
// Resize properly
|
||||
QList<int> sizes;
|
||||
sizes.append(3);
|
||||
sizes.append(1); // TODO Probably not the best way to go
|
||||
splitter->setSizes(sizes);
|
||||
|
||||
setAllowedAreas(Qt::AllDockWidgetAreas);
|
||||
|
||||
@ -214,6 +231,16 @@ QString DisassemblyWidget::getWidgetType()
|
||||
return "Disassembly";
|
||||
}
|
||||
|
||||
QFontMetrics DisassemblyWidget::getFontMetrics()
|
||||
{
|
||||
return mDisasTextEdit->fontMetrics();
|
||||
}
|
||||
|
||||
QList<DisassemblyLine> DisassemblyWidget::getLines()
|
||||
{
|
||||
return lines;
|
||||
}
|
||||
|
||||
void DisassemblyWidget::refreshDisasm(RVA offset)
|
||||
{
|
||||
if(!disasmRefresh->attemptRefresh(offset == RVA_INVALID ? nullptr : new RVA(offset))) {
|
||||
@ -239,11 +266,12 @@ void DisassemblyWidget::refreshDisasm(RVA offset)
|
||||
int horizontalScrollValue = mDisasTextEdit->horizontalScrollBar()->value();
|
||||
mDisasTextEdit->setLockScroll(true); // avoid flicker
|
||||
|
||||
QList<DisassemblyLine> disassemblyLines;
|
||||
// Retrieve disassembly lines
|
||||
{
|
||||
TempConfig tempConfig;
|
||||
tempConfig.set("scr.color", COLOR_MODE_16M);
|
||||
disassemblyLines = Core()->disassembleLines(topOffset, maxLines);
|
||||
tempConfig.set("scr.color", COLOR_MODE_16M)
|
||||
.set("asm.lines", false);
|
||||
lines = Core()->disassembleLines(topOffset, maxLines);
|
||||
}
|
||||
|
||||
connectCursorPositionChanged(true);
|
||||
@ -251,7 +279,7 @@ void DisassemblyWidget::refreshDisasm(RVA offset)
|
||||
mDisasTextEdit->document()->clear();
|
||||
QTextCursor cursor(mDisasTextEdit->document());
|
||||
QTextBlockFormat regular = cursor.blockFormat();
|
||||
for (const DisassemblyLine &line : disassemblyLines) {
|
||||
for (const DisassemblyLine &line : lines) {
|
||||
if (line.offset < topOffset) { // overflow
|
||||
break;
|
||||
}
|
||||
@ -267,8 +295,8 @@ void DisassemblyWidget::refreshDisasm(RVA offset)
|
||||
cursor.setBlockFormat(regular);
|
||||
}
|
||||
|
||||
if (!disassemblyLines.isEmpty()) {
|
||||
bottomOffset = disassemblyLines[qMin(disassemblyLines.size(), maxLines) - 1].offset;
|
||||
if (!lines.isEmpty()) {
|
||||
bottomOffset = lines[qMin(lines.size(), maxLines) - 1].offset;
|
||||
if (bottomOffset < topOffset) {
|
||||
bottomOffset = RVA_MAX;
|
||||
}
|
||||
@ -290,6 +318,9 @@ void DisassemblyWidget::refreshDisasm(RVA offset)
|
||||
|
||||
mDisasTextEdit->setLockScroll(false);
|
||||
mDisasTextEdit->horizontalScrollBar()->setValue(horizontalScrollValue);
|
||||
|
||||
// Refresh the left panel (trigger paintEvent)
|
||||
leftPanel->update();
|
||||
}
|
||||
|
||||
|
||||
@ -729,3 +760,87 @@ void DisassemblyWidget::seekPrev()
|
||||
{
|
||||
Core()->seekPrev();
|
||||
}
|
||||
|
||||
/*********************
|
||||
* Left panel
|
||||
*********************/
|
||||
DisassemblyLeftPanel::DisassemblyLeftPanel(DisassemblyWidget *disas)
|
||||
{
|
||||
this->disas = disas;
|
||||
}
|
||||
|
||||
void DisassemblyLeftPanel::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
RVA currentOffset = Core()->getOffset(); // TODO Use the seekable from DisassemblyWidget
|
||||
int rightOffset = size().rwidth();
|
||||
int lineHeight = disas->getFontMetrics().height();
|
||||
QColor arrowColor = ConfigColor("flow");
|
||||
QPainter p(this);
|
||||
QPen pen(arrowColor, 1, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);
|
||||
p.setPen(pen);
|
||||
|
||||
QList<DisassemblyLine> lines = disas->getLines();
|
||||
|
||||
// Precompute pixel position of the arrows
|
||||
// TODO This can probably be done in another loop for performance purposes
|
||||
QMap<RVA, int> linesPixPosition;
|
||||
int i = 0;
|
||||
int baseOffset = lineHeight / 2;
|
||||
for (auto l : lines) {
|
||||
linesPixPosition[l.offset] = i * lineHeight + baseOffset;
|
||||
i++;
|
||||
}
|
||||
|
||||
// Draw the lines
|
||||
// TODO Use better var names
|
||||
QPolygon arrow;
|
||||
int direction;
|
||||
int lineFinalHeight;
|
||||
int lineOffset = 10;
|
||||
for (auto l : lines) {
|
||||
// Skip until we reach a line that jumps to a destination
|
||||
if (l.arrow == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute useful variables
|
||||
if (l.arrow > currentOffset) {
|
||||
direction = 1;
|
||||
} else {
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
bool endVisible = true;
|
||||
int currentLineYPos = linesPixPosition[l.offset];
|
||||
lineFinalHeight = linesPixPosition.value(l.arrow, -1);
|
||||
|
||||
if (lineFinalHeight == -1) {
|
||||
if (direction == 1) {
|
||||
lineFinalHeight = 0;
|
||||
} else {
|
||||
lineFinalHeight = size().height();
|
||||
}
|
||||
endVisible = false;
|
||||
}
|
||||
|
||||
// Draw the lines
|
||||
p.drawLine(rightOffset, currentLineYPos, rightOffset - lineOffset, currentLineYPos);
|
||||
p.drawLine(rightOffset - lineOffset, currentLineYPos, rightOffset - lineOffset, lineFinalHeight);
|
||||
|
||||
if (endVisible) {
|
||||
p.drawLine(rightOffset - lineOffset, lineFinalHeight, rightOffset, lineFinalHeight);
|
||||
|
||||
// Draw the arrow
|
||||
arrow.clear();
|
||||
arrow.append(QPoint(rightOffset - 3, lineFinalHeight + 3));
|
||||
arrow.append(QPoint(rightOffset - 3, lineFinalHeight - 3));
|
||||
arrow.append(QPoint(rightOffset, lineFinalHeight));
|
||||
}
|
||||
p.drawConvexPolygon(arrow);
|
||||
|
||||
// Shift next jump line
|
||||
lineOffset += 10;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "MemoryDockWidget.h"
|
||||
#include "common/CutterSeekable.h"
|
||||
#include "common/RefreshDeferrer.h"
|
||||
#include "common/CachedFontMetrics.h"
|
||||
|
||||
#include <QTextEdit>
|
||||
#include <QPlainTextEdit>
|
||||
@ -15,6 +16,7 @@
|
||||
class DisassemblyTextEdit;
|
||||
class DisassemblyScrollArea;
|
||||
class DisassemblyContextMenu;
|
||||
class DisassemblyLeftPanel;
|
||||
|
||||
class DisassemblyWidget : public MemoryDockWidget
|
||||
{
|
||||
@ -32,6 +34,8 @@ public slots:
|
||||
void colorsUpdatedSlot();
|
||||
void seekPrev();
|
||||
void setPreviewMode(bool previewMode);
|
||||
QFontMetrics getFontMetrics();
|
||||
QList<DisassemblyLine> getLines();
|
||||
|
||||
protected slots:
|
||||
void on_seekChanged(RVA offset);
|
||||
@ -49,6 +53,8 @@ protected:
|
||||
DisassemblyContextMenu *mCtxMenu;
|
||||
DisassemblyScrollArea *mDisasScrollArea;
|
||||
DisassemblyTextEdit *mDisasTextEdit;
|
||||
DisassemblyLeftPanel *leftPanel;
|
||||
QList<DisassemblyLine> lines;
|
||||
|
||||
private:
|
||||
RVA topOffset;
|
||||
@ -128,4 +134,18 @@ private:
|
||||
bool lockScroll;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class This class is used to draw the left pane of the disassembly
|
||||
* widget. Its goal is to draw proper arrows for the jumps of the disassembly.
|
||||
*/
|
||||
class DisassemblyLeftPanel: public QFrame
|
||||
{
|
||||
public:
|
||||
DisassemblyLeftPanel(DisassemblyWidget *disas);
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
DisassemblyWidget *disas;
|
||||
};
|
||||
|
||||
#endif // DISASSEMBLYWIDGET_H
|
||||
|
Loading…
Reference in New Issue
Block a user