diff options
Diffstat (limited to 'src/qml/qml/qqmlvaluetypewrapper.cpp')
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper.cpp | 624 |
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 |