From 0c029b420ea9b7631cf87d427c0821320064c01b Mon Sep 17 00:00:00 2001 From: Maximilian Goldstein Date: Tue, 9 Feb 2021 14:29:24 +0100 Subject: Fix QJSValue singletons only supporting object types Now primitives such as integers and strings should also work. Fixes: QTBUG-85615 Change-Id: I201d1844b7272ca50e32f1e33e9ac357b5e68dfe Reviewed-by: Fabian Kosmale Reviewed-by: Ulf Hermann (cherry picked from commit 64102ae231317eb6f637304918e55153dadef72d) Reviewed-by: Qt Cherry-pick Bot --- src/qml/jsruntime/qv4lookup_p.h | 5 +-- src/qml/jsruntime/qv4qmlcontext.cpp | 23 +++++++++---- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 42 ++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 6232d78a57..950e5da768 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -138,8 +138,9 @@ struct Q_QML_PRIVATE_EXPORT Lookup { int scriptIndex; } qmlContextScriptLookup; struct { - Heap::Object *singleton; - quintptr unused; + Heap::Base *singletonObject; + quintptr unused2; + QV4::ReturnedValue singletonValue; } qmlContextSingletonLookup; struct { quintptr unused1; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index ec3aa00341..5581ea9a53 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -240,15 +240,22 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) { e->singletonInstance(r.type); - lookup->qmlContextSingletonLookup.singleton = - static_cast( + lookup->qmlContextSingletonLookup.singletonObject = Value::fromReturnedValue( QQmlTypeWrapper::create(v4, nullptr, r.type) - ).heapObject()); + ).heapObject(); } else { QJSValue singleton = e->singletonInstance(r.type); - QV4::ScopedObject o(scope, QJSValuePrivate::asReturnedValue(&singleton)); - lookup->qmlContextSingletonLookup.singleton = o->d(); + + // QSrting values should already have been put on the engine heap at this point + // to manage their memory. We later assume this has already happened. + Q_ASSERT(!QJSValuePrivate::asQString(&singleton)); + + if (QV4::Value *val = QJSValuePrivate::takeManagedValue(&singleton)) { + lookup->qmlContextSingletonLookup.singletonObject = val->heapObject(); + } else { + lookup->qmlContextSingletonLookup.singletonValue = QJSValuePrivate::asReturnedValue(&singleton); + } } lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton; return lookup->qmlContextPropertyGetter(lookup, v4, base); @@ -539,7 +546,11 @@ ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *en { Q_UNUSED(engine); Q_UNUSED(base); - return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue(); + + if (l->qmlContextSingletonLookup.singletonObject != nullptr) + return l->qmlContextSingletonLookup.singletonObject->asReturnedValue(); + + return l->qmlContextSingletonLookup.singletonValue; } ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base) diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 66beb839f0..3b527a8f50 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -352,6 +352,8 @@ private slots: void invalidInlineComponent(); void warnOnInjectedParameters(); + void qtbug_85615(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -6206,6 +6208,46 @@ void tst_qqmllanguage::qtbug_86482() QCOMPARE(o->property("result").toString(), QStringLiteral("Hello world!")); } +void tst_qqmllanguage::qtbug_85615() +{ + qmlRegisterSingletonType("Test.Singleton", 1, 0, "SingletonString", [](QQmlEngine *, QJSEngine *) -> QJSValue { + return QJSValue("Test"); + }); + qmlRegisterSingletonType("Test.Singleton", 1, 0, "SingletonInt", [](QQmlEngine *, QJSEngine *) -> QJSValue { + return QJSValue(123); + }); + qmlRegisterSingletonType("Test.Singleton", 1, 0, "SingletonDouble", [](QQmlEngine *, QJSEngine *) -> QJSValue { + return QJSValue(1.23); + }); + qmlRegisterSingletonType("Test.Singleton", 1, 0, "SingletonUndefined", [](QQmlEngine *, QJSEngine *) -> QJSValue { + return QJSValue(QJSValue::UndefinedValue); + }); + qmlRegisterSingletonType("Test.Singleton", 1, 0, "SingletonNull", [](QQmlEngine *, QJSEngine *) -> QJSValue { + return QJSValue(QJSValue::NullValue); + }); + + QQmlEngine e; + QQmlComponent c(&engine); + c.setData("import QtQml 2.0\n" + "import Test.Singleton\n" + "QtObject {\n" + " property var resultString: SingletonString\n" + " property var resultInt: SingletonInt\n" + " property var resultDouble: SingletonDouble\n" + " property var resultUndefined: SingletonUndefined\n" + " property var resultNull: SingletonNull\n" + "}", QUrl()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QScopedPointer o(c.create()); + QCOMPARE(o->property("resultString").toString(), "Test"); + QCOMPARE(o->property("resultInt").toInt(), 123); + QCOMPARE(o->property("resultDouble").toDouble(), 1.23); + QVERIFY(!o->property("resultUndefined").isValid()); + QCOMPARE(o->property("resultUndefined").metaType(), QMetaType(QMetaType::UnknownType)); + QCOMPARE(o->property("resultNull").metaType(), QMetaType(QMetaType::Nullptr)); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" -- cgit v1.2.3