diff options
Diffstat (limited to 'src/qml/qml/qqmlvmemetaobject.cpp')
-rw-r--r-- | src/qml/qml/qqmlvmemetaobject.cpp | 1036 |
1 files changed, 630 insertions, 406 deletions
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 9b5490b6e5..dffddd2e0d 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -1,53 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 BasysKom GmbH. -** 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. +// Copyright (C) 2016 BasysKom GmbH. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmlvmemetaobject_p.h" - -#include "qqml.h" #include <private/qqmlrefcount_p.h> -#include "qqmlexpression.h" -#include "qqmlexpression_p.h" -#include "qqmlcontext_p.h" -#include "qqmlbinding_p.h" #include "qqmlpropertyvalueinterceptor_p.h" +#include <qqmlinfo.h> #include <private/qqmlglobal_p.h> @@ -57,63 +16,162 @@ #include <private/qv4scopedvalue_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4sequenceobject_p.h> #include <private/qqmlpropertycachecreator_p.h> #include <private/qqmlpropertycachemethodarguments_p.h> +#include <private/qqmlvaluetypewrapper_p.h> + +#include <QtCore/qsequentialiterable.h> + +#include <climits> // for CHAR_BIT QT_BEGIN_NAMESPACE -static void list_append(QQmlListProperty<QObject> *prop, QObject *o) +QQmlVMEResolvedList::QQmlVMEResolvedList(QQmlListProperty<QObject> *prop) +{ + // see QQmlVMEMetaObject::metaCall for how this was constructed + auto encodedIndex = quintptr(prop->data); + constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT; + quintptr inheritanceDepth = encodedIndex >> (usableBits / 2); + m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1); + + // walk up to the correct meta object if necessary + auto mo = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(prop->object)->metaObject); + while (inheritanceDepth--) + mo = mo->parentVMEMetaObject(); + m_metaObject = mo; + Q_ASSERT(m_metaObject); + Q_ASSERT(::strstr(m_metaObject->toDynamicMetaObject(prop->object) + ->property(m_metaObject->propOffset() + m_id) + .typeName(), + "QQmlListProperty")); + Q_ASSERT(m_metaObject->object == prop->object); + + // readPropertyAsList() with checks transformed into Q_ASSERT + // and without allocation. + if (m_metaObject->propertyAndMethodStorage.isUndefined() + && m_metaObject->propertyAndMethodStorage.valueRef()) { + return; + } + + if (auto *md = static_cast<QV4::MemberData *>( + m_metaObject->propertyAndMethodStorage.asManaged())) { + const QV4::Value *v = md->data() + m_id; + Q_ASSERT(v->as<QV4::Object>()); + m_list = static_cast<QV4::Heap::Object *>(v->heapObject()); + Q_ASSERT(m_list); + } +} + +void QQmlVMEResolvedList::append(QObject *o) const { - QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); - list->append(o); - static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), nullptr); + QV4::Scope scope(m_list->internalClass->engine); + QV4::Heap::ArrayData *arrayData = m_list->arrayData; + + const uint length = arrayData->length(); + if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) { + scope.engine->throwRangeError(QLatin1String("Too many elements.")); + return; + } + + QV4::ScopedObject object(scope, m_list); + QV4::ArrayData::realloc(object, QV4::Heap::ArrayData::Simple, length + 1, false); + arrayData->vtable()->put( + object, length, QV4::QObjectWrapper::wrap(scope.engine, o)); } -static int list_count(QQmlListProperty<QObject> *prop) +QObject *QQmlVMEResolvedList::at(qsizetype i) const { - QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); - return list->count(); + QV4::Scope scope(m_list->internalClass->engine); + QV4::Scoped<QV4::QObjectWrapper> result(scope, m_list->arrayData->get(i)); + return result ? result->object() : nullptr; } -static QObject *list_at(QQmlListProperty<QObject> *prop, int index) +void QQmlVMEResolvedList::replace(qsizetype i, QObject *o) const { - QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); - return list->at(index); + QV4::Scope scope(m_list->internalClass->engine); + QV4::ScopedObject object(scope, m_list); + m_list->arrayData->vtable()->put(object, i, QV4::QObjectWrapper::wrap(scope.engine, o)); } -static void list_clear(QQmlListProperty<QObject> *prop) +QQmlVMEResolvedList::~QQmlVMEResolvedList() = default; + +void QQmlVMEResolvedList::activateSignal() const { - QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data); - list->clear(); - static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), nullptr); + m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()), nullptr); } -QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr() - : QQmlGuard<QObject>(nullptr), m_target(nullptr), m_index(-1) +void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o) +{ + const QQmlVMEResolvedList resolved(prop); + resolved.append(o); + resolved.activateSignal(); +} + +void QQmlVMEMetaObject::list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o) +{ + QQmlVMEResolvedList(prop).append(o); +} + +static qsizetype list_count(QQmlListProperty<QObject> *prop) +{ + return QQmlVMEResolvedList(prop).size(); +} + +static QObject *list_at(QQmlListProperty<QObject> *prop, qsizetype index) { + return QQmlVMEResolvedList(prop).at(index); } -QQmlVMEVariantQObjectPtr::~QQmlVMEVariantQObjectPtr() +void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop) { + const QQmlVMEResolvedList resolved(prop); + resolved.clear(); + resolved.activateSignal(); } -void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *) +void QQmlVMEMetaObject::list_clear_nosignal(QQmlListProperty<QObject> *prop) { - if (!m_target || QQmlData::wasDeleted(m_target->object)) + QQmlVMEResolvedList(prop).clear(); +} + +static void list_replace(QQmlListProperty<QObject> *prop, qsizetype index, QObject *o) +{ + const QQmlVMEResolvedList resolved(prop); + resolved.replace(index, o); + resolved.activateSignal(); +} + +static void list_removeLast(QQmlListProperty<QObject> *prop) +{ + const QQmlVMEResolvedList resolved(prop); + resolved.removeLast(); + resolved.activateSignal(); +} + +QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr() + : QQmlGuard<QObject>(QQmlVMEVariantQObjectPtr::objectDestroyedImpl, nullptr), m_target(nullptr), m_index(-1) +{ +} + +void QQmlVMEVariantQObjectPtr::objectDestroyedImpl(QQmlGuardImpl *guard) +{ + auto This = static_cast<QQmlVMEVariantQObjectPtr *>(guard); + if (!This->m_target || QQmlData::wasDeleted(This->m_target->object)) return; - if (m_index >= 0) { - QV4::ExecutionEngine *v4 = m_target->propertyAndMethodStorage.engine(); + if (This->m_index >= 0) { + QV4::ExecutionEngine *v4 = This->m_target->propertyAndMethodStorage.engine(); if (v4) { QV4::Scope scope(v4); - QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value()); + QV4::Scoped<QV4::MemberData> sp(scope, This->m_target->propertyAndMethodStorage.value()); if (sp) { - QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + m_index }; + QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + This->m_index }; index.set(v4, QV4::Value::nullValue()); } } - m_target->activate(m_target->object, m_target->methodOffset() + m_index, nullptr); + This->m_target->activate(This->m_target->object, This->m_target->methodOffset() + This->m_index, nullptr); } } @@ -130,7 +188,12 @@ public: QQmlVMEMetaObjectEndpoint(); void tryConnect(); - QFlagPointer<QQmlVMEMetaObject> metaObject; + enum Tag { + NoTag, + EndPointIsConnected + }; + + QTaggedPointer<QQmlVMEMetaObject, Tag> metaObject; }; QQmlVMEMetaObjectEndpoint::QQmlVMEMetaObjectEndpoint() @@ -149,29 +212,26 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() Q_ASSERT(metaObject->compiledObject); int aliasId = this - metaObject->aliasEndpoints; - if (metaObject.flag()) { + if (metaObject.tag() == EndPointIsConnected) { // This is actually notify int sigIdx = metaObject->methodOffset() + aliasId + metaObject->compiledObject->nProperties; metaObject->activate(metaObject->object, sigIdx, nullptr); } else { const QV4::CompiledData::Alias *aliasData = &metaObject->compiledObject->aliasTable()[aliasId]; if (!aliasData->isObjectAlias()) { - QQmlContextData *ctxt = metaObject->ctxt; - QObject *target = ctxt->idValues[aliasData->targetObjectId].data(); + QQmlRefPointer<QQmlContextData> ctxt = metaObject->ctxt; + QObject *target = ctxt->idValue(aliasData->targetObjectId()); if (!target) return; - QQmlData *targetDData = QQmlData::get(target, /*create*/false); - if (!targetDData) - return; QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex); int coreIndex = encodedIndex.coreIndex(); int valueTypeIndex = encodedIndex.valueTypeIndex(); - const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); - if (pd && valueTypeIndex != -1 && !QQmlValueTypeFactory::valueType(pd->propType())) { + const QQmlPropertyData *pd = QQmlData::ensurePropertyCache(target)->property(coreIndex); + if (pd && valueTypeIndex != -1 && !QQmlMetaType::valueType(pd->propType())) { // deep alias - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine()); - auto const *newPropertyCache = enginePriv->propertyCacheForType(pd->propType()); + const QQmlPropertyCache::ConstPtr newPropertyCache + = QQmlMetaType::propertyCacheForType(pd->propType()); void *argv[1] = { &target }; QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); Q_ASSERT(newPropertyCache); @@ -180,20 +240,18 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() if (!pd) return; - if (pd->notifyIndex() != -1) - connect(target, pd->notifyIndex(), ctxt->engine); + if (pd->notifyIndex() != -1 && ctxt->engine()) + connect(target, pd->notifyIndex(), ctxt->engine()); } - metaObject.setFlag(); + metaObject.setTag(EndPointIsConnected); } } -QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache) +QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, const QQmlPropertyCache::ConstPtr &cache) : object(obj), - cache(cache), - interceptors(nullptr), - hasAssignedMetaObjectData(false) + cache(cache) { QObjectPrivate *op = QObjectPrivate::get(obj); @@ -216,6 +274,15 @@ QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject() void QQmlInterceptorMetaObject::registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor) { + for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { + if (Q_UNLIKELY(vi->m_propertyIndex.coreIndex() == index.coreIndex())) { + qWarning() << "Attempting to set another interceptor on " + << object->metaObject()->className() << "property" + << object->metaObject()->property(index.coreIndex()).name() + << "- unsupported"; + } + } + interceptor->m_propertyIndex = index; interceptor->m_next = interceptors; interceptors = interceptor; @@ -231,21 +298,31 @@ int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, return object->qt_metacall(c, id, a); } -bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) -{ - if (c == QMetaObject::WriteProperty && interceptors && - !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyData::BypassInterceptor)) { - - for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { - if (vi->m_propertyIndex.coreIndex() != id) - continue; - - const int valueIndex = vi->m_propertyIndex.valueTypeIndex(); - int type = QQmlData::get(object)->propertyCache->property(id)->propType(); - - if (type != QVariant::Invalid) { - if (valueIndex != -1) { - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type); +bool QQmlInterceptorMetaObject::doIntercept(QMetaObject::Call c, int id, void **a) +{ + for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) { + if (vi->m_propertyIndex.coreIndex() != id) + continue; + + const int valueIndex = vi->m_propertyIndex.valueTypeIndex(); + const QQmlData *data = QQmlData::get(object); + const QMetaType metaType = data->propertyCache->property(id)->propType(); + + if (metaType.isValid()) { + if (valueIndex != -1 && c == QMetaObject::WriteProperty) { + + // If we didn't intend to change the property this interceptor cares about, + // then don't bother intercepting it. There may be an animation running on + // the property. We shouldn't disturb it. + const int changedProperty + = (*static_cast<int *>(a[3]) & QQmlPropertyData::HasInternalIndex) + ? *static_cast<int *>(a[4]) + : QV4::ReferenceObject::AllProperties; + if (changedProperty == QV4::ReferenceObject::AllProperties + || changedProperty == valueIndex) { + // TODO: handle intercepting bindable properties for value types? + QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance( + data->context->engine(), metaType); Q_ASSERT(valueType); // @@ -279,57 +356,65 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) // (7) Issue the interceptor call with the new component value. // - QMetaProperty valueProp = valueType->metaObject()->property(valueIndex); - QVariant newValue(type, a[0]); + QMetaProperty valueProp = valueType->property(valueIndex); + QVariant newValue(metaType, a[0]); valueType->read(object, id); - QVariant prevComponentValue = valueProp.read(valueType); + QVariant prevComponentValue = valueType->readOnGadget(valueProp); valueType->setValue(newValue); - QVariant newComponentValue = valueProp.read(valueType); + QVariant newComponentValue = valueType->readOnGadget(valueProp); - // Don't apply the interceptor if the intercepted value has not changed - bool updated = false; - if (newComponentValue != prevComponentValue) { - valueProp.write(valueType, prevComponentValue); - valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); - - vi->write(newComponentValue); - updated = true; - } + // If the intercepted value seemingly has not changed, we still need to + // invoke the interceptor. There may be a pending animation that will + // change the value soon. Such an animation needs to be canceled if the + // current value is explicitly set. + // So, we cannot return here if prevComponentValue == newComponentValue. + valueType->writeOnGadget(valueProp, std::move(prevComponentValue)); + valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor); - if (updated) - return true; - } else { - vi->write(QVariant(type, a[0])); + vi->write(newComponentValue); return true; } + } else if (c == QMetaObject::WriteProperty) { + vi->write(QVariant(metaType, a[0])); + return true; + } else { + object->qt_metacall(c, id, a); + QUntypedBindable target = *reinterpret_cast<QUntypedBindable *>(a[0]); + return vi->bindable(reinterpret_cast<QUntypedBindable *>(a[0]), target); } } } + return false; } - -QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObject *o) +static QMetaObject *stringCastMetaObject(QObject *o, const QMetaObject *top) { - if (!hasAssignedMetaObjectData) { - *static_cast<QMetaObject *>(this) = *cache->createMetaObject(); + for (const QMetaObject *mo = top; mo; mo = mo->superClass()) { + if (o->qt_metacast(mo->className()) != nullptr) + return const_cast<QMetaObject *>(mo); + } + return nullptr; +} - if (parent.isT1()) - this->d.superdata = parent.asT1()->toDynamicMetaObject(o); - else - this->d.superdata = parent.asT2(); +QMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObject *o) +{ + if (!metaObject) + metaObject = cache->createMetaObject(); - hasAssignedMetaObjectData = true; - } + if (Q_UNLIKELY(metaObject.tag() == MetaObjectInvalid)) + return stringCastMetaObject(o, metaObject->superClass()); - return this; + // ### Qt7: The const_cast is only due to toDynamicMetaObject having the wrong return type. + // It should be const QMetaObject *. Fix this. + return const_cast<QMetaObject *>(metaObject.data()); } QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, - const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId) + const QQmlPropertyCache::ConstPtr &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId) : QQmlInterceptorMetaObject(obj, cache), engine(engine), ctxt(QQmlData::get(obj, true)->outerContext), @@ -345,6 +430,12 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine, uint size = compiledObject->nProperties + compiledObject->nFunctions; if (size) { QV4::Heap::MemberData *data = QV4::MemberData::allocate(engine, size); + // we only have a weak reference below; if the VMEMetaObject is already marked + // (triggered by the allocate call above) + // we therefore might never mark the member data; consequently, mark it now + QV4::WriteBarrier::markCustom(engine, [data](QV4::MarkStack *ms) { + data->mark(ms); + }); propertyAndMethodStorage.set(engine, data); std::fill(data->values.values, data->values.values + data->values.size, QV4::Encode::undefined()); } @@ -401,57 +492,20 @@ void QQmlVMEMetaObject::writeProperty(int id, double v) void QQmlVMEMetaObject::writeProperty(int id, const QString& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); - if (md) - md->set(engine, id, engine->newString(v)); -} - -void QQmlVMEMetaObject::writeProperty(int id, const QUrl& v) -{ - QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); - if (md) - md->set(engine, id, engine->newVariantObject(QVariant::fromValue(v))); -} - -void QQmlVMEMetaObject::writeProperty(int id, const QDate& v) -{ - QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); - if (md) - md->set(engine, id, engine->newVariantObject(QVariant::fromValue(v))); -} - -void QQmlVMEMetaObject::writeProperty(int id, const QDateTime& v) -{ - QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); - if (md) - md->set(engine, id, engine->newVariantObject(QVariant::fromValue(v))); -} - -void QQmlVMEMetaObject::writeProperty(int id, const QPointF& v) -{ - QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); - if (md) - md->set(engine, id, engine->newVariantObject(QVariant::fromValue(v))); -} - -void QQmlVMEMetaObject::writeProperty(int id, const QSizeF& v) -{ - QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); - if (md) - md->set(engine, id, engine->newVariantObject(QVariant::fromValue(v))); -} - -void QQmlVMEMetaObject::writeProperty(int id, const QRectF& v) -{ - QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); - if (md) - md->set(engine, id, engine->newVariantObject(QVariant::fromValue(v))); + if (md) { + QV4::Scope scope(engine); + QV4::Scoped<QV4::MemberData>(scope, md)->set(engine, id, engine->newString(v)); + } } void QQmlVMEMetaObject::writeProperty(int id, QObject* v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); - if (md) - md->set(engine, id, QV4::Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, v))); + if (md) { + QV4::Scope scope(engine); + QV4::Scoped<QV4::MemberData>(scope, md)->set(engine, id, QV4::Value::fromReturnedValue( + QV4::QObjectWrapper::wrap(engine, v))); + } QQmlVMEVariantQObjectPtr *guard = getQObjectGuardForProperty(id); if (v && !guard) { @@ -523,7 +577,7 @@ QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id) const QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); - if (!v || v->d()->data().type() != QVariant::Url) + if (!v || v->d()->data().userType() != QMetaType::QUrl) return QUrl(); return v->d()->data().value<QUrl>(); } @@ -537,12 +591,26 @@ QDate QQmlVMEMetaObject::readPropertyAsDate(int id) const QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); - if (!v || v->d()->data().type() != QVariant::Date) + if (!v || v->d()->data().userType() != QMetaType::QDate) return QDate(); return v->d()->data().value<QDate>(); } -QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) +QTime QQmlVMEMetaObject::readPropertyAsTime(int id) const +{ + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); + if (!md) + return QTime(); + + QV4::Scope scope(engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); + if (!v || v->d()->data().userType() != QMetaType::QTime) + return QTime(); + return v->d()->data().value<QTime>(); +} + +QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) const { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) @@ -551,11 +619,27 @@ QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id) QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); - if (!v || v->d()->data().type() != QVariant::DateTime) + if (!v || v->d()->data().userType() != QMetaType::QDateTime) return QDateTime(); return v->d()->data().value<QDateTime>(); } +#if QT_CONFIG(regularexpression) +QRegularExpression QQmlVMEMetaObject::readPropertyAsRegularExpression(int id) const +{ + QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); + if (!md) + return QRegularExpression(); + + QV4::Scope scope(engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); + if (!v || v->d()->data().userType() != QMetaType::QRegularExpression) + return QRegularExpression(); + return v->d()->data().value<QRegularExpression>(); +} +#endif + QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) const { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); @@ -565,7 +649,7 @@ QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id) const QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); - if (!v || v->d()->data().type() != QVariant::SizeF) + if (!v || v->d()->data().userType() != QMetaType::QSizeF) return QSizeF(); return v->d()->data().value<QSizeF>(); } @@ -579,7 +663,7 @@ QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id) const QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); - if (!v || v->d()->data().type() != QVariant::PointF) + if (!v || v->d()->data().userType() != QMetaType::QPointF) return QPointF(); return v->d()->data().value<QPointF>(); } @@ -598,20 +682,19 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id) const return wrapper->object(); } -QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) const +void QQmlVMEMetaObject::initPropertyAsList(int id) const { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) - return nullptr; + return; QV4::Scope scope(engine); - QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id)); - if (!v || (int)v->d()->data().userType() != qMetaTypeId<QList<QObject *> >()) { - QVariant variant(QVariant::fromValue(QList<QObject*>())); - v = engine->newVariantObject(variant); + QV4::ScopedObject v(scope, *(md->data() + id)); + if (!v) { + v = engine->newObject(); + v->arrayCreate(); md->set(engine, id, v); } - return static_cast<QList<QObject *> *>(v->d()->data().data()); } QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const @@ -623,14 +706,11 @@ QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id) const QV4::Scope scope(engine); QV4::ScopedValue sv(scope, *(md->data() + id)); const QV4::VariantObject *v = sv->as<QV4::VariantObject>(); - if (!v || v->d()->data().type() != QVariant::RectF) + if (!v || v->d()->data().userType() != QMetaType::QRectF) return QRectF(); return v->d()->data().value<QRectF>(); } -#if defined(Q_OS_WINRT) && defined(_M_ARM) -#pragma optimize("", off) -#endif int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void **a) { Q_ASSERT(o == object); @@ -646,171 +726,301 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * const int signalCount = compiledObject ? int(compiledObject->nSignals) : 0; const int methodCount = compiledObject ? int(compiledObject->nFunctions) : 0; - if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) { + if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty || c == QMetaObject::BindableProperty) { if (id >= propOffset()) { id -= propOffset(); if (id < propertyCount) { + // if we reach this point, propertyCount must have been > 0, and thus compiledObject != nullptr + Q_ASSERT(compiledObject); const QV4::CompiledData::Property &property = compiledObject->propertyTable()[id]; - const QV4::CompiledData::BuiltinType t = property.builtinType(); + const QV4::CompiledData::CommonType t = property.commonType(); // the context can be null if accessing var properties from cpp after re-parenting an item. - QQmlEnginePrivate *ep = (ctxt == nullptr || ctxt->engine == nullptr) ? nullptr : QQmlEnginePrivate::get(ctxt->engine); - - const int fallbackMetaType = QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(t); + QQmlEnginePrivate *ep = (ctxt.isNull() || ctxt->engine() == nullptr) + ? nullptr + : QQmlEnginePrivate::get(ctxt->engine()); if (c == QMetaObject::ReadProperty) { - switch (t) { - case QV4::CompiledData::BuiltinType::Int: - *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id); - break; - case QV4::CompiledData::BuiltinType::Bool: - *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id); - break; - case QV4::CompiledData::BuiltinType::Real: - *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id); - break; - case QV4::CompiledData::BuiltinType::String: - *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id); - break; - case QV4::CompiledData::BuiltinType::Url: - *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id); - break; - case QV4::CompiledData::BuiltinType::Date: - *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id); - break; - case QV4::CompiledData::BuiltinType::DateTime: - *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id); - break; - case QV4::CompiledData::BuiltinType::Rect: - *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id); - break; - case QV4::CompiledData::BuiltinType::Size: - *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id); - break; - case QV4::CompiledData::BuiltinType::Point: - *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id); - break; - case QV4::CompiledData::BuiltinType::Variant: - *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); - break; - case QV4::CompiledData::BuiltinType::Font: - case QV4::CompiledData::BuiltinType::Time: - case QV4::CompiledData::BuiltinType::Color: - case QV4::CompiledData::BuiltinType::Vector2D: - case QV4::CompiledData::BuiltinType::Vector3D: - case QV4::CompiledData::BuiltinType::Vector4D: - case QV4::CompiledData::BuiltinType::Matrix4x4: - case QV4::CompiledData::BuiltinType::Quaternion: - Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); - if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { - QVariant propertyAsVariant; - if (const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) - propertyAsVariant = v->d()->data(); - QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType); - } - break; - case QV4::CompiledData::BuiltinType::Var: - if (ep) { - *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); + if (property.isList()) { + // _id because this is an absolute property ID. + const QQmlPropertyData *propertyData = cache->property(_id); + const QMetaType propType = propertyData->propType(); + + if (propType.flags().testFlag(QMetaType::IsQmlList)) { + // when reading from the list, we need to find the correct MetaObject, + // namely this. However, obejct->metaObject might point to any + // MetaObject down the inheritance hierarchy, so we need to store how + // far we have to go down + // To do this, we encode the hierarchy depth together with the id of the + // property in a single quintptr, with the first half storing the depth + // and the second half storing the property id + auto mo = static_cast<QQmlVMEMetaObject *>( + QObjectPrivate::get(object)->metaObject); + quintptr inheritanceDepth = 0u; + while (mo && mo != this) { + mo = mo->parentVMEMetaObject(); + ++inheritanceDepth; + } + constexpr quintptr idBits = sizeof(quintptr) * CHAR_BIT / 2u; + if (Q_UNLIKELY(inheritanceDepth >= (quintptr(1) << idBits))) { + qmlWarning(object) << "Too many objects in inheritance hierarchy " + "for list property"; + return -1; + } + if (Q_UNLIKELY(quintptr(id) >= (quintptr(1) << idBits))) { + qmlWarning(object) << "Too many properties in object " + "for list property"; + return -1; + } + quintptr encodedIndex = (inheritanceDepth << idBits) + id; + + initPropertyAsList(id); + *static_cast<QQmlListProperty<QObject> *>(a[0]) + = QQmlListProperty<QObject>( + object, reinterpret_cast<void *>(quintptr(encodedIndex)), + list_append, list_count, list_at, + list_clear, list_replace, list_removeLast); + } else if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + // Value type list + QV4::Scope scope(engine); + QV4::Scoped<QV4::Sequence> sequence(scope, *(md->data() + id)); + const void *data = sequence + ? QV4::SequencePrototype::getRawContainerPtr(sequence, propType) + : nullptr; + propType.destruct(a[0]); + propType.construct(a[0], data); } else { - // if the context was disposed, we just return an invalid variant from read. - *reinterpret_cast<QVariant *>(a[0]) = QVariant(); + qmlWarning(object) << "Cannot find member data"; } - break; - case QV4::CompiledData::BuiltinType::InvalidBuiltin: - if (property.isList) { - QList<QObject *> *list = readPropertyAsList(id); - QQmlListProperty<QObject> *p = static_cast<QQmlListProperty<QObject> *>(a[0]); - *p = QQmlListProperty<QObject>(object, list, - list_append, list_count, list_at, - list_clear); - p->dummy1 = this; - p->dummy2 = reinterpret_cast<void *>(quintptr(methodOffset() + id)); - } else { - *reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id); + } else { + switch (t) { + case QV4::CompiledData::CommonType::Void: + break; + case QV4::CompiledData::CommonType::Int: + *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id); + break; + case QV4::CompiledData::CommonType::Bool: + *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id); + break; + case QV4::CompiledData::CommonType::Real: + *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id); + break; + case QV4::CompiledData::CommonType::String: + *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id); + break; + case QV4::CompiledData::CommonType::Url: + *reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id); + break; + case QV4::CompiledData::CommonType::Date: + *reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id); + break; + case QV4::CompiledData::CommonType::DateTime: + *reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id); + break; + case QV4::CompiledData::CommonType::RegExp: +#if QT_CONFIG(regularexpression) + *reinterpret_cast<QRegularExpression *>(a[0]) + = readPropertyAsRegularExpression(id); +#endif + break; + case QV4::CompiledData::CommonType::Rect: + *reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id); + break; + case QV4::CompiledData::CommonType::Size: + *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id); + break; + case QV4::CompiledData::CommonType::Point: + *reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id); + break; + case QV4::CompiledData::CommonType::Time: + *reinterpret_cast<QTime *>(a[0]) = readPropertyAsTime(id); + break; + case QV4::CompiledData::CommonType::Var: + if (ep) { + *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id); + } else { + // if the context was disposed, + // we just return an invalid variant from read. + *reinterpret_cast<QVariant *>(a[0]) = QVariant(); + } + break; + case QV4::CompiledData::CommonType::Invalid: + if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + QV4::Scope scope(engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + + // _id because this is an absolute property ID. + const QQmlPropertyData *propertyData = cache->property(_id); + + if (propertyData->isQObject()) { + if (const auto *wrap = sv->as<QV4::QObjectWrapper>()) + *reinterpret_cast<QObject **>(a[0]) = wrap->object(); + else + *reinterpret_cast<QObject **>(a[0]) = nullptr; + } else { + const QMetaType propType = propertyData->propType(); + const void *data = nullptr; + if (const auto *v = sv->as<QV4::VariantObject>()) { + const QVariant &variant = v->d()->data(); + if (variant.metaType() == propType) + data = variant.constData(); + } + propType.destruct(a[0]); + propType.construct(a[0], data); + } + } else { + qmlWarning(object) << "Cannot find member data"; + } } } - } else if (c == QMetaObject::WriteProperty) { bool needActivate = false; - switch (t) { - case QV4::CompiledData::BuiltinType::Int: - needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id); - writeProperty(id, *reinterpret_cast<int *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Bool: - needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id); - writeProperty(id, *reinterpret_cast<bool *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Real: - needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id); - writeProperty(id, *reinterpret_cast<double *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::String: - needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id); - writeProperty(id, *reinterpret_cast<QString *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Url: - needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id); - writeProperty(id, *reinterpret_cast<QUrl *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Date: - needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id); - writeProperty(id, *reinterpret_cast<QDate *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::DateTime: - needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id); - writeProperty(id, *reinterpret_cast<QDateTime *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Rect: - needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id); - writeProperty(id, *reinterpret_cast<QRectF *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Size: - needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id); - writeProperty(id, *reinterpret_cast<QSizeF *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Point: - needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id); - writeProperty(id, *reinterpret_cast<QPointF *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Variant: - writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::Font: - case QV4::CompiledData::BuiltinType::Time: - case QV4::CompiledData::BuiltinType::Color: - case QV4::CompiledData::BuiltinType::Vector2D: - case QV4::CompiledData::BuiltinType::Vector3D: - case QV4::CompiledData::BuiltinType::Vector4D: - case QV4::CompiledData::BuiltinType::Matrix4x4: - case QV4::CompiledData::BuiltinType::Quaternion: - Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); - if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { - const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); - if (!v) { - md->set(engine, id, engine->newVariantObject(QVariant())); - v = (md->data() + id)->as<QV4::VariantObject>(); - QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data()); + + if (property.isList()) { + // _id because this is an absolute property ID. + const QQmlPropertyData *propertyData = cache->property(_id); + const QMetaType propType = propertyData->propType(); + + if (propType.flags().testFlag(QMetaType::IsQmlList)) { + // Writing such a property is not supported. Content is added through + // the list property methods. + } else if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + // Value type list + QV4::Scope scope(engine); + QV4::Scoped<QV4::Sequence> sequence(scope, *(md->data() + id)); + void *data = sequence + ? QV4::SequencePrototype::getRawContainerPtr(sequence, propType) + : nullptr; + if (data) { + if (!propType.equals(data, a[0])) { + propType.destruct(data); + propType.construct(data, a[0]); + needActivate = true; + } + } else { + if (const QQmlType type = QQmlMetaType::qmlListType(propType); + type.isSequentialContainer()) { + sequence = QV4::SequencePrototype::fromData( + engine, propType, type.listMetaSequence(), a[0]); + } else if (QSequentialIterable iterable; + QMetaType::convert( + propType, a[0], + QMetaType::fromType<QSequentialIterable>(), + &iterable)) { + sequence = QV4::SequencePrototype::fromData( + engine, propType, iterable.metaContainer(), a[0]); + } else { + sequence = QV4::Encode::undefined(); + } + md->set(engine, id, sequence); + if (sequence->isUndefined()) { + qmlWarning(object) + << "Could not create a QML sequence object for " + << propType.name(); + } + needActivate = true; } - needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data()); - QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data()); - } - break; - case QV4::CompiledData::BuiltinType::Var: - if (ep) - writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); - break; - case QV4::CompiledData::BuiltinType::InvalidBuiltin: - if (property.isList) { - // Writing such a property is not supported. Content is added through the list property - // methods. } else { - needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id); - writeProperty(id, *reinterpret_cast<QObject **>(a[0])); + qmlWarning(object) << "Cannot find member data"; + } + } else { + switch (t) { + case QV4::CompiledData::CommonType::Void: + break; + case QV4::CompiledData::CommonType::Int: + needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id); + writeProperty(id, *reinterpret_cast<int *>(a[0])); + break; + case QV4::CompiledData::CommonType::Bool: + needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id); + writeProperty(id, *reinterpret_cast<bool *>(a[0])); + break; + case QV4::CompiledData::CommonType::Real: + needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id); + writeProperty(id, *reinterpret_cast<double *>(a[0])); + break; + case QV4::CompiledData::CommonType::String: + needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id); + writeProperty(id, *reinterpret_cast<QString *>(a[0])); + break; + case QV4::CompiledData::CommonType::Url: + needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id); + writeProperty(id, *reinterpret_cast<QUrl *>(a[0])); + break; + case QV4::CompiledData::CommonType::Date: + needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id); + writeProperty(id, *reinterpret_cast<QDate *>(a[0])); + break; + case QV4::CompiledData::CommonType::DateTime: + needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id); + writeProperty(id, *reinterpret_cast<QDateTime *>(a[0])); + break; + case QV4::CompiledData::CommonType::RegExp: +#if QT_CONFIG(regularexpression) + needActivate = *reinterpret_cast<QRegularExpression *>(a[0]) + != readPropertyAsRegularExpression(id); + writeProperty(id, *reinterpret_cast<QRegularExpression *>(a[0])); +#endif + break; + case QV4::CompiledData::CommonType::Rect: + needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id); + writeProperty(id, *reinterpret_cast<QRectF *>(a[0])); + break; + case QV4::CompiledData::CommonType::Size: + needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id); + writeProperty(id, *reinterpret_cast<QSizeF *>(a[0])); + break; + case QV4::CompiledData::CommonType::Point: + needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id); + writeProperty(id, *reinterpret_cast<QPointF *>(a[0])); + break; + case QV4::CompiledData::CommonType::Time: + needActivate = *reinterpret_cast<QTime *>(a[0]) != readPropertyAsTime(id); + writeProperty(id, *reinterpret_cast<QTime *>(a[0])); + break; + case QV4::CompiledData::CommonType::Var: + if (ep) + writeProperty(id, *reinterpret_cast<QVariant *>(a[0])); + break; + case QV4::CompiledData::CommonType::Invalid: + if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { + QV4::Scope scope(engine); + QV4::ScopedValue sv(scope, *(md->data() + id)); + + // _id because this is an absolute property ID. + const QQmlPropertyData *propertyData = cache->property(_id); + + if (propertyData->isQObject()) { + QObject *arg = *reinterpret_cast<QObject **>(a[0]); + if (const auto *wrap = sv->as<QV4::QObjectWrapper>()) + needActivate = wrap->object() != arg; + else if (arg != nullptr || !sv->isNull()) + needActivate = true; + if (needActivate) + writeProperty(id, arg); + } else { + const QMetaType propType = propertyData->propType(); + if (const auto *v = sv->as<QV4::VariantObject>()) { + QVariant &variant = v->d()->data(); + if (variant.metaType() != propType) { + needActivate = true; + variant = QVariant(propType, a[0]); + } else if (!propType.equals(variant.constData(), a[0])) { + needActivate = true; + propType.destruct(variant.data()); + propType.construct(variant.data(), a[0]); + } + } else { + needActivate = true; + md->set(engine, id, engine->newVariantObject( + propType, a[0])); + } + } + } else { + qmlWarning(object) << "Cannot find member data"; + } } - } if (needActivate) @@ -825,18 +1035,18 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (id < aliasCount) { const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[id]; - if ((aliasData->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject) && c == QMetaObject::ReadProperty) - *reinterpret_cast<void **>(a[0]) = nullptr; + if (aliasData->hasFlag(QV4::CompiledData::Alias::AliasPointsToPointerObject) + && c == QMetaObject::ReadProperty){ + *reinterpret_cast<void **>(a[0]) = nullptr; + } - if (!ctxt) return -1; + if (ctxt.isNull()) + return -1; - while (aliasData->aliasToLocalAlias) + while (aliasData->isAliasToLocalAlias()) aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex]; - QQmlContext *context = ctxt->asQQmlContext(); - QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); - - QObject *target = ctxtPriv->data->idValues[aliasData->targetObjectId].data(); + QObject *target = ctxt->idValue(aliasData->targetObjectId()); if (!target) return -1; @@ -855,39 +1065,50 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * int coreIndex = encodedIndex.coreIndex(); const int valueTypePropertyIndex = encodedIndex.valueTypeIndex(); - // Remove binding (if any) on write - if(c == QMetaObject::WriteProperty) { - int flags = *reinterpret_cast<int*>(a[3]); - if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) { - QQmlData *targetData = QQmlData::get(target); - if (targetData && targetData->hasBindingBit(coreIndex)) - QQmlPropertyPrivate::removeBinding(target, encodedIndex); + const auto removePendingBinding + = [c, a](QObject *target, int coreIndex, QQmlPropertyIndex encodedIndex) { + // Remove binding (if any) on write + if (c == QMetaObject::WriteProperty) { + int flags = *reinterpret_cast<int*>(a[3]); + if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) { + QQmlData *targetData = QQmlData::get(target); + if (targetData && targetData->hasBindingBit(coreIndex)) { + QQmlPropertyPrivate::removeBinding(target, encodedIndex); + targetData->clearBindingBit(coreIndex); + } + } } - } + }; if (valueTypePropertyIndex != -1) { if (!targetDData->propertyCache) return -1; const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); // Value type property or deep alias - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType()); + QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance( + ctxt->engine(), pd->propType()); if (valueType) { - + removePendingBinding(target, coreIndex, encodedIndex); valueType->read(target, coreIndex); int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); if (c == QMetaObject::WriteProperty) - valueType->write(target, coreIndex, nullptr); + valueType->write(target, coreIndex, QQmlPropertyData::HasInternalIndex, + valueTypePropertyIndex); return rv; } else { // deep alias void *argv[1] = { &target }; QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); + removePendingBinding( + target, valueTypePropertyIndex, + QQmlPropertyIndex(valueTypePropertyIndex)); return QMetaObject::metacall(target, c, valueTypePropertyIndex, a); } } else { + removePendingBinding(target, coreIndex, encodedIndex); return QMetaObject::metacall(target, c, coreIndex, a); } @@ -910,7 +1131,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * id -= plainSignals; if (id < methodCount) { - QQmlEngine *engine = ctxt->engine; + QQmlEngine *engine = ctxt->engine(); if (!engine) return -1; // We can't run the method @@ -920,17 +1141,17 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * QV4::Scope scope(v4); - QV4::ScopedFunctionObject function(scope, method(id)); + QV4::Scoped<QV4::JavaScriptFunctionObject> function(scope, method(id)); if (!function) { // The function was not compiled. There are some exceptional cases which the // expression rewriter does not rewrite properly (e.g., \r-terminated lines // are not rewritten correctly but this bug is deemed out-of-scope to fix for // performance reasons; see QTBUG-24064) and thus compilation will have failed. QQmlError e; - e.setDescription(QLatin1String("Exception occurred during compilation of " - "function: ") - + QString::fromUtf8(QMetaObject::method(_id) - .methodSignature())); + e.setDescription( + QStringLiteral( + "Exception occurred during compilation of function: ") + + QString::fromUtf8(metaObject->method(_id).methodSignature())); ep->warning(e); return -1; // The dynamic method with that id is not available. } @@ -938,35 +1159,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * auto methodData = cache->method(_id); auto arguments = methodData->hasArguments() ? methodData->arguments() : nullptr; - const unsigned int parameterCount = (arguments && arguments->names) ? arguments->names->count() : 0; - Q_ASSERT(parameterCount == function->formalParameterCount()); - - QV4::JSCallData jsCallData(scope, parameterCount); - *jsCallData->thisObject = v4->global(); - - for (uint ii = 0; ii < parameterCount; ++ii) { - jsCallData->args[ii] = scope.engine->metaTypeToJS(arguments->arguments[ii + 1], a[ii + 1]); + if (arguments && arguments->names) { + const quint32 parameterCount = arguments->names->size(); + Q_ASSERT(parameterCount == function->formalParameterCount()); + function->call(object, a, arguments->types, parameterCount); + } else { + Q_ASSERT(function->formalParameterCount() == 0); + const QMetaType returnType = methodData->propType(); + function->call(object, a, &returnType, 0); } - const int returnType = methodData->propType(); - QV4::ScopedValue result(scope, function->call(jsCallData)); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); if (error.isValid()) ep->warning(error); - if (a[0]) { - QMetaType::destruct(returnType, a[0]); - QMetaType::construct(returnType, a[0], nullptr); - } - } else { - if (a[0]) { - // When the return type is QVariant, JS objects are to be returned as QJSValue wrapped in - // QVariant. - if (returnType == QMetaType::QVariant) - *(QVariant *)a[0] = scope.engine->toVariant(result, 0); - else - scope.engine->metaTypeFromJS(result, returnType, a[0]); - } } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. @@ -981,13 +1187,10 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * else return object->qt_metacall(c, _id, a); } -#if defined(Q_OS_WINRT) && defined(_M_ARM) -#pragma optimize("", on) -#endif QV4::ReturnedValue QQmlVMEMetaObject::method(int index) const { - if (!ctxt || !ctxt->isValid() || !compiledObject) { + if (ctxt.isNull() || !ctxt->isValid() || !compiledObject) { qWarning("QQmlVMEMetaObject: Internal error - attempted to evaluate a function in an invalid context"); return QV4::Encode::undefined(); } @@ -1001,7 +1204,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::method(int index) const QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) const { - Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var); + Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var); QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) @@ -1019,14 +1222,14 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); if (v) return v->d()->data(); - return engine->toVariant(*(md->data() + id), -1); + return QV4::ExecutionEngine::toVariant(*(md->data() + id), QMetaType {}); } return QVariant(); } void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) { - Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var); + Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var); QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) @@ -1045,6 +1248,7 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) // automatically released by the engine until no other references to it exist. if (QV4::VariantObject *v = const_cast<QV4::VariantObject*>(value.as<QV4::VariantObject>())) { v->addVmePropertyReference(); + md->set(engine, id, value); } else if (QV4::QObjectWrapper *wrapper = const_cast<QV4::QObjectWrapper*>(value.as<QV4::QObjectWrapper>())) { // We need to track this QObject to signal its deletion valueObject = wrapper->object(); @@ -1054,19 +1258,39 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) guard = new QQmlVMEVariantQObjectPtr(); varObjectGuards.append(guard); } + md->set(engine, id, value); + } else if (const QV4::Sequence *sequence = value.as<QV4::Sequence>()) { + QV4::Heap::Sequence *p = sequence->d(); + if (p->enforcesLocation()) { + // If the sequence enforces its location, we don't want it to be updated anymore after + // being written to a property. + md->set(engine, id, QV4::ReferenceObject::detached(p)); + } else { + // Otherwise, make sure the reference carries some value so that we can still call + // toVariant() on it (see note in QV4::SequencePrototype::toVariant). + if (!p->hasData()) + QV4::ReferenceObject::readReference(p); + md->set(engine, id, p); + } + } else if (const QV4::QQmlValueTypeWrapper *wrapper = value.as<QV4::QQmlValueTypeWrapper>()) { + // If the value type enforces its location, we don't want it to be updated anymore after + // being written to a property. + QV4::Heap::QQmlValueTypeWrapper *p = wrapper->d(); + md->set(engine, id, p->enforcesLocation() ? QV4::ReferenceObject::detached(p) : p); + } else { + md->set(engine, id, value); } if (guard) guard->setGuardedValue(valueObject, this, id); - // Write the value and emit change signal as appropriate. - md->set(engine, id, value); + // Emit change signal as appropriate. activate(object, methodOffset() + id, nullptr); } void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) { - if (compiledObject && compiledObject->propertyTable()[id].builtinType() == QV4::CompiledData::BuiltinType::Var) { + if (compiledObject && compiledObject->propertyTable()[id].commonType() == QV4::CompiledData::CommonType::Var) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return; @@ -1105,7 +1329,7 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) v->d()->data() != value); if (v) v->removeVmePropertyReference(); - md->set(engine, id, engine->newVariantObject(value)); + md->set(engine, id, engine->newVariantObject(value.metaType(), value.constData())); v = static_cast<const QV4::VariantObject *>(md->data() + id); v->addVmePropertyReference(); } @@ -1170,12 +1394,12 @@ void QQmlVMEMetaObject::setVMEProperty(int index, const QV4::Value &v) void QQmlVMEMetaObject::ensureQObjectWrapper() { Q_ASSERT(cache); - QV4::QObjectWrapper::wrap(engine, object); + QV4::QObjectWrapper::ensureWrapper(engine, object); } void QQmlVMEMetaObject::mark(QV4::MarkStack *markStack) { - if (engine != markStack->engine) + if (engine != markStack->engine()) return; propertyAndMethodStorage.markOnce(markStack); @@ -1192,14 +1416,14 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, *coreIndex = -1; *valueTypeIndex = -1; - if (!ctxt) + if (ctxt.isNull()) return false; const int aliasId = index - propOffset() - compiledObject->nProperties; const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId]; - while (aliasData->aliasToLocalAlias) + while (aliasData->isAliasToLocalAlias()) aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex]; - *target = ctxt->idValues[aliasData->targetObjectId].data(); + *target = ctxt->idValue(aliasData->targetObjectId()); if (!*target) return false; @@ -1227,7 +1451,7 @@ void QQmlVMEMetaObject::connectAlias(int aliasId) } endpoint->metaObject = this; - endpoint->connect(&ctxt->idValues[aliasData->targetObjectId].bindings); + endpoint->connect(ctxt->idValueBindings(aliasData->targetObjectId())); endpoint->tryConnect(); } |