diff --git a/src/Cutter.cpp b/src/Cutter.cpp index 78bc6a4c..da248781 100644 --- a/src/Cutter.cpp +++ b/src/Cutter.cpp @@ -2014,6 +2014,7 @@ QList CutterCore::getAnalClassMethods(const QString &cls) desc.name = QString::fromUtf8(meth->name); desc.addr = meth->addr; desc.vtableOffset = meth->vtable_offset; + ret.append(desc); } r_vector_free(meths); @@ -2036,6 +2037,7 @@ QList CutterCore::getAnalClassBaseClasses(const QStrin desc.id = QString::fromUtf8(base->id); desc.offset = base->offset; desc.className = QString::fromUtf8(base->class_name); + ret.append(desc); } r_vector_free(bases); @@ -2058,6 +2060,7 @@ QList CutterCore::getAnalClassVTables(const QString &cls) desc.id = QString::fromUtf8(vtable->id); desc.offset = vtable->offset; desc.addr = vtable->addr; + ret.append(desc); } r_vector_free(vtables); diff --git a/src/Cutter.h b/src/Cutter.h index a3085c6a..4aec0654 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -391,6 +391,9 @@ Q_DECLARE_METATYPE(BinClassDescription) Q_DECLARE_METATYPE(const BinClassDescription *) Q_DECLARE_METATYPE(const BinClassMethodDescription *) Q_DECLARE_METATYPE(const BinClassFieldDescription *) +Q_DECLARE_METATYPE(AnalBaseClassDescription) +Q_DECLARE_METATYPE(AnalMethodDescription) +Q_DECLARE_METATYPE(AnalVTableDescription) Q_DECLARE_METATYPE(ResourcesDescription) Q_DECLARE_METATYPE(VTableDescription) Q_DECLARE_METATYPE(TypeDescription) diff --git a/src/widgets/ClassesWidget.cpp b/src/widgets/ClassesWidget.cpp index 277b2903..017ca851 100644 --- a/src/widgets/ClassesWidget.cpp +++ b/src/widgets/ClassesWidget.cpp @@ -48,21 +48,24 @@ void BinClassesModel::setClasses(const QList &classes) QModelIndex BinClassesModel::index(int row, int column, const QModelIndex &parent) const { - if (!parent.isValid()) + if (!parent.isValid()) { return createIndex(row, column, (quintptr)0); // root function nodes have id = 0 + } return createIndex(row, column, (quintptr)parent.row() + 1); // sub-nodes have id = class index + 1 } QModelIndex BinClassesModel::parent(const QModelIndex &index) const { - if (!index.isValid() || index.column() != 0) - return QModelIndex(); + if (!index.isValid()) { + return {}; + } - if (index.internalId() == 0) // root function node - return QModelIndex(); - else // sub-node + if (index.internalId() == 0) { // root function node + return {}; + } else { // sub-node return this->index((int)(index.internalId() - 1), 0); + } } int BinClassesModel::rowCount(const QModelIndex &parent) const @@ -134,7 +137,7 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const case NameRole: return meth->name; case TypeRole: - return QVariant::fromValue(METHOD); + return QVariant::fromValue(RowType::Method); case DataRole: return QVariant::fromValue(*meth); default: @@ -158,7 +161,7 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const case NameRole: return field->name; case TypeRole: - return QVariant::fromValue(FIELD); + return QVariant::fromValue(RowType::Field); default: return QVariant(); } @@ -178,7 +181,7 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const case NameRole: return base->name; case TypeRole: - return QVariant::fromValue(BASE); + return QVariant::fromValue(RowType::Base); default: return QVariant(); } @@ -205,7 +208,7 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const case NameRole: return cls->name; case TypeRole: - return QVariant::fromValue(CLASS); + return QVariant::fromValue(RowType::Class); default: return QVariant(); } @@ -214,7 +217,7 @@ QVariant BinClassesModel::data(const QModelIndex &index, int role) const AnalClassesModel::AnalClassesModel(QObject *parent) - : ClassesModel(parent) + : ClassesModel(parent), attrs(new QMap>) { } @@ -225,24 +228,54 @@ void AnalClassesModel::refreshClasses() endResetModel(); } +const QVector &AnalClassesModel::getAttrs(const QString &cls) const +{ + auto it = attrs->find(cls); + if(it != attrs->end()) { + return it.value(); + } + + QVector clsAttrs; + QList bases = Core()->getAnalClassBaseClasses(cls); + QList meths = Core()->getAnalClassMethods(cls); + QList vtables = Core()->getAnalClassVTables(cls); + clsAttrs.reserve(bases.size() + meths.size() + vtables.size()); + + for(const AnalBaseClassDescription &base : bases) { + clsAttrs.push_back(Attribute(Attribute::Type::Base, QVariant::fromValue(base))); + } + + for(const AnalVTableDescription &vtable : vtables) { + clsAttrs.push_back(Attribute(Attribute::Type::VTable, QVariant::fromValue(vtable))); + } + + for(const AnalMethodDescription &meth : meths) { + clsAttrs.push_back(Attribute(Attribute::Type::Method, QVariant::fromValue(meth))); + } + + return attrs->insert(cls, clsAttrs).value(); +} QModelIndex AnalClassesModel::index(int row, int column, const QModelIndex &parent) const { - if (!parent.isValid()) + if (!parent.isValid()) { return createIndex(row, column, (quintptr)0); // root function nodes have id = 0 + } return createIndex(row, column, (quintptr)parent.row() + 1); // sub-nodes have id = class index + 1 } QModelIndex AnalClassesModel::parent(const QModelIndex &index) const { - if (!index.isValid() || index.column() != 0) - return QModelIndex(); + if (!index.isValid()) { + return {}; + } - if (index.internalId() == 0) // root function node - return QModelIndex(); - else // sub-node + if (index.internalId() == 0) { // root function node + return {}; + } else { // sub-node return this->index((int)(index.internalId() - 1), 0); + } } int AnalClassesModel::rowCount(const QModelIndex &parent) const @@ -252,9 +285,7 @@ int AnalClassesModel::rowCount(const QModelIndex &parent) const } if (parent.internalId() == 0) { // methods/fields - return 0; - // TODO const BinClassDescription *cls = &classes.at(parent.row()); - // TODO return cls->baseClasses.length() + cls->methods.length() + cls->fields.length(); + return getAttrs(classes[parent.row()]).size(); } return 0; // below methods/fields @@ -262,7 +293,7 @@ int AnalClassesModel::rowCount(const QModelIndex &parent) const bool AnalClassesModel::hasChildren(const QModelIndex &parent) const { - return true; + return !parent.isValid() || !parent.parent().isValid(); } int AnalClassesModel::columnCount(const QModelIndex &) const @@ -272,104 +303,12 @@ int AnalClassesModel::columnCount(const QModelIndex &) const QVariant AnalClassesModel::data(const QModelIndex &index, int role) const { - QString cls; - const BinClassMethodDescription *meth = nullptr; - const BinClassFieldDescription *field = nullptr; - const BinClassBaseClassDescription *base = nullptr; if (index.internalId() == 0) { // class row if (index.row() >= classes.count()) { return QVariant(); } - cls = classes.at(index.row()); - } else { // method/field/base row - cls = classes.at(static_cast(index.internalId() - 1)); - - /*if (index.row() >= cls->baseClasses.length() + cls->methods.length() + cls->fields.length()) { - return QVariant(); - } - - if (index.row() < cls->baseClasses.length()) { - base = &cls->baseClasses[index.row()]; - } else if (index.row() - cls->baseClasses.length() < cls->methods.length()) { - meth = &cls->methods[index.row() - cls->baseClasses.length()]; - } else { - field = &cls->fields[index.row() - cls->baseClasses.length() - cls->methods.length()]; - }*/ - } - - if (meth) { - switch (role) { - case Qt::DisplayRole: - switch (index.column()) { - case NAME: - return meth->name; - case TYPE: - return tr("method"); - case OFFSET: - return meth->addr == RVA_INVALID ? QString() : RAddressString(meth->addr); - case VTABLE: - return meth->vtableOffset < 0 ? QString() : QString("+%1").arg(meth->vtableOffset); - default: - return QVariant(); - } - case OffsetRole: - return QVariant::fromValue(meth->addr); - case VTableOffsetRole: - return QVariant::fromValue(index.parent().data(VTableOffsetRole).toULongLong() + meth->vtableOffset); - case NameRole: - return meth->name; - case TypeRole: - return QVariant::fromValue(METHOD); - case DataRole: - return QVariant::fromValue(*meth); - default: - return QVariant(); - } - } else if (field) { - switch (role) { - case Qt::DisplayRole: - switch (index.column()) { - case NAME: - return field->name; - case TYPE: - return tr("field"); - case OFFSET: - return field->addr == RVA_INVALID ? QString() : RAddressString(field->addr); - default: - return QVariant(); - } - case OffsetRole: - return QVariant::fromValue(field->addr); - case NameRole: - return field->name; - case TypeRole: - return QVariant::fromValue(FIELD); - default: - return QVariant(); - } - } else if (base) { - switch (role) { - case Qt::DisplayRole: - switch (index.column()) { - case NAME: - return base->name; - case TYPE: - return tr("base class"); - case OFFSET: - return QString("+%1").arg(base->offset); - default: - return QVariant(); - } - case NameRole: - return base->name; - case TypeRole: - return QVariant::fromValue(BASE); - default: - return QVariant(); - } - } - else { + QString cls = classes.at(index.row()); switch (role) { case Qt::DisplayRole: switch (index.column()) { @@ -377,25 +316,97 @@ QVariant AnalClassesModel::data(const QModelIndex &index, int role) const return cls; case TYPE: return tr("class"); - // TODO case OFFSET: - // TODO return cls->addr == RVA_INVALID ? QString() : RAddressString(cls->addr); - // TODO case VTABLE: - // TODO return cls->vtableAddr == RVA_INVALID ? QString() : RAddressString(cls->vtableAddr); default: return QVariant(); } - // TODO case OffsetRole: - // TODO return QVariant::fromValue(cls->addr); - // TODO case VTableOffsetRole: - // TODO return QVariant::fromValue(cls->vtableAddr); - // TODO case NameRole: - // TODO return cls->name; case TypeRole: - return QVariant::fromValue(CLASS); + return QVariant::fromValue(RowType::Class); default: return QVariant(); } + } else { // method/field/base row + QString cls = classes.at(static_cast(index.internalId() - 1)); + const Attribute &attr = getAttrs(cls)[index.row()]; + + switch (attr.type) { + case Attribute::Type::Base: { + AnalBaseClassDescription base = attr.data.value(); + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case NAME: + return base.className; + case TYPE: + return tr("base"); + case OFFSET: + return QString("+%1").arg(base.offset); + default: + return QVariant(); + } + case NameRole: + return base.className; + case TypeRole: + return QVariant::fromValue(RowType::Base); + default: + return QVariant(); + } + break; + } + case Attribute::Type::Method: { + AnalMethodDescription meth = attr.data.value(); + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case NAME: + return meth.name; + case TYPE: + return tr("method"); + case OFFSET: + return meth.addr == RVA_INVALID ? QString() : RAddressString(meth.addr); + case VTABLE: + return meth.vtableOffset < 0 ? QString() : QString("+%1").arg(meth.vtableOffset); + default: + return QVariant(); + } + case OffsetRole: + return QVariant::fromValue(meth.addr); + case VTableOffsetRole: + return QVariant::fromValue(index.parent().data(VTableOffsetRole).toULongLong() + meth.vtableOffset); + case NameRole: + return meth.name; + case TypeRole: + return QVariant::fromValue(RowType::Method); + default: + return QVariant(); + } + break; + } + case Attribute::Type::VTable: { + AnalVTableDescription vtable = attr.data.value(); + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case NAME: + return "vtable"; + case TYPE: + return tr("vtable"); + case OFFSET: + return RAddressString(vtable.addr); + default: + return QVariant(); + } + case OffsetRole: + return QVariant::fromValue(vtable.addr); + case TypeRole: + return QVariant::fromValue(RowType::VTable); + default: + return QVariant(); + } + break; + } + } } + return QVariant(); } @@ -531,7 +542,7 @@ void ClassesWidget::showContextMenu(const QPoint &pt) menu.addAction(ui->addMethodAction); - if (index.data(ClassesModel::TypeRole) == ClassesModel::METHOD) { + if (index.data(ClassesModel::TypeRole).toInt() == static_cast(ClassesModel::RowType::Method)) { menu.addAction(ui->editMethodAction); } @@ -555,7 +566,7 @@ void ClassesWidget::on_addMethodAction_triggered() } QString className; - if (index.data(ClassesModel::TypeRole).toInt() == ClassesModel::CLASS) { + if (index.data(ClassesModel::TypeRole).toInt() == static_cast(ClassesModel::RowType::Class)) { className = index.data(ClassesModel::NameRole).toString(); } else { className = index.parent().data(ClassesModel::NameRole).toString(); @@ -570,7 +581,7 @@ void ClassesWidget::on_addMethodAction_triggered() void ClassesWidget::on_editMethodAction_triggered() { QModelIndex index = ui->classesTreeView->selectionModel()->currentIndex(); - if (!index.isValid() || index.data(ClassesModel::TypeRole).toInt() != ClassesModel::METHOD) { + if (!index.isValid() || index.data(ClassesModel::TypeRole).toInt() != static_cast(ClassesModel::RowType::Method)) { return; } diff --git a/src/widgets/ClassesWidget.h b/src/widgets/ClassesWidget.h index 348f4d44..c721b3a5 100644 --- a/src/widgets/ClassesWidget.h +++ b/src/widgets/ClassesWidget.h @@ -22,7 +22,7 @@ class ClassesModel: public QAbstractItemModel { public: enum Columns { NAME = 0, TYPE, OFFSET, VTABLE, COUNT }; - enum RowType { CLASS = 0, METHOD = 1, FIELD = 2, BASE = 3 }; + enum class RowType { Class = 0, Method = 1, Field = 2, Base = 3, VTable = 4 }; static const int OffsetRole = Qt::UserRole; static const int NameRole = Qt::UserRole + 1; @@ -64,7 +64,20 @@ class AnalClassesModel: public ClassesModel Q_OBJECT private: + struct Attribute + { + enum class Type { VTable, Base, Method }; + Type type; + QVariant data; + + Attribute() = default; + Attribute(Type type, const QVariant &data) : type(type), data(data) {} + }; + QList classes; + std::unique_ptr>> attrs; + + const QVector &getAttrs(const QString &cls) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;