Create SyntaxHighlighter based on RAnnotatedCode. (#2409)

This commit is contained in:
karliss 2020-08-31 09:00:55 +03:00 committed by GitHub
parent 6fc902894d
commit 503907909f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 189 additions and 17 deletions

View File

@ -440,7 +440,8 @@ SOURCES += \
widgets/R2GraphWidget.cpp \
widgets/CallGraph.cpp \
widgets/AddressableDockWidget.cpp \
dialogs/preferences/AnalOptionsWidget.cpp
dialogs/preferences/AnalOptionsWidget.cpp \
common/DecompilerHighlighter.cpp
GRAPHVIZ_SOURCES = \
widgets/GraphvizLayout.cpp
@ -599,7 +600,8 @@ HEADERS += \
widgets/R2GraphWidget.h \
widgets/CallGraph.h \
widgets/AddressableDockWidget.h \
dialogs/preferences/AnalOptionsWidget.h
dialogs/preferences/AnalOptionsWidget.h \
common/DecompilerHighlighter.h
GRAPHVIZ_HEADERS = widgets/GraphvizLayout.h

View File

@ -724,6 +724,17 @@ void Configuration::setDecompilerAutoRefreshEnabled(bool enabled)
s.setValue("decompilerAutoRefresh", enabled);
}
void Configuration::enableDecompilerAnnotationHighlighter(bool useDecompilerHighlighter)
{
s.setValue("decompilerAnnotationHighlighter", useDecompilerHighlighter);
emit colorsUpdated();
}
bool Configuration::isDecompilerAnnotationHighlighterEnabled()
{
return s.value("decompilerAnnotationHighlighter", true).value<bool>();
}
bool Configuration::getBitmapTransparentState()
{
return s.value("bitmapGraphExportTransparency", false).value<bool>();

View File

@ -163,6 +163,9 @@ public:
bool getDecompilerAutoRefreshEnabled();
void setDecompilerAutoRefreshEnabled(bool enabled);
void enableDecompilerAnnotationHighlighter(bool useDecompilerHighlighter);
bool isDecompilerAnnotationHighlighterEnabled();
// Graph
int getGraphBlockMaxChars() const
{

View File

@ -0,0 +1,71 @@
#include "DecompilerHighlighter.h"
#include "common/Configuration.h"
DecompilerHighlighter::DecompilerHighlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
setupTheme();
connect(Config(), &Configuration::colorsUpdated, this, [this]() {
setupTheme();
rehighlight();
});
}
void DecompilerHighlighter::setAnnotations(RAnnotatedCode *code)
{
this->code = code;
}
void DecompilerHighlighter::setupTheme()
{
struct {
RSyntaxHighlightType type;
QString name;
} mapping[] = {
{R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD, "pop"},
{R_SYNTAX_HIGHLIGHT_TYPE_COMMENT, "comment"},
{R_SYNTAX_HIGHLIGHT_TYPE_DATATYPE, "func_var_type"},
{R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME, "fname"},
{R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER, "args"},
{R_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE, "func_var"},
{R_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE, "num"},
{R_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE, "flag"},
};
for (const auto &pair : mapping) {
assert(pair.type < format.size());
format[pair.type].setForeground(Config()->getColor(pair.name));
}
}
void DecompilerHighlighter::highlightBlock(const QString &)
{
if (!code) {
return;
}
auto block = currentBlock();
size_t start = block.position();
size_t end = block.position() + block.length();
std::unique_ptr<RPVector, decltype(&r_pvector_free)> annotations(r_annotated_code_annotations_range(code, start, end), &r_pvector_free);
void **iter;
r_pvector_foreach(annotations.get(), iter) {
RCodeAnnotation *annotation = static_cast<RCodeAnnotation*>(*iter);
if (annotation->type != R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT) {
continue;
}
auto type = annotation->syntax_highlight.type;
if (size_t(type) >= HIGHLIGHT_COUNT) {
continue;
}
auto annotationStart = annotation->start;
if (annotationStart < start) {
annotationStart = 0;
} else {
annotationStart -= start;
}
auto annotationEnd = annotation->end - start;
setFormat(annotationStart, annotationEnd - annotationStart, format[type]);
}
}

View File

@ -0,0 +1,44 @@
#ifndef DECOMPILER_HIGHLIGHTER_H
#define DECOMPILER_HIGHLIGHTER_H
#include "CutterCommon.h"
#include <r_util/r_annotated_code.h>
#include <QSyntaxHighlighter>
#include <QTextDocument>
#include <QTextCharFormat>
#include <array>
/**
* \brief SyntaxHighlighter based on annotations from decompiled code.
* Can be only used in combination with DecompilerWidget.
*/
class CUTTER_EXPORT DecompilerHighlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
DecompilerHighlighter(QTextDocument *parent = nullptr);
virtual ~DecompilerHighlighter() = default;
/**
* @brief Set the code with annotations to be used for highlighting.
*
* It is callers responsibility to ensure that it is synchronized with currentTextDocument and
* has sufficiently long lifetime.
*
* @param code
*/
void setAnnotations(RAnnotatedCode *code);
protected:
void highlightBlock(const QString &text) override;
private:
void setupTheme();
static const int HIGHLIGHT_COUNT = R_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE + 1;
std::array<QTextCharFormat, HIGHLIGHT_COUNT> format;
RAnnotatedCode *code = nullptr;
};
#endif

