mirror of
https://github.com/rizinorg/cutter.git
synced 2025-02-11 17:32:11 +00:00
Create SyntaxHighlighter based on RAnnotatedCode. (#2409)
This commit is contained in:
parent
6fc902894d
commit
503907909f
@ -440,7 +440,8 @@ SOURCES += \
|
|||||||
widgets/R2GraphWidget.cpp \
|
widgets/R2GraphWidget.cpp \
|
||||||
widgets/CallGraph.cpp \
|
widgets/CallGraph.cpp \
|
||||||
widgets/AddressableDockWidget.cpp \
|
widgets/AddressableDockWidget.cpp \
|
||||||
dialogs/preferences/AnalOptionsWidget.cpp
|
dialogs/preferences/AnalOptionsWidget.cpp \
|
||||||
|
common/DecompilerHighlighter.cpp
|
||||||
|
|
||||||
GRAPHVIZ_SOURCES = \
|
GRAPHVIZ_SOURCES = \
|
||||||
widgets/GraphvizLayout.cpp
|
widgets/GraphvizLayout.cpp
|
||||||
@ -599,7 +600,8 @@ HEADERS += \
|
|||||||
widgets/R2GraphWidget.h \
|
widgets/R2GraphWidget.h \
|
||||||
widgets/CallGraph.h \
|
widgets/CallGraph.h \
|
||||||
widgets/AddressableDockWidget.h \
|
widgets/AddressableDockWidget.h \
|
||||||
dialogs/preferences/AnalOptionsWidget.h
|
dialogs/preferences/AnalOptionsWidget.h \
|
||||||
|
common/DecompilerHighlighter.h
|
||||||
|
|
||||||
GRAPHVIZ_HEADERS = widgets/GraphvizLayout.h
|
GRAPHVIZ_HEADERS = widgets/GraphvizLayout.h
|
||||||
|
|
||||||
|
@ -724,6 +724,17 @@ void Configuration::setDecompilerAutoRefreshEnabled(bool enabled)
|
|||||||
s.setValue("decompilerAutoRefresh", 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()
|
bool Configuration::getBitmapTransparentState()
|
||||||
{
|
{
|
||||||
return s.value("bitmapGraphExportTransparency", false).value<bool>();
|
return s.value("bitmapGraphExportTransparency", false).value<bool>();
|
||||||
|
@ -163,6 +163,9 @@ public:
|
|||||||
bool getDecompilerAutoRefreshEnabled();
|
bool getDecompilerAutoRefreshEnabled();
|
||||||
void setDecompilerAutoRefreshEnabled(bool enabled);
|
void setDecompilerAutoRefreshEnabled(bool enabled);
|
||||||
|
|
||||||
|
void enableDecompilerAnnotationHighlighter(bool useDecompilerHighlighter);
|
||||||
|
bool isDecompilerAnnotationHighlighterEnabled();
|
||||||
|
|
||||||
// Graph
|
// Graph
|
||||||
int getGraphBlockMaxChars() const
|
int getGraphBlockMaxChars() const
|
||||||
{
|
{
|
||||||
|
71
src/common/DecompilerHighlighter.cpp
Normal file
71
src/common/DecompilerHighlighter.cpp
Normal 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]);
|
||||||
|
}
|
||||||
|
}
|
44
src/common/DecompilerHighlighter.h
Normal file
44
src/common/DecompilerHighlighter.h
Normal 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
|
@ -67,6 +67,10 @@ AppearanceOptionsWidget::AppearanceOptionsWidget(PreferencesDialog *dialog)
|
|||||||
static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||||
this,
|
this,
|
||||||
&AppearanceOptionsWidget::onFontZoomBoxValueChanged);
|
&AppearanceOptionsWidget::onFontZoomBoxValueChanged);
|
||||||
|
|
||||||
|
ui->useDecompilerHighlighter->setChecked(Config()->isDecompilerAnnotationHighlighterEnabled());
|
||||||
|
connect(ui->useDecompilerHighlighter, &QCheckBox::toggled,
|
||||||
|
this, [](bool checked){ Config()->enableDecompilerAnnotationHighlighter(checked); });
|
||||||
}
|
}
|
||||||
|
|
||||||
AppearanceOptionsWidget::~AppearanceOptionsWidget() {}
|
AppearanceOptionsWidget::~AppearanceOptionsWidget() {}
|
||||||
|
@ -283,6 +283,16 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "common/Decompiler.h"
|
#include "common/Decompiler.h"
|
||||||
#include "common/CutterSeekable.h"
|
#include "common/CutterSeekable.h"
|
||||||
#include "core/MainWindow.h"
|
#include "core/MainWindow.h"
|
||||||
|
#include "common/DecompilerHighlighter.h"
|
||||||
|
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
@ -38,7 +39,7 @@ DecompilerWidget::DecompilerWidget(MainWindow *main) :
|
|||||||
: getWidgetType());
|
: getWidgetType());
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
|
|
||||||
syntaxHighlighter = Config()->createSyntaxHighlighter(ui->textEdit->document());
|
setHighlighter(Config()->isDecompilerAnnotationHighlighterEnabled());
|
||||||
// Event filter to intercept double click and right click in the textbox
|
// Event filter to intercept double click and right click in the textbox
|
||||||
ui->textEdit->viewport()->installEventFilter(this);
|
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.
|
// the decompiler selection combo box as we are not waiting for any decompilation to finish.
|
||||||
ui->progressLabel->setVisible(false);
|
ui->progressLabel->setVisible(false);
|
||||||
ui->decompilerComboBox->setEnabled(true);
|
ui->decompilerComboBox->setEnabled(true);
|
||||||
connectCursorPositionChanged(false);
|
setCode(Decompiler::makeWarning(
|
||||||
ui->textEdit->setPlainText(
|
tr("No function found at this offset. "
|
||||||
tr("No function found at this offset. Seek to a function or define one in order to decompile it."));
|
"Seek to a function or define one in order to decompile it.")));
|
||||||
connectCursorPositionChanged(true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mCtxMenu->setDecompiledFunctionAddress(decompiledFunctionAddr);
|
mCtxMenu->setDecompiledFunctionAddress(decompiledFunctionAddr);
|
||||||
@ -297,24 +297,18 @@ void DecompilerWidget::decompilationFinished(RAnnotatedCode *codeDecompiled)
|
|||||||
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);
|
||||||
|
|
||||||
mCtxMenu->setAnnotationHere(nullptr);
|
mCtxMenu->setAnnotationHere(nullptr);
|
||||||
this->code.reset(codeDecompiled);
|
setCode(codeDecompiled);
|
||||||
|
|
||||||
Decompiler *dec = getCurrentDecompiler();
|
Decompiler *dec = getCurrentDecompiler();
|
||||||
QObject::disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
|
QObject::disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);
|
||||||
decompilerBusy = false;
|
decompilerBusy = false;
|
||||||
|
|
||||||
QString codeString = QString::fromUtf8(this->code->code);
|
if (ui->textEdit->toPlainText().isEmpty()) {
|
||||||
if (codeString.isEmpty()) {
|
setCode(Decompiler::makeWarning(tr("Cannot decompile at this address (Not a function?)")));
|
||||||
connectCursorPositionChanged(false);
|
|
||||||
ui->textEdit->setPlainText(tr("Cannot decompile at this address (Not a function?)"));
|
|
||||||
connectCursorPositionChanged(true);
|
|
||||||
lowestOffsetInCode = RVA_MAX;
|
lowestOffsetInCode = RVA_MAX;
|
||||||
highestOffsetInCode = 0;
|
highestOffsetInCode = 0;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
connectCursorPositionChanged(false);
|
|
||||||
ui->textEdit->setPlainText(codeString);
|
|
||||||
connectCursorPositionChanged(true);
|
|
||||||
updateCursorPosition();
|
updateCursorPosition();
|
||||||
highlightPC();
|
highlightPC();
|
||||||
highlightBreakpoints();
|
highlightBreakpoints();
|
||||||
@ -467,6 +461,10 @@ void DecompilerWidget::fontsUpdatedSlot()
|
|||||||
|
|
||||||
void DecompilerWidget::colorsUpdatedSlot()
|
void DecompilerWidget::colorsUpdatedSlot()
|
||||||
{
|
{
|
||||||
|
bool useAnotationHiglighter = Config()->isDecompilerAnnotationHighlighterEnabled();
|
||||||
|
if (useAnotationHiglighter != usingAnnotationBasedHighlighting) {
|
||||||
|
setHighlighter(useAnotationHiglighter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecompilerWidget::showDecompilerContextMenu(const QPoint &pt)
|
void DecompilerWidget::showDecompilerContextMenu(const QPoint &pt)
|
||||||
@ -566,3 +564,27 @@ bool DecompilerWidget::addressInRange(RVA addr)
|
|||||||
}
|
}
|
||||||
return false;
|
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());
|
||||||
|
}
|
||||||
|
@ -61,7 +61,8 @@ private:
|
|||||||
|
|
||||||
RefreshDeferrer *refreshDeferrer;
|
RefreshDeferrer *refreshDeferrer;
|
||||||
|
|
||||||
QSyntaxHighlighter *syntaxHighlighter;
|
bool usingAnnotationBasedHighlighting = false;
|
||||||
|
std::unique_ptr<QSyntaxHighlighter> syntaxHighlighter;
|
||||||
bool decompilerSelectionEnabled;
|
bool decompilerSelectionEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -233,6 +234,10 @@ private:
|
|||||||
* @return True if the specified is a part of the decompiled function, False otherwise.
|
* @return True if the specified is a part of the decompiled function, False otherwise.
|
||||||
*/
|
*/
|
||||||
bool addressInRange(RVA addr);
|
bool addressInRange(RVA addr);
|
||||||
|
|
||||||
|
void setCode(RAnnotatedCode *code);
|
||||||
|
|
||||||
|
void setHighlighter(bool annotationBasedHighlighter);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DECOMPILERWIDGET_H
|
#endif // DECOMPILERWIDGET_H
|
||||||
|
Loading…
Reference in New Issue
Block a user