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.cpp830
1 files changed, 520 insertions, 310 deletions
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index f23921497c..5c95ee28aa 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,7 +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
@@ -62,116 +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();
- }
- QQmlQPointer<QObject> object;
- int property;
-};
-
+ return internalClass->engine->memoryManager->allocate<QV4::QQmlValueTypeWrapper>(
+ m_gadgetPtr, QMetaType(m_metaType), m_metaObject, nullptr, -1, NoFlag);
}
-struct QQmlValueTypeReference : public QQmlValueTypeWrapper
-{
- V4_OBJECT2(QQmlValueTypeReference, QQmlValueTypeWrapper)
- V4_NEEDS_DESTROY
-
- bool readReferenceValue() const;
-};
-
-}
-
-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);
}
- if (m_propertyCache)
- m_propertyCache->release();
- 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.id(), 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);
-
- int variantReferenceType = variantReferenceValue.userType();
- if (variantReferenceType != typeId()) {
- // 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 (QQmlValueTypeFactory::isValueType(variantReferenceType)) {
- QQmlPropertyCache *cache = nullptr;
- if (const QMetaObject *mo = QQmlValueTypeFactory::metaObjectForMetaType(variantReferenceType))
- cache = QJSEnginePrivate::get(engine())->cache(mo);
- if (d()->gadgetPtr()) {
- d()->valueType()->metaType.destruct(d()->gadgetPtr());
- ::operator delete(d()->gadgetPtr());
- }
- d()->setGadgetPtr(nullptr);
- d()->setPropertyCache(cache);
- d()->setValueType(QQmlValueTypeFactory::valueType(variantReferenceType));
- if (!cache)
- 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,49 +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, int typeId)
+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()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
- r->d()->setValueType(QQmlValueTypeFactory::valueType(typeId));
- r->d()->setGadgetPtr(nullptr);
+ if (!type.isValid()) {
+ return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
+ .arg(QString::fromUtf8(type.name())));
+ }
+
+ // 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, int typeId)
+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()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
- r->d()->setValueType(QQmlValueTypeFactory::valueType(typeId));
- r->d()->setGadgetPtr(nullptr);
- r->d()->setValue(value);
+ if (!type.isValid()) {
+ return engine->throwTypeError(QLatin1String("Type %1 is not a value type")
+ .arg(QString::fromUtf8(type.name())));
+ }
+
+ 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::destruct(typeId, data);
- QMetaType::construct(typeId, data, d()->gadgetPtr());
+ if (d()->isReference() && !readReferenceValue())
+ return false;
+ const QMetaType type = d()->metaType();
+ type.destruct(data);
+ type.construct(data, d()->gadgetPtr());
return true;
}
@@ -243,14 +256,191 @@ 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)
+{
+ if (isFunction) {
+ // calling a Q_INVOKABLE function of a value type
+ return QV4::QObjectMethod::create(engine, 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) \
+ case metatype: { \
+ cpptype v; \
+ void *args[] = { &v, nullptr }; \
+ doStaticReadCall(metaObject, valueTypeWrapper, index, args); \
+ return QV4::Encode(constructor(v)); \
+ }
+
+ 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::QString, QString, engine->newString);
+ 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);
+ }
+ 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
+}
+
PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
if (id.isString()) {
- Scope scope(m);
- ScopedString n(scope, id.asStringOrSymbol());
const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
- QQmlPropertyData *result = r->d()->propertyCache()->property(n.getPointer(), nullptr, nullptr);
- return result ? 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);
@@ -267,24 +457,25 @@ 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()->propertyCache()) {
- const QMetaObject *mo = that->d()->propertyCache()->createMetaObject();
- const int propertyCount = mo->propertyCount();
- if (propertyIndex < propertyCount) {
- Scope scope(that->engine());
- ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(propertyIndex).name())));
- ++propertyIndex;
- if (attrs)
- *attrs = QV4::Attr_Data;
- if (pd)
- pd->value = that->QV4::Object::get(propName);
- return propName->toPropertyKey();
+ 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
+ const int propertyCount = mo->propertyCount();
+ if (propertyIndex < propertyCount) {
+ Scope scope(that->engine());
+ QMetaProperty p = mo->property(propertyIndex); // TODO: Implement and use QBasicMetaProperty
+ ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(p.name())));
+ ++propertyIndex;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd) {
+ QQmlPropertyData data;
+ data.load(p);
+ pd->value = getGadgetProperty(that->engine(), that->d(), data.propType(), data.coreIndex(), data.isFunction(), data.isEnum());
}
+ return propName->toPropertyKey();
}
return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
@@ -299,29 +490,75 @@ OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Objec
bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
{
- if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>())
- if (!ref->readReferenceValue())
- return false;
- return (value == d()->toVariant());
+ if (d()->isReference() && !readReferenceValue())
+ return false;
+ int id1 = value.metaType().id();
+ QVariant v = d()->toVariant();
+ int id2 = v.metaType().id();
+ if (id1 != id2) {
+ // conversions for weak comparison
+ switch (id1) {
+ case QMetaType::QPoint:
+ if (id2 == QMetaType::QPointF)
+ return value.value<QPointF>() == v.value<QPointF>();
+ break;
+ case QMetaType::QPointF:
+ if (id2 == QMetaType::QPoint)
+ return value.value<QPointF>() == v.value<QPointF>();
+ break;
+ case QMetaType::QRect:
+ if (id2 == QMetaType::QRectF)
+ return value.value<QRectF>() == v.value<QRectF>();
+ break;
+ case QMetaType::QRectF:
+ if (id2 == QMetaType::QRect)
+ return value.value<QRectF>() == v.value<QRectF>();
+ break;
+ case QMetaType::QLine:
+ if (id2 == QMetaType::QLineF)
+ return value.value<QLineF>() == v.value<QLineF>();
+ break;
+ case QMetaType::QLineF:
+ if (id2 == QMetaType::QLine)
+ return value.value<QLineF>() == v.value<QLineF>();
+ break;
+ case QMetaType::QSize:
+ if (id2 == QMetaType::QSizeF)
+ return value.value<QSizeF>() == v.value<QSizeF>();
+ break;
+ case QMetaType::QSizeF:
+ if (id2 == QMetaType::QSize)
+ return value.value<QSizeF>() == v.value<QSizeF>();
+ break;
+ default:
+ break;
+ }
+ }
+ return (value == v);
}
int QQmlValueTypeWrapper::typeId() const
{
- return d()->valueType()->metaType.id();
+ return d()->metaType().id();
+}
+
+QMetaType QQmlValueTypeWrapper::type() const
+{
+ 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;
}
@@ -331,12 +568,30 @@ 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;
}
+QQmlPropertyData QQmlValueTypeWrapper::dataForPropertyKey(PropertyKey id) const
+{
+ if (!id.isStringOrSymbol())
+ return QQmlPropertyData {};
+ QByteArray name = id.asStringOrSymbol()->toQString().toUtf8();
+ const QMetaObject *mo = d()->metaObject();
+ QQmlPropertyData result;
+ QMetaMethod metaMethod = QMetaObjectPrivate::firstMethod(mo, name);
+ if (metaMethod.isValid()) {
+ result.load(metaMethod);
+ } else {
+ int propertyIndex = d()->metaObject()->indexOfProperty(name.constData());
+ if (propertyIndex >= 0)
+ result.load(mo->property(propertyIndex));
+ }
+ return result;
+}
+
ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
const Object *o = thisObject->as<Object>();
@@ -346,20 +601,14 @@ 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;
- // Prepare a buffer to pass to QMetaType::convert()
- QString convertResult;
- convertResult.~QString();
- if (QMetaType::convert(w->d()->gadgetPtr(), w->d()->valueType()->metaType.id(), &convertResult, QMetaType::QString)) {
- result = convertResult;
- } else {
- result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType()->metaType.id()))
- + QLatin1Char('(');
- const QMetaObject *mo = w->d()->propertyCache()->metaObject();
+ if (!QMetaType::convert(w->d()->metaType(), w->d()->gadgetPtr(),
+ QMetaType(QMetaType::QString), &result)) {
+ 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) {
if (mo->property(i).isDesignable()) {
@@ -374,50 +623,6 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
return Encode(b->engine()->newString(result));
}
-Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
- Heap::QQmlValueTypeWrapper *valueTypeWrapper,
- QQmlPropertyData *property)
-{
- if (property->isFunction()) {
- // calling a Q_INVOKABLE function of a value type
- return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, property->coreIndex());
- }
-
-#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
- if (property->propType() == metatype) { \
- cpptype v; \
- void *args[] = { &v, nullptr }; \
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), \
- QMetaObject::ReadProperty, index, args); \
- return QV4::Encode(constructor(v)); \
- }
-
- const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject();
-
- int index = property->coreIndex();
- QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
-
- // These four types are the most common used by the value type wrappers
- VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
- VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int);
- VALUE_TYPE_LOAD(QMetaType::Int, int, int);
- VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString);
- VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
-
- QVariant v;
- void *args[] = { nullptr, nullptr };
- if (property->propType() == QMetaType::QVariant) {
- args[0] = &v;
- } else {
- v = QVariant(property->propType(), static_cast<void *>(nullptr));
- args[0] = v.data();
- }
- metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), QMetaObject::ReadProperty,
- index, args);
- return engine->fromVariant(v);
-#undef VALUE_TYPE_LOAD
-}
-
ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine,
Lookup *lookup)
{
@@ -432,19 +637,20 @@ 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->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
- if (!result)
+ QQmlPropertyData result = r->dataForPropertyKey(id);
+ if (!result.isValid())
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
- lookup->qgadgetLookup.ic = r->internalClass();
- lookup->qgadgetLookup.propertyCache = r->d()->propertyCache();
- lookup->qgadgetLookup.propertyCache->addref();
- lookup->qgadgetLookup.propertyData = result;
+ 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();
+ lookup->qgadgetLookup.coreIndex = result.coreIndex();
+ lookup->qgadgetLookup.isFunction = result.isFunction();
+ lookup->qgadgetLookup.isEnum = result.isEnum();
lookup->getter = QQmlValueTypeWrapper::lookupGetter;
return lookup->getter(lookup, engine, *object);
}
@@ -452,8 +658,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *obj
ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
{
const auto revertLookup = [lookup, engine, &object]() {
- lookup->qgadgetLookup.propertyCache->release();
- lookup->qgadgetLookup.propertyCache = nullptr;
+ lookup->qgadgetLookup.metaObject = quintptr(0);
lookup->getter = Lookup::getterGeneric;
return Lookup::getterGeneric(lookup, engine, object);
};
@@ -466,17 +671,22 @@ ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine
Heap::QQmlValueTypeWrapper *valueTypeWrapper =
const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o));
- if (valueTypeWrapper->propertyCache() != lookup->qgadgetLookup.propertyCache)
+ 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();
- QQmlPropertyData *property = lookup->qgadgetLookup.propertyData;
- return getGadgetProperty(engine, valueTypeWrapper, property);
+ 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,
@@ -494,23 +704,19 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id,
const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
QV4::ExecutionEngine *v4 = r->engine();
- Scope scope(v4);
- 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->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
- if (!result)
+ QQmlPropertyData result = r->dataForPropertyKey(id);
+ if (!result.isValid())
return Object::virtualGet(m, id, receiver, hasProperty);
if (hasProperty)
*hasProperty = true;
- return getGadgetProperty(v4, r->d(), result);
+ return getGadgetProperty(v4, r->d(), result.propType(), result.coreIndex(), result.isFunction(), result.isEnum());
}
bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
@@ -525,30 +731,30 @@ 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());
-
- int writeBackPropertyType = -1;
-
- 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.userType();
}
- ScopedString name(scope, id.asStringOrSymbol());
-
- const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
- const QQmlPropertyData *pd = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
- if (!pd)
+ const QMetaObject *metaObject = r->d()->metaObject();
+ const QQmlPropertyData pd = r->dataForPropertyKey(id);
+ if (!pd.isValid())
return false;
- if (reference) {
+ if (heapObject) {
+ QObject *referenceObject = nullptr;
QV4::ScopedFunctionObject f(scope, value);
- const QQmlQPointer<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()) {
@@ -559,7 +765,18 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
return false;
}
- QQmlContextData *context = v4->callingQmlContext();
+ 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;
cacheData.setWritable(true);
@@ -568,64 +785,57 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
- QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
+ QV4::Scoped<JavaScriptFunctionObject> f(scope, bindingFunction->bindingFunction());
QV4::ScopedContext ctx(scope, f->scope());
QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx);
newBinding->setSourceLocation(bindingFunction->currentLocation());
if (f->isBoundFunction())
newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
newBinding->setSourceLocation(bindingFunction->currentLocation());
- newBinding->setTarget(referenceObject, cacheData, pd);
+ 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());
+ if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex()))) {
+ Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding);
const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
const auto stackFrame = v4->currentStackFrame;
qCInfo(lcBindingRemoval,
"Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(),
qPrintable(qmlBinding->expressionIdentifier()),
- metaObject->property(pd->coreIndex()).name(),
+ metaObject->property(pd.coreIndex()).name(),
qPrintable(stackFrame->source()), stackFrame->lineNumber());
}
}
- QQmlPropertyPrivate::removeBinding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()));
+ QQmlPropertyPrivate::removeBinding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex()));
}
}
- QMetaProperty property = metaObject->property(pd->coreIndex());
+ 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.type() == QMetaType::Double)
+ 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::QVariant) {
- QVariant variantReferenceValue = r->d()->toVariant();
+ property.writeOnGadget(gadget, std::move(v));
- int flags = 0;
- int status = -1;
- void *a[] = { &variantReferenceValue, nullptr, &status, &flags };
- QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a);
-
- } 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