diff options
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 11 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper.cpp | 49 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper_p.h | 40 | ||||
-rw-r--r-- | tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp | 60 |
4 files changed, 112 insertions, 48 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 5940729b78..578c789575 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -2211,20 +2211,29 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * d()->ensureMethodsCache(); + Scope scope(v4); QQmlObjectOrGadget object(d()->object()); + QV4::Scoped<QQmlValueTypeReference> valueTypeReference(scope); if (!d()->object()) { if (!d()->valueTypeWrapper) return Encode::undefined(); + valueTypeReference = d()->valueTypeWrapper.get(); object = QQmlObjectOrGadget(d()->metaObject(), d()->valueTypeWrapper->gadgetPtr()); } - Scope scope(v4); JSCallData cData(thisObject, argv, argc); CallData *callData = cData.callData(scope); auto method = d()->methods[0]; + // If we call the method, we have to write back any value type references afterwards. + // The method might change the value. + auto guard = qScopeGuard([&valueTypeReference]() { + if (valueTypeReference) + valueTypeReference->d()->writeBack(); + }); + if (method.isV4Function()) { QV4::ScopedValue rv(scope, QV4::Value::undefinedValue()); QQmlV4Function func(callData, rv, v4); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index b3c073a3f0..5d34a593a5 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -64,36 +64,6 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval) DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper); - -namespace QV4 { -namespace Heap { - -struct QQmlValueTypeReference : QQmlValueTypeWrapper -{ - 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; -}; - -} - DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeReference); using namespace QV4; @@ -693,23 +663,8 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v 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); - - } 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 (reference) + reference->d()->writeBack(); return true; } diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index 0eb1088a64..ed58157c26 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -111,6 +111,38 @@ private: const QMetaObject *m_metaObject; }; +struct QQmlValueTypeReference : QQmlValueTypeWrapper +{ + void init() { + QQmlValueTypeWrapper::init(); + object.init(); + } + void destroy() { + object.destroy(); + QQmlValueTypeWrapper::destroy(); + } + + void writeBack() { + const QMetaProperty writebackProperty = object->metaObject()->property(property); + if (!writebackProperty.isWritable()) + return; + + int flags = 0; + int status = -1; + if (writebackProperty.metaType() == QMetaType::fromType<QVariant>()) { + QVariant variantReferenceValue = toVariant(); + void *a[] = { &variantReferenceValue, nullptr, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, property, a); + } else { + void *a[] = { gadgetPtr(), nullptr, &status, &flags }; + QMetaObject::metacall(object, QMetaObject::WriteProperty, property, a); + } + } + + QV4QPointer<QObject> object; + int property; +}; + } struct Q_QML_EXPORT QQmlValueTypeWrapper : Object @@ -149,6 +181,14 @@ public: static void initProto(ExecutionEngine *v4); }; +struct QQmlValueTypeReference : public QQmlValueTypeWrapper +{ + V4_OBJECT2(QQmlValueTypeReference, QQmlValueTypeWrapper) + V4_NEEDS_DESTROY + + bool readReferenceValue() const; +}; + } QT_END_NAMESPACE diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index 47387ad837..bc21238c7b 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -100,6 +100,7 @@ private slots: void scarceTypes(); void nonValueTypes(); void char16Type(); + void writeBackOnFunctionCall(); private: QQmlEngine engine; @@ -1862,6 +1863,65 @@ void tst_qqmlvaluetypes::char16Type() QCOMPARE(scoped->toQString(), "a"); } +struct Foo { + Q_GADGET + QML_ANONYMOUS +public: + int val = 1; + Q_INVOKABLE int value() { return val; } + Q_INVOKABLE void setValue(int v) { val = v; } +}; + +Q_DECLARE_METATYPE(Foo); + +class S : public QObject +{ + Q_OBJECT + Q_PROPERTY(Foo foo READ foo WRITE setFoo NOTIFY fooChanged); + QML_ELEMENT +public: + Foo f; + Foo foo() { return f; } + void setFoo(Foo f) + { + this->f = f; + emit fooChanged(); + } + Q_INVOKABLE Foo get() { return f; } +signals: + void fooChanged(); +}; + +void tst_qqmlvaluetypes::writeBackOnFunctionCall() +{ + qmlRegisterTypesAndRevisions<Foo>("WriteBack", 1); + qmlRegisterTypesAndRevisions<S>("WriteBack", 1); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQml 2.15\n" + "import WriteBack 1.0\n" + "QtObject {\n" + " property S s: S {}\n" + " property int a: -1\n" + " property int b: -1\n" + " Component.onCompleted: {\n" + " var f = s.foo\n" + " f.setValue(3)\n" + " s.foo = f\n" + " a = f.value()\n" + " f = s.get()\n" + " f.setValue(3)\n" + " b = f.value()\n" + " }\n" + "}\n", QUrl()); + QVERIFY2(component.isReady(), component.errorString().toUtf8()); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + QCOMPARE(o->property("a").toInt(), 3); + QCOMPARE(o->property("b").toInt(), 3); +} + #undef CHECK_TYPE_IS_NOT_VALUETYPE QTEST_MAIN(tst_qqmlvaluetypes) |