aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp11
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp49
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h40
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp60
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)