QmlCompiler: Handle various date and time conversions correctly

We can coerce QDateTime, QDate and QTime into each other because they
would all be represented by a Date object in JavaScript. Furthermore we
can coerce them all to QString. Technically, we could also coerce
strings to all of them, but we don't want to because that is terrible.

Fixes: QTBUG-109380
Change-Id: I176bfb5b715a6a6750cb5918c44261fa23fb8832
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
This commit is contained in:
Ulf Hermann 2023-01-12 10:06:35 +01:00
parent 5bc63de881
commit bda7b2a444
8 changed files with 114 additions and 0 deletions

View File

@ -3100,6 +3100,25 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from,
return variable + u".toUtf8()"_s;
}
for (const auto &originType : {
m_typeResolver->dateTimeType(),
m_typeResolver->dateType(),
m_typeResolver->timeType()}) {
if (m_typeResolver->equals(from, originType)) {
for (const auto &targetType : {
m_typeResolver->dateTimeType(),
m_typeResolver->dateType(),
m_typeResolver->timeType(),
m_typeResolver->stringType()}) {
if (m_typeResolver->equals(to, targetType)) {
return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
originType->internalName(), targetType->internalName(), variable);
}
}
break;
}
}
const auto retrieveFromPrimitive = [&](
const QQmlJSScope::ConstPtr &type, const QString &expression) -> QString
{

View File

@ -752,6 +752,7 @@ QQmlJSAotFunction QQmlJSAotCompiler::globalCode() const
u"QtQml/qqmllist.h"_s,
u"QtCore/qdatetime.h"_s,
u"QtCore/qtimezone.h"_s,
u"QtCore/qobject.h"_s,
u"QtCore/qstring.h"_s,
u"QtCore/qstringlist.h"_s,

View File

@ -36,6 +36,8 @@ QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
m_byteArrayType = builtinTypes.type(u"QByteArray"_s).scope;
m_urlType = builtinTypes.type(u"QUrl"_s).scope;
m_dateTimeType = builtinTypes.type(u"QDateTime"_s).scope;
m_dateType = builtinTypes.type(u"QDate"_s).scope;
m_timeType = builtinTypes.type(u"QTime"_s).scope;
m_variantListType = builtinTypes.type(u"QVariantList"_s).scope;
m_varType = builtinTypes.type(u"QVariant"_s).scope;
m_jsValueType = builtinTypes.type(u"QJSValue"_s).scope;
@ -710,6 +712,7 @@ QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
if (isPrimitive(type) || equals(type, m_jsValueType)
|| equals(type, m_urlType) || equals(type, m_dateTimeType)
|| equals(type, m_dateType) || equals(type, m_timeType)
|| equals(type, m_variantListType) || equals(type, m_varType)
|| equals(type, m_stringListType) || equals(type, m_emptyListType)
|| equals(type, m_byteArrayType)) {
@ -948,6 +951,18 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
if (equals(from, m_stringType) && equals(to, m_dateTimeType))
return true;
for (const auto &originType : {m_dateTimeType, m_dateType, m_timeType}) {
if (!equals(from, originType))
continue;
for (const auto &targetType : {m_dateTimeType, m_dateType, m_timeType, m_stringType}) {
if (equals(to, targetType))
return true;
}
break;;
}
if (equals(from, m_nullType)
&& to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
return true;

View File

@ -53,6 +53,8 @@ public:
QQmlJSScope::ConstPtr byteArrayType() const { return m_byteArrayType; }
QQmlJSScope::ConstPtr urlType() const { return m_urlType; }
QQmlJSScope::ConstPtr dateTimeType() const { return m_dateTimeType; }
QQmlJSScope::ConstPtr dateType() const { return m_dateType; }
QQmlJSScope::ConstPtr timeType() const { return m_timeType; }
QQmlJSScope::ConstPtr variantListType() const { return m_variantListType; }
QQmlJSScope::ConstPtr varType() const { return m_varType; }
QQmlJSScope::ConstPtr jsValueType() const { return m_jsValueType; }
@ -195,6 +197,8 @@ protected:
QQmlJSScope::ConstPtr m_byteArrayType;
QQmlJSScope::ConstPtr m_urlType;
QQmlJSScope::ConstPtr m_dateTimeType;
QQmlJSScope::ConstPtr m_dateType;
QQmlJSScope::ConstPtr m_timeType;
QQmlJSScope::ConstPtr m_variantListType;
QQmlJSScope::ConstPtr m_varType;
QQmlJSScope::ConstPtr m_jsValueType;

View File

@ -79,6 +79,7 @@ set(qml_files
conversions2.qml
curlygrouped.qml
cycleHead.qml
dateConversions.qml
deadShoeSize.qml
deadStoreLoop.qml
dialog.qml

View File

@ -0,0 +1,25 @@
pragma Strict
import QtQml
import TestTypes
QtObject {
property date date: Druggeljug.myDate
property date time: Druggeljug.myTime
property string dateString: date
property string timeString: time
function shuffle() {
Druggeljug.myDate = date;
Druggeljug.myTime = time;
dateString = Druggeljug.myDate;
timeString = Druggeljug.myTime;
}
function fool() {
var tmp = Druggeljug.myTime;
Druggeljug.myTime = Druggeljug.myDate;
Druggeljug.myDate = tmp;
}
}

View File

@ -2,6 +2,7 @@
#define DRUGGELJUG_H
#include <QtCore/qobject.h>
#include <QtCore/qdatetime.h>
#include <qqmlregistration.h>
#define STORE_FUNCTION(type, name, member, signal) \
@ -29,6 +30,9 @@ class Druggeljug : public QObject
Q_PROPERTY(qint64 myInt64 MEMBER m_myInt64 NOTIFY myInt64Changed FINAL)
Q_PROPERTY(quint64 myUint64 MEMBER m_myUint64 NOTIFY myUint64Changed FINAL)
Q_PROPERTY(QTime myTime MEMBER m_myTime NOTIFY myTimeChanged)
Q_PROPERTY(QDate myDate MEMBER m_myDate NOTIFY myDateChanged)
public:
Druggeljug(QObject* parent = nullptr) : QObject(parent) {}
@ -43,6 +47,9 @@ public:
STORE_FUNCTION(qint64, storeMyInt64, m_myInt64, myInt64Changed)
STORE_FUNCTION(quint64, storeMyUint64, m_myUint64, myUint64Changed)
QTime myTime() const { return m_myTime; }
QDate myDate() const { return m_myDate; }
private:
int m_myInt = 0;
uint m_myUint = 0;
@ -55,6 +62,9 @@ private:
qint64 m_myInt64 = 0;
quint64 m_myUint64 = 0;
QTime m_myTime = QTime(11, 55, 0);
QDate m_myDate = QDate(2017, 9, 3);
signals:
void myIntChanged(int);
void myUintChanged(uint);
@ -66,6 +76,8 @@ signals:
void myUint32Changed(quint32);
void myInt64Changed(qint64);
void myUint64Changed(quint64);
void myTimeChanged();
void myDateChanged();
};
#endif

View File

@ -1,5 +1,6 @@
// Copyright (C) 2021 The Qt Company Ltd.
#include "data/druggeljug.h"
#include <data/birthdayparty.h>
#include <data/cppbaseclass.h>
#include <data/enumproblems.h>
@ -160,6 +161,7 @@ private slots:
void infinitiesToInt();
void equalityVarAndNonStorable();
void equalityQObjects();
void dateConversions();
};
void tst_QmlCppCodegen::initTestCase()
@ -3088,6 +3090,41 @@ void tst_QmlCppCodegen::equalityQObjects()
QVERIFY(object->property("compareObjectWithNullObject").toBool());
}
void tst_QmlCppCodegen::dateConversions()
{
QQmlEngine engine;
QQmlComponent c(&engine, QUrl(u"qrc:/qt/qml/TestTypes/dateConversions.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
Druggeljug *ref = engine.singletonInstance<Druggeljug *>("TestTypes", "Druggeljug");
const QDateTime refDate = engine.coerceValue<QDate, QDateTime>(ref->myDate());
const QDateTime refTime = engine.coerceValue<QTime, QDateTime>(ref->myTime());
QCOMPARE(o->property("date").value<QDateTime>(), refDate);
QCOMPARE(o->property("time").value<QDateTime>(), refTime);
QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDateTime, QString>(refDate)));
QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QDateTime, QString>(refTime)));
QMetaObject::invokeMethod(o.data(), "shuffle");
QCOMPARE(ref->myDate(), (engine.coerceValue<QDateTime, QDate>(refDate)));
QCOMPARE(ref->myTime(), (engine.coerceValue<QDateTime, QTime>(refTime)));
const QDate date = ref->myDate();
const QTime time = ref->myTime();
QCOMPARE(o->property("dateString").toString(), (engine.coerceValue<QDate, QString>(date)));
QCOMPARE(o->property("timeString").toString(), (engine.coerceValue<QTime, QString>(time)));
QMetaObject::invokeMethod(o.data(), "fool");
QCOMPARE(ref->myDate(), (engine.coerceValue<QTime, QDate>(time)));
QCOMPARE(ref->myTime(), (engine.coerceValue<QDate, QTime>(date)));
}
QTEST_MAIN(tst_QmlCppCodegen)
#include "tst_qmlcppcodegen.moc"