diff --git a/src/common/Decompiler.cpp b/src/common/Decompiler.cpp index b06aca91..a80e3e0c 100644 --- a/src/common/Decompiler.cpp +++ b/src/common/Decompiler.cpp @@ -5,6 +5,42 @@ #include #include + +ut64 AnnotatedCode::OffsetForPosition(size_t pos) const +{ + size_t closestPos = SIZE_MAX; + ut64 closestOffset = UT64_MAX; + for (const auto &annotation : annotations) { + if (annotation.type != CodeAnnotation::Type::Offset || annotation.start > pos || annotation.end <= pos) { + continue; + } + if (closestPos != SIZE_MAX && closestPos >= annotation.start) { + continue; + } + closestPos = annotation.start; + closestOffset = annotation.offset.offset; + } + return closestOffset; +} + +size_t AnnotatedCode::PositionForOffset(ut64 offset) const +{ + size_t closestPos = SIZE_MAX; + ut64 closestOffset = UT64_MAX; + for (const auto &annotation : annotations) { + if (annotation.type != CodeAnnotation::Type::Offset || annotation.offset.offset > offset) { + continue; + } + if (closestOffset != UT64_MAX && closestOffset >= annotation.offset.offset) { + continue; + } + closestPos = annotation.start; + closestOffset = annotation.offset.offset; + } + return closestPos; +} + + Decompiler::Decompiler(const QString &id, const QString &name, QObject *parent) : QObject(parent), id(id), @@ -22,9 +58,9 @@ bool R2DecDecompiler::isAvailable() return Core()->cmdList("e cmd.pdc=?").contains(QStringLiteral("pdd")); } -DecompiledCode R2DecDecompiler::decompileAt(RVA addr) +AnnotatedCode R2DecDecompiler::decompileAt(RVA addr) { - DecompiledCode code; + AnnotatedCode code = {}; QString s; QJsonObject json = Core()->cmdj("pddj @ " + QString::number(addr)).object(); @@ -36,31 +72,32 @@ DecompiledCode R2DecDecompiler::decompileAt(RVA addr) if (!line.isString()) { continue; } - code.lines.append(DecompiledCode::Line(line.toString())); + code.code.append(line.toString() + "\n"); } auto linesArray = json["lines"].toArray(); - code.lines.reserve(code.lines.size() + linesArray.size()); for (const auto &line : linesArray) { QJsonObject lineObject = line.toObject(); if (lineObject.isEmpty()) { continue; } - DecompiledCode::Line codeLine; - codeLine.str = lineObject["str"].toString(); + 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; - codeLine.addr = lineObject["offset"].toVariant().toULongLong(&ok); - if (!ok) { - codeLine.addr = RVA_INVALID; + annotation.offset.offset = lineObject["offset"].toVariant().toULongLong(&ok); + if (ok) { + code.annotations.push_back(annotation); } - code.lines.append(codeLine); } for (const auto &line : json["errors"].toArray()) { if (!line.isString()) { continue; } - code.lines.append(DecompiledCode::Line(line.toString())); + code.code.append(line.toString() + "\n"); } return code; diff --git a/src/common/Decompiler.h b/src/common/Decompiler.h index 0bb37471..87c564f3 100644 --- a/src/common/Decompiler.h +++ b/src/common/Decompiler.h @@ -6,34 +6,37 @@ #include #include +struct CodeAnnotation +{ + size_t start; + size_t end; + + enum class Type { Offset }; + Type type; + + union + { + struct + { + ut64 offset; + } offset; + }; +}; + /** * Describes the result of a Decompilation Process with optional metadata */ -struct DecompiledCode { +struct AnnotatedCode +{ /** - * A single line of decompiled code + * The entire decompiled code */ - struct Line - { - QString str; + QString code; - /** - * Offset of the original instruction - */ - RVA addr; + QList annotations; - Line() - { - this->addr = RVA_INVALID; - } - - explicit Line(const QString &str, RVA addr = RVA_INVALID) - { - this->str = str; - this->addr = addr; - } - }; - QList lines = {}; + ut64 OffsetForPosition(size_t pos) const; + size_t PositionForOffset(ut64 offset) const; }; /** @@ -54,7 +57,7 @@ public: QString getId() const { return id; } QString getName() const { return name; } - virtual DecompiledCode decompileAt(RVA addr) =0; + virtual AnnotatedCode decompileAt(RVA addr) =0; }; class R2DecDecompiler: public Decompiler @@ -63,7 +66,7 @@ class R2DecDecompiler: public Decompiler public: explicit R2DecDecompiler(QObject *parent = nullptr); - DecompiledCode decompileAt(RVA addr) override; + AnnotatedCode decompileAt(RVA addr) override; static bool isAvailable(); }; diff --git a/src/widgets/PseudocodeWidget.cpp b/src/widgets/PseudocodeWidget.cpp index e305ca2a..a5fba078 100644 --- a/src/widgets/PseudocodeWidget.cpp +++ b/src/widgets/PseudocodeWidget.cpp @@ -14,27 +14,6 @@ #include #include -/** - * Represents a single line of decompiled code as part of the displayed text, - * including the position inside the QTextDocument - */ -struct DecompiledCodeTextLine -{ - /** - * position inside the QTextDocument - */ - int position; - - DecompiledCode::Line line; - - DecompiledCodeTextLine(int position, const DecompiledCode::Line &line) - { - this->position = position; - this->line = line; - } -}; - - PseudocodeWidget::PseudocodeWidget(MainWindow *main, QAction *action) : MemoryDockWidget(MemoryWidgetType::Pseudocode, main, action), mCtxMenu(new DisassemblyContextMenu(this, main)), @@ -104,24 +83,14 @@ void PseudocodeWidget::doRefresh(RVA addr) return; } - DecompiledCode decompiledCode = dec->decompileAt(addr); - - textLines = {}; - textLines.reserve(decompiledCode.lines.size()); - - if (decompiledCode.lines.isEmpty()) { + code = dec->decompileAt(addr); + if (code.code.isEmpty()) { ui->textEdit->setPlainText(tr("Cannot decompile at") + " " + RAddressString( addr) + " " + tr("(Not a function?)")); return; } else { connectCursorPositionChanged(true); - ui->textEdit->document()->clear(); - QTextCursor cursor(ui->textEdit->document()); - for (const DecompiledCode::Line &line : decompiledCode.lines) { - textLines.append(DecompiledCodeTextLine(cursor.position(), line)); - // Can't use cursor.block()->setUserData() here, because the Syntax Highlighter will mess it up. - cursor.insertText(line.str + "\n"); - } + ui->textEdit->setPlainText(code.code); connectCursorPositionChanged(false); seekChanged(); } @@ -148,7 +117,8 @@ void PseudocodeWidget::connectCursorPositionChanged(bool disconnect) void PseudocodeWidget::cursorPositionChanged() { - RVA offset = getOffsetAtLine(ui->textEdit->textCursor()); + size_t pos = ui->textEdit->textCursor().position(); + RVA offset = code.OffsetForPosition(pos); if (offset != RVA_INVALID && offset != Core()->getOffset()) { seekFromCursor = true; Core()->seek(offset); @@ -169,61 +139,18 @@ void PseudocodeWidget::seekChanged() void PseudocodeWidget::updateCursorPosition() { RVA offset = Core()->getOffset(); + size_t pos = code.PositionForOffset(offset); + if (pos == SIZE_MAX) { + return; + } connectCursorPositionChanged(true); - - auto it = findLineByOffset(offset); - if (it != textLines.end()) { - // move back if the offset is identical (so we don't land on closing braces for example) - while (it != textLines.begin()) { - auto prev = it - 1; - if (prev->line.addr != it->line.addr) { - break; - } - it = prev; - } - QTextCursor cursor = ui->textEdit->textCursor(); - cursor.setPosition((*it).position); - ui->textEdit->setTextCursor(cursor); - updateSelection(); - } - + QTextCursor cursor = ui->textEdit->textCursor(); + cursor.setPosition(pos); + ui->textEdit->setTextCursor(cursor); + updateSelection(); connectCursorPositionChanged(false); } -QList::iterator PseudocodeWidget::findLine(int position) -{ - return std::upper_bound(textLines.begin(), textLines.end(), position, - [](int pos, const DecompiledCodeTextLine &line) { - return pos < line.position; - }); -} - -QList::iterator PseudocodeWidget::findLineByOffset(RVA offset) -{ - auto it = textLines.begin(); - auto candidate = it; - for (; it != textLines.end(); it++) { - RVA lineOffset = it->line.addr; - if (lineOffset != RVA_INVALID && lineOffset > offset) { - break; - } - if (candidate->line.addr == RVA_INVALID || (lineOffset != RVA_INVALID && lineOffset > candidate->line.addr)) { - candidate = it; - } - } - return candidate; -} - -RVA PseudocodeWidget::getOffsetAtLine(const QTextCursor &tc) -{ - auto it = findLine(tc.position()); - if (it == textLines.begin()) { - return RVA_INVALID; - } - it--; - return (*it).line.addr; -} - void PseudocodeWidget::setupFonts() { QFont font = Config()->getFont(); @@ -236,20 +163,7 @@ void PseudocodeWidget::updateSelection() // Highlight the current line auto cursor = ui->textEdit->textCursor(); - - RVA cursorOffset = getOffsetAtLine(cursor); - if (cursorOffset != RVA_INVALID) { - for (auto it = findLineByOffset(cursorOffset); - it != textLines.end() && it->line.addr != RVA_INVALID && it->line.addr <= cursorOffset; - it++) { - auto lineCursor = cursor; - lineCursor.setPosition(it->position); - extraSelections.append(createLineHighlightSelection(lineCursor)); - } - } else { - // if the cursor position has no valid offset, just highlight the line - extraSelections.append(createLineHighlightSelection(cursor)); - } + extraSelections.append(createLineHighlightSelection(cursor)); // Highlight all the words in the document same as the current one cursor.select(QTextCursor::WordUnderCursor); diff --git a/src/widgets/PseudocodeWidget.h b/src/widgets/PseudocodeWidget.h index 2dec0f6d..11f200c4 100644 --- a/src/widgets/PseudocodeWidget.h +++ b/src/widgets/PseudocodeWidget.h @@ -5,6 +5,7 @@ #include "core/Cutter.h" #include "MemoryDockWidget.h" +#include "Decompiler.h" namespace Ui { class PseudocodeWidget; @@ -41,10 +42,7 @@ private: QSyntaxHighlighter *syntaxHighlighter; - /** - * Index of all lines that are currently displayed, ordered by the position in the text - */ - QList textLines; + AnnotatedCode code; bool seekFromCursor = false; @@ -54,17 +52,6 @@ private: void connectCursorPositionChanged(bool disconnect); void updateCursorPosition(); - /** - * @return Iterator to the first line that is after position or last if not found. - */ - QList::iterator findLine(int position); - - /** - * @return Iterator to the first line that is considered to contain offset - */ - QList::iterator findLineByOffset(RVA offset); - RVA getOffsetAtLine(const QTextCursor &tc); - QString getWindowTitle() const override; };