aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlvmemetaobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlvmemetaobject.cpp')
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp1036
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();
}