mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-29 15:55:04 +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/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
|
||||
|
||||
|
@ -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>();
|
||||
|
@ -163,6 +163,9 @@ public:
|
||||
bool getDecompilerAutoRefreshEnabled();
|
||||
void setDecompilerAutoRefreshEnabled(bool enabled);
|
||||
|
||||
void enableDecompilerAnnotationHighlighter(bool useDecompilerHighlighter);
|
||||
bool isDecompilerAnnotationHighlighterEnabled();
|
||||
|
||||
// Graph
|
||||
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),
|
||||
this,
|
||||
&AppearanceOptionsWidget::onFontZoomBoxValueChanged);
|
||||
|
||||
ui->useDecompilerHighlighter->setChecked(Config()->isDecompilerAnnotationHighlighterEnabled());
|
||||
connect(ui->useDecompilerHighlighter, &QCheckBox::toggled,
|
||||
this, [](bool checked){ Config()->enableDecompilerAnnotationHighlighter(checked); });
|
||||
}
|
||||
|
||||
AppearanceOptionsWidget::~AppearanceOptionsWidget() {}
|
||||
|
@ -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">
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user