Introduce some helpers to deal with rizin C API more cleanly. (#3020)

This commit is contained in:
karliss 2022-08-13 23:12:57 +03:00 committed by GitHub
parent 39bf5c6429
commit 41c4857ed9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 238 additions and 198 deletions

View File

@ -5,6 +5,7 @@ set(SOURCES
Main.cpp Main.cpp
core/Cutter.cpp core/Cutter.cpp
core/CutterJson.cpp core/CutterJson.cpp
core/RizinCpp.cpp
dialogs/EditStringDialog.cpp dialogs/EditStringDialog.cpp
dialogs/WriteCommandsDialogs.cpp dialogs/WriteCommandsDialogs.cpp
widgets/DisassemblerGraphView.cpp widgets/DisassemblerGraphView.cpp
@ -154,6 +155,7 @@ set(HEADER_FILES
core/CutterCommon.h core/CutterCommon.h
core/CutterDescriptions.h core/CutterDescriptions.h
core/CutterJson.h core/CutterJson.h
core/RizinCpp.h
dialogs/EditStringDialog.h dialogs/EditStringDialog.h
dialogs/WriteCommandsDialogs.h dialogs/WriteCommandsDialogs.h
widgets/DisassemblerGraphView.h widgets/DisassemblerGraphView.h

View File

@ -49,8 +49,7 @@ void DecompilerHighlighter::highlightBlock(const QString &)
size_t start = block.position(); size_t start = block.position();
size_t end = block.position() + block.length(); size_t end = block.position() + block.length();
std::unique_ptr<RzPVector, decltype(&rz_pvector_free)> annotations( auto annotations = fromOwned(rz_annotated_code_annotations_range(code, start, end));
rz_annotated_code_annotations_range(code, start, end), &rz_pvector_free);
void **iter; void **iter;
rz_pvector_foreach(annotations.get(), iter) rz_pvector_foreach(annotations.get(), iter)
{ {

View File

@ -120,13 +120,6 @@ static void updateOwnedCharPtr(char *&variable, const QString &newValue)
variable = strdup(data.data()); variable = strdup(data.data());
} }
static QString fromOwnedCharPtr(char *str)
{
QString result(str ? str : "");
rz_mem_free(str);
return result;
}
static bool reg_sync(RzCore *core, RzRegisterType type, bool write) static bool reg_sync(RzCore *core, RzRegisterType type, bool write)
{ {
if (rz_core_is_debug(core)) { if (rz_core_is_debug(core)) {
@ -782,11 +775,10 @@ PRzAnalysisBytes CutterCore::getRzAnalysisBytesSingle(RVA addr)
CORE_LOCK(); CORE_LOCK();
ut8 buf[128]; ut8 buf[128];
rz_io_read_at(core->io, addr, buf, sizeof(buf)); rz_io_read_at(core->io, addr, buf, sizeof(buf));
std::unique_ptr<RzPVector, decltype(rz_pvector_free) *> vec {
returnAtSeek<RzPVector *>( auto seek = seekTemp(addr);
[&]() { return rz_core_analysis_bytes(core, buf, sizeof(buf), 1); }, addr), auto vec = fromOwned(rz_core_analysis_bytes(core, buf, sizeof(buf), 1));
rz_pvector_free
};
auto ab = vec && rz_pvector_len(vec.get()) > 0 auto ab = vec && rz_pvector_len(vec.get()) > 0
? reinterpret_cast<RzAnalysisBytes *>(rz_pvector_pop_front(vec.get())) ? reinterpret_cast<RzAnalysisBytes *>(rz_pvector_pop_front(vec.get()))
: nullptr; : nullptr;
@ -819,14 +811,20 @@ void CutterCore::editInstruction(RVA addr, const QString &inst, bool fillWithNop
void CutterCore::nopInstruction(RVA addr) void CutterCore::nopInstruction(RVA addr)
{ {
CORE_LOCK(); CORE_LOCK();
applyAtSeek([&]() { rz_core_hack(core, "nop"); }, addr); {
auto seek = seekTemp(addr);
rz_core_hack(core, "nop");
}
emit instructionChanged(addr); emit instructionChanged(addr);
} }
void CutterCore::jmpReverse(RVA addr) void CutterCore::jmpReverse(RVA addr)
{ {
CORE_LOCK(); CORE_LOCK();
applyAtSeek([&]() { rz_core_hack(core, "recj"); }, addr); {
auto seek = seekTemp(addr);
rz_core_hack(core, "recj");
}
emit instructionChanged(addr); emit instructionChanged(addr);
} }
@ -908,8 +906,8 @@ QString CutterCore::getString(RVA addr, uint64_t len, RzStrEnc encoding, bool es
opt.length = len; opt.length = len;
opt.encoding = encoding; opt.encoding = encoding;
opt.escape_nl = escape_nl; opt.escape_nl = escape_nl;
char *s = returnAtSeek<char *>([&]() { return rz_str_stringify_raw_buffer(&opt, NULL); }, addr); auto seek = seekTemp(addr);
return fromOwnedCharPtr(s); return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, NULL));
} }
QString CutterCore::getMetaString(RVA addr) QString CutterCore::getMetaString(RVA addr)
@ -993,12 +991,11 @@ void CutterCore::applyStructureOffset(const QString &structureOffset, RVA offset
offset = getOffset(); offset = getOffset();
} }
applyAtSeek( {
[&]() { CORE_LOCK();
CORE_LOCK(); auto seek = seekTemp(offset);
rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData()); rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData());
}, }
offset);
emit instructionChanged(offset); emit instructionChanged(offset);
} }
@ -1084,22 +1081,19 @@ RVA CutterCore::prevOpAddr(RVA startAddr, int count)
RVA CutterCore::nextOpAddr(RVA startAddr, int count) RVA CutterCore::nextOpAddr(RVA startAddr, int count)
{ {
CORE_LOCK(); CORE_LOCK();
auto vec = returnAtSeek<RzPVector *>( auto seek = seekTemp(startAddr);
[&]() { auto vec =
return rz_core_analysis_bytes(core, core->block, (int)core->blocksize, count + 1); fromOwned(rz_core_analysis_bytes(core, core->block, (int)core->blocksize, count + 1));
},
startAddr);
RVA addr = startAddr + 1; RVA addr = startAddr + 1;
if (!vec) { if (!vec) {
return addr; return addr;
} }
auto ab = reinterpret_cast<RzAnalysisBytes *>(rz_pvector_tail(vec)); auto ab = reinterpret_cast<RzAnalysisBytes *>(rz_pvector_tail(vec.get()));
if (!(ab && ab->op)) { if (!(ab && ab->op)) {
rz_pvector_free(vec);
return addr; return addr;
} }
addr = ab->op->addr; addr = ab->op->addr;
rz_pvector_free(vec);
return addr; return addr;
} }
@ -4193,34 +4187,32 @@ void CutterCore::loadPDB(const QString &file)
QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines) QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
{ {
CORE_LOCK(); CORE_LOCK();
RzPVector *vec = rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)); auto vec = fromOwned(
rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));
if (!vec) { if (!vec) {
return {}; return {};
} }
RzCoreDisasmOptions options = {}; RzCoreDisasmOptions options = {};
options.cbytes = 1; options.cbytes = 1;
options.vec = vec; options.vec = vec.get();
applyAtSeek( {
[&]() { auto restoreSeek = seekTemp(offset);
if (rz_cons_singleton()->is_html) { if (rz_cons_singleton()->is_html) {
rz_cons_singleton()->is_html = false; rz_cons_singleton()->is_html = false;
rz_cons_singleton()->was_html = true; rz_cons_singleton()->was_html = true;
} }
rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, NULL, rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, NULL, &options);
&options); }
},
offset);
QList<DisassemblyLine> r; QList<DisassemblyLine> r;
for (const auto &t : CutterPVector<RzAnalysisDisasmText>(vec)) { for (const auto &t : CutterPVector<RzAnalysisDisasmText>(vec.get())) {
DisassemblyLine line; DisassemblyLine line;
line.offset = t->offset; line.offset = t->offset;
line.text = ansiEscapeToHtml(t->text); line.text = ansiEscapeToHtml(t->text);
line.arrow = t->arrow; line.arrow = t->arrow;
r << line; r << line;
} }
rz_pvector_free(vec);
return r; return r;
} }

