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:
parent
7b8e6714e1
commit
b5b5782088
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 ¤tType = types.at(ii);
|
const QQmlType ¤tType = 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 ¶m, QString *customTypeName = nullptr);
|
QMetaType metaTypeForParameter(const QV4::CompiledData::ParameterType ¶m, 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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue