aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-02-15 13:04:03 +0100
committerUlf Hermann <ulf.hermann@qt.io>2023-02-22 12:35:21 +0100
commit4f7cda0c9fadecbe4c715f515ccc7535b53b28d1 (patch)
treef4aff4699fa189add86b68883981eb9a949bbde5
parentbfa301f0cb2d62b77920c0ae8d9f85ec3769f151 (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.cpp6
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp18
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/undefinedToDouble.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp28
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"