File indexing completed on 2024-04-21 04:41:45

0001 /***************************************************************************
0002  *   Copyright (C) 2018 by Emmanuel Lepage Vallee                          *
0003  *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@kde.org>             *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 3 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0017  **************************************************************************/
0018 #include "contextadapterfactory.h"
0019 
0020 // Qt
0021 #include <QtCore/QAbstractItemModel>
0022 #include <QtCore/QMutex>
0023 #include <QtCore/private/qmetaobjectbuilder_p.h>
0024 #include <QQmlContext>
0025 #include <QQuickItem>
0026 #include <QQmlEngine>
0027 
0028 // KQuickItemViews
0029 #include "adapters/abstractitemadapter.h"
0030 #include "viewbase.h"
0031 #include "viewport.h"
0032 #include "private/viewport_p.h"
0033 #include "private/indexmetadata_p.h"
0034 #include "extensions/contextextension.h"
0035 #include "adapters/modeladapter.h"
0036 #include "private/statetracker/viewitem_p.h"
0037 #include "adapters/contextadapter.h"
0038 
0039 using FactoryFunctor = std::function<ContextAdapter*(QQmlContext*)>;
0040 
0041 /**
0042  * Add some metadata to be able to use QAbstractItemModel roles as QObject
0043  * properties.
0044  */
0045 struct MetaProperty
0046 {
0047     // Bitmask to hold (future) metadata regarding how the property is used
0048     // by QML.
0049     enum Flags : int {
0050         UNUSED      = 0x0 << 0, /*!< This property was never used          */
0051         READ        = 0x1 << 0, /*!< If data() was ever called             */
0052         HAS_DATA    = 0x1 << 1, /*!< When the QVariant is valid            */
0053         TRIED_WRITE = 0x1 << 2, /*!< When setData returns false            */
0054         HAS_WRITTEN = 0x1 << 3, /*!< When setData returns true             */
0055         HAS_CHANGED = 0x1 << 4, /*!< When the value was queried many times */
0056         HAS_SUBSET  = 0x1 << 5, /*!< When dataChanged has a role list      */
0057         HAS_GLOBAL  = 0x1 << 6, /*!< When dataChanged has no role          */
0058         IS_ROLE     = 0x1 << 7, /*!< When the MetaProperty map to a model role */
0059     };
0060     int flags {Flags::UNUSED};
0061 
0062     /// Q_PROPERTY internal id
0063     int propId;
0064 
0065     /// The role ID from QAbstractItemModel::roleNames
0066     int roleId {-1};
0067 
0068     /**
0069      * The name ID from QAbstractItemModel::roleNames
0070      *
0071      * (the pointer *is* on purpose reduce cache faults in this hot code path)
0072      */
0073     QByteArray* name {nullptr};
0074 
0075     uint signalId;
0076 };
0077 
0078 struct GroupMetaData
0079 {
0080     ContextExtension* ptr;
0081     uint offset;
0082 };
0083 
0084 /**
0085  * Keep the offset and id of the extension for validation and change notification.
0086  */
0087 class ContextExtensionPrivate
0088 {
0089 public:
0090     uint m_Id     {0};
0091     uint m_Offset {0};
0092     ContextAdapterFactoryPrivate *d_ptr {nullptr};
0093 };
0094 
0095 /**
0096  * This struct is the internal representation normally built by the Qt MOC
0097  * generator.
0098  *
0099  * It holds a "fake" type of QObject designed to reflect the model roles as
0100  * QObject properties. It also tracks the property being *used* by QML to
0101  * prevent too many events being pushed into the QML context.
0102  */
0103 struct DynamicMetaType final
0104 {
0105     explicit DynamicMetaType(const QHash<int, QByteArray>& roles);
0106     ~DynamicMetaType() {
0107         delete[] roles;//FIXME leak [roleCount*sizeof(MetaProperty))];
0108     }
0109 
0110     const size_t              roleCount     {   0   };
0111     size_t                    propertyCount {   0   };
0112     MetaProperty*             roles         {nullptr};
0113     QSet<MetaProperty*>       m_lUsed       {       };
0114     QMetaObject              *m_pMetaObject {nullptr};
0115     bool                      m_GroupInit   { false };
0116     QHash<int, MetaProperty*> m_hRoleIds    {       };
0117     uint8_t                  *m_pCacheMap   {nullptr};
0118 
0119     /**
0120      * Assuming the number of role is never *that* high, keep a jump map to
0121      * prevent doing dispatch vTable when checking the properties source.
0122      *
0123      * In theory it can be changed at runtime if the need arise, but for now
0124      * its static. Better harden the binaries a bit, having call maps on the
0125      * heap isn't the most secure scheme in the universe.
0126      */
0127     GroupMetaData* m_lGroupMapping {nullptr};
0128 };
0129 
0130 class DynamicContext final : public QObject
0131 {
0132 public:
0133     explicit DynamicContext(ContextAdapterFactory* mt);
0134     virtual ~DynamicContext();
0135 
0136     // Some "secret" QObject methods.
0137     virtual int qt_metacall(QMetaObject::Call call, int id, void **argv) override;
0138     virtual void* qt_metacast(const char *name) override;
0139     virtual const QMetaObject *metaObject() const override;
0140 
0141     // Use a C array to prevent the array bound checks
0142     QVariant              **m_lVariants {nullptr};
0143     DynamicMetaType       * m_pMetaType {nullptr};
0144     bool                    m_Cache     { true  };
0145     QQmlContext           * m_pCtx      {nullptr};
0146     QPersistentModelIndex   m_Index     {       };
0147     QQmlContext           * m_pParentCtx{nullptr};
0148     QMetaObject::Connection m_Conn;
0149 
0150     ContextAdapterFactoryPrivate* d_ptr {nullptr};
0151     ContextAdapter* m_pBuilder;
0152 };
0153 
0154 class ContextAdapterFactoryPrivate
0155 {
0156 public:
0157     // Attributes
0158     QList<ContextExtension*>  m_lGroups   {       };
0159     mutable DynamicMetaType  *m_pMetaType {nullptr};
0160     QAbstractItemModel       *m_pModel    {nullptr};
0161 
0162     FactoryFunctor m_fFactory;
0163 
0164     // Helper
0165     void initGroup(const QHash<int, QByteArray>& rls);
0166     void finish();
0167 
0168     ContextAdapterFactory* q_ptr;
0169 };
0170 
0171 /**
0172  * Create a group of virtual Q_PROPERTY to match the model role names.
0173  */
0174 class RoleGroup final : public ContextExtension
0175 {
0176 public:
0177     explicit RoleGroup(ContextAdapterFactoryPrivate* d) : d_ptr(d) {}
0178 
0179     // The implementation isn't necessary in this case given it uses a second
0180     // layer of vTable instead of a static list. It goes against the
0181     // documentation, but that's on purpose.
0182     virtual QVariant getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const override;
0183     virtual uint size() const override;
0184     virtual QByteArray getPropertyName(uint id) const override;
0185     virtual bool setProperty(AbstractItemAdapter* item, uint id, const QVariant& value, const QModelIndex& index) const;
0186 
0187 
0188     ContextAdapterFactoryPrivate* d_ptr;
0189 };
0190 
0191 ContextAdapterFactory::ContextAdapterFactory(FactoryFunctor f) : d_ptr(new ContextAdapterFactoryPrivate())
0192 {
0193     d_ptr->q_ptr = this;
0194     d_ptr->m_fFactory = f;
0195 
0196     addContextExtension(new RoleGroup(d_ptr));
0197 }
0198 
0199 ContextAdapterFactory::~ContextAdapterFactory()
0200 {
0201     delete d_ptr;
0202 }
0203 
0204 uint ContextExtension::size() const
0205 {
0206     return propertyNames().size();
0207 }
0208 
0209 bool ContextExtension::supportCaching(uint id) const
0210 {
0211     Q_UNUSED(id)
0212     return true;
0213 }
0214 
0215 QVector<QByteArray>& ContextExtension::propertyNames() const
0216 {
0217     static QVector<QByteArray> r;
0218     return r;
0219 }
0220 
0221 QByteArray ContextExtension::getPropertyName(uint id) const
0222 {
0223     return propertyNames()[id];
0224 }
0225 
0226 bool ContextExtension::setProperty(AbstractItemAdapter* item, uint id, const QVariant& value, const QModelIndex& index) const
0227 {
0228     Q_UNUSED(item)
0229     Q_UNUSED(id)
0230     Q_UNUSED(value)
0231     Q_UNUSED(index)
0232     return false;
0233 }
0234 
0235 void ContextExtension::changeProperty(AbstractItemAdapter* item, uint id)
0236 {
0237     Q_UNUSED(item)
0238     Q_UNUSED(id)
0239     Q_ASSERT(false);
0240 /*
0241     const auto metaRole = &d_ptr->d_ptr->m_pMetaType->roles[id];
0242     Q_ASSERT(metaRole);
0243 
0244     auto mo = d_ptr->d_ptr->m_pMetaType->m_pMetaObject;
0245 
0246     index()*/
0247 }
0248 
0249 void ContextAdapter::flushCache()
0250 {
0251     for (uint i = 0; i < d_ptr->m_pMetaType->propertyCount; i++) {
0252         if (d_ptr->m_lVariants[i])
0253             delete d_ptr->m_lVariants[i];
0254 
0255         d_ptr->m_lVariants[i] = nullptr;
0256     }
0257 }
0258 
0259 void AbstractItemAdapter::dismissCacheEntry(ContextExtension* e, int id)
0260 {
0261     auto dx = s_ptr->m_pMetadata->contextAdapter()->d_ptr;
0262     if (!dx)
0263         return;
0264 
0265     Q_ASSERT(e);
0266     Q_ASSERT(e->d_ptr->d_ptr->q_ptr == viewport()->modelAdapter()->contextAdapterFactory());
0267     Q_ASSERT(e->d_ptr->d_ptr->m_lGroups[e->d_ptr->m_Id] == e);
0268     Q_ASSERT(id >= 0 && id < (int) e->size());
0269 
0270     dx->m_lVariants[e->d_ptr->m_Offset + id] = nullptr;
0271 }
0272 
0273 QVariant RoleGroup::getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const
0274 {
0275     Q_UNUSED(item)
0276     const auto metaRole = &d_ptr->m_pMetaType->roles[id];
0277 
0278     // Keep track of the accessed roles
0279     if (!(metaRole->flags & MetaProperty::Flags::READ))
0280         d_ptr->m_pMetaType->m_lUsed << metaRole;
0281 
0282     metaRole->flags |= MetaProperty::Flags::READ;
0283 
0284     return index.data(metaRole->roleId);
0285 }
0286 
0287 uint RoleGroup::size() const
0288 {
0289     return d_ptr->m_pMetaType->roleCount;
0290 }
0291 
0292 QByteArray RoleGroup::getPropertyName(uint id) const
0293 {
0294     return *d_ptr->m_pMetaType->roles[id].name;
0295 }
0296 
0297 bool RoleGroup::setProperty(AbstractItemAdapter* item, uint id, const QVariant& value, const QModelIndex& index) const
0298 {
0299     // Avoid "useless" setData. With binding loops this can get very nasty.
0300     // Yes: You could to do this on purpose in the past, but it's no longer
0301     // safe.
0302     if (getProperty(item, id, index) == value)
0303         return false;
0304 
0305     const auto metaRole = &d_ptr->m_pMetaType->roles[id];
0306 
0307     // Keep track of the accessed roles
0308     if (!(metaRole->flags & MetaProperty::Flags::TRIED_WRITE))
0309         d_ptr->m_pMetaType->m_lUsed << metaRole;
0310 
0311     metaRole->flags |= MetaProperty::Flags::TRIED_WRITE;
0312 
0313     if (d_ptr->m_pModel->setData(index, value, metaRole->roleId)) {
0314         metaRole->flags |= MetaProperty::Flags::HAS_WRITTEN;
0315         return true;
0316     }
0317 
0318     return false;
0319 }
0320 
0321 const QMetaObject *DynamicContext::metaObject() const
0322 {
0323     Q_ASSERT(m_pMetaType);
0324     return m_pMetaType->m_pMetaObject;
0325 }
0326 
0327 int DynamicContext::qt_metacall(QMetaObject::Call call, int id, void **argv)
0328 {
0329     const int realId = id - m_pMetaType->m_pMetaObject->propertyOffset();
0330 
0331     //qDebug() << "META" << id << realId << call << QMetaObject::ReadProperty;
0332     if (realId < 0)
0333         return QObject::qt_metacall(call, id, argv);
0334 
0335     const auto group = &m_pMetaType->m_lGroupMapping[realId];
0336     Q_ASSERT(group->ptr);
0337 
0338     if (call == QMetaObject::ReadProperty) {
0339         if (Q_UNLIKELY(((size_t)realId) >= m_pMetaType->propertyCount)) {
0340             Q_ASSERT(false);
0341             return -1;
0342         }
0343 
0344         const QModelIndex idx = m_pBuilder->item() ? m_pBuilder->item()->index() : m_Index;
0345 
0346         const bool supportsCache = m_Cache &&
0347             (m_pMetaType->m_pCacheMap[realId/8] & (1 << (realId % 8)));
0348 
0349         // Use a special function for the role case. It's only known at runtime.
0350         QVariant *value = m_lVariants[realId] && supportsCache
0351             ? m_lVariants[realId] : new QVariant(
0352                 group->ptr->getProperty(m_pBuilder->item(), realId - group->offset, idx));
0353 
0354         if (supportsCache && !m_lVariants[realId])
0355             m_lVariants[realId] = value;
0356 
0357         QMetaType::construct(QMetaType::QVariant, argv[0], value->data());
0358 
0359 //         if (!supportsCache)
0360 //             delete value;
0361     }
0362     else if (call == QMetaObject::WriteProperty) {
0363         const QVariant  value = QVariant(QMetaType::QVariant, argv[0]).value<QVariant>();
0364         const QModelIndex idx = m_pBuilder->item() ? m_pBuilder->item()->index() : m_Index;
0365 
0366         const bool ret = group->ptr->setProperty(
0367             m_pBuilder->item(), realId - group->offset, value, idx
0368         );
0369 
0370         // Register if setting the property worked
0371         *reinterpret_cast<int*>(argv[2]) = ret ? 1 : 0;
0372 
0373         QMetaObject::activate(this, m_pMetaType->m_pMetaObject, realId, argv);
0374 
0375     }
0376     else if (call == QMetaObject::InvokeMetaMethod) {
0377         int sigId = id - m_pMetaType->m_pMetaObject->methodOffset();
0378         qDebug() << "LA LA INVOKE" << sigId << id;
0379         QMetaObject::activate(this,  m_pMetaType->m_pMetaObject, id, nullptr);
0380         return -1;
0381     }
0382 
0383     return -1;
0384 }
0385 
0386 void* DynamicContext::qt_metacast(const char *name)
0387 {
0388     if (!strcmp(name, m_pMetaType->m_pMetaObject->className()))
0389         return this;
0390 
0391     return QObject::qt_metacast(name);
0392 }
0393 
0394 DynamicMetaType::DynamicMetaType(const QHash<int, QByteArray>& rls) :
0395 roleCount(rls.size())
0396 {}
0397 
0398 /// Populate a vTable with the propertyId -> group object
0399 void ContextAdapterFactoryPrivate::initGroup(const QHash<int, QByteArray>& rls)
0400 {
0401     Q_ASSERT(!m_pMetaType->m_GroupInit);
0402 
0403     for (auto group : qAsConst(m_lGroups))
0404         m_pMetaType->propertyCount += group->size();
0405 
0406     m_pMetaType->m_lGroupMapping = (GroupMetaData*) malloc(
0407         sizeof(GroupMetaData) * m_pMetaType->propertyCount
0408     );
0409 
0410     uint offset(0), realId(0), groupId(0);
0411 
0412     for (auto group : qAsConst(m_lGroups)) {
0413         Q_ASSERT(!group->d_ptr->d_ptr);
0414         group->d_ptr->d_ptr    = this;
0415         group->d_ptr->m_Offset = offset;
0416         group->d_ptr->m_Id     = groupId++;
0417 
0418         const uint gs = group->size();
0419 
0420         for (uint i = 0; i < gs; i++)
0421             m_pMetaType->m_lGroupMapping[offset+i] = {group, offset};
0422 
0423         offset += gs;
0424     }
0425     Q_ASSERT(offset == m_pMetaType->propertyCount);
0426 
0427     // Add a bitfield to store the properties that need to skip the cache
0428     const int fieldSize = m_pMetaType->propertyCount / 8 + (m_pMetaType->propertyCount%8?1:0);
0429     m_pMetaType->m_pCacheMap = (uint8_t*) malloc(fieldSize);
0430     for (int i = 0; i < fieldSize; i++)
0431         m_pMetaType->m_pCacheMap[i] = 0;
0432 
0433     m_pMetaType->m_GroupInit = true;
0434 
0435     // Create the metaobject
0436     QMetaObjectBuilder builder;
0437     builder.setClassName("DynamicContext");
0438     builder.setSuperClass(&QObject::staticMetaObject);
0439 
0440     // Use a C array like the moc would do because this is called **A LOT**
0441     m_pMetaType->roles = new MetaProperty[m_pMetaType->propertyCount];
0442 
0443     // Setup the role metadata
0444     for (auto i = rls.constBegin(); i != rls.constEnd(); i++) {
0445         uint id = realId++;
0446         MetaProperty* r = &m_pMetaType->roles[id];
0447 
0448         r->roleId = i.key();
0449         r->name   = new QByteArray(i.value());
0450         r->flags |= MetaProperty::Flags::IS_ROLE;
0451 
0452         m_pMetaType->m_hRoleIds[i.key()] = r;
0453     }
0454 
0455     realId = 0;
0456 
0457     // Add all object virtual properties
0458     for (const auto g : qAsConst(m_lGroups)) {
0459         for (uint j = 0; j < g->size(); j++) {
0460             uint id = realId++;
0461             Q_ASSERT(id < m_pMetaType->propertyCount);
0462 
0463             MetaProperty* r = &m_pMetaType->roles[id];
0464             r->propId   = id;
0465             const auto name = g->getPropertyName(j);
0466 
0467             auto property = builder.addProperty(name, "QVariant");
0468             property.setWritable(true);
0469 
0470             auto signal = builder.addSignal(name + "Changed()");
0471             r->signalId = signal.index();
0472             property.setNotifySignal(signal);
0473 
0474             // Set the cache bit
0475             m_pMetaType->m_pCacheMap[id/8] |= (g->supportCaching(j)?1:0) << (id % 8);
0476         }
0477     }
0478 
0479     m_pMetaType->m_pMetaObject = builder.toMetaObject();
0480 }
0481 
0482 DynamicContext::DynamicContext(ContextAdapterFactory* cm) :
0483     m_pMetaType(cm->d_ptr->m_pMetaType)
0484 {
0485     Q_ASSERT(m_pMetaType);
0486     Q_ASSERT(m_pMetaType->roleCount <= m_pMetaType->propertyCount);
0487 
0488     m_lVariants = (QVariant**) malloc(sizeof(QVariant*)*m_pMetaType->propertyCount);
0489 
0490     //TODO SIMD this
0491     for (uint i = 0; i < m_pMetaType->propertyCount; i++)
0492         m_lVariants[i] = nullptr;
0493 }
0494 
0495 DynamicContext::~DynamicContext()
0496 {}
0497 
0498 //FIXME delete the metatype now that it's invalid.
0499 //     if (m_pMetaType) {
0500 //         qDeleteAll(m_hContextMapper);
0501 //         m_hContextMapper.clear();
0502 //
0503 //         delete m_pMetaType;
0504 //         m_pMetaType = nullptr;
0505 //     }
0506 
0507 bool ContextAdapter::updateRoles(const QVector<int> &modified) const
0508 {
0509     if (!d_ptr->d_ptr->m_pMetaType)
0510         return false;
0511 
0512     bool ret = false;
0513 
0514     if (!modified.isEmpty()) {
0515         for (auto r : qAsConst(modified)) {
0516             if (auto mr = d_ptr->d_ptr->m_pMetaType->m_hRoleIds.value(r)) {
0517                 // This works because the role offset is always 0
0518                 if (d_ptr->m_lVariants[mr->propId]) {
0519                     delete d_ptr->m_lVariants[mr->propId];
0520                     d_ptr->m_lVariants[mr->propId] = nullptr;
0521                     QMetaMethod m = d_ptr->metaObject()->method(mr->signalId);
0522                     m.invoke(d_ptr);
0523                     ret |= ret;
0524                 }
0525             }
0526         }
0527     }
0528     else {
0529         // Only update the roles known to have an impact
0530         for (auto mr : qAsConst(d_ptr->d_ptr->m_pMetaType->m_lUsed)) {
0531             // Use `READ` instead of checking the cache because it could have
0532             // been dismissed for many reasons.
0533             if ((!d_ptr->m_Cache) || mr->flags & MetaProperty::Flags::READ) {
0534                 if (auto v = d_ptr->m_lVariants[mr->propId])
0535                     delete v;
0536 
0537                 d_ptr->m_lVariants[mr->propId] = nullptr;
0538 
0539                 //FIXME this should work, but it doesn't
0540                 auto mo = d_ptr->d_ptr->m_pMetaType->m_pMetaObject;
0541                 const int methodId = mo->methodOffset() + mr->signalId;
0542                 QMetaMethod m = mo->method(methodId);
0543                 //m.invoke(d_ptr);
0544                 Q_ASSERT(m.name() == (*mr->name)+"Changed");
0545 
0546                 //FIXME this should also work, but also doesn't
0547                 //QMetaObject::activate(d_ptr, mo, mr->signalId, nullptr);
0548 
0549                 //FIXME Use this for now, but it prevent setData from being implemented
0550                 d_ptr->setProperty(*mr->name, d_ptr->property(*mr->name));
0551 
0552                 QMetaObject::activate(d_ptr, mo, mr->signalId, nullptr);
0553 
0554                 ret = true;
0555             }
0556         }
0557     }
0558 
0559     return ret;
0560 }
0561 
0562 QAbstractItemModel *ContextAdapterFactory::model() const
0563 {
0564     return d_ptr->m_pModel;
0565 }
0566 
0567 void ContextAdapterFactory::setModel(QAbstractItemModel *m)
0568 {
0569     d_ptr->m_pModel = m;
0570 }
0571 
0572 void ContextAdapterFactoryPrivate::finish()
0573 {
0574     Q_ASSERT(m_pModel);
0575 
0576     if (m_pMetaType)
0577         return;
0578 
0579     const auto roles = m_pModel->roleNames();
0580 
0581     m_pMetaType = new DynamicMetaType(roles);
0582     initGroup(roles);
0583 }
0584 
0585 void ContextAdapterFactory::addContextExtension(ContextExtension* pg)
0586 {
0587     Q_ASSERT(!d_ptr->m_pMetaType);
0588 
0589     if (d_ptr->m_pMetaType) {
0590         qWarning() << "It is not possible to add property group after creating a builder";
0591         return;
0592     }
0593 
0594     d_ptr->m_lGroups << pg;
0595 }
0596 
0597 QSet<QByteArray> ContextAdapterFactory::usedRoles() const
0598 {
0599     if (!d_ptr->m_pMetaType)
0600         return {};
0601 
0602     QSet<QByteArray> ret;
0603 
0604     for (const auto mr : qAsConst(d_ptr->m_pMetaType->m_lUsed)) {
0605         if (mr->roleId != -1)
0606             ret << *mr->name;
0607     }
0608 
0609     return ret;
0610 }
0611 
0612 ContextAdapter*
0613 ContextAdapterFactory::createAdapter(FactoryFunctor f, QQmlContext *parentContext) const
0614 {
0615     ContextAdapter* ret = f(parentContext);
0616 
0617     Q_ASSERT(!ret->d_ptr);
0618 
0619     d_ptr->finish();
0620     ret->d_ptr = new DynamicContext(const_cast<ContextAdapterFactory*>(this));
0621     ret->d_ptr->d_ptr = d_ptr;
0622     ret->d_ptr->m_pBuilder   = ret;
0623     ret->d_ptr->m_pParentCtx = parentContext;
0624 
0625     // Use the core application because the parentContext have an unpredictable
0626     // lifecycle and the object may be reparented anyway. If it has no parent,
0627     // QtQuick can take ownership and will also destroy it.
0628     ret->d_ptr->setParent(QCoreApplication::instance());
0629 
0630     //HACK QtQuick ignores
0631     ret->d_ptr->m_Conn = QObject::connect(ret->d_ptr, &QObject::destroyed, ret->d_ptr, [ret, this, parentContext]() {
0632         qWarning() << "Rebuilding the cache because QtQuick bugs trashed it";
0633         ret->d_ptr = new DynamicContext(const_cast<ContextAdapterFactory*>(this));
0634         ret->d_ptr->d_ptr = d_ptr;
0635         ret->d_ptr->setParent(QCoreApplication::instance());
0636         ret->d_ptr->m_pBuilder   = ret;
0637         ret->d_ptr->m_pParentCtx = parentContext;
0638     });
0639 
0640     return ret;
0641 }
0642 
0643 ContextAdapter* ContextAdapterFactory::createAdapter(QQmlContext *parentContext) const
0644 {
0645     return createAdapter(d_ptr->m_fFactory, parentContext);
0646 }
0647 
0648 ContextAdapter::ContextAdapter(QQmlContext *parentContext)
0649 {
0650     Q_UNUSED(parentContext)
0651 }
0652 
0653 ContextAdapter::~ContextAdapter()
0654 {
0655     if (d_ptr->m_pCtx)
0656         d_ptr->m_pCtx->setContextObject(nullptr);
0657 
0658     QObject::disconnect(d_ptr->m_Conn);
0659 
0660     d_ptr->m_pBuilder = nullptr;
0661 
0662     delete d_ptr;
0663 }
0664 
0665 bool ContextAdapter::isCacheEnabled() const
0666 {
0667     return d_ptr->m_Cache;
0668 }
0669 
0670 void ContextAdapter::setCacheEnabled(bool v)
0671 {
0672     d_ptr->m_Cache = v;
0673 }
0674 
0675 QModelIndex ContextAdapter::index() const
0676 {
0677     return d_ptr->m_Index;
0678 }
0679 
0680 void ContextAdapter::setModelIndex(const QModelIndex& index)
0681 {
0682     const bool hasIndex = d_ptr->m_Index.isValid();
0683 
0684     if (d_ptr->m_Cache)
0685         flushCache();
0686 
0687     d_ptr->m_Index = index;
0688 
0689     if (hasIndex)
0690         updateRoles({});
0691 }
0692 
0693 QQmlContext* ContextAdapter::context() const
0694 {
0695     Q_ASSERT(d_ptr);
0696 
0697     if (!d_ptr->m_pCtx) {
0698         d_ptr->m_pCtx = new QQmlContext(d_ptr->m_pParentCtx, d_ptr->parent());
0699         d_ptr->m_pCtx->setContextObject(d_ptr);
0700         d_ptr->m_pCtx->engine()->setObjectOwnership(
0701             d_ptr, QQmlEngine::CppOwnership
0702         );
0703         d_ptr->m_pCtx->engine()->setObjectOwnership(
0704             d_ptr->m_pCtx, QQmlEngine::CppOwnership
0705         );
0706 
0707         // No amount of setObjectOwnership will prevent Qt 5.12 from doing
0708         // what's it's told. Mitigate this.
0709         QObject::connect(d_ptr->m_pCtx, &QObject::destroyed, d_ptr, [this]() {
0710             d_ptr->m_pCtx = nullptr;
0711         });
0712     }
0713 
0714     return d_ptr->m_pCtx;
0715 }
0716 
0717 QObject *ContextAdapter::contextObject() const
0718 {
0719     return d_ptr;
0720 }
0721 
0722 bool ContextAdapter::isActive() const
0723 {
0724     return d_ptr->m_pCtx;
0725 }
0726 
0727 AbstractItemAdapter* ContextAdapter::item() const
0728 {
0729     return nullptr;
0730 }
0731 
0732 ContextExtension::ContextExtension() : d_ptr(new ContextExtensionPrivate())
0733 {}
0734