diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2023-02-15 13:04:03 +0100 |
|---|---|---|
| committer | Ulf Hermann <ulf.hermann@qt.io> | 2023-02-22 12:35:21 +0100 |
| commit | 4f7cda0c9fadecbe4c715f515ccc7535b53b28d1 (patch) | |
| tree | f4aff4699fa189add86b68883981eb9a949bbde5 | |
| parent | bfa301f0cb2d62b77920c0ae8d9f85ec3769f151 (diff) | |
QmlCompiler: Fix coercion of undefined to float and double
It should result in NaN, not in 0. The typedArray() test exposes that
ExecutionEngine::toVariant() also gets this wrong. Fix that, too.
[ChangeLog][QtQml][Important Behavior Changes] Converting a JavaScript
value to a double or float, for example by inserting it into a typed
array, now assumes JavaScript type coercion semantics. In particular,
converting a value that is not actually a number now results in NaN
where it previously sometimes resulted in 0.
Fixes: QTBUG-111179
Change-Id: If24444ae9014c8972761c565a6920f06699e485c
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit b9834e0ee9f086add6dd8a42e5cb40f87f91756b)
| -rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 6 | ||||
| -rw-r--r-- | src/qmlcompiler/qqmljscodegenerator.cpp | 18 | ||||
| -rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml | 6 | ||||
| -rw-r--r-- | tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp | 28 |
5 files changed, 50 insertions, 9 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index f4b96c53b9..30924dd76d 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1517,6 +1517,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet if (metaType == QMetaType::fromType<bool>()) return QVariant(value.toBoolean()); + if (metaType == QMetaType::fromType<double>()) + return QVariant(value.toNumber()); + + if (metaType == QMetaType::fromType<float>()) + return QVariant(float(value.toNumber())); + if (metaType == QMetaType::fromType<QJsonValue>()) return QVariant::fromValue(QV4::JsonObject::toJsonValue(value)); diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index 04926ca519..f8009d6ae7 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -2544,24 +2544,24 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType(); const auto boolType = m_typeResolver->boolType(); - auto zeroBoolOrNumeric = [&](const QQmlJSScope::ConstPtr &to) { + auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) { if (m_typeResolver->equals(to, boolType)) return u"false"_s; if (m_typeResolver->equals(to, m_typeResolver->intType())) return u"0"_s; - if (m_typeResolver->equals(to, m_typeResolver->floatType())) - return u"0.0f"_s; - if (m_typeResolver->equals(to, m_typeResolver->realType())) - return u"0.0"_s; return QString(); }; if (m_typeResolver->equals(from, m_typeResolver->voidType())) { if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s; - const QString zero = zeroBoolOrNumeric(to); + const QString zero = zeroBoolOrInt(to); if (!zero.isEmpty()) return zero; + if (m_typeResolver->equals(to, m_typeResolver->floatType())) + return u"std::numeric_limits<float>::quiet_NaN()"_s; + if (m_typeResolver->equals(to, m_typeResolver->realType())) + return u"std::numeric_limits<double>::quiet_NaN()"_s; if (m_typeResolver->equals(to, m_typeResolver->stringType())) return QQmlJSUtils::toLiteral(u"undefined"_s); if (m_typeResolver->equals(from, to)) @@ -2579,9 +2579,13 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s; if (m_typeResolver->equals(to, varType)) return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s; - const QString zero = zeroBoolOrNumeric(to); + const QString zero = zeroBoolOrInt(to); if (!zero.isEmpty()) return zero; + if (m_typeResolver->equals(to, m_typeResolver->floatType())) + return u"0.0f"_s; + if (m_typeResolver->equals(to, m_typeResolver->realType())) + return u"0.0"_s; if (m_typeResolver->equals(to, m_typeResolver->stringType())) return QQmlJSUtils::toLiteral(u"null"_s); if (m_typeResolver->equals(from, to)) diff --git a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt index 86a1b5ea78..4d38c3d1d5 100644 --- a/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt +++ b/tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt @@ -157,6 +157,7 @@ set(qml_files typePropertyClash.qml typedArray.qml undefinedResets.qml + undefinedToDouble.qml unknownAttached.qml unknownParameter.qml unstoredUndefined.qml diff --git a/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml b/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml new file mode 100644 index 0000000000..e76443a2e0 --- /dev/null +++ b/tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml @@ -0,0 +1,6 @@ +pragma Strict +import QtQml + +QtObject { + property double d: Math.max(undefined, 40) +} diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp index aa0b8d94f6..469e13e9be 100644 --- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp +++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp @@ -141,6 +141,7 @@ private slots: void enumConversion(); void enumProblems(); void storeElementSideEffects(); + void undefinedToDouble(); }; void tst_QmlCppCodegen::simpleBinding() @@ -1965,8 +1966,19 @@ void tst_QmlCppCodegen::typedArray() QList<int>({1, 2, 3, 4})); QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")), QList<QDateTime>({date, date, date})); - QCOMPARE(qvariant_cast<QList<double>>(o->property("values5")), - QList<double>({1, 2, 3.4, 30, 0, 0})); + { + const QList<double> actual + = qvariant_cast<QList<double>>(o->property("values5")); + const QList<double> expected + = QList<double>({1, 2, 3.4, 30, std::numeric_limits<double>::quiet_NaN(), 0}); + QCOMPARE(actual.size(), expected.size()); + for (qsizetype i = 0, end = actual.size(); i != end; ++i) { + if (std::isnan(expected[i])) + QVERIFY(std::isnan(actual[i])); + else + QCOMPARE(actual[i], expected[i]); + } + } date = QDateTime::currentDateTime(); o->setProperty("aDate", date); QCOMPARE(qvariant_cast<QList<QDateTime>>(o->property("values4")), @@ -2557,6 +2569,18 @@ void tst_QmlCppCodegen::storeElementSideEffects() QCOMPARE(prop.property(0).toInt(), 10); }; +void tst_QmlCppCodegen::undefinedToDouble() +{ + QQmlEngine engine; + QQmlComponent c(&engine, QUrl(u"qrc:/TestTypes/undefinedToDouble.qml"_s)); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + const QVariant d = o->property("d"); + QCOMPARE(d.metaType(), QMetaType::fromType<double>()); + QVERIFY(std::isnan(d.toDouble())); +} + QTEST_MAIN(tst_QmlCppCodegen) #include "tst_qmlcppcodegen.moc" |
