diff options
Diffstat (limited to 'src/qml/qml/qqmlopenmetaobject.cpp')
-rw-r--r-- | src/qml/qml/qqmlopenmetaobject.cpp | 234 |
1 files changed, 126 insertions, 108 deletions
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index fe0946c6de..2d0a78c6cb 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -1,48 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmlopenmetaobject_p.h" #include <private/qqmlpropertycache_p.h> #include <private/qqmldata_p.h> +#include <private/qqmlmetatype_p.h> + #include <private/qmetaobjectbuilder_p.h> -#include <qqmlengine.h> #include <qdebug.h> +#include <QtCore/qpointer.h> +#include <QtCore/qset.h> QT_BEGIN_NAMESPACE @@ -50,7 +17,7 @@ QT_BEGIN_NAMESPACE class QQmlOpenMetaObjectTypePrivate { public: - QQmlOpenMetaObjectTypePrivate() : mem(nullptr), cache(nullptr), engine(nullptr) {} + QQmlOpenMetaObjectTypePrivate() : mem(nullptr) {} void init(const QMetaObject *metaObj); @@ -59,15 +26,19 @@ public: QHash<QByteArray, int> names; QMetaObjectBuilder mob; QMetaObject *mem; - QQmlPropertyCache *cache; - QQmlEngine *engine; + + // 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; }; -QQmlOpenMetaObjectType::QQmlOpenMetaObjectType(const QMetaObject *base, QQmlEngine *engine) - : QQmlCleanup(engine), d(new QQmlOpenMetaObjectTypePrivate) +QQmlOpenMetaObjectType::QQmlOpenMetaObjectType(const QMetaObject *base) + : d(new QQmlOpenMetaObjectTypePrivate) { - d->engine = engine; d->init(base); } @@ -75,16 +46,9 @@ QQmlOpenMetaObjectType::~QQmlOpenMetaObjectType() { if (d->mem) free(d->mem); - if (d->cache) - d->cache->release(); delete d; } -void QQmlOpenMetaObjectType::clear() -{ - d->engine = nullptr; -} - int QQmlOpenMetaObjectType::propertyOffset() const { return d->propertyOffset; @@ -97,24 +61,19 @@ int QQmlOpenMetaObjectType::signalOffset() const int QQmlOpenMetaObjectType::propertyCount() const { - return d->names.count(); + return d->names.size(); } QByteArray QQmlOpenMetaObjectType::propertyName(int idx) const { - Q_ASSERT(idx >= 0 && idx < d->names.count()); + Q_ASSERT(idx >= 0 && idx < d->names.size()); return d->mob.property(idx).name(); } -QMetaObject *QQmlOpenMetaObjectType::metaObject() const -{ - return d->mem; -} - void QQmlOpenMetaObjectType::createProperties(const QVector<QByteArray> &names) { - for (int i = 0; i < names.count(); ++i) { + for (int i = 0; i < names.size(); ++i) { const QByteArray &name = names.at(i); const int id = d->mob.propertyCount(); d->mob.addSignal("__" + QByteArray::number(id) + "()"); @@ -136,13 +95,13 @@ void QQmlOpenMetaObjectType::createProperties(const QVector<QByteArray> &names) int QQmlOpenMetaObjectType::createProperty(const QByteArray &name) { - int id = d->mob.propertyCount(); - d->mob.addSignal("__" + QByteArray::number(id) + "()"); - QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id); - propertyCreated(id, build); + const int signalIdx = d->mob.addSignal( + "__" + QByteArray::number(d->mob.propertyCount()) + "()").index(); + QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", signalIdx); + propertyCreated(build.index(), build); free(d->mem); d->mem = d->mob.toMetaObject(); - d->names.insert(name, id); + d->names.insert(name, build.index()); QSet<QQmlOpenMetaObject*>::iterator it = d->referers.begin(); while (it != d->referers.end()) { QQmlOpenMetaObject *omo = *it; @@ -152,12 +111,12 @@ int QQmlOpenMetaObjectType::createProperty(const QByteArray &name) ++it; } - return d->propertyOffset + id; + return d->propertyOffset + build.index(); } void QQmlOpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder) { - if (d->referers.count()) + if (d->referers.size()) (*d->referers.begin())->propertyCreated(id, builder); } @@ -166,7 +125,7 @@ void QQmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj) if (!mem) { mob.setSuperClass(metaObj); mob.setClassName(metaObj->className()); - mob.setFlags(QMetaObjectBuilder::DynamicMetaObject); + mob.setFlags(MetaObjectFlag::DynamicMetaObject); mem = mob.toMetaObject(); @@ -180,8 +139,8 @@ void QQmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj) class QQmlOpenMetaObjectPrivate { public: - QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q, bool _autoCreate, QObject *obj) - : q(_q), object(obj), autoCreate(_autoCreate) {} + QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q, QObject *obj) + : q(_q), object(obj) {} struct Property { private: @@ -191,7 +150,7 @@ public: bool valueSet = false; QVariant value() const { - if (QMetaType::typeFlags(m_value.userType()) & QMetaType::PointerToQObject + if (m_value.metaType().flags() & QMetaType::PointerToQObject && qobjectTracker.isNull()) return QVariant::fromValue<QObject*>(nullptr); return m_value; @@ -200,19 +159,19 @@ public: void setValue(const QVariant &v) { m_value = v; valueSet = true; - if (QMetaType::typeFlags(v.userType()) & QMetaType::PointerToQObject) + if (v.metaType().flags() & QMetaType::PointerToQObject) qobjectTracker = m_value.value<QObject*>(); } }; inline void setPropertyValue(int idx, const QVariant &value) { - if (data.count() <= idx) + if (data.size() <= idx) data.resize(idx + 1); data[idx].setValue(value); } inline Property &propertyRef(int idx) { - if (data.count() <= idx) + if (data.size() <= idx) data.resize(idx + 1); Property &prop = data[idx]; if (!prop.valueSet) @@ -231,40 +190,47 @@ public: } inline bool hasProperty(int idx) const { - if (idx >= data.count()) + if (idx >= data.size()) return false; return data[idx].valueSet; } + void dropPropertyCache() { + if (QQmlData *ddata = QQmlData::get(object, /*create*/false)) + ddata->propertyCache.reset(); + } + QQmlOpenMetaObject *q; - QAbstractDynamicMetaObject *parent = nullptr; + QDynamicMetaObjectData *parent = nullptr; QVector<Property> data; QObject *object; QQmlRefPointer<QQmlOpenMetaObjectType> type; - bool autoCreate; + QVector<QByteArray> *deferredPropertyNames = nullptr; + bool autoCreate = true; bool cacheProperties = false; }; -QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bool automatic) -: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj)) +QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base) +: d(new QQmlOpenMetaObjectPrivate(this, obj)) { - d->type.adopt(new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr)); + d->type.adopt(new QQmlOpenMetaObjectType(base ? base : obj->metaObject())); d->type->d->referers.insert(this); QObjectPrivate *op = QObjectPrivate::get(obj); - d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject); + d->parent = op->metaObject; *static_cast<QMetaObject *>(this) = *d->type->d->mem; op->metaObject = this; } -QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, QQmlOpenMetaObjectType *type, bool automatic) -: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj)) +QQmlOpenMetaObject::QQmlOpenMetaObject( + QObject *obj, const QQmlRefPointer<QQmlOpenMetaObjectType> &type) +: d(new QQmlOpenMetaObjectPrivate(this, obj)) { d->type = type; d->type->d->referers.insert(this); QObjectPrivate *op = QObjectPrivate::get(obj); - d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject); + d->parent = op->metaObject; *static_cast<QMetaObject *>(this) = *d->type->d->mem; op->metaObject = this; } @@ -290,6 +256,11 @@ void QQmlOpenMetaObject::emitPropertyNotification(const QByteArray &propertyName activate(d->object, *iter + d->type->d->signalOffset, nullptr); } +void QQmlOpenMetaObject::unparent() +{ + d->parent = nullptr; +} + int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void **a) { Q_ASSERT(d->object == o); @@ -301,7 +272,7 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void * propertyRead(propId); *reinterpret_cast<QVariant *>(a[0]) = d->propertyValue(propId); } else if (c == QMetaObject::WriteProperty) { - if (propId >= d->data.count() || d->data.at(propId).value() != *reinterpret_cast<QVariant *>(a[0])) { + if (propId >= d->data.size() || d->data.at(propId).value() != *reinterpret_cast<QVariant *>(a[0])) { propertyWrite(propId); d->setPropertyValue(propId, propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]))); propertyWritten(propId); @@ -317,11 +288,21 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void * } } -QAbstractDynamicMetaObject *QQmlOpenMetaObject::parent() const +QDynamicMetaObjectData *QQmlOpenMetaObject::parent() const { return d->parent; } +bool QQmlOpenMetaObject::checkedSetValue(int index, const QVariant &value, bool force) +{ + if (!force && d->propertyValue(index) == value) + return false; + + d->setPropertyValue(index, value); + activate(d->object, index + d->type->d->signalOffset, nullptr); + return true; +} + QVariant QQmlOpenMetaObject::value(int id) const { return d->propertyValue(id); @@ -361,16 +342,43 @@ bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val, b id = *iter; } - if (id >= 0) { - if (!force && d->propertyValue(id) == val) - return false; + if (id >= 0) + return checkedSetValue(id, val, force); - d->setPropertyValue(id, val); - activate(d->object, id + d->type->d->signalOffset, nullptr); - return true; + return false; +} + +void QQmlOpenMetaObject::setValues(const QHash<QByteArray, QVariant> &values, bool force) +{ + QVector<QByteArray> missingProperties; + d->deferredPropertyNames = &missingProperties; + const auto &names = d->type->d->names; + + for (auto valueIt = values.begin(), end = values.end(); valueIt != end; ++valueIt) { + const auto nameIt = names.constFind(valueIt.key()); + if (nameIt == names.constEnd()) { + const int id = createProperty(valueIt.key(), "") - d->type->d->propertyOffset; + + // If id >= 0 some override of createProperty() created it. Then set it. + // Else it either ends up in missingProperties and we create it later + // or it cannot be created. + + if (id >= 0) + checkedSetValue(id, valueIt.value(), force); + } else { + checkedSetValue(*nameIt, valueIt.value(), force); + } } - return false; + d->deferredPropertyNames = nullptr; + if (missingProperties.isEmpty()) + return; + + d->type->createProperties(missingProperties); + d->dropPropertyCache(); + + for (const QByteArray &name : std::as_const(missingProperties)) + checkedSetValue(names[name], values[name], force); } // returns true if this value has been initialized by a call to either value() or setValue() @@ -381,37 +389,47 @@ bool QQmlOpenMetaObject::hasValue(int id) const void QQmlOpenMetaObject::setCached(bool c) { - if (c == d->cacheProperties || !d->type->d->engine) + if (c == d->cacheProperties) return; d->cacheProperties = c; QQmlData *qmldata = QQmlData::get(d->object, true); if (d->cacheProperties) { + // As the propertyCache is not saved in QQmlMetaType (due to it being dynamic) + // we cannot leak it to other places before we're done with it. Yes, it's still + // terrible. if (!d->type->d->cache) - d->type->d->cache = new QQmlPropertyCache(this); + d->type->d->cache = QQmlPropertyCache::createStandalone(this); qmldata->propertyCache = d->type->d->cache; - d->type->d->cache->addref(); } else { - if (d->type->d->cache) - d->type->d->cache->release(); - qmldata->propertyCache = nullptr; + d->type->d->cache.reset(); + qmldata->propertyCache.reset(); } } +bool QQmlOpenMetaObject::autoCreatesProperties() const +{ + return d->autoCreate; +} + +void QQmlOpenMetaObject::setAutoCreatesProperties(bool autoCreate) +{ + d->autoCreate = autoCreate; +} + int QQmlOpenMetaObject::createProperty(const char *name, const char *) { if (d->autoCreate) { - int result = d->type->createProperty(name); - - if (QQmlData *ddata = QQmlData::get(d->object, /*create*/false)) { - if (ddata->propertyCache) { - ddata->propertyCache->release(); - ddata->propertyCache = nullptr; - } + if (d->deferredPropertyNames) { + // Defer the creation of new properties. See setValues(QHash<QByteArray, QVariant>) + d->deferredPropertyNames->append(name); + return -1; } + const int result = d->type->createProperty(name); + d->dropPropertyCache(); return result; } else return -1; @@ -445,12 +463,12 @@ QVariant QQmlOpenMetaObject::initialValue(int) int QQmlOpenMetaObject::count() const { - return d->type->d->names.count(); + return d->type->d->names.size(); } QByteArray QQmlOpenMetaObject::name(int idx) const { - Q_ASSERT(idx >= 0 && idx < d->type->d->names.count()); + Q_ASSERT(idx >= 0 && idx < d->type->d->names.size()); return d->type->d->mob.property(idx).name(); } |