diff options
Diffstat (limited to 'src/qml/qml/qqmlvaluetypewrapper.cpp')
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper.cpp | 192 |
1 files changed, 137 insertions, 55 deletions
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 721917bb6d..561b2f888d 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -17,6 +17,7 @@ #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> @@ -41,6 +42,12 @@ DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper); namespace QV4 { +Heap::QQmlValueTypeWrapper *Heap::QQmlValueTypeWrapper::detached() const +{ + return internalClass->engine->memoryManager->allocate<QV4::QQmlValueTypeWrapper>( + m_gadgetPtr, m_valueType, m_metaObject, nullptr, -1, NoFlag); +} + void Heap::QQmlValueTypeWrapper::destroy() { if (m_gadgetPtr) { @@ -50,7 +57,7 @@ void Heap::QQmlValueTypeWrapper::destroy() ReferenceObject::destroy(); } -void Heap::QQmlValueTypeWrapper::setData(const void *data) const +void Heap::QQmlValueTypeWrapper::setData(const void *data) { if (auto *gadget = gadgetPtr()) valueType()->metaType.destruct(gadget); @@ -59,12 +66,6 @@ void Heap::QQmlValueTypeWrapper::setData(const void *data) const valueType()->metaType.construct(gadgetPtr(), data); } -void Heap::QQmlValueTypeWrapper::setValue(const QVariant &value) const -{ - Q_ASSERT(valueType()->metaType.id() == value.userType()); - setData(value.constData()); -} - QVariant Heap::QQmlValueTypeWrapper::toVariant() const { Q_ASSERT(gadgetPtr()); @@ -73,6 +74,8 @@ QVariant Heap::QQmlValueTypeWrapper::toVariant() const bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant) { + Q_ASSERT(isVariant()); + const QMetaType variantReferenceType = variant.metaType(); if (variantReferenceType != valueType()->metaType) { // This is a stale VariantReference. That is, the variant has been @@ -94,7 +97,8 @@ bool Heap::QQmlValueTypeWrapper::setVariant(const QVariant &variant) return false; } } - setValue(variant); + + setData(variant.constData()); return true; } @@ -119,19 +123,19 @@ bool Heap::QQmlValueTypeWrapper::writeBack() } ReturnedValue QQmlValueTypeWrapper::create( - ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *cloneFrom, QObject *object) + ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *cloneFrom, Heap::Object *object) { - Scope scope(engine); + QV4::Scope scope(engine); initProto(engine); - Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); - r->d()->setObject(object); - r->d()->setProperty(cloneFrom->property()); - r->d()->setCanWriteBack(cloneFrom->canWriteBack()); - r->d()->setMetaObject(cloneFrom->metaObject()); - r->d()->setValueType(cloneFrom->valueType()); - r->d()->setGadgetPtr(nullptr); + // 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->valueType(), 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(); } @@ -146,24 +150,67 @@ 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::InvokeMetaMethod: + case QMetaObject::WriteProperty: + case QMetaObject::ResetProperty: + 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<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); - r->d()->setObject(object); - r->d()->setProperty(property); - r->d()->setMetaObject(metaObject); auto valueType = QQmlMetaType::valueType(type); if (!valueType) { 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, valueType, 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(); } @@ -173,17 +220,15 @@ ReturnedValue QQmlValueTypeWrapper::create( Scope scope(engine); initProto(engine); - Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>()); - r->d()->setMetaObject(metaObject); auto valueType = QQmlMetaType::valueType(type); if (!valueType) { return engine->throwTypeError(QLatin1String("Type %1 is not a value type") .arg(QString::fromUtf8(type.name()))); } - r->d()->setProperty(-1); - r->d()->setValueType(valueType); - r->d()->setGadgetPtr(nullptr); - r->d()->setData(data); + + Scoped<QQmlValueTypeWrapper> r( + scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>( + data, valueType, metaObject, nullptr, -1, Heap::ReferenceObject::NoFlag)); return r->asReturnedValue(); } @@ -244,6 +289,22 @@ bool QQmlValueTypeWrapper::virtualHasProperty(const Managed *m, PropertyKey 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) @@ -279,13 +340,14 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine, 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); + QQmlMetaObject::resolveGadgetMethodOrPropertyIndex( + QMetaObject::ReadProperty, &metaObject, &index); const int metaTypeId = isEnum ? QMetaType::Int @@ -333,24 +395,25 @@ static ReturnedValue getGadgetProperty(ExecutionEngine *engine, case QMetaType::QImage: { QVariant v(metaType); void *args[] = { v.data(), nullptr }; - metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr()), - QMetaObject::ReadProperty, index, args); - return Encode(engine->newVariantObject(metaType, v.constData())); + 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; - void *args[] = { nullptr, nullptr }; - if (metaType == QMetaType::fromType<QVariant>()) { - args[0] = &v; - } else { - v = QVariant(metaType, 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); + + 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 } @@ -611,10 +674,13 @@ ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine if (valueTypeWrapper->metaObject() != reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1)) return revertLookup(); - if (valueTypeWrapper->isReference()) - valueTypeWrapper->readReference(); + 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( @@ -665,9 +731,9 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v return false; Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m)); - QObject *referenceObject = nullptr; + Heap::Object *heapObject = nullptr; if (r->d()->isReference()) { - referenceObject = r->d()->object(); + heapObject = r->d()->object(); if (!r->readReferenceValue() || !r->d()->canWriteBack()) return false; } @@ -677,9 +743,18 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v if (!pd.isValid()) return false; - if (referenceObject) { + if (heapObject) { + QObject *referenceObject = nullptr; QV4::ScopedFunctionObject f(scope, value); 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()) { @@ -690,6 +765,13 @@ 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(); @@ -713,7 +795,7 @@ 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->kind() == QQmlAbstractBinding::QmlBinding); @@ -742,7 +824,7 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v void *gadget = r->d()->gadgetPtr(); property.writeOnGadget(gadget, v); - if (referenceObject) + if (heapObject) r->d()->writeBack(); return true; |