mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-19 19:36:11 +00:00
Use REvent for classes (#1169)
* React to Anal Class REvents * Correctly react to specific Class Events * Adapt to Class REvent changes * Update r2 submodule for Class REvents
This commit is contained in:
parent
d876c9deee
commit
bcb3a162f7
2
radare2
2
radare2
@ -1 +1 @@
|
||||
Subproject commit b022c9533482a8f1c9ae825ced5c1a3e251837d9
|
||||
Subproject commit b4178702c88ed361fcb98e1b87cd74f0af4b2f44
|
@ -131,11 +131,19 @@ RCoreLocked CutterCore::core() const
|
||||
|
||||
#define CORE_LOCK() RCoreLocked core_lock__(this->core_)
|
||||
|
||||
static void cutterREventCallback(REvent *, int type, void *user, void *data)
|
||||
{
|
||||
auto core = reinterpret_cast<CutterCore *>(user);
|
||||
core->handleREvent(type, data);
|
||||
}
|
||||
|
||||
CutterCore::CutterCore(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
r_cons_new(); // initialize console
|
||||
this->core_ = r_core_new();
|
||||
core_ = r_core_new();
|
||||
|
||||
r_event_hook(core_->anal->ev, R_EVENT_ALL, cutterREventCallback, this);
|
||||
|
||||
#if defined(APPIMAGE) || defined(MACOS_R2_BUNDLED)
|
||||
auto prefix = QDir(QCoreApplication::applicationDirPath());
|
||||
@ -1970,11 +1978,11 @@ QList<BinClassDescription> CutterCore::getAllClassesFromFlags()
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<QString> CutterCore::getAllAnalClasses()
|
||||
QList<QString> CutterCore::getAllAnalClasses(bool sorted)
|
||||
{
|
||||
QList<QString> ret;
|
||||
|
||||
SdbList *l = r_anal_class_get_all(core_->anal, true);
|
||||
SdbList *l = r_anal_class_get_all(core_->anal, sorted);
|
||||
if (!l) {
|
||||
return ret;
|
||||
}
|
||||
@ -2081,13 +2089,11 @@ void CutterCore::setAnalMethod(const QString &className, const AnalMethodDescrip
|
||||
analMeth.vtable_offset = meth.vtableOffset;
|
||||
r_anal_class_method_set(core_->anal, className.toUtf8().constData(), &analMeth);
|
||||
r_anal_class_method_fini(&analMeth);
|
||||
emit classesChanged();
|
||||
}
|
||||
|
||||
void CutterCore::renameAnalMethod(const QString &className, const QString &oldMethodName, const QString &newMethodName)
|
||||
{
|
||||
r_anal_class_method_rename(core_->anal, className.toUtf8().constData(), oldMethodName.toUtf8().constData(), newMethodName.toUtf8().constData());
|
||||
emit classesChanged();
|
||||
}
|
||||
|
||||
QList<ResourcesDescription> CutterCore::getAllResources()
|
||||
@ -2397,6 +2403,44 @@ void CutterCore::addFlag(RVA offset, QString name, RVA size)
|
||||
emit flagsChanged();
|
||||
}
|
||||
|
||||
void CutterCore::handleREvent(int type, void *data)
|
||||
{
|
||||
switch (type) {
|
||||
case R_EVENT_CLASS_NEW: {
|
||||
auto ev = reinterpret_cast<REventClass *>(data);
|
||||
emit classNew(QString::fromUtf8(ev->name));
|
||||
break;
|
||||
}
|
||||
case R_EVENT_CLASS_DEL: {
|
||||
auto ev = reinterpret_cast<REventClass *>(data);
|
||||
emit classDeleted(QString::fromUtf8(ev->name));
|
||||
break;
|
||||
}
|
||||
case R_EVENT_CLASS_RENAME: {
|
||||
auto ev = reinterpret_cast<REventClassRename *>(data);
|
||||
emit classRenamed(QString::fromUtf8(ev->name_old), QString::fromUtf8(ev->name_new));
|
||||
break;
|
||||
}
|
||||
case R_EVENT_CLASS_ATTR_SET: {
|
||||
auto ev = reinterpret_cast<REventClassAttrSet *>(data);
|
||||
emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));
|
||||
break;
|
||||
}
|
||||
case R_EVENT_CLASS_ATTR_DEL: {
|
||||
auto ev = reinterpret_cast<REventClassAttr *>(data);
|
||||
emit classAttrsChanged(QString::fromUtf8(ev->class_name));
|
||||
break;
|
||||
}
|
||||
case R_EVENT_CLASS_ATTR_RENAME: {
|
||||
auto ev = reinterpret_cast<REventClassAttrRename *>(data);
|
||||
emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CutterCore::triggerFlagsChanged()
|
||||
{
|
||||
emit flagsChanged();
|
||||
|
10
src/Cutter.h
10
src/Cutter.h
@ -480,7 +480,7 @@ public:
|
||||
void setCurrentBits(int bits, RVA offset = RVA_INVALID);
|
||||
|
||||
/* Classes */
|
||||
QList<QString> getAllAnalClasses();
|
||||
QList<QString> getAllAnalClasses(bool sorted);
|
||||
QList<AnalMethodDescription> getAnalClassMethods(const QString &cls);
|
||||
QList<AnalBaseClassDescription> getAnalClassBaseClasses(const QString &cls);
|
||||
QList<AnalVTableDescription> getAnalClassVTables(const QString &cls);
|
||||
@ -697,6 +697,8 @@ public:
|
||||
QList<StringDescription> parseStringsJson(const QJsonDocument &doc);
|
||||
QList<FunctionDescription> parseFunctionsJson(const QJsonDocument &doc);
|
||||
|
||||
void handleREvent(int type, void *data);
|
||||
|
||||
/* Signals related */
|
||||
void triggerVarsChanged();
|
||||
void triggerFunctionRenamed(const QString &prevName, const QString &newName);
|
||||
@ -721,13 +723,17 @@ signals:
|
||||
void functionsChanged();
|
||||
void flagsChanged();
|
||||
void commentsChanged();
|
||||
void classesChanged();
|
||||
void registersChanged();
|
||||
void instructionChanged(RVA offset);
|
||||
void breakpointsChanged();
|
||||
void refreshCodeViews();
|
||||
void stackChanged();
|
||||
|
||||
void classNew(const QString &cls);
|
||||
void classDeleted(const QString &cls);
|
||||
void classRenamed(const QString &oldName, const QString &newName);
|
||||
void classAttrsChanged(const QString &cls);
|
||||
|
||||
void projectSaved(bool successfully, const QString &name);
|
||||
|
||||
/*!
|
||||
|
@ -14,7 +14,7 @@ EditMethodDialog::EditMethodDialog(bool classFixed, QWidget *parent) :
|
||||
} else {
|
||||
classComboBox = new QComboBox(this);
|
||||
ui->formLayout->setItem(0, QFormLayout::FieldRole, new QWidgetItem(classComboBox));
|
||||
for (auto &cls : Core()->getAllAnalClasses()) {
|
||||
for (auto &cls : Core()->getAllAnalClasses(true)) {
|
||||
classComboBox->addItem(cls, cls);
|
||||
}
|
||||
}
|
||||
|
@ -210,19 +210,114 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
|
||||
|
||||
AnalClassesModel::AnalClassesModel(QObject *parent)
|
||||
AnalClassesModel::AnalClassesModel(CutterDockWidget *parent)
|
||||
: ClassesModel(parent), attrs(new QMap<QString, QVector<Attribute>>)
|
||||
{
|
||||
// Just use a simple refresh deferrer. If an event was triggered in the background, simply refresh everything later.
|
||||
refreshDeferrer = parent->createRefreshDeferrer([this]() {
|
||||
this->refreshAll();
|
||||
});
|
||||
|
||||
connect(Core(), &CutterCore::refreshAll, this, &AnalClassesModel::refreshAll);
|
||||
connect(Core(), &CutterCore::classNew, this, &AnalClassesModel::classNew);
|
||||
connect(Core(), &CutterCore::classDeleted, this, &AnalClassesModel::classDeleted);
|
||||
connect(Core(), &CutterCore::classRenamed, this, &AnalClassesModel::classRenamed);
|
||||
connect(Core(), &CutterCore::classAttrsChanged, this, &AnalClassesModel::classAttrsChanged);
|
||||
|
||||
refreshAll();
|
||||
}
|
||||
|
||||
void AnalClassesModel::refreshClasses()
|
||||
void AnalClassesModel::refreshAll()
|
||||
{
|
||||
if (!refreshDeferrer->attemptRefresh(nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
attrs->clear();
|
||||
classes = Core()->getAllAnalClasses();
|
||||
classes = Core()->getAllAnalClasses(true); // must be sorted
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void AnalClassesModel::classNew(const QString &cls)
|
||||
{
|
||||
if (!refreshDeferrer->attemptRefresh(nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the destination position using binary search and add the row
|
||||
auto it = std::lower_bound(classes.begin(), classes.end(), cls);
|
||||
int index = it - classes.begin();
|
||||
beginInsertRows(QModelIndex(), index, index);
|
||||
classes.insert(it, cls);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void AnalClassesModel::classDeleted(const QString &cls)
|
||||
{
|
||||
if (!refreshDeferrer->attemptRefresh(nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the position using binary search and remove the row
|
||||
auto it = std::lower_bound(classes.begin(), classes.end(), cls);
|
||||
if(it == classes.end() || *it != cls) {
|
||||
return;
|
||||
}
|
||||
int index = it - classes.begin();
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
classes.erase(it);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void AnalClassesModel::classRenamed(const QString &oldName, const QString &newName)
|
||||
{
|
||||
if (!refreshDeferrer->attemptRefresh(nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto oldIt = std::lower_bound(classes.begin(), classes.end(), oldName);
|
||||
if (oldIt == classes.end() || *oldIt != oldName) {
|
||||
return;
|
||||
}
|
||||
auto newIt = std::lower_bound(classes.begin(), classes.end(), newName);
|
||||
int oldRow = oldIt - classes.begin();
|
||||
int newRow = newIt - classes.begin();
|
||||
// oldRow == newRow means the name stayed the same.
|
||||
// oldRow == newRow - 1 means the name changed, but the row stays the same.
|
||||
if (oldRow != newRow && oldRow != newRow - 1) {
|
||||
beginMoveRows(QModelIndex(), oldRow, oldRow, QModelIndex(), newRow);
|
||||
classes.erase(oldIt);
|
||||
// iterators are invalid now, so we calculate the new position from the rows.
|
||||
if (oldRow < newRow) {
|
||||
// if we move down, we need to account for the removed old element above.
|
||||
newRow--;
|
||||
}
|
||||
classes.insert(newRow, newName);
|
||||
endMoveRows();
|
||||
} else if (oldRow == newRow - 1) { // class name changed, but not the row
|
||||
newRow--;
|
||||
classes[newRow] = newName;
|
||||
}
|
||||
emit dataChanged(index(newRow, 0), index(newRow, 0));
|
||||
}
|
||||
|
||||
void AnalClassesModel::classAttrsChanged(const QString &cls)
|
||||
{
|
||||
if (!refreshDeferrer->attemptRefresh(nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = std::lower_bound(classes.begin(), classes.end(), cls);
|
||||
if(it == classes.end() || *it != cls) {
|
||||
return;
|
||||
}
|
||||
QPersistentModelIndex persistentIndex = QPersistentModelIndex(index(it - classes.begin(), 0));
|
||||
layoutAboutToBeChanged({persistentIndex});
|
||||
attrs->remove(cls);
|
||||
layoutChanged({persistentIndex});
|
||||
}
|
||||
|
||||
const QVector<AnalClassesModel::Attribute> &AnalClassesModel::getAttrs(const QString &cls) const
|
||||
{
|
||||
auto it = attrs->find(cls);
|
||||
@ -439,22 +534,24 @@ bool ClassesSortFilterProxyModel::lessThan(const QModelIndex &left, const QModel
|
||||
case ClassesModel::OFFSET: {
|
||||
RVA left_offset = left.data(ClassesModel::OffsetRole).toULongLong();
|
||||
RVA right_offset = right.data(ClassesModel::OffsetRole).toULongLong();
|
||||
if (left_offset != right_offset)
|
||||
if (left_offset != right_offset) {
|
||||
return left_offset < right_offset;
|
||||
}
|
||||
}
|
||||
// fallthrough
|
||||
case ClassesModel::TYPE: {
|
||||
auto left_type = left.data(ClassesModel::TypeRole).value<ClassesModel::RowType>();
|
||||
auto right_type = right.data(ClassesModel::TypeRole).value<ClassesModel::RowType>();
|
||||
if (left_type != right_type)
|
||||
if (left_type != right_type) {
|
||||
return left_type < right_type;
|
||||
}
|
||||
}
|
||||
// fallthrough
|
||||
case ClassesModel::NAME:
|
||||
default:
|
||||
QString left_name = left.data(ClassesModel::NameRole).toString();
|
||||
QString right_name = right.data(ClassesModel::NameRole).toString();
|
||||
return left_name < right_name;
|
||||
return QString::compare(left_name, right_name, Qt::CaseInsensitive) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,14 +577,10 @@ ClassesWidget::ClassesWidget(MainWindow *main, QAction *action) :
|
||||
|
||||
ui->classSourceCombo->setCurrentIndex(1);
|
||||
|
||||
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshClasses()));
|
||||
connect(Core(), &CutterCore::classesChanged, this, [this]() {
|
||||
if (getSource() == Source::ANAL) {
|
||||
refreshClasses();
|
||||
}
|
||||
});
|
||||
connect(ui->classSourceCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(refreshClasses()));
|
||||
connect(ui->classesTreeView, &QTreeView::customContextMenuRequested, this, &ClassesWidget::showContextMenu);
|
||||
|
||||
refreshClasses();
|
||||
}
|
||||
|
||||
ClassesWidget::~ClassesWidget() {}
|
||||
@ -523,7 +616,6 @@ void ClassesWidget::refreshClasses()
|
||||
anal_model = new AnalClassesModel(this);
|
||||
proxy_model->setSourceModel(anal_model);
|
||||
}
|
||||
anal_model->refreshClasses();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -104,8 +104,13 @@ private:
|
||||
Attribute(Type type, const QVariant &data) : type(type), data(data) {}
|
||||
};
|
||||
|
||||
/*!
|
||||
* This must always stay sorted alphabetically.
|
||||
*/
|
||||
QList<QString> classes;
|
||||
|
||||
RefreshDeferrer *refreshDeferrer;
|
||||
|
||||
/*!
|
||||
* \brief Cache for class attributes
|
||||
*
|
||||
@ -130,9 +135,14 @@ private:
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
public:
|
||||
explicit AnalClassesModel(QObject *parent = nullptr);
|
||||
explicit AnalClassesModel(CutterDockWidget *parent);
|
||||
|
||||
void refreshClasses();
|
||||
public slots:
|
||||
void refreshAll();
|
||||
void classNew(const QString &cls);
|
||||
void classDeleted(const QString &cls);
|
||||
void classRenamed(const QString &oldName, const QString &newName);
|
||||
void classAttrsChanged(const QString &cls);
|
||||
};
|
||||
|
||||
|
||||
|
@ -17,21 +17,6 @@ public:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
bool isVisibleToUser() { return isVisibleToUserCurrent; }
|
||||
|
||||
public slots:
|
||||
void toggleDockWidget(bool show);
|
||||
|
||||
signals:
|
||||
void becameVisibleToUser();
|
||||
|
||||
private:
|
||||
QAction *action;
|
||||
|
||||
bool isVisibleToUserCurrent = false;
|
||||
void updateIsVisibleToUser();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
/*!
|
||||
* \brief Convenience method for creating and registering a RefreshDeferrer without any parameters
|
||||
* \param refreshNowFunc lambda taking no parameters, called when a refresh should occur
|
||||
@ -63,6 +48,21 @@ protected:
|
||||
});
|
||||
return deferrer;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void toggleDockWidget(bool show);
|
||||
|
||||
signals:
|
||||
void becameVisibleToUser();
|
||||
|
||||
private:
|
||||
QAction *action;
|
||||
|
||||
bool isVisibleToUserCurrent = false;
|
||||
void updateIsVisibleToUser();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
};
|
||||
|
||||
#endif // CUTTERWIDGET_H
|
||||
|
Loading…
Reference in New Issue
Block a user