QML: Add more safety to QQmlPropertyCache usages

We can almost always use QQmlPropertyCache::ConstPtr. The property cache
creator needs mutable property caches for a while, but it can seal them
when done. The designer integration does ugly stuff, but that should be
limited to a specific environment. And the QQmlOpenMetaObject is rather
wrong (again). This needs to be addresses in a later change.

Task-number: QTBUG-73271
Change-Id: I1c31fd5936c745029d25b909c30b8d14a30f25d3
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2022-03-10 12:49:35 +01:00
parent 7b8e6714e1
commit b5b5782088
14 changed files with 175 additions and 84 deletions

View File

@ -149,7 +149,7 @@ public:
// QML specific fields // QML specific fields
QQmlPropertyCacheVector propertyCaches; QQmlPropertyCacheVector propertyCaches;
QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } QQmlPropertyCache::ConstPtr rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QQmlRefPointer<QQmlTypeNameCache> typeNameCache;

View File

@ -83,7 +83,7 @@ QQmlPropertyCache::ConstPtr ResolvedTypeReference::propertyCache() const
/*! /*!
Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
*/ */
QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache() QQmlPropertyCache::ConstPtr ResolvedTypeReference::createPropertyCache()
{ {
if (m_typePropertyCache) { if (m_typePropertyCache) {
return m_typePropertyCache; return m_typePropertyCache;

View File

@ -74,7 +74,7 @@ public:
} }
QQmlPropertyCache::ConstPtr propertyCache() const; QQmlPropertyCache::ConstPtr propertyCache() const;
QQmlRefPointer<QQmlPropertyCache> createPropertyCache(); QQmlPropertyCache::ConstPtr createPropertyCache();
bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums); bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums);
void doDynamicTypeCheck(); void doDynamicTypeCheck();
@ -115,7 +115,7 @@ public:
} }
QQmlPropertyCache::ConstPtr typePropertyCache() const { return m_typePropertyCache; } QQmlPropertyCache::ConstPtr typePropertyCache() const { return m_typePropertyCache; }
void setTypePropertyCache(QQmlRefPointer<QQmlPropertyCache> cache) void setTypePropertyCache(QQmlPropertyCache::ConstPtr cache)
{ {
m_typePropertyCache = std::move(cache); m_typePropertyCache = std::move(cache);
} }
@ -128,7 +128,7 @@ public:
private: private:
QQmlType m_type; QQmlType m_type;
QQmlRefPointer<QQmlPropertyCache> m_typePropertyCache; QQmlPropertyCache::ConstPtr m_typePropertyCache;
QV4::ExecutableCompilationUnit *m_compilationUnit = nullptr; QV4::ExecutableCompilationUnit *m_compilationUnit = nullptr;
QTypeRevision m_version = QTypeRevision::zero(); QTypeRevision m_version = QTypeRevision::zero();

View File

