aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlvaluetypewrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlvaluetypewrapper.cpp')
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp624
1 files changed, 384 insertions, 240 deletions
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 1b3034d5d9..7075d0f5f6 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlvaluetypewrapper_p.h"
@@ -53,11 +17,23 @@
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4identifiertable_p.h>
#include <private/qv4lookup_p.h>
+#include <private/qv4sequenceobject_p.h>
+#include <private/qv4arraybuffer_p.h>
+#include <private/qv4dateobject_p.h>
+#include <private/qv4jsonobject_p.h>
+#if QT_CONFIG(regularexpression)
+#include <private/qv4regexpobject_p.h>
+#endif
+#if QT_CONFIG(qml_locale)
+#include <private/qqmllocale_p.h>
+#endif
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qdatetime.h>
#include <QtCore/QLine>
#include <QtCore/QLineF>
#include <QtCore/QSize>
#include <QtCore/QSizeF>
+#include <QtCore/QTimeZone>
QT_BEGIN_NAMESPACE
@@ -66,112 +42,95 @@ Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval)
DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper);
namespace QV4 {
-namespace Heap {
-struct QQmlValueTypeReference : QQmlValueTypeWrapper
+Heap::QQmlValueTypeWrapper *Heap::QQmlValueTypeWrapper::detached() const
{
- void init() {
- QQmlValueTypeWrapper::init();
- object.init();
- }
- void destroy() {
- object.destroy();
- QQmlValueTypeWrapper::destroy();
- }
- QV4QPointer<QObject> object;
- int property;
-};
-
-}
-
-struct QQmlValueTypeReference : public QQmlValueTypeWrapper
-{
- V4_OBJECT2(QQmlValueTypeReference, QQmlValueTypeWrapper)
- V4_NEEDS_DESTROY
-
- bool readReferenceValue() const;
-};
-
+ return internalClass->engine->memoryManager->allocate<QV4::QQmlValueTypeWrapper>(
+ m_gadgetPtr, QMetaType(m_metaType), m_metaObject, nullptr, -1, NoFlag);
}
-DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeReference);
-
-using namespace QV4;
-
void Heap::QQmlValueTypeWrapper::destroy()
{
if (m_gadgetPtr) {
- m_valueType->metaType.destruct(m_gadgetPtr);
+ metaType().destruct(m_gadgetPtr);
::operator delete(m_gadgetPtr);
}
- Object::destroy();
-}
-
-void Heap::QQmlValueTypeWrapper::setValue(const QVariant &value) const
-{
- Q_ASSERT(valueType()->metaType.id() == value.userType());
- if (auto *gadget = gadgetPtr())
- valueType()->metaType.destruct(gadget);
- if (!gadgetPtr())
- setGadgetPtr(::operator new(valueType()->metaType.sizeOf()));
- valueType()->metaType.construct(gadgetPtr(), value.constData());
+ ReferenceObject::destroy();
}
QVariant Heap::QQmlValueTypeWrapper::toVariant() const
{
Q_ASSERT(gadgetPtr());
- return QVariant(valueType()->metaType, gadgetPtr());
+ return QVariant(metaType(), gadgetPtr());
}
-
-bool QQmlValueTypeReference::readReferenceValue() const
+bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant)
{
- if (!d()->object)
- return false;
- // A reference resource may be either a "true" reference (eg, to a QVector3D property)
- // or a "variant" reference (eg, to a QVariant property which happens to contain a value-type).
- QMetaProperty writebackProperty = d()->object->metaObject()->property(d()->property);
- if (writebackProperty.userType() == QMetaType::QVariant) {
- // variant-containing-value-type reference
- QVariant variantReferenceValue;
-
- void *a[] = { &variantReferenceValue, nullptr };
- QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->property, a);
-
- const QMetaType variantReferenceType = variantReferenceValue.metaType();
- if (variantReferenceType != type()) {
- // This is a stale VariantReference. That is, the variant has been
- // overwritten with a different type in the meantime.
- // We need to modify this reference to the updated value type, if
- // possible, or return false if it is not a value type.
- if (QQmlMetaType::isValueType(variantReferenceType)) {
- const QMetaObject *mo = QQmlMetaType::metaObjectForMetaType(variantReferenceType);
- if (d()->gadgetPtr()) {
- d()->valueType()->metaType.destruct(d()->gadgetPtr());
- ::operator delete(d()->gadgetPtr());
- }
- d()->setGadgetPtr(nullptr);
- d()->setMetaObject(mo);
- d()->setValueType(QQmlMetaType::valueType(variantReferenceType));
- if (!mo)
- return false;
- } else {
- return false;
+ Q_ASSERT(isVariant());
+
+ const QMetaType variantReferenceType = variant.metaType();
+ if (variantReferenceType != metaType()) {
+ // This is a stale VariantReference. That is, the variant has been
+ // overwritten with a different type in the meantime.
+ // We need to modify this reference to the updated value type, if
+ // possible, or return false if it is not a value type.
+ if (QQmlMetaType::isValueType(variantReferenceType)) {
+ const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(variantReferenceType);
+ if (gadgetPtr()) {
+ metaType().destruct(gadgetPtr());
+ ::operator delete(gadgetPtr());
}
+ setGadgetPtr(nullptr);
+ setMetaObject(mo);
+ setMetaType(variantReferenceType);
+ if (!mo)
+ return false;
+ } else {
+ return false;
}
- d()->setValue(variantReferenceValue);
- } else {
- if (!d()->gadgetPtr()) {
- d()->setGadgetPtr(::operator new(d()->valueType()->metaType.sizeOf()));
- d()->valueType()->metaType.construct(d()->gadgetPtr(), nullptr);
- }
- // value-type reference
- void *args[] = { d()->gadgetPtr(), nullptr };
- QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->property, args);
}
+
+ setData(variant.constData());
return true;
}
+void *Heap::QQmlValueTypeWrapper::storagePointer()
+{
+ if (!gadgetPtr()) {
+ setGadgetPtr(::operator new(metaType().sizeOf()));
+ metaType().construct(gadgetPtr(), nullptr);
+ }
+ return gadgetPtr();
+}
+
+bool Heap::QQmlValueTypeWrapper::readReference()
+{
+ // If locations are enforced we only read once
+ return enforcesLocation() || QV4::ReferenceObject::readReference(this);
+}
+
+bool Heap::QQmlValueTypeWrapper::writeBack(int propertyIndex)
+{
+ return isAttachedToProperty() && QV4::ReferenceObject::writeBack(this, propertyIndex);
+}
+
+ReturnedValue QQmlValueTypeWrapper::create(
+ ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *cloneFrom, Heap::Object *object)
+{
+ QV4::Scope scope(engine);
+ initProto(engine);
+
+ // Either we're enforcing the location, then we have to read right away.
+ // Or we don't then we lazy-load. In neither case we pass any data.
+ Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
+ nullptr, cloneFrom->metaType(), cloneFrom->metaObject(),
+ object, cloneFrom->property(), cloneFrom->flags()));
+ r->d()->setLocation(cloneFrom->function(), cloneFrom->statementIndex());
+ if (cloneFrom->enforcesLocation())
+ QV4::ReferenceObject::readReference(r->d());
+ return r->asReturnedValue();
+}
+
void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4)
{
if (v4->valueTypeWrapperPrototype()->d_unchecked())
@@ -183,59 +142,103 @@ void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4)
v4->jsObjects[QV4::ExecutionEngine::ValueTypeProto] = o->d();
}
-ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *object, int property, const QMetaObject *metaObject, QMetaType type)
+int QQmlValueTypeWrapper::virtualMetacall(
+ Object *object, QMetaObject::Call call, int index, void **a)
+{
+ QQmlValueTypeWrapper *wrapper = object->as<QQmlValueTypeWrapper>();
+ Q_ASSERT(wrapper);
+
+ switch (call) {
+ case QMetaObject::InvokeMetaMethod:
+ case QMetaObject::ReadProperty:
+ case QMetaObject::BindableProperty:
+ case QMetaObject::CustomCall:
+ if (wrapper->d()->object())
+ wrapper->d()->readReference();
+ break;
+ default:
+ break;
+ }
+
+ const QMetaObject *mo = wrapper->d()->metaObject();
+ if (!mo->d.static_metacall)
+ return 0;
+
+ mo->d.static_metacall(static_cast<QObject *>(wrapper->d()->gadgetPtr()), call, index, a);
+
+ switch (call) {
+ case QMetaObject::ReadProperty:
+ break;
+ case QMetaObject::WriteProperty:
+ case QMetaObject::ResetProperty:
+ if (wrapper->d()->object())
+ wrapper->d()->writeBack(index);
+ break;
+ case QMetaObject::InvokeMetaMethod:
+ case QMetaObject::CustomCall:
+ if (wrapper->d()->object())
+ wrapper->d()->writeBack();
+ break;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+ReturnedValue QQmlValueTypeWrapper::create(
+ ExecutionEngine *engine, const void *data, const QMetaObject *metaObject, QMetaType type,
+ Heap::Object *object, int property, Heap::ReferenceObject::Flags flags)
{
Scope scope(engine);
initProto(engine);
- Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocate<QQmlValueTypeReference>());
- r->d()->object = object;
- r->d()->property = property;
- r->d()->setMetaObject(metaObject);
- auto valueType = QQmlMetaType::valueType(type);
- if (!valueType) {
+ if (!type.isValid()) {
return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
.arg(QString::fromUtf8(type.name())));
}
- r->d()->setValueType(valueType);
- r->d()->setGadgetPtr(nullptr);
+
+ // If data is given explicitly, we assume it has just been read from the property
+ Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
+ data, type, metaObject, object, property, flags));
+ if (CppStackFrame *frame = engine->currentStackFrame)
+ r->d()->setLocation(frame->v4Function, frame->statementNumber());
+ if (!data && r->d()->enforcesLocation())
+ QV4::ReferenceObject::readReference(r->d());
return r->asReturnedValue();
}
-ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVariant &value, const QMetaObject *metaObject, QMetaType type)
+ReturnedValue QQmlValueTypeWrapper::create(
+ ExecutionEngine *engine, const void *data, const QMetaObject *metaObject, QMetaType type)
{
Scope scope(engine);
initProto(engine);
- Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>());
- r->d()->setMetaObject(metaObject);
- auto valueType = QQmlMetaType::valueType(type);
- if (!valueType) {
+ if (!type.isValid()) {
return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
.arg(QString::fromUtf8(type.name())));
}
- r->d()->setValueType(valueType);
- r->d()->setGadgetPtr(nullptr);
- r->d()->setValue(value);
+
+ Scoped<QQmlValueTypeWrapper> r(
+ scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>(
+ data, type, metaObject, nullptr, -1, Heap::ReferenceObject::NoFlag));
return r->asReturnedValue();
}
QVariant QQmlValueTypeWrapper::toVariant() const
{
- if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>())
- if (!ref->readReferenceValue())
- return QVariant();
+ if (d()->isReference() && !readReferenceValue())
+ return QVariant();
return d()->toVariant();
}
bool QQmlValueTypeWrapper::toGadget(void *data) const
{
- if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>())
- if (!ref->readReferenceValue())
- return false;
- const int typeId = d()->valueType()->metaType.id();
- QMetaType(typeId).destruct(data);
- QMetaType(typeId).construct(data, d()->gadgetPtr());
+ if (d()->isReference() && !readReferenceValue())
+ return false;
+ const QMetaType type = d()->metaType();
+ type.destruct(data);
+ type.construct(data, d()->gadgetPtr());
return true;
}
@@ -253,6 +256,48 @@ bool QQmlValueTypeWrapper::virtualIsEqualTo(Managed *m, Managed *other)
return false;
}
+bool QQmlValueTypeWrapper::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ if (!id.isString())
+ return Object::virtualHasProperty(m, id);
+ Q_ASSERT(m && m->as<QQmlValueTypeWrapper>());
+ auto wrapper = static_cast<const QQmlValueTypeWrapper *>(m);
+ if (auto mo = wrapper->d()->metaObject())
+ if (mo->indexOfProperty(id.toQString().toUtf8()) != -1)
+ return true;
+
+ /* we don't want to fallback to QObject::virtualHasProperty
+ as that would end up calling getOwnProperty which is wasteful,
+ as it calls our own virtualGetOwnProperty.
+ As we know that our own properties are only those found on the meta-object,
+ we can instead skip the call, and simply check whether the property exists
+ on the prototype.
+ */
+ Scope scope(m->engine());
+ ScopedObject o(scope, m);
+ o = o->getPrototypeOf();
+ if (o)
+ return o->hasProperty(id);
+
+ return false;
+}
+
+static Heap::ReferenceObject::Flags referenceFlags(const QMetaObject *metaObject, int index)
+{
+ return metaObject->property(index).isWritable()
+ ? (Heap::ReferenceObject::CanWriteBack | Heap::ReferenceObject::EnforcesLocation)
+ : Heap::ReferenceObject::EnforcesLocation;
+}
+
+static void doStaticReadCall(
+ const QMetaObject *metaObject, Heap::QQmlValueTypeWrapper *valueTypeWrapper,
+ int index, void **args)
+{
+ metaObject->d.static_metacall(
+ reinterpret_cast<QObject*>(
+ valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty, index, args);
+}
+
static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
Heap::QQmlValueTypeWrapper *valueTypeWrapper,
QMetaType metaType, quint16 coreIndex, bool isFunction, bool isEnum)
@@ -261,35 +306,114 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
// calling a Q_INVOKABLE function of a value type
return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, coreIndex);
}
+
+ const QMetaObject *metaObject = valueTypeWrapper->metaObject();
+ int index = coreIndex;
+
+ const auto wrapChar16 = [engine](char16_t c) {
+ return engine->newString(QChar(c));
+ };
+ const auto wrapQObject = [engine](QObject *object) {
+ return QObjectWrapper::wrap(engine, object);
+ };
+ const auto wrapJsonValue = [engine](const QJsonValue &value) {
+ return JsonObject::fromJsonValue(engine, value);
+ };
+ const auto wrapJsonObject = [engine](const QJsonObject &object) {
+ return JsonObject::fromJsonObject(engine, object);
+ };
+ const auto wrapJsonArray = [engine](const QJsonArray &array) {
+ return JsonObject::fromJsonArray(engine, array);
+ };
+
+ const auto wrapQDateTime = [&](const QDateTime &dateTime) {
+ return engine->newDateObject(
+ dateTime, valueTypeWrapper, index, referenceFlags(metaObject, index));
+ };
+ const auto wrapQDate = [&](QDate date) {
+ return engine->newDateObject(
+ date, valueTypeWrapper, index, referenceFlags(metaObject, index));
+ };
+ const auto wrapQTime = [&](QTime time) {
+ return engine->newDateObject(
+ time, valueTypeWrapper, index, referenceFlags(metaObject, index));
+ };
+
#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
- if (metaTypeId == metatype) { \
+ case metatype: { \
cpptype v; \
void *args[] = { &v, nullptr }; \
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), \
- QMetaObject::ReadProperty, index, args); \
+ doStaticReadCall(metaObject, valueTypeWrapper, index, args); \
return QV4::Encode(constructor(v)); \
}
- const QMetaObject *metaObject = valueTypeWrapper->metaObject();
- int index = coreIndex;
- QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
- // These four types are the most common used by the value type wrappers
- int metaTypeId = metaType.id();
+
+ QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(
+ QMetaObject::ReadProperty, &metaObject, &index);
+
+ const int metaTypeId = isEnum
+ ? metaType.underlyingType().id()
+ : (metaType.flags() & QMetaType::PointerToQObject)
+ ? QMetaType::QObjectStar
+ : metaType.id();
+
+ switch (metaTypeId) {
+ case QMetaType::UnknownType:
+ case QMetaType::Void:
+ return Encode::undefined();
+ case QMetaType::Nullptr:
+ case QMetaType::VoidStar:
+ return Encode::null();
+ VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
+ VALUE_TYPE_LOAD(QMetaType::Int, int, int);
+ VALUE_TYPE_LOAD(QMetaType::UInt, uint, uint);
+ VALUE_TYPE_LOAD(QMetaType::Long, long, double);
+ VALUE_TYPE_LOAD(QMetaType::ULong, ulong, double);
+ VALUE_TYPE_LOAD(QMetaType::LongLong, qlonglong, double);
+ VALUE_TYPE_LOAD(QMetaType::ULongLong, qulonglong, double);
VALUE_TYPE_LOAD(QMetaType::Double, double, double);
- VALUE_TYPE_LOAD(QMetaType::Float, float, float);
- VALUE_TYPE_LOAD(QMetaType::Int || isEnum, int, int);
VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString);
- VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
- QVariant v;
- void *args[] = { nullptr, nullptr };
- if (metaType == QMetaType::fromType<QVariant>()) {
- args[0] = &v;
- } else {
- v = QVariant(metaType, static_cast<void *>(nullptr));
- args[0] = v.data();
+ VALUE_TYPE_LOAD(QMetaType::QByteArray, QByteArray, engine->newArrayBuffer);
+ VALUE_TYPE_LOAD(QMetaType::Float, float, float);
+ VALUE_TYPE_LOAD(QMetaType::Short, short, int);
+ VALUE_TYPE_LOAD(QMetaType::UShort, unsigned short, int);
+ VALUE_TYPE_LOAD(QMetaType::Char, char, int);
+ VALUE_TYPE_LOAD(QMetaType::UChar, unsigned char, int);
+ VALUE_TYPE_LOAD(QMetaType::SChar, signed char, int);
+ VALUE_TYPE_LOAD(QMetaType::QChar, QChar, engine->newString);
+ VALUE_TYPE_LOAD(QMetaType::Char16, char16_t, wrapChar16);
+ VALUE_TYPE_LOAD(QMetaType::QDateTime, QDateTime, wrapQDateTime);
+ VALUE_TYPE_LOAD(QMetaType::QDate, QDate, wrapQDate);
+ VALUE_TYPE_LOAD(QMetaType::QTime, QTime, wrapQTime);
+#if QT_CONFIG(regularexpression)
+ VALUE_TYPE_LOAD(QMetaType::QRegularExpression, QRegularExpression, engine->newRegExpObject);
+#endif
+ VALUE_TYPE_LOAD(QMetaType::QObjectStar, QObject*, wrapQObject);
+ VALUE_TYPE_LOAD(QMetaType::QJsonValue, QJsonValue, wrapJsonValue);
+ VALUE_TYPE_LOAD(QMetaType::QJsonObject, QJsonObject, wrapJsonObject);
+ VALUE_TYPE_LOAD(QMetaType::QJsonArray, QJsonArray, wrapJsonArray);
+ case QMetaType::QPixmap:
+ case QMetaType::QImage: {
+ QVariant v(metaType);
+ void *args[] = { v.data(), nullptr };
+ doStaticReadCall(metaObject, valueTypeWrapper, index, args);
+ return Encode(engine->newVariantObject(metaType, v.data()));
+ }
+ case QMetaType::QVariant: {
+ QVariant v;
+ void *args[] = { &v, nullptr };
+ doStaticReadCall(metaObject, valueTypeWrapper, index, args);
+ return engine->fromVariant(
+ v, valueTypeWrapper, index,
+ referenceFlags(metaObject, index) | Heap::ReferenceObject::IsVariant);
}
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty,
- index, args);
- return engine->fromVariant(v);
+ default:
+ break;
+ }
+
+ QVariant v(metaType);
+ void *args[] = { v.data(), nullptr };
+ doStaticReadCall(metaObject, valueTypeWrapper, index, args);
+ return engine->fromVariant(v, valueTypeWrapper, index, referenceFlags(metaObject, index));
#undef VALUE_TYPE_LOAD
}
@@ -297,10 +421,26 @@ PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m,
{
if (id.isString()) {
const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
- QQmlPropertyData result = r->dataForPropertyKey(id);
- if (p && result.isValid())
- p->value = getGadgetProperty(r->engine(), r->d(), result.propType(), result.coreIndex(), result.isFunction(), result.isEnum());
- return result.isValid() ? Attr_Data : Attr_Invalid;
+ Q_ASSERT(r);
+
+ const QQmlPropertyData result = r->dataForPropertyKey(id);
+ if (!result.isValid())
+ return Attr_Invalid; // Property doesn't exist. Object shouldn't meddle with it.
+
+ if (!p)
+ return Attr_Data; // Property exists, but we're not interested in the value
+
+ if (!r->d()->isReference() || r->readReferenceValue()) {
+ // Property exists, and we can retrieve it
+ p->value = getGadgetProperty(
+ r->engine(), r->d(), result.propType(), result.coreIndex(),
+ result.isFunction(), result.isEnum());
+ } else {
+ // Property exists, but we can't retrieve it. Make it undefined.
+ p->value = Encode::undefined();
+ }
+
+ return Attr_Data;
}
return QV4::Object::virtualGetOwnProperty(m, id, p);
@@ -317,10 +457,8 @@ struct QQmlValueTypeWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) {
const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o);
- if (const QQmlValueTypeReference *ref = that->as<QQmlValueTypeReference>()) {
- if (!ref->readReferenceValue())
- return PropertyKey::invalid();
- }
+ if (that->d()->isReference() && !that->readReferenceValue())
+ return PropertyKey::invalid();
const QMetaObject *mo = that->d()->metaObject();
// We don't return methods, ie. they are not visible when iterating
@@ -352,9 +490,8 @@ OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Objec
bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
{
- if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>())
- if (!ref->readReferenceValue())
- return false;
+ if (d()->isReference() && !readReferenceValue())
+ return false;
int id1 = value.metaType().id();
QVariant v = d()->toVariant();
int id2 = v.metaType().id();
@@ -402,26 +539,26 @@ bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
int QQmlValueTypeWrapper::typeId() const
{
- return d()->valueType()->metaType.id();
+ return d()->metaType().id();
}
QMetaType QQmlValueTypeWrapper::type() const
{
- return d()->valueType()->metaType;
+ return d()->metaType();
}
bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
{
bool destructGadgetOnExit = false;
Q_ALLOCA_DECLARE(void, gadget);
- if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) {
+ if (d()->isReference()) {
if (!d()->gadgetPtr()) {
- Q_ALLOCA_ASSIGN(void, gadget, d()->valueType()->metaType.sizeOf());
+ Q_ALLOCA_ASSIGN(void, gadget, d()->metaType().sizeOf());
d()->setGadgetPtr(gadget);
- d()->valueType()->metaType.construct(d()->gadgetPtr(), nullptr);
+ d()->metaType().construct(d()->gadgetPtr(), nullptr);
destructGadgetOnExit = true;
}
- if (!ref->readReferenceValue())
+ if (!readReferenceValue())
return false;
}
@@ -431,7 +568,7 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a);
if (destructGadgetOnExit) {
- d()->valueType()->metaType.destruct(d()->gadgetPtr());
+ d()->metaType().destruct(d()->gadgetPtr());
d()->setGadgetPtr(nullptr);
}
return true;
@@ -464,14 +601,13 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
if (!w)
return b->engine()->throwTypeError();
- if (const QQmlValueTypeReference *ref = w->as<QQmlValueTypeReference>())
- if (!ref->readReferenceValue())
- RETURN_UNDEFINED();
+ if (w->d()->isReference() && !w->readReferenceValue())
+ RETURN_UNDEFINED();
QString result;
- if (!QMetaType::convert(w->d()->valueType()->metaType, w->d()->gadgetPtr(),
+ if (!QMetaType::convert(w->d()->metaType(), w->d()->gadgetPtr(),
QMetaType(QMetaType::QString), &result)) {
- result = QString::fromUtf8(w->d()->valueType()->metaType.name()) + QLatin1Char('(');
+ result = QString::fromUtf8(w->d()->metaType().name()) + QLatin1Char('(');
const QMetaObject *mo = w->d()->metaObject();
const int propCount = mo->propertyCount();
for (int i = 0; i < propCount; ++i) {
@@ -501,16 +637,14 @@ ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *obj
ScopedString name(scope, id.asStringOrSymbol());
// Note: readReferenceValue() can change the reference->type.
- if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) {
- if (!reference->readReferenceValue())
- return Value::undefinedValue().asReturnedValue();
- }
+ if (r->d()->isReference() && !r->readReferenceValue())
+ return Value::undefinedValue().asReturnedValue();
QQmlPropertyData result = r->dataForPropertyKey(id);
if (!result.isValid())
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
- lookup->qgadgetLookup.ic = r->internalClass();
+ lookup->qgadgetLookup.ic.set(engine, r->internalClass());
// & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
lookup->qgadgetLookup.metaObject = quintptr(r->d()->metaObject()) + 1;
lookup->qgadgetLookup.metaType = result.propType().iface();
@@ -540,13 +674,19 @@ ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine
if (valueTypeWrapper->metaObject() != reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1))
return revertLookup();
- if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) {
- Scope scope(engine);
- Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper);
- referenceWrapper->readReferenceValue();
- }
+ if (valueTypeWrapper->isReference() && !valueTypeWrapper->readReference())
+ return Encode::undefined();
+
+ return getGadgetProperty(
+ engine, valueTypeWrapper, QMetaType(lookup->qgadgetLookup.metaType),
+ lookup->qgadgetLookup.coreIndex, lookup->qgadgetLookup.isFunction,
+ lookup->qgadgetLookup.isEnum);
+}
- return getGadgetProperty(engine, valueTypeWrapper, QMetaType(lookup->qgadgetLookup.metaType), lookup->qgadgetLookup.coreIndex, lookup->qgadgetLookup.isFunction, lookup->qgadgetLookup.isEnum);
+bool QQmlValueTypeWrapper::lookupSetter(
+ Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+{
+ return QV4::Lookup::setterFallback(l, engine, object, value);
}
bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
@@ -566,10 +706,8 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id,
QV4::ExecutionEngine *v4 = r->engine();
// Note: readReferenceValue() can change the reference->type.
- if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) {
- if (!reference->readReferenceValue())
- return Value::undefinedValue().asReturnedValue();
- }
+ if (r->d()->isReference() && !r->readReferenceValue())
+ return Value::undefinedValue().asReturnedValue();
QQmlPropertyData result = r->dataForPropertyKey(id);
if (!result.isValid())
@@ -593,17 +731,11 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
return false;
Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m));
- Scoped<QQmlValueTypeReference> reference(scope, m->d());
-
- QMetaType writeBackPropertyType;
-
- if (reference) {
- QMetaProperty writebackProperty = reference->d()->object->metaObject()->property(reference->d()->property);
-
- if (!writebackProperty.isWritable() || !reference->readReferenceValue())
+ Heap::Object *heapObject = nullptr;
+ if (r->d()->isReference()) {
+ heapObject = r->d()->object();
+ if (!r->readReferenceValue() || !r->d()->canWriteBack())
return false;
-
- writeBackPropertyType = writebackProperty.metaType();
}
const QMetaObject *metaObject = r->d()->metaObject();
@@ -611,10 +743,18 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
if (!pd.isValid())
return false;
- if (reference) {
+ if (heapObject) {
+ QObject *referenceObject = nullptr;
QV4::ScopedFunctionObject f(scope, value);
- const QV4QPointer<QObject> &referenceObject = reference->d()->object;
- const int referencePropertyIndex = reference->d()->property;
+ const int referencePropertyIndex = r->d()->property();
+ QV4::Scoped<QV4::QObjectWrapper> o(scope, heapObject);
+ if (o) {
+ referenceObject = o->object();
+ } else {
+ QV4::Scoped<QV4::QQmlTypeWrapper> t(scope, heapObject);
+ if (t)
+ referenceObject = t->object();
+ }
if (f) {
if (!f->isBinding()) {
@@ -625,6 +765,17 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
return false;
}
+ if (!referenceObject) {
+ QString error = QStringLiteral("Cannot create binding on nested value type property");
+ ScopedString e(scope, v4->newString(error));
+ v4->throwError(e);
+ return false;
+ }
+
+ const QMetaProperty writebackProperty
+ = referenceObject->metaObject()->property(referencePropertyIndex);
+ const QMetaType writeBackPropertyType = writebackProperty.metaType();
+
QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext();
QQmlPropertyData cacheData;
@@ -644,10 +795,10 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
newBinding->setTarget(referenceObject, cacheData, &pd);
QQmlPropertyPrivate::setBinding(newBinding);
return true;
- } else {
+ } else if (referenceObject) {
if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex()))) {
- Q_ASSERT(!binding->isValueTypeProxy());
+ Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding);
const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
const auto stackFrame = v4->currentStackFrame;
qCInfo(lcBindingRemoval,
@@ -664,34 +815,27 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
QMetaProperty property = metaObject->property(pd.coreIndex());
Q_ASSERT(property.isValid());
+ if (value.isUndefined() && pd.isResettable()) {
+ property.resetOnGadget(reinterpret_cast<QObject *>(r->d()->gadgetPtr()));
+ if (heapObject)
+ r->d()->writeBack(pd.coreIndex());
+ return true;
+ }
- QVariant v = v4->toVariant(value, property.userType());
+ QVariant v = QV4::ExecutionEngine::toVariant(value, property.metaType());
if (property.isEnumType() && (QMetaType::Type)v.userType() == QMetaType::Double)
v = v.toInt();
void *gadget = r->d()->gadgetPtr();
- property.writeOnGadget(gadget, v);
-
-
- if (reference) {
- if (writeBackPropertyType == QMetaType::fromType<QVariant>()) {
- QVariant variantReferenceValue = r->d()->toVariant();
-
- int flags = 0;
- int status = -1;
- void *a[] = { &variantReferenceValue, nullptr, &status, &flags };
- QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a);
+ property.writeOnGadget(gadget, std::move(v));
- } else {
- int flags = 0;
- int status = -1;
- void *a[] = { r->d()->gadgetPtr(), nullptr, &status, &flags };
- QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a);
- }
- }
+ if (heapObject)
+ r->d()->writeBack(pd.coreIndex());
return true;
}
+} // namespace QV4
+
QT_END_NAMESPACE