View File

@ -67,6 +67,10 @@ AppearanceOptionsWidget::AppearanceOptionsWidget(PreferencesDialog *dialog)
static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
this,
&AppearanceOptionsWidget::onFontZoomBoxValueChanged);
ui->useDecompilerHighlighter->setChecked(Config()->isDecompilerAnnotationHighlighterEnabled());
connect(ui->useDecompilerHighlighter, &QCheckBox::toggled,
this, [](bool checked){ Config()->enableDecompilerAnnotationHighlighter(checked); });
}
AppearanceOptionsWidget::~AppearanceOptionsWidget() {}

View File

@ -283,6 +283,16 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="useDecompilerHighlighter">
<property name="toolTip">
<string>Use information provided by decompiler when highlighting code.</string>
</property>
<property name="text">
<string>Decompiler based highlighting</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View File

@ -9,6 +9,7 @@
#include "common/Decompiler.h"
#include "common/CutterSeekable.h"
#include "core/MainWindow.h"
#include "common/DecompilerHighlighter.h"
#include <QTextEdit>
#include <QPlainTextEdit>
@ -38,7 +39,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
: getWidgetType());
updateWindowTitle();
syntaxHighlighter = Config()->createSyntaxHighlighter(ui->textEdit->document());
setHighlighter(Config()->isDecompilerAnnotationHighlighterEnabled());
// Event filter to intercept double click and right click in the textbox
ui->textEdit->viewport()->installEventFilter(this);
@ -255,10 +256,9 @@ void DecompilerWidget::doRefresh()
// the decompiler selection combo box as we are not waiting for any decompilation to finish.
ui->progressLabel->setVisible(false);
ui->decompilerComboBox->setEnabled(true);
connectCursorPositionChanged(false);
ui->textEdit->setPlainText(
tr("No function found at this offset. Seek to a function or define one in order to decompile it."));
connectCursorPositionChanged(true);
setCode(Decompiler::makeWarning(
tr("No function found at this offset. "
"Seek to a function or define one in order to decompile it.")));
return;
}
mCtxMenu->setDecompiledFunctionAddress(decompiledFunctionAddr);
@ -297,24 +297,18 @@ void DecompilerWidget::decompilationFinished(RAnnotatedCode *codeDecompiled)
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
mCtxMenu->setAnnotationHere(nullptr);
this->code.reset(codeDecompiled);
setCode(codeDecompiled);
Decompiler *dec = getCurrentDecompiler();
QObject::disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
decompilerBusy = false;
QString codeString = QString::fromUtf8(this->code->code);
if (codeString.isEmpty()) {
connectCursorPositionChanged(false);
ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)"));
connectCursorPositionChanged(true);
if (ui->textEdit->toPlainText().isEmpty()) {
setCode(Decompiler::makeWarning(tr("Cannot decompile at this address (Not a function?)")));
lowestOffsetInCode = RVA_MAX;
highestOffsetInCode = 0;
return;
} else {
connectCursorPositionChanged(false);
ui->textEdit->setPlainText(codeString);
connectCursorPositionChanged(true);
updateCursorPosition();
highlightPC();
highlightBreakpoints();
@ -467,6 +461,10 @@ void DecompilerWidget::fontsUpdatedSlot()
void DecompilerWidget::colorsUpdatedSlot()
{
bool useAnotationHiglighter = Config()->isDecompilerAnnotationHighlighterEnabled();
if (useAnotationHiglighter != usingAnnotationBasedHighlighting) {
setHighlighter(useAnotationHiglighter);
}
}
void DecompilerWidget::showDecompilerContextMenu(const QPoint &pt)
@ -566,3 +564,27 @@ bool DecompilerWidget::addressInRange(RVA addr)
}
return false;
}
void DecompilerWidget::setCode(RAnnotatedCode *code)
{
connectCursorPositionChanged(false);
if (auto highlighter = qobject_cast<DecompilerHighlighter*>(syntaxHighlighter.get())) {
highlighter->setAnnotations(code);
}
this->code.reset(code);
this->ui->textEdit->setPlainText(QString::fromUtf8(this->code->code));
connectCursorPositionChanged(true);
syntaxHighlighter->rehighlight();
}
void DecompilerWidget::setHighlighter(bool annotationBasedHighlighter)
{
usingAnnotationBasedHighlighting = annotationBasedHighlighter;
if (usingAnnotationBasedHighlighting) {
syntaxHighlighter.reset(new DecompilerHighlighter());
static_cast<DecompilerHighlighter*>(syntaxHighlighter.get())->setAnnotations(code.get());
} else {
syntaxHighlighter.reset(Config()->createSyntaxHighlighter(nullptr));
}
syntaxHighlighter->setDocument(ui->textEdit->document());
}

View File

@ -61,7 +61,8 @@ private:
RefreshDeferrer *refreshDeferrer;
QSyntaxHighlighter *syntaxHighlighter;
bool usingAnnotationBasedHighlighting = false;
std::unique_ptr<QSyntaxHighlighter> syntaxHighlighter;
bool decompilerSelectionEnabled;
/**
@ -233,6 +234,10 @@ private:
* @return True if the specified is a part of the decompiled function, False otherwise.
*/
bool addressInRange(RVA addr);
void setCode(RAnnotatedCode *code);
void setHighlighter(bool annotationBasedHighlighter);
};
#endif // DECOMPILERWIDGET_H