@ -1294,21 +1294,28 @@ Returns a QQmlPropertyCache for \a obj if one is available.
If \a obj is null, being deleted or contains a dynamic meta object, If \a obj is null, being deleted or contains a dynamic meta object,
nullptr is returned. nullptr is returned.
*/ */
QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::propertyCache(QObject *obj, QTypeRevision version) QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(QObject *obj, QTypeRevision version)
{ {
if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted) if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted)
return QQmlRefPointer<QQmlPropertyCache>(); return QQmlPropertyCache::ConstPtr();
return QQmlMetaType::propertyCache(obj->metaObject(), version); return QQmlMetaType::propertyCache(obj->metaObject(), version);
} }
QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::propertyCache( QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(
const QMetaObject *metaObject, QTypeRevision version) const QMetaObject *metaObject, QTypeRevision version)
{ {
QQmlMetaTypeDataPtr data; // not const: the cache is created on demand QQmlMetaTypeDataPtr data; // not const: the cache is created on demand
return data->propertyCache(metaObject, version); return data->propertyCache(metaObject, version);
} }
QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::propertyCache( QQmlPropertyCache::Ptr QQmlMetaType::createPropertyCache(
const QMetaObject *metaObject)
{
QQmlMetaTypeDataPtr data; // not const: the cache is created
return data->createPropertyCache(metaObject);
}
QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(
const QQmlType &type, QTypeRevision version) const QQmlType &type, QTypeRevision version)
{ {
QQmlMetaTypeDataPtr data; // not const: the cache is created on demand QQmlMetaTypeDataPtr data; // not const: the cache is created on demand
@ -1352,7 +1359,7 @@ QQmlMetaObject QQmlMetaType::metaObjectForType(QMetaType metaType)
* *
* Look up by type's metaObject and version. * Look up by type's metaObject and version.
*/ */
QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::propertyCacheForType(QMetaType metaType) QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCacheForType(QMetaType metaType)
{ {
QQmlMetaTypeDataPtr data; QQmlMetaTypeDataPtr data;
if (auto composite = data->findPropertyCacheInCompositeTypes(metaType)) if (auto composite = data->findPropertyCacheInCompositeTypes(metaType))
@ -1361,7 +1368,7 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::propertyCacheForType(QMetaType m
const QQmlTypePrivate *type = data->idToType.value(metaType.id()); const QQmlTypePrivate *type = data->idToType.value(metaType.id());
return (type && type->typeId == metaType) return (type && type->typeId == metaType)
? data->propertyCache(QQmlType(type).metaObject(), type->version) ? data->propertyCache(QQmlType(type).metaObject(), type->version)
: QQmlRefPointer<QQmlPropertyCache>(); : QQmlPropertyCache::ConstPtr();
} }
/*! /*!
@ -1371,7 +1378,7 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::propertyCacheForType(QMetaType m
* TODO: Is this correct? Passing a plain QTypeRevision() rather than QTypeRevision::zero() or * TODO: Is this correct? Passing a plain QTypeRevision() rather than QTypeRevision::zero() or
* the actual type's version seems strange. The behavior has been in place for a while. * the actual type's version seems strange. The behavior has been in place for a while.
*/ */
QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::rawPropertyCacheForType(QMetaType metaType) QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(QMetaType metaType)
{ {
QQmlMetaTypeDataPtr data; QQmlMetaTypeDataPtr data;
if (auto composite = QQmlMetaType::findPropertyCacheInCompositeTypes(metaType)) if (auto composite = QQmlMetaType::findPropertyCacheInCompositeTypes(metaType))
@ -1380,7 +1387,7 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::rawPropertyCacheForType(QMetaTyp
const QQmlTypePrivate *type = data->idToType.value(metaType.id()); const QQmlTypePrivate *type = data->idToType.value(metaType.id());
return (type && type->typeId == metaType) return (type && type->typeId == metaType)
? data->propertyCache(type->baseMetaObject, QTypeRevision()) ? data->propertyCache(type->baseMetaObject, QTypeRevision())
: QQmlRefPointer<QQmlPropertyCache>(); : QQmlPropertyCache::ConstPtr();
} }
/*! /*!
@ -1389,7 +1396,7 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::rawPropertyCacheForType(QMetaTyp
* Look up by QQmlType and version. We only fall back to lookup by metaobject if the type * Look up by QQmlType and version. We only fall back to lookup by metaobject if the type
* has no revisiononed attributes here. Unspecified versions are interpreted as "any". * has no revisiononed attributes here. Unspecified versions are interpreted as "any".
*/ */
QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::rawPropertyCacheForType( QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(
QMetaType metaType, QTypeRevision version) QMetaType metaType, QTypeRevision version)
{ {
QQmlMetaTypeDataPtr data; QQmlMetaTypeDataPtr data;
@ -1398,7 +1405,7 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::rawPropertyCacheForType(
const QQmlTypePrivate *typePriv = data->idToType.value(metaType.id()); const QQmlTypePrivate *typePriv = data->idToType.value(metaType.id());
if (!typePriv || typePriv->typeId != metaType) if (!typePriv || typePriv->typeId != metaType)
return QQmlRefPointer<QQmlPropertyCache>(); return QQmlPropertyCache::ConstPtr();
const QQmlType type(typePriv); const QQmlType type(typePriv);
if (type.containsRevisionedAttributes()) if (type.containsRevisionedAttributes())
@ -1407,7 +1414,7 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::rawPropertyCacheForType(
if (const QMetaObject *metaObject = type.metaObject()) if (const QMetaObject *metaObject = type.metaObject())
return data->propertyCache(metaObject, version); return data->propertyCache(metaObject, version);
return QQmlRefPointer<QQmlPropertyCache>(); return QQmlPropertyCache::ConstPtr();
} }
void QQmlMetaType::unregisterType(int typeIndex) void QQmlMetaType::unregisterType(int typeIndex)
@ -1761,7 +1768,7 @@ QQmlValueType *QQmlMetaType::valueType(QMetaType type)
return *data->metaTypeToValueType.insert(type.id(), nullptr); return *data->metaTypeToValueType.insert(type.id(), nullptr);
} }
QQmlRefPointer<QQmlPropertyCache> QQmlMetaType::findPropertyCacheInCompositeTypes(QMetaType t) QQmlPropertyCache::ConstPtr QQmlMetaType::findPropertyCacheInCompositeTypes(QMetaType t)
{ {
const QQmlMetaTypeDataPtr data; const QQmlMetaTypeDataPtr data;
return data->findPropertyCacheInCompositeTypes(t); return data->findPropertyCacheInCompositeTypes(t);

View File

@ -122,6 +122,8 @@ public:
class Q_QML_PRIVATE_EXPORT QQmlMetaType class Q_QML_PRIVATE_EXPORT QQmlMetaType
{ {
friend struct CompositeMetaTypeIds; friend struct CompositeMetaTypeIds;
friend class QQmlDesignerMetaObject;
static CompositeMetaTypeIds registerInternalCompositeType(const QByteArray &className); static CompositeMetaTypeIds registerInternalCompositeType(const QByteArray &className);
static void unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds); static void unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds);
@ -176,19 +178,34 @@ public:
static QQmlType qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports = false); static QQmlType qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports = false);
static QQmlRefPointer<QQmlPropertyCache> propertyCache( static QQmlPropertyCache::ConstPtr propertyCache(
QObject *object, QTypeRevision version = QTypeRevision()); QObject *object, QTypeRevision version = QTypeRevision());
static QQmlRefPointer<QQmlPropertyCache> propertyCache( static QQmlPropertyCache::ConstPtr propertyCache(
const QMetaObject *metaObject, QTypeRevision version = QTypeRevision()); const QMetaObject *metaObject, QTypeRevision version = QTypeRevision());
static QQmlRefPointer<QQmlPropertyCache> propertyCache( static QQmlPropertyCache::ConstPtr propertyCache(
const QQmlType &type, QTypeRevision version); const QQmlType &type, QTypeRevision version);
// This only works for a new metaObject that doesn't have an associated property cache, yet.
// Do not call it more than once for the same metaObject!
//
// ------------------------------------------------------------------------------------
// --> The caller has to uphold the immutability guarantees for the returned property cache <--
// ------------------------------------------------------------------------------------
//
// This means: You cannot expose the metaObject, any objects created from it, or the property
// cache to _anything_ that allows concurrent access before you are done changing the property
// cache!
//
// In general, don't use this method. It's only for the designer integration. The designer
// assumes that there is only one QML engine running in a single thread.
static QQmlPropertyCache::Ptr createPropertyCache(const QMetaObject *metaObject);
// These methods may be called from the loader thread // These methods may be called from the loader thread
static QQmlMetaObject rawMetaObjectForType(QMetaType metaType); static QQmlMetaObject rawMetaObjectForType(QMetaType metaType);
static QQmlMetaObject metaObjectForType(QMetaType metaType); static QQmlMetaObject metaObjectForType(QMetaType metaType);
static QQmlRefPointer<QQmlPropertyCache> propertyCacheForType(QMetaType metaType); static QQmlPropertyCache::ConstPtr propertyCacheForType(QMetaType metaType);
static QQmlRefPointer<QQmlPropertyCache> rawPropertyCacheForType(QMetaType metaType); static QQmlPropertyCache::ConstPtr rawPropertyCacheForType(QMetaType metaType);
static QQmlRefPointer<QQmlPropertyCache> rawPropertyCacheForType( static QQmlPropertyCache::ConstPtr rawPropertyCacheForType(
QMetaType metaType, QTypeRevision version); QMetaType metaType, QTypeRevision version);
static void freeUnusedTypesAndCaches(); static void freeUnusedTypesAndCaches();
@ -268,7 +285,7 @@ public:
static QQmlValueType *valueType(QMetaType metaType); static QQmlValueType *valueType(QMetaType metaType);
static const QMetaObject *metaObjectForValueType(QMetaType type); static const QMetaObject *metaObjectForValueType(QMetaType type);
static QQmlRefPointer<QQmlPropertyCache> findPropertyCacheInCompositeTypes(QMetaType t); static QQmlPropertyCache::ConstPtr findPropertyCacheInCompositeTypes(QMetaType t);
static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); static void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
static QV4::ExecutableCompilationUnit *obtainExecutableCompilationUnit(QMetaType type); static QV4::ExecutableCompilationUnit *obtainExecutableCompilationUnit(QMetaType type);

View File

@ -119,16 +119,16 @@ bool QQmlMetaTypeData::registerModuleTypes(const QString &uri)
return false; return false;
} }
QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::propertyCacheForVersion( QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCacheForVersion(
int index, QTypeRevision version) const int index, QTypeRevision version) const
{ {
return (index < typePropertyCaches.length()) return (index < typePropertyCaches.length())
? typePropertyCaches.at(index).value(version) ? typePropertyCaches.at(index).value(version)
: QQmlRefPointer<QQmlPropertyCache>(); : QQmlPropertyCache::ConstPtr();
} }
void QQmlMetaTypeData::setPropertyCacheForVersion(int index, QTypeRevision version, void QQmlMetaTypeData::setPropertyCacheForVersion(int index, QTypeRevision version,
const QQmlRefPointer<QQmlPropertyCache> &cache) const QQmlPropertyCache::ConstPtr &cache)
{ {
if (index >= typePropertyCaches.length()) if (index >= typePropertyCaches.length())
typePropertyCaches.resize(index + 1); typePropertyCaches.resize(index + 1);
@ -141,13 +141,13 @@ void QQmlMetaTypeData::clearPropertyCachesForVersion(int index)
typePropertyCaches[index].clear(); typePropertyCaches[index].clear();
} }
QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::propertyCache( QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache(
const QMetaObject *metaObject, QTypeRevision version) const QMetaObject *metaObject, QTypeRevision version)
{ {
if (QQmlRefPointer<QQmlPropertyCache> rv = propertyCaches.value(metaObject)) if (QQmlPropertyCache::ConstPtr rv = propertyCaches.value(metaObject))
return rv; return rv;
QQmlRefPointer<QQmlPropertyCache> rv; QQmlPropertyCache::ConstPtr rv;
if (const QMetaObject *superMeta = metaObject->superClass()) if (const QMetaObject *superMeta = metaObject->superClass())
rv = propertyCache(superMeta, version)->copyAndAppend(metaObject, version); rv = propertyCache(superMeta, version)->copyAndAppend(metaObject, version);
else else
@ -160,7 +160,22 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::propertyCache(
return rv; return rv;
} }
QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::propertyCache( QQmlPropertyCache::Ptr QQmlMetaTypeData::createPropertyCache(const QMetaObject *metaObject)
{
QQmlPropertyCache::Ptr rv;
if (const QMetaObject *superMeta = metaObject->superClass())
rv = propertyCache(superMeta, QTypeRevision())->copyAndAppend(metaObject, QTypeRevision());
else
rv.adopt(new QQmlPropertyCache(metaObject));
const auto *mop = reinterpret_cast<const QMetaObjectPrivate *>(metaObject->d.data);
if (!(mop->flags & DynamicMetaObject))
propertyCaches.insert(metaObject, rv);
return rv;
}
QQmlPropertyCache::ConstPtr QQmlMetaTypeData::propertyCache(
const QQmlType &type, QTypeRevision version) const QQmlType &type, QTypeRevision version)
{ {
Q_ASSERT(type.isValid()); Q_ASSERT(type.isValid());
@ -200,9 +215,8 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::propertyCache(
return pc; return pc;
} }
QQmlRefPointer<QQmlPropertyCache> raw = propertyCache(type.metaObject(), combinedVersion); QQmlPropertyCache::ConstPtr raw = propertyCache(type.metaObject(), combinedVersion);
QQmlPropertyCache::Ptr copied;
bool hasCopied = false;
for (int ii = 0; ii < types.count(); ++ii) { for (int ii = 0; ii < types.count(); ++ii) {
const QQmlType &currentType = types.at(ii); const QQmlType &currentType = types.at(ii);
@ -213,14 +227,11 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::propertyCache(
int moIndex = types.count() - 1 - ii; int moIndex = types.count() - 1 - ii;
if (raw->allowedRevision(moIndex) != rev) { if (raw->allowedRevision(moIndex) != rev) {
if (!hasCopied) { if (copied.isNull()) {
// TODO: The copy should be mutable, and the original should be const copied = raw->copy();
// Considering this, the setAllowedRevision() below does not violate raw = copied;
// the immutability of already published property caches.
raw = raw->copy();
hasCopied = true;
} }
raw->setAllowedRevision(moIndex, rev); copied->setAllowedRevision(moIndex, rev);
} }
} }
@ -275,7 +286,7 @@ QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::propertyCache(
return raw; return raw;
} }
static QQmlRefPointer<QQmlPropertyCache> propertyCacheForPotentialInlineComponentType( static QQmlPropertyCache::ConstPtr propertyCacheForPotentialInlineComponentType(
QMetaType t, QMetaType t,
const QHash<const QtPrivate::QMetaTypeInterface *, const QHash<const QtPrivate::QMetaTypeInterface *,
QV4::ExecutableCompilationUnit *>::const_iterator &iter) { QV4::ExecutableCompilationUnit *>::const_iterator &iter) {
@ -288,12 +299,11 @@ static QQmlRefPointer<QQmlPropertyCache> propertyCacheForPotentialInlineComponen
return (*iter)->rootPropertyCache(); return (*iter)->rootPropertyCache();
} }
QQmlRefPointer<QQmlPropertyCache> QQmlMetaTypeData::findPropertyCacheInCompositeTypes( QQmlPropertyCache::ConstPtr QQmlMetaTypeData::findPropertyCacheInCompositeTypes(QMetaType t) const
QMetaType t) const
{ {
auto iter = compositeTypes.constFind(t.iface()); auto iter = compositeTypes.constFind(t.iface());
return (iter == compositeTypes.constEnd()) return (iter == compositeTypes.constEnd())
? QQmlRefPointer<QQmlPropertyCache>() ? QQmlPropertyCache::ConstPtr()
: propertyCacheForPotentialInlineComponentType(t, iter); : propertyCacheForPotentialInlineComponentType(t, iter);
} }

View File

@ -83,7 +83,7 @@ struct QQmlMetaTypeData
// a module via QQmlPrivate::RegisterCompositeType // a module via QQmlPrivate::RegisterCompositeType
typedef QMultiHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects; typedef QMultiHash<const QMetaObject *, QQmlTypePrivate *> MetaObjects;
MetaObjects metaObjectToType; MetaObjects metaObjectToType;
QVector<QHash<QTypeRevision, QQmlRefPointer<QQmlPropertyCache>>> typePropertyCaches; QVector<QHash<QTypeRevision, QQmlPropertyCache::ConstPtr>> typePropertyCaches;
QHash<int, QQmlValueType *> metaTypeToValueType; QHash<int, QQmlValueType *> metaTypeToValueType;
QHash<const QtPrivate::QMetaTypeInterface *, QV4::ExecutableCompilationUnit *> compositeTypes; QHash<const QtPrivate::QMetaTypeInterface *, QV4::ExecutableCompilationUnit *> compositeTypes;
@ -130,16 +130,18 @@ struct QQmlMetaTypeData
QList<QQmlPrivate::AutoParentFunction> parentFunctions; QList<QQmlPrivate::AutoParentFunction> parentFunctions;
QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit; QVector<QQmlPrivate::QmlUnitCacheLookupFunction> lookupCachedQmlUnit;
QHash<const QMetaObject *, QQmlRefPointer<QQmlPropertyCache>> propertyCaches; QHash<const QMetaObject *, QQmlPropertyCache::ConstPtr> propertyCaches;
QQmlRefPointer<QQmlPropertyCache> propertyCacheForVersion(int index, QTypeRevision version) const; QQmlPropertyCache::ConstPtr propertyCacheForVersion(int index, QTypeRevision version) const;
void setPropertyCacheForVersion( void setPropertyCacheForVersion(
int index, QTypeRevision version, const QQmlRefPointer<QQmlPropertyCache> &cache); int index, QTypeRevision version, const QQmlPropertyCache::ConstPtr &cache);
void clearPropertyCachesForVersion(int index); void clearPropertyCachesForVersion(int index);
QQmlRefPointer<QQmlPropertyCache> propertyCache(const QMetaObject *metaObject, QTypeRevision version); QQmlPropertyCache::ConstPtr propertyCache(const QMetaObject *metaObject, QTypeRevision version);
QQmlRefPointer<QQmlPropertyCache> propertyCache(const QQmlType &type, QTypeRevision version); QQmlPropertyCache::ConstPtr propertyCache(const QQmlType &type, QTypeRevision version);
QQmlRefPointer<QQmlPropertyCache> findPropertyCacheInCompositeTypes(QMetaType t) const; QQmlPropertyCache::ConstPtr findPropertyCacheInCompositeTypes(QMetaType t) const;
QQmlPropertyCache::Ptr createPropertyCache(const QMetaObject *metaObject);
void setTypeRegistrationFailures(QStringList *failures) void setTypeRegistrationFailures(QStringList *failures)
{ {

View File

@ -59,7 +59,13 @@ public:
QHash<QByteArray, int> names; QHash<QByteArray, int> names;
QMetaObjectBuilder mob; QMetaObjectBuilder mob;
QMetaObject *mem; QMetaObject *mem;
QQmlRefPointer<QQmlPropertyCache> cache;
// TODO: We need to make sure that this does not escape into other threads.
// In particular, all its non-const uses are probably wrong. You should
// only set the open metaobject to "cached" once it's not going to be
// modified anymore.
QQmlPropertyCache::Ptr cache;
QSet<QQmlOpenMetaObject*> referers; QSet<QQmlOpenMetaObject*> referers;
}; };

View File

@ -139,8 +139,7 @@ bool QQmlBindingInstantiationContext::resolveInstantiatingProperty()
return instantiatingProperty != nullptr; return instantiatingProperty != nullptr;
} }
QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCache::ConstPtr QQmlBindingInstantiationContext::instantiatingPropertyCache() const
QQmlBindingInstantiationContext::instantiatingPropertyCache() const
{ {
if (instantiatingProperty) { if (instantiatingProperty) {
if (instantiatingProperty->isQObject()) { if (instantiatingProperty->isQObject()) {
@ -152,7 +151,7 @@ QQmlBindingInstantiationContext::instantiatingPropertyCache() const
return QQmlMetaType::propertyCache(vtmo, instantiatingProperty->typeVersion()); return QQmlMetaType::propertyCache(vtmo, instantiatingProperty->typeVersion());
} }
} }
return QQmlRefPointer<QQmlPropertyCache>(); return QQmlPropertyCache::ConstPtr();
} }
void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches( void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(

View File

@ -81,7 +81,7 @@ struct QQmlBindingInstantiationContext {
const QQmlPropertyCache::ConstPtr &referencingObjectPropertyCache); const QQmlPropertyCache::ConstPtr &referencingObjectPropertyCache);
bool resolveInstantiatingProperty(); bool resolveInstantiatingProperty();
QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache() const; QQmlPropertyCache::ConstPtr instantiatingPropertyCache() const;
int referencingObjectIndex = -1; int referencingObjectIndex = -1;
const QV4::CompiledData::Binding *instantiatingBinding = nullptr; const QV4::CompiledData::Binding *instantiatingBinding = nullptr;
@ -131,6 +131,7 @@ public:
QQmlEnginePrivate *enginePrivate, QQmlEnginePrivate *enginePrivate,
const ObjectContainer *objectContainer, const QQmlImports *imports, const ObjectContainer *objectContainer, const QQmlImports *imports,
const QByteArray &typeClassName); const QByteArray &typeClassName);
~QQmlPropertyCacheCreator() { propertyCaches->seal(); }
/*! /*!
@ -164,7 +165,7 @@ public:
}; };
protected: protected:
QQmlError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired); QQmlError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired);
QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const; QQmlPropertyCache::ConstPtr propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const;
QQmlError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &baseTypeCache); QQmlError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlPropertyCache::ConstPtr &baseTypeCache);
QMetaType metaTypeForParameter(const QV4::CompiledData::ParameterType &param, QString *customTypeName = nullptr); QMetaType metaTypeForParameter(const QV4::CompiledData::ParameterType &param, QString *customTypeName = nullptr);
@ -324,7 +325,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
} }
} }
QQmlRefPointer<QQmlPropertyCache> baseTypeCache; QQmlPropertyCache::ConstPtr baseTypeCache;
{ {
QQmlError error; QQmlError error;
baseTypeCache = propertyCacheForObject(obj, context, &error); baseTypeCache = propertyCacheForObject(obj, context, &error);
@ -382,7 +383,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecur
} }
template <typename ObjectContainer> template <typename ObjectContainer>
inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const inline QQmlPropertyCache::ConstPtr QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlError *error) const
{ {
if (context.instantiatingProperty) { if (context.instantiatingProperty) {
return context.instantiatingPropertyCache(); return context.instantiatingPropertyCache();
@ -456,7 +457,7 @@ inline QQmlError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(
obj->signalCount() + obj->propertyCount() + obj->aliasCount(), obj->signalCount() + obj->propertyCount() + obj->aliasCount(),
obj->enumCount()); obj->enumCount());
propertyCaches->set(objectIndex, cache); propertyCaches->setOwn(objectIndex, cache);
propertyCaches->setNeedsVMEMetaObject(objectIndex); propertyCaches->setNeedsVMEMetaObject(objectIndex);
QByteArray newClassName; QByteArray newClassName;
@ -1030,7 +1031,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesTo
if (!object.aliasCount()) if (!object.aliasCount())
return QQmlError(); return QQmlError();
QQmlRefPointer<QQmlPropertyCache> propertyCache = propertyCaches->at(objectIndex); QQmlPropertyCache::Ptr propertyCache = propertyCaches->ownAt(objectIndex);
Q_ASSERT(propertyCache); Q_ASSERT(propertyCache);
int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();

View File

@ -52,6 +52,7 @@
// //
#include <private/qqmlpropertycache_p.h> #include <private/qqmlpropertycache_p.h>
#include <private/qbipointer_p.h>
#include <QtCore/qtaggedpointer.h> #include <QtCore/qtaggedpointer.h>
@ -60,11 +61,6 @@ QT_BEGIN_NAMESPACE
class QQmlPropertyCacheVector class QQmlPropertyCacheVector
{ {
public: public:
enum Tag {
NoTag,
CacheNeedsVMEMetaObject
};
QQmlPropertyCacheVector() = default; QQmlPropertyCacheVector() = default;
QQmlPropertyCacheVector(QQmlPropertyCacheVector &&) = default; QQmlPropertyCacheVector(QQmlPropertyCacheVector &&) = default;
QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&) = default; QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&) = default;
@ -80,32 +76,85 @@ public:
void clear() void clear()
{ {
for (int i = 0; i < data.count(); ++i) { for (int i = 0; i < data.count(); ++i) {
if (QQmlPropertyCache *cache = data.at(i).data()) const auto &cache = data.at(i);
cache->release(); if (cache.isT2()) {
if (QQmlPropertyCache *data = cache.asT2())
data->release();
} else if (const QQmlPropertyCache *data = cache.asT1()) {
data->release();
}
} }
data.clear(); data.clear();
} }
void append(const QQmlRefPointer<QQmlPropertyCache> &cache) { void append(const QQmlPropertyCache::ConstPtr &cache) {
cache->addref(); cache->addref();
data.append(QTaggedPointer<QQmlPropertyCache, Tag>(cache.data())); data.append(QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>(cache.data()));
Q_ASSERT(data.last().isT1());
} }
QQmlRefPointer<QQmlPropertyCache> at(int index) const { return data.at(index).data(); }
void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) { void appendOwn(const QQmlPropertyCache::Ptr &cache) {
if (QQmlPropertyCache *oldCache = data.at(index).data()) { cache->addref();
if (replacement.data() == oldCache) data.append(QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>(cache.data()));
Q_ASSERT(data.last().isT2());
}
QQmlPropertyCache::ConstPtr at(int index) const
{
const auto entry = data.at(index);
if (entry.isT2())
return entry.asT2();
return entry.asT1();
}
QQmlPropertyCache::Ptr ownAt(int index) const
{
const auto entry = data.at(index);
if (entry.isT2())
return entry.asT2();
return QQmlPropertyCache::Ptr();
}
void set(int index, const QQmlPropertyCache::ConstPtr &replacement) {
if (QQmlPropertyCache::ConstPtr oldCache = at(index)) {
// If it is our own, we keep it our own
if (replacement.data() == oldCache.data())
return; return;
oldCache->release(); oldCache->release();
} }
data[index] = replacement.data(); data[index] = replacement.data();
replacement->addref(); replacement->addref();
Q_ASSERT(data[index].isT1());
}
void setOwn(int index, const QQmlPropertyCache::Ptr &replacement) {
if (QQmlPropertyCache::ConstPtr oldCache = at(index)) {
if (replacement.data() != oldCache.data()) {
oldCache->release();
replacement->addref();
}
} else {
replacement->addref();
}
data[index] = replacement.data();
Q_ASSERT(data[index].isT2());
}
void setNeedsVMEMetaObject(int index) { data[index].setFlag(); }
bool needsVMEMetaObject(int index) const { return data.at(index).flag(); }
void seal()
{
for (auto &entry: data) {
if (entry.isT2())
entry = static_cast<const QQmlPropertyCache *>(entry.asT2());
Q_ASSERT(entry.isT1());
}
} }
void setNeedsVMEMetaObject(int index) { data[index].setTag(CacheNeedsVMEMetaObject); }
bool needsVMEMetaObject(int index) const { return data.at(index).tag() == CacheNeedsVMEMetaObject; }
private: private:
Q_DISABLE_COPY(QQmlPropertyCacheVector) Q_DISABLE_COPY(QQmlPropertyCacheVector)
QVector<QTaggedPointer<QQmlPropertyCache, Tag>> data; QVector<QBiPointer<const QQmlPropertyCache, QQmlPropertyCache>> data;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -855,7 +855,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(
qmlObjects->append(syntheticComponent); qmlObjects->append(syntheticComponent);
const int componentIndex = qmlObjects->count() - 1; const int componentIndex = qmlObjects->count() - 1;
// Keep property caches symmetric // Keep property caches symmetric
QQmlRefPointer<QQmlPropertyCache> componentCache QQmlPropertyCache::ConstPtr componentCache
= QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject); = QQmlMetaType::propertyCache(&QQmlComponent::staticMetaObject);
propertyCaches.append(componentCache); propertyCaches.append(componentCache);

View File

@ -245,11 +245,11 @@ void QQmlTypeData::createTypeAndPropertyCaches(
setError(error); setError(error);
return; return;
} }
}
QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator( QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator(
&m_compiledData->propertyCaches, m_compiledData.data()); &m_compiledData->propertyCaches, m_compiledData.data());
aliasCreator.appendAliasPropertiesToMetaObjects(engine); aliasCreator.appendAliasPropertiesToMetaObjects(engine);
}
pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_compiledData->propertyCaches); pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_compiledData->propertyCaches);
} }

View File

@ -118,7 +118,7 @@ void QQmlDesignerMetaObject::init(QObject *object)
QObjectPrivate *op = QObjectPrivate::get(object); QObjectPrivate *op = QObjectPrivate::get(object);
op->metaObject = this; op->metaObject = this;
m_cache = QQmlMetaType::propertyCache(metaObject); m_cache = QQmlMetaType::createPropertyCache(metaObject);
cache = m_cache; cache = m_cache;
nodeInstanceMetaObjectList.insert(this, true); nodeInstanceMetaObjectList.insert(this, true);