First implementation of new disassembly widget

This commit is contained in:
xarkes 2019-06-26 09:12:39 +02:00
parent 21e3944feb
commit 33dca54176
4 changed files with 147 additions and 9 deletions

View File

@ -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;
}

View File

@ -186,6 +186,7 @@ struct RAsmPluginDescription {
struct DisassemblyLine {
RVA offset;
QString text;
RVA arrow;
};
struct BinClassBaseClassDescription {

View File

@ -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;
}
}

View File

@ -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