File indexing completed on 2024-05-12 05:43:32

0001 /*
0002     Copyright (C) 2013-2014 Volker Krause <vkrause@kde.org>
0003 
0004     This program is free software; you can redistribute it and/or modify it
0005     under the terms of the GNU Library General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or (at your
0007     option) any later version.
0008 
0009     This program is distributed in the hope that it will be useful, but WITHOUT
0010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
0012     License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program.  If not, see <https://www.gnu.org/licenses/>.
0016 */
0017 
0018 #include "elfmodel.h"
0019 
0020 #include <elf/elffile.h>
0021 #include <elf/elfheader.h>
0022 #include <elf/elfgotentry.h>
0023 
0024 #include "rowcountvisitor.h"
0025 #include "indexvisitor.h"
0026 #include "parentvisitor.h"
0027 #include "datavisitor.h"
0028 
0029 #include <QUrl>
0030 
0031 ElfModel::ElfModel(QObject* parent) : QAbstractItemModel(parent)
0032 {
0033 }
0034 
0035 ElfModel::~ElfModel()
0036 {
0037     clearInternalPointerMap();
0038 }
0039 
0040 ElfFileSet* ElfModel::fileSet() const
0041 {
0042     return m_fileSet;
0043 }
0044 
0045 void ElfModel::setFileSet(ElfFileSet *fileSet)
0046 {
0047     beginResetModel();
0048     clearInternalPointerMap();
0049     m_fileSet = fileSet;
0050 
0051     auto v = new ElfNodeVariant;
0052     v->payload = m_fileSet;
0053     v->type = ElfNodeVariant::FileSet;
0054     m_internalPointerMap.insert(m_fileSet, v);
0055 
0056     endResetModel();
0057 }
0058 
0059 void ElfModel::clearInternalPointerMap()
0060 {
0061     for (auto it = m_internalPointerMap.cbegin(); it != m_internalPointerMap.cend(); ++it)
0062         delete it.value();
0063     m_internalPointerMap.clear();
0064 }
0065 
0066 QVariant ElfModel::data(const QModelIndex& index, int role) const
0067 {
0068     if (!index.isValid() || !m_fileSet)
0069         return QVariant();
0070 
0071     if (role == NodeUrl)
0072         return urlForIndex(index);
0073 
0074     ElfNodeVariant var = contentForIndex(index);
0075 
0076     DataVisitor v(this, index.column());
0077     switch (index.column()) {
0078         case 0:
0079             return v.visit(&var, role);
0080         case 1:
0081             return v.visit(&var, role == Qt::DisplayRole ? SizeRole : role); // TODO proper formatting
0082     }
0083 
0084     return QVariant();
0085 }
0086 
0087 int ElfModel::columnCount(const QModelIndex& parent) const
0088 {
0089     Q_UNUSED(parent);
0090     return 2;
0091 }
0092 
0093 int ElfModel::rowCount(const QModelIndex& parent) const
0094 {
0095     if (!m_fileSet || (parent.isValid() && parent.column() != 0))
0096         return 0;
0097 
0098     RowCountVisitor v;
0099     ElfNodeVariant var = contentForIndex(parent);
0100     return v.visit(&var);
0101 }
0102 
0103 QModelIndex ElfModel::parent(const QModelIndex& child) const
0104 {
0105     if (!m_fileSet || !child.isValid())
0106         return QModelIndex();
0107 
0108     ParentVisitor v(this);
0109     auto parentData = v.visit(variantForIndex(child));
0110     if (!parentData.first)
0111         return QModelIndex();
0112     Q_ASSERT(m_internalPointerMap.contains(parentData.first->payload));
0113     return createIndex(parentData.second, 0, parentData.first);
0114 }
0115 
0116 QModelIndex ElfModel::index(int row, int column, const QModelIndex& parent) const
0117 {
0118     if (!m_fileSet || !hasIndex(row, column, parent))
0119         return QModelIndex();
0120 
0121     if (!parent.isValid())
0122         return createIndex(row, column, m_internalPointerMap.value(m_fileSet));
0123 
0124     IndexVisitor v;
0125     auto childValue = v.visit(variantForIndex(parent), parent.row());
0126     ElfNodeVariant *var = makeVariant(childValue.first, childValue.second);
0127     return createIndex(row, column, var);
0128 }
0129 
0130 QVariant ElfModel::headerData(int section, Qt::Orientation orientation, int role) const
0131 {
0132     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
0133         switch (section) {
0134             case 0: return tr("Entry");
0135             case 1: return tr("Size");
0136         }
0137     }
0138     return QAbstractItemModel::headerData(section, orientation, role);
0139 }
0140 
0141 QModelIndex ElfModel::indexForNode(ElfSection* section) const
0142 {
0143     return indexForNode(section, ElfNodeVariant::Section);
0144 }
0145 
0146 QModelIndex ElfModel::indexForNode(ElfSymbolTableEntry* symbol) const
0147 {
0148     return indexForNode(symbol, ElfNodeVariant::SymbolTableEntry);
0149 }
0150 
0151 QModelIndex ElfModel::indexForNode(ElfGotEntry* entry) const
0152 {
0153     return indexForNode(entry, ElfNodeVariant::GotEntry);
0154 }
0155 
0156 QModelIndex ElfModel::indexForNode(ElfPltEntry* entry) const
0157 {
0158     return indexForNode(entry, ElfNodeVariant::PltEntry);
0159 }
0160 
0161 QModelIndex ElfModel::indexForNode(DwarfDie* die) const
0162 {
0163     return indexForNode(die, ElfNodeVariant::DwarfDie);
0164 }
0165 
0166 QModelIndex ElfModel::indexForNode(void* payload, ElfNodeVariant::Type type) const
0167 {
0168     if (!m_fileSet || !payload)
0169         return {};
0170 
0171     ElfNodeVariant var;
0172     var.payload = payload;
0173     var.type = type;
0174     ParentVisitor parentV(this);
0175     const auto parentData = parentV.visit(&var);
0176     Q_ASSERT(m_internalPointerMap.contains(parentData.first->payload));
0177     return createIndex(parentData.second, 0, parentData.first);
0178 }
0179 
0180 ElfNodeVariant* ElfModel::variantForIndex(const QModelIndex& index) const
0181 {
0182     return static_cast<ElfNodeVariant*>(index.internalPointer());
0183 }
0184 
0185 ElfNodeVariant ElfModel::contentForIndex(const QModelIndex& index) const
0186 {
0187     if (!index.isValid())
0188         return *m_internalPointerMap.value(m_fileSet);
0189 
0190     ElfNodeVariant *parentVar = variantForIndex(index);
0191 
0192     IndexVisitor v;
0193     auto childData = v.visit(parentVar, index.row());
0194     ElfNodeVariant var;
0195     var.payload = childData.first;
0196     var.type = childData.second;
0197     return var;
0198 }
0199 
0200 ElfNodeVariant* ElfModel::makeVariant(void* payload, ElfNodeVariant::Type type) const
0201 {
0202     auto it = m_internalPointerMap.constFind(payload);
0203     if (it != m_internalPointerMap.cend())
0204         return it.value();
0205     auto var = new ElfNodeVariant;
0206     var->payload = payload;
0207     var->type = type;
0208     m_internalPointerMap.insert(payload, var);
0209     return var;
0210 }
0211 
0212 QUrl ElfModel::urlForIndex(const QModelIndex& index) const
0213 {
0214     QUrl url;
0215     if (!index.isValid())
0216         return url;
0217 
0218     const auto parentIdx = parent(index);
0219     if (!parentIdx.isValid()) {
0220         url.setScheme(QStringLiteral("elfmodel"));
0221         url.setPath(QString::number(index.row()));
0222         return url;
0223     }
0224 
0225     url = parentIdx.data(NodeUrl).toUrl();
0226     url.setPath(url.path() + "/" + QString::number(index.row()));
0227 
0228     return url;
0229 }
0230 
0231 QModelIndex ElfModel::indexForUrl(const QUrl& url) const
0232 {
0233     Q_ASSERT(url.scheme() == QLatin1String("elfmodel"));
0234 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
0235     const auto rows = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts);
0236 #else
0237     const auto rows = url.path().split(QLatin1Char('/'), Qt::SkipEmptyParts);
0238 #endif
0239 
0240     QModelIndex idx;
0241     for (const auto &r : rows) {
0242         idx = index(r.toInt(), 0, idx);
0243     }
0244     return idx;
0245 }