View File

@ -156,33 +156,34 @@ public:
return cmdRawAt(str.toUtf8().constData(), address); return cmdRawAt(str.toUtf8().constData(), address);
} }
void applyAtSeek(const std::function<void()> &fn, RVA address) class SeekReturn
{ {
RVA oldOffset = getOffset(); RVA returnAddress;
seekSilent(address); bool empty = true;
fn();
seekSilent(oldOffset);
}
template<typename T> public:
T returnAtSeek(const std::function<T()> &fn, RVA address) SeekReturn(RVA returnAddress) : returnAddress(returnAddress), empty(false) {}
{ ~SeekReturn()
RVA oldOffset = getOffset(); {
seekSilent(address); if (!empty) {
T ret = fn(); Core()->seekSilent(returnAddress);
seekSilent(oldOffset); }
return ret; }
} SeekReturn(SeekReturn &&from)
{
std::unique_ptr<RVA, std::function<void(const RVA *)>> seekTemp(RVA address) if (this != &from) {
{ returnAddress = from.returnAddress;
auto seekBack = [&](const RVA *x) { empty = from.empty;
seekSilent(*x); from.empty = true;
delete x; }
}; };
std::unique_ptr<RVA, decltype(seekBack)> p { new RVA(getOffset()), seekBack }; };
SeekReturn seekTemp(RVA address)
{
SeekReturn returner(getOffset());
seekSilent(address); seekSilent(address);
return p; return returner;
} }
CutterJson cmdj(const char *str); CutterJson cmdj(const char *str);

View File

@ -7,6 +7,7 @@
#include "rz_core.h" #include "rz_core.h"
#include <QString> #include <QString>
#include "RizinCpp.h"
// Workaround for compile errors on Windows // Workaround for compile errors on Windows
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -14,103 +15,6 @@
# undef max # undef max
#endif // Q_OS_WIN #endif // Q_OS_WIN
// Rizin list iteration macros
#define CutterRzListForeach(list, it, type, x) \
if (list) \
for (it = list->head; it && ((x = static_cast<type *>(it->data))); it = it->n)
#define CutterRzVectorForeach(vec, it, type) \
if ((vec) && (vec)->a) \
for (it = (type *)(vec)->a; \
(char *)it != (char *)(vec)->a + ((vec)->len * (vec)->elem_size); \
it = (type *)((char *)it + (vec)->elem_size))
template<typename T>
class CutterPVector
{
private:
const RzPVector *const vec;
public:
class iterator : public std::iterator<std::input_iterator_tag, T *>
{
private:
T **p;
public:
iterator(T **p) : p(p) {}
iterator(const iterator &o) : p(o.p) {}
iterator &operator++()
{
p++;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const iterator &rhs) const { return p == rhs.p; }
bool operator!=(const iterator &rhs) const { return p != rhs.p; }
T *operator*() { return *p; }
};
CutterPVector(const RzPVector *vec) : vec(vec) {}
iterator begin() const { return iterator(reinterpret_cast<T **>(vec->v.a)); }
iterator end() const { return iterator(reinterpret_cast<T **>(vec->v.a) + vec->v.len); }
};
template<typename T>
class CutterRzList
{
private:
const RzList *const list;
public:
class iterator : public std::iterator<std::input_iterator_tag, T *>
{
private:
RzListIter *iter;
public:
explicit iterator(RzListIter *iter) : iter(iter) {}
iterator(const iterator &o) : iter(o.iter) {}
iterator &operator++()
{
if (!iter) {
return *this;
}
iter = iter->n;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const iterator &rhs) const { return iter == rhs.iter; }
bool operator!=(const iterator &rhs) const { return iter != rhs.iter; }
T *operator*()
{
if (!iter) {
return nullptr;
}
return reinterpret_cast<T *>(iter->data);
}
};
explicit CutterRzList(const RzList *l) : list(l) {}
iterator begin() const
{
if (!list) {
return iterator(nullptr);
}
return iterator(list->head);
}
iterator end() const { return iterator(nullptr); }
};
// Global information for Cutter // Global information for Cutter
#define APPNAME "Cutter" #define APPNAME "Cutter"

View File

@ -1768,12 +1768,12 @@ void MainWindow::on_actionExport_as_code_triggered()
return; return;
} }
std::unique_ptr<char, decltype(free) *> string {
auto string = fromOwned(
dialog.selectedNameFilter() != instructionsInComments dialog.selectedNameFilter() != instructionsInComments
? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()]) ? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()])
: rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size), : rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size));
free
};
fileOut << string.get(); fileOut << string.get();
} }

2
src/core/RizinCpp.cpp Normal file
View File

@ -0,0 +1,2 @@
#include "RizinCpp.h"

154
src/core/RizinCpp.h Normal file
View File

@ -0,0 +1,154 @@
/** \file RizinCpp.h
* Various utilities for easier and safer interactions with Rizin
* from C++ code.
*/
#ifndef RIZINCPP_H
#define RIZINCPP_H
#include "rz_core.h"
#include <QString>
#include <memory>
static inline QString fromOwnedCharPtr(char *str)
{
QString result(str ? str : "");
rz_mem_free(str);
return result;
}
template<class T, class F>
std::unique_ptr<T, F *> fromOwned(T *data, F *freeFunction)
{
return std::unique_ptr<T, F *> { data, freeFunction };
}
static inline std::unique_ptr<char, decltype(&rz_mem_free)> fromOwned(char *text)
{
return { text, rz_mem_free };
}
template<class T, void (*func)(T *)>
class FreeBinder
{
public:
void operator()(T *data) { func(data); }
};
template<class T, void (*func)(T *)>
using UniquePtrC = std::unique_ptr<T, FreeBinder<T, func>>;
template<typename T, void (*func)(T)>
using UniquePtrCP = UniquePtrC<typename std::remove_pointer<T>::type, func>;
static inline auto fromOwned(RZ_OWN RzPVector *data)
-> UniquePtrCP<decltype(data), &rz_pvector_free>
{
return { data, {} };
}
static inline auto fromOwned(RZ_OWN RzList *data)
-> UniquePtrCP<decltype(data), &rz_list_free>
{
return { data, {} };
}
// Rizin list iteration macros
// deprecated, prefer using CutterPVector and CutterRzList instead
#define CutterRzListForeach(list, it, type, x) \
if (list) \
for (it = list->head; it && ((x = static_cast<type *>(it->data))); it = it->n)
#define CutterRzVectorForeach(vec, it, type) \
if ((vec) && (vec)->a) \
for (it = (type *)(vec)->a; \
(char *)it != (char *)(vec)->a + ((vec)->len * (vec)->elem_size); \
it = (type *)((char *)it + (vec)->elem_size))
template<typename T>
class CutterPVector
{
private:
const RzPVector *const vec;
public:
class iterator : public std::iterator<std::input_iterator_tag, T *>
{
private:
T **p;
public:
iterator(T **p) : p(p) {}
iterator(const iterator &o) : p(o.p) {}
iterator &operator++()
{
p++;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const iterator &rhs) const { return p == rhs.p; }
bool operator!=(const iterator &rhs) const { return p != rhs.p; }
T *operator*() { return *p; }
};
CutterPVector(const RzPVector *vec) : vec(vec) {}
iterator begin() const { return iterator(reinterpret_cast<T **>(vec->v.a)); }
iterator end() const { return iterator(reinterpret_cast<T **>(vec->v.a) + vec->v.len); }
};
template<typename T>
class CutterRzList
{
private:
const RzList *const list;
public:
class iterator : public std::iterator<std::input_iterator_tag, T *>
{
private:
RzListIter *iter;
public:
explicit iterator(RzListIter *iter) : iter(iter) {}
iterator(const iterator &o) : iter(o.iter) {}
iterator &operator++()
{
if (!iter) {
return *this;
}
iter = iter->n;
return *this;
}
iterator operator++(int)
{
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(const iterator &rhs) const { return iter == rhs.iter; }
bool operator!=(const iterator &rhs) const { return iter != rhs.iter; }
T *operator*()
{
if (!iter) {
return nullptr;
}
return reinterpret_cast<T *>(iter->data);
}
};
explicit CutterRzList(const RzList *l) : list(l) {}
iterator begin() const
{
if (!list) {
return iterator(nullptr);
}
return iterator(list->head);
}
iterator end() const { return iterator(nullptr); }
};
#endif // RIZINCPP_H

View File

@ -86,11 +86,7 @@ bool EditMethodDialog::inputValid()
QString EditMethodDialog::convertRealNameToName(const QString &realName) QString EditMethodDialog::convertRealNameToName(const QString &realName)
{ {
std::unique_ptr<const char, void (*)(const char *)> sanitizedCString( return fromOwnedCharPtr(rz_str_sanitize_sdb_key(realName.toUtf8().constData()));
rz_str_sanitize_sdb_key(realName.toUtf8().constData()),
[](const char *s) { rz_mem_free((void*)s); });
return QString(sanitizedCString.get());
} }
void EditMethodDialog::setClass(const QString &className) void EditMethodDialog::setClass(const QString &className)

View File

@ -314,9 +314,8 @@ void DisassemblyContextMenu::addDebugMenu()
QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset) QVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)
{ {
RzCoreLocked core(Core()); RzCoreLocked core(Core());
auto p = std::unique_ptr<RzCoreAnalysisName, decltype(rz_core_analysis_name_free) *> { auto p = fromOwned(
rz_core_analysis_name(core, offset), rz_core_analysis_name_free rz_core_analysis_name(core, offset), rz_core_analysis_name_free);
};
if (!p) { if (!p) {
return {}; return {};
} }

View File

@ -74,7 +74,6 @@ static inline bool isBetween(ut64 a, ut64 x, ut64 b)
return (a == UT64_MAX || a <= x) && (b == UT64_MAX || x <= b); return (a == UT64_MAX || a <= x) && (b == UT64_MAX || x <= b);
} }
using PRzList = std::unique_ptr<RzList, decltype(rz_list_free) *>;
void CallGraphView::loadCurrentGraph() void CallGraphView::loadCurrentGraph()
{ {
@ -90,7 +89,7 @@ void CallGraphView::loadCurrentGraph()
GraphLayout::GraphBlock block; GraphLayout::GraphBlock block;
block.entry = fcn->addr; block.entry = fcn->addr;
auto xrefs = PRzList { rz_analysis_function_get_xrefs_from(fcn), rz_list_free }; auto xrefs = fromOwned(rz_analysis_function_get_xrefs_from(fcn));
auto calls = std::unordered_set<ut64>(); auto calls = std::unordered_set<ut64>();
for (const auto &xref : CutterRzList<RzAnalysisXRef>(xrefs.get())) { for (const auto &xref : CutterRzList<RzAnalysisXRef>(xrefs.get())) {
const auto x = xref->to; const auto x = xref->to;

View File

@ -193,9 +193,7 @@ void DisassemblerGraphView::loadCurrentGraph()
windowTitle = tr("Graph"); windowTitle = tr("Graph");
if (fcn && RZ_STR_ISNOTEMPTY(fcn->name)) { if (fcn && RZ_STR_ISNOTEMPTY(fcn->name)) {
std::unique_ptr<char, decltype(std::free) *> fcnName { auto fcnName = fromOwned(rz_str_escape_utf8_for_json(fcn->name, -1));
rz_str_escape_utf8_for_json(fcn->name, -1), std::free
};
windowTitle += QString("(%0)").arg(fcnName.get()); windowTitle += QString("(%0)").arg(fcnName.get());
} else { } else {
windowTitle += "(Empty)"; windowTitle += "(Empty)";
@ -267,10 +265,8 @@ void DisassemblerGraphView::loadCurrentGraph()
} }
rz_io_read_at(core->io, bbi->addr, buf.get(), (int)bbi->size); rz_io_read_at(core->io, bbi->addr, buf.get(), (int)bbi->size);
std::unique_ptr<RzPVector, decltype(rz_pvector_free) *> vec { auto vec = fromOwned(
rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)), rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));
rz_pvector_free
};
if (!vec) { if (!vec) {
break; break;
} }

View File

@ -234,12 +234,10 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
QStringList summary {}; QStringList summary {};
{ {
auto seeker = Core()->seekTemp(function.offset); auto seeker = Core()->seekTemp(function.offset);
auto strings = std::unique_ptr<char, decltype(free) *> { auto strings = fromOwnedCharPtr(
rz_core_print_disasm_strings(Core()->core(), RZ_CORE_DISASM_STRINGS_MODE_FUNCTION, rz_core_print_disasm_strings(Core()->core(), RZ_CORE_DISASM_STRINGS_MODE_FUNCTION,
0, NULL), 0, NULL));
free summary = strings.split('\n', CUTTER_QT_SKIP_EMPTY_PARTS);
};
summary = QString(strings.get()).split('\n', CUTTER_QT_SKIP_EMPTY_PARTS);
} }
const QFont &fnt = Config()->getFont(); const QFont &fnt = Config()->getFont();

View File

@ -21,8 +21,7 @@ VisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent)
graphicsView(new QGraphicsView), graphicsView(new QGraphicsView),
seekGraphicsItem(nullptr), seekGraphicsItem(nullptr),
PCGraphicsItem(nullptr), PCGraphicsItem(nullptr),
main(main), main(main)
stats(nullptr, rz_core_analysis_stats_free)
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
@ -119,7 +118,7 @@ void VisualNavbar::fetchStats()
RzCoreLocked core(Core()); RzCoreLocked core(Core());
stats.reset(nullptr); stats.reset(nullptr);
RzList *list = rz_core_get_boundaries_prot(core, -1, NULL, "search"); auto list = fromOwned(rz_core_get_boundaries_prot(core, -1, NULL, "search"));
if (!list) { if (!list) {
return; return;
} }
@ -127,7 +126,7 @@ void VisualNavbar::fetchStats()
RzIOMap *map; RzIOMap *map;
ut64 from = UT64_MAX; ut64 from = UT64_MAX;
ut64 to = 0; ut64 to = 0;
CutterRzListForeach (list, iter, RzIOMap, map) { CutterRzListForeach (list.get(), iter, RzIOMap, map) {
ut64 f = rz_itv_begin(map->itv); ut64 f = rz_itv_begin(map->itv);
ut64 t = rz_itv_end(map->itv); ut64 t = rz_itv_end(map->itv);
if (f < from) { if (f < from) {
@ -137,7 +136,6 @@ void VisualNavbar::fetchStats()
to = t; to = t;
} }
} }
rz_list_free(list);
to--; // rz_core_analysis_get_stats takes inclusive ranges to--; // rz_core_analysis_get_stats takes inclusive ranges
if (to < from) { if (to < from) {
return; return;

View File

@ -47,7 +47,7 @@ private:
QGraphicsRectItem *PCGraphicsItem; QGraphicsRectItem *PCGraphicsItem;
MainWindow *main; MainWindow *main;
std::unique_ptr<RzCoreAnalysisStats, decltype(&rz_core_analysis_stats_free)> stats; UniquePtrC<RzCoreAnalysisStats, &rz_core_analysis_stats_free> stats;
unsigned int statsWidth = 0; unsigned int statsWidth = 0;
unsigned int previousWidth = 0; unsigned int previousWidth = 0;