From 42aa740df72af0c14f19a498f779d130e8303e54 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 10 Dec 2019 09:00:11 +0100 Subject: [PATCH 01/12] rcc: Fix namespace handling for initializer rcc currently always writes the namespace mangling macros in both the initializer constructor and destructor. This patch add the missing handling of the --namespace option for that part of the generated code. [ChangeLog][Tools][rcc] rcc now generates correct code when using the --namespace option. Change-Id: I7e5e608eb0ad267d11d601fc69c1a87d3f655a6e Fixes: QTBUG-80649 Reviewed-by: hjk --- src/tools/rcc/rcc.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp index 9acbce25ff9..7185219d342 100644 --- a/src/tools/rcc/rcc.cpp +++ b/src/tools/rcc/rcc.cpp @@ -1478,13 +1478,19 @@ bool RCCResourceLibrary::writeInitializer() writeString(" return 1;\n"); writeString("}\n\n"); - writeByteArray( - "namespace {\n" - " struct initializer {\n" - " initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources + ")(); }\n" - " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources + ")(); }\n" - " } dummy;\n" - "}\n"); + + writeString("namespace {\n" + " struct initializer {\n"); + + if (m_useNameSpace) { + writeByteArray(" initializer() { QT_RCC_MANGLE_NAMESPACE(" + initResources + ")(); }\n" + " ~initializer() { QT_RCC_MANGLE_NAMESPACE(" + cleanResources + ")(); }\n"); + } else { + writeByteArray(" initializer() { " + initResources + "(); }\n" + " ~initializer() { " + cleanResources + "(); }\n"); + } + writeString(" } dummy;\n" + "}\n"); } else if (m_format == Binary) { int i = 4; From c5777ad81cf395dfebbe46c629caca2b933d8008 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Wed, 27 Nov 2019 14:52:32 +0100 Subject: [PATCH 02/12] Fix calls to qtFlattenResources from outside resources.prf The xml_escape function must be part of resources_functions.prf, and the qmake_immediate resource must not be created multiple times. Instead, create another qmake_immediate resource with a number suffix. This commit amends 577b6554. Task-number: QTBUG-79672 Change-Id: Ibbe20c0fd1940f1fe7733cd1e5b0891f65689782 Reviewed-by: Oliver Wolff Reviewed-by: Kai Koehne --- mkspecs/features/resources.prf | 10 ---------- mkspecs/features/resources_functions.prf | 25 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/mkspecs/features/resources.prf b/mkspecs/features/resources.prf index ca95768de87..cee0981db01 100644 --- a/mkspecs/features/resources.prf +++ b/mkspecs/features/resources.prf @@ -6,16 +6,6 @@ isEmpty(QMAKE_MOD_RCC):QMAKE_MOD_RCC = qrc !contains(QMAKE_RESOURCE_FLAGS, -root):!isEmpty(QMAKE_RESOURCE_ROOT):QMAKE_RESOURCE_FLAGS += -root $$QMAKE_RESOURCE_ROOT !contains(QMAKE_RESOURCE_FLAGS, -name): QMAKE_RESOURCE_FLAGS += -name ${QMAKE_FILE_BASE} -# http://www.w3.org/TR/xml/#syntax -defineReplace(xml_escape) { - 1 ~= s,&,&, - 1 ~= s,\',', - 1 ~= s,\",", - 1 ~= s,<,<, - 1 ~= s,>,>, - return($$1) -} - load(resources_functions) qtFlattenResources() diff --git a/mkspecs/features/resources_functions.prf b/mkspecs/features/resources_functions.prf index f7fed9e524b..b7a15fd1d95 100644 --- a/mkspecs/features/resources_functions.prf +++ b/mkspecs/features/resources_functions.prf @@ -1,5 +1,21 @@ +# http://www.w3.org/TR/xml/#syntax +defineReplace(xml_escape) { + 1 ~= s,&,&, + 1 ~= s,\',', + 1 ~= s,\",", + 1 ~= s,<,<, + 1 ~= s,>,>, + return($$1) +} + defineTest(qtFlattenResources) { - RESOURCES += qmake_immediate + immediate = qmake_immediate$$QMAKE_RESOURCES_IMMEDIATE_NR + defined(QMAKE_RESOURCES_IMMEDIATE_NR, var): \ + QMAKE_RESOURCES_IMMEDIATE_NR = $$num_add($$QMAKE_RESOURCES_IMMEDIATE_NR, 1) + else: \ + QMAKE_RESOURCES_IMMEDIATE_NR = 1 + + RESOURCES += $$immediate for(resource, RESOURCES) { # Regular case of user qrc file contains(resource, ".*\\.qrc$"): \ @@ -7,10 +23,10 @@ defineTest(qtFlattenResources) { # Fallback for stand-alone files/directories !defined($${resource}.files, var) { - !equals(resource, qmake_immediate) { + !equals(resource, $$immediate) { !exists($$absolute_path($$resource, $$_PRO_FILE_PWD_)): \ warning("Failure to find: $$resource") - qmake_immediate.files += $$resource + $${immediate}.files += $$resource OTHER_FILES *= $$resource } RESOURCES -= $$resource @@ -56,8 +72,9 @@ defineTest(qtFlattenResources) { RESOURCES -= $$resource RESOURCES += $$resource_file } + export(QMAKE_RESOURCES_IMMEDIATE_NR) export(RESOURCES) export(OTHER_FILES) - export(qmake_immediate.files) + export($${immediate}.files) return(true) } From bf65c277892f6f322fa689c06d81ba9b1d9a8038 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Mon, 2 Dec 2019 16:15:14 +0100 Subject: [PATCH 03/12] Fix more mis-handling of spaces in ISO date format strings ISO date format doesn't allow spaces within a date, although 3339 does allow a space to replace the T between date and time. Sixteen tests added to check this all failed. So clean up the handling of spaces in the parsing of ISO date-time strings. [ChangeLog][QtCore][QDateTime] ISO 8601: parsing of dates now requires a punctuator as separator (it previously allowed any non-digit; officially only a dash should be allowed) and parsing of date-times no longer tolerates spaces in the numeric fields: an internal space is only allowed in an ISO 8601 date-time as replacement for the T between date and time. Change-Id: I24d110e71d416ecef74e196d5ee270b59d1bd813 Reviewed-by: Thiago Macieira --- src/corelib/time/qdatetime.cpp | 104 ++++++++++++------ .../corelib/time/qdatetime/tst_qdatetime.cpp | 38 +++++++ 2 files changed, 107 insertions(+), 35 deletions(-) diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 0d8aaabd2ea..a8d643d4839 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -1626,6 +1626,29 @@ qint64 QDate::daysTo(const QDate &d) const */ #if QT_CONFIG(datestring) +namespace { + +struct ParsedInt { int value = 0; bool ok = false; }; + +/* + /internal + + Read an int that must be the whole text. QStringRef::toInt() will ignore + spaces happily; but ISO date format should not. +*/ +ParsedInt readInt(QStringView text) +{ + ParsedInt result; + for (const auto &ch : text) { + if (ch.isSpace()) + return result; + } + result.value = QLocale::c().toInt(text, &result.ok); + return result; +} + +} + /*! Returns the QDate represented by the \a string, using the \a format given, or an invalid date if the string cannot be @@ -1677,17 +1700,18 @@ QDate QDate::fromString(const QString &string, Qt::DateFormat format) return QDate(year, month, day); } #endif // textdate - case Qt::ISODate: { - // Semi-strict parsing, must be long enough and have non-numeric separators - if (string.size() < 10 || string.at(4).isDigit() || string.at(7).isDigit() - || (string.size() > 10 && string.at(10).isDigit())) { - return QDate(); - } - const int year = string.midRef(0, 4).toInt(); - if (year <= 0 || year > 9999) - return QDate(); - return QDate(year, string.midRef(5, 2).toInt(), string.midRef(8, 2).toInt()); + case Qt::ISODate: + // Semi-strict parsing, must be long enough and have punctuators as separators + if (string.size() >= 10 && string.at(4).isPunct() && string.at(7).isPunct() + && (string.size() == 10 || !string.at(10).isDigit())) { + QStringView view(string); + const ParsedInt year = readInt(view.mid(0, 4)); + const ParsedInt month = readInt(view.mid(5, 2)); + const ParsedInt day = readInt(view.mid(8, 2)); + if (year.ok && year.value > 0 && year.value <= 9999 && month.ok && day.ok) + return QDate(year.value, month.value, day.value); } + break; } return QDate(); } @@ -2331,17 +2355,15 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool * *isMidnight24 = false; const int size = string.size(); - if (size < 5) + if (size < 5 || string.at(2) != QLatin1Char(':')) return QTime(); - const QLocale C(QLocale::c()); - bool ok = false; - int hour = C.toInt(string.mid(0, 2), &ok); - if (!ok) - return QTime(); - const int minute = C.toInt(string.mid(3, 2), &ok); - if (!ok) + ParsedInt hour = readInt(string.mid(0, 2)); + ParsedInt minute = readInt(string.mid(3, 2)); + if (!hour.ok || !minute.ok) return QTime(); + // FIXME: ISO 8601 allows [,.]\d+ after hour, just as it does after minute + int second = 0; int msec = 0; @@ -2361,44 +2383,56 @@ static QTime fromIsoTimeString(QStringView string, Qt::DateFormat format, bool * // will then be rounded up AND clamped to 999. const QStringView minuteFractionStr = string.mid(6, qMin(qsizetype(5), string.size() - 6)); - const long minuteFractionInt = C.toLong(minuteFractionStr, &ok); - if (!ok) + const ParsedInt parsed = readInt(minuteFractionStr); + if (!parsed.ok) return QTime(); - const float minuteFraction = double(minuteFractionInt) / (std::pow(double(10), minuteFractionStr.size())); + const float secondWithMs + = double(parsed.value) * 60 / (std::pow(double(10), minuteFractionStr.size())); - const float secondWithMs = minuteFraction * 60; - const float secondNoMs = std::floor(secondWithMs); - const float secondFraction = secondWithMs - secondNoMs; - second = secondNoMs; + second = std::floor(secondWithMs); + const float secondFraction = secondWithMs - second; msec = qMin(qRound(secondFraction * 1000.0), 999); - } else { + } else if (string.at(5) == QLatin1Char(':')) { // HH:mm:ss or HH:mm:ss.zzz - second = C.toInt(string.mid(6, qMin(qsizetype(2), string.size() - 6)), &ok); - if (!ok) + const ParsedInt parsed = readInt(string.mid(6, qMin(qsizetype(2), string.size() - 6))); + if (!parsed.ok) return QTime(); - if (size > 8 && (string.at(8) == QLatin1Char(',') || string.at(8) == QLatin1Char('.'))) { + second = parsed.value; + if (size <= 8) { + // No fractional part to read + } else if (string.at(8) == QLatin1Char(',') || string.at(8) == QLatin1Char('.')) { QStringView msecStr(string.mid(9, qMin(qsizetype(4), string.size() - 9))); - // toInt() ignores leading spaces, so catch them before calling it + bool ok = true; + // Can't use readInt() here, as we *do* allow trailing space - but not leading: if (!msecStr.isEmpty() && !msecStr.at(0).isDigit()) return QTime(); - // We do, however, want to ignore *trailing* spaces. msecStr = msecStr.trimmed(); - int msecInt = msecStr.isEmpty() ? 0 : C.toInt(msecStr, &ok); + int msecInt = msecStr.isEmpty() ? 0 : QLocale::c().toInt(msecStr, &ok); if (!ok) return QTime(); const double secondFraction(msecInt / (std::pow(double(10), msecStr.size()))); msec = qMin(qRound(secondFraction * 1000.0), 999); + } else { +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) // behavior change + // Stray cruft after date-time: tolerate trailing space, but nothing else. + for (const auto &ch : string.mid(8)) { + if (!ch.isSpace()) + return QTime(); + } +#endif } + } else { + return QTime(); } const bool isISODate = format == Qt::ISODate || format == Qt::ISODateWithMs; - if (isISODate && hour == 24 && minute == 0 && second == 0 && msec == 0) { + if (isISODate && hour.value == 24 && minute.value == 0 && second == 0 && msec == 0) { if (isMidnight24) *isMidnight24 = true; - hour = 0; + hour.value = 0; } - return QTime(hour, minute, second, msec); + return QTime(hour.value, minute.value, second, msec); } /*! diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index 77785427362..c03d1125603 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -2213,8 +2213,46 @@ void tst_QDateTime::fromStringDateFormat_data() QTest::newRow("trailing space") // QTBUG-80445 << QString("2000-01-02 03:04:05.678 ") << Qt::ISODate << QDateTime(QDate(2000, 1, 2), QTime(3, 4, 5, 678)); + + // Invalid spaces (but keeping field widths correct): QTest::newRow("space before millis") << QString("2000-01-02 03:04:05. 678") << Qt::ISODate << QDateTime(); + QTest::newRow("space after seconds") + << QString("2000-01-02 03:04:5 .678") << Qt::ISODate << QDateTime(); + QTest::newRow("space before seconds") + << QString("2000-01-02 03:04: 5.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space after minutes") + << QString("2000-01-02 03:4 :05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space before minutes") + << QString("2000-01-02 03: 4:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space after hour") + << QString("2000-01-02 3 :04:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space before hour") + << QString("2000-01-02 3:04:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space after day") + << QString("2000-01-2 03:04:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space before day") + << QString("2000-01- 2 03:04:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space after month") + << QString("2000-1 -02 03:04:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space before month") + << QString("2000- 1-02 03:04:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("space after year") + << QString("200 -01-02 03:04:05.678") << Qt::ISODate << QDateTime(); + + // Spaces as separators: + QTest::newRow("sec-milli space") + << QString("2000-01-02 03:04:05 678") << Qt::ISODate + // Should be invalid, but we ignore trailing cruft (in some cases) + << QDateTime(QDate(2000, 1, 2), QTime(3, 4, 5)); + QTest::newRow("min-sec space") + << QString("2000-01-02 03:04 05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("hour-min space") + << QString("2000-01-02 03 04:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("mon-day space") + << QString("2000-01 02 03:04:05.678") << Qt::ISODate << QDateTime(); + QTest::newRow("year-mon space") + << QString("2000 01-02 03:04:05.678") << Qt::ISODate << QDateTime(); // Normal usage: QTest::newRow("ISO +01:00") << QString::fromLatin1("1987-02-13T13:24:51+01:00") From 93d261b9650e3d1eb9347310fe8fc1f18e54e328 Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Mon, 26 Aug 2019 18:18:34 +1000 Subject: [PATCH 04/12] wasm: enforce emscripten version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This creates a define in the binary, as well as writing emscripten version for qmake. It also enforces app builder to use a certain known version. Task-number: QTBUG-77745 Change-Id: I37691512171635cec66aa3ffa16258081f3f1e1b Reviewed-by: Morten Johan Sørvig Reviewed-by: Edward Welbourne --- configure.json | 4 ++++ configure.pri | 20 ++++++++++++++++++++ mkspecs/features/wasm/default_pre.prf | 25 +++++++++++++++++++++++++ mkspecs/features/wasm/wasm.prf | 5 +++++ 4 files changed, 54 insertions(+) create mode 100644 mkspecs/features/wasm/default_pre.prf diff --git a/configure.json b/configure.json index 159fdf12fa4..23ef7625a6a 100644 --- a/configure.json +++ b/configure.json @@ -1453,6 +1453,10 @@ Configure with '-qreal float' to create a build that is binary-compatible with 5 "type": "error", "condition": "!features.stl", "message": "Qt requires a compliant STL library." + }, + { + "type": "emccVersion", + "condition": "config.wasm" } ], diff --git a/configure.pri b/configure.pri index 2b8d20d6074..557b8c2809e 100644 --- a/configure.pri +++ b/configure.pri @@ -1224,6 +1224,12 @@ defineReplace(qtConfOutputPostProcess_publicPro) { "QT_RELEASE_DATE = $$config.input.qt_release_date" } + wasm: { + qt_emcc_version = $$qtSystemEmccVersion() + output += \ + "QT_EMCC_VERSION = $$qt_emcc_version" + } + return($$output) } @@ -1256,6 +1262,12 @@ defineReplace(qtConfOutputPostProcess_publicHeader) { !isEmpty(config.input.qt_libinfix): \ output += "$${LITERAL_HASH}define QT_LIBINFIX \"$$eval(config.input.qt_libinfix)\"" + wasm: { + qt_emcc_version = $$qtSystemEmccVersion() +output += \ + "$${LITERAL_HASH}define QT_EMCC_VERSION \"$$qt_emcc_version\"" + } + return($$output) } @@ -1338,6 +1350,14 @@ defineTest(qtConfReport_buildMode) { qtConfReportPadded($$1, $$build_mode) } +defineTest(qtConfReport_emccVersion) { + EMCC_VERSION = $$qtSystemEmccVersion() + REQ_VERSION = $$qtEmccRecommendedVersion() + !equals(EMCC_VERSION, $$REQ_VERSION) { + qtConfAddReport("You should use the recommended Emscripten version $$REQ_VERSION with this Qt. You have $$EMCC_VERSION $$QT_EMCC_VERSION") + } +} + # ensure pristine environment for configuration discard_from($$[QT_HOST_DATA/get]/mkspecs/qconfig.pri) discard_from($$[QT_HOST_DATA/get]/mkspecs/qmodule.pri) diff --git a/mkspecs/features/wasm/default_pre.prf b/mkspecs/features/wasm/default_pre.prf new file mode 100644 index 00000000000..982ef0498fe --- /dev/null +++ b/mkspecs/features/wasm/default_pre.prf @@ -0,0 +1,25 @@ +load(default_pre) + +defineReplace(qtEmccRecommendedVersion) { + return (1.38.27) +} + +defineReplace(qtSystemEmccVersion) { + E_VERSION = $$system("emcc -v 2>&1 | perl -alne $$shell_quote($_ = $F[9]; s/://; print;) ") + return ($${E_VERSION}) +} + +defineTest(qtConfTest_emccVersion) { + + REQ_VERSION = $$qtEmccRecommendedVersion() + EMCC_VERSION = $$qtSystemEmccVersion() + + !defined(QT_EMCC_VERSION, var):!equals(EMCC_VERSION, $${REQ_VERSION}) { + warning ("You should use the recommended Emscripten version $$REQ_VERSION with this Qt. You have $${EMCC_VERSION} ") + } + contains(TEMPLATE, .*app) { + !equals(QT_EMCC_VERSION, $$EMCC_VERSION) { + warning("This Qt was built with Emscripten version $${QT_EMCC_VERSION}. You have $${EMCC_VERSION}. The difference may cause issues.") + } + } +} diff --git a/mkspecs/features/wasm/wasm.prf b/mkspecs/features/wasm/wasm.prf index 5773e580104..2e886fc4a5f 100644 --- a/mkspecs/features/wasm/wasm.prf +++ b/mkspecs/features/wasm/wasm.prf @@ -1,8 +1,11 @@ + # DESTDIR will be empty if not set in the app .pro file; make sure it has a value isEmpty(DESTDIR): DESTDIR = $$OUT_PWD exists($$QMAKE_QT_CONFIG) { +## this may be subject to change + qtConfig(thread) { EMCC_THREAD_LFLAGS += -s USE_PTHREADS=1 @@ -109,6 +112,8 @@ contains(TEMPLATE, .*app) { } } +qtConfTest_emccVersion() + # Pass --source-map-base on the linker line. This informs the # browser where to find the source files when debugging. WASM_SOURCE_MAP_BASE = http://localhost:8000/ From 382f1a221b353892844dc7b1495741c4669955e7 Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Thu, 31 Oct 2019 12:26:00 +1000 Subject: [PATCH 05/12] wasm: fix getOpenFileContent doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The callback should be named the same as the function expects Change-Id: I4ca73958313c93c0d68e7205d8641c4104247e0c Reviewed-by: Morten Johan Sørvig --- src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp b/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp index 7ccd827a04c..4b4fb869f9d 100644 --- a/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp +++ b/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp @@ -146,13 +146,13 @@ dialog.exec(); //! [14] //! [15] -auto fileOpenCompleted = [](const QString &fileName, const QByteArray &fileContent) { +auto fileContentReady = [](const QString &fileName, const QByteArray &fileContent) { if (fileName.isEmpty()) { // No file was selected } else { // Use fileName and fileContent } -} +}; QFileDialog::getOpenFileContent("Images (*.png *.xpm *.jpg)", fileContentReady); //! [15] From 6ffedc07a77a8e3d46851b74cf9456d5abf6f612 Mon Sep 17 00:00:00 2001 From: Robert Szefner Date: Sat, 7 Dec 2019 00:05:47 +0100 Subject: [PATCH 06/12] PSQL: Optimize QPSQLResult::data() function for date and time types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minor performance optimalizations: - No need to check if the date and time are correct because the QDate, QTime and QDateTime parsing functions already perform these checks - No need to add minute part to the UTC offset before parsing the date, because the QDateTime class can parse time zone offset both in form ±hh:mm and ±hh Change-Id: Id74b7ae075135c5c8cf420247c49b5f12fe88899 Reviewed-by: Edward Welbourne --- src/plugins/sqldrivers/psql/qsql_psql.cpp | 34 ++++++----------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/plugins/sqldrivers/psql/qsql_psql.cpp b/src/plugins/sqldrivers/psql/qsql_psql.cpp index 78ede6a0d31..e0f82eee730 100644 --- a/src/plugins/sqldrivers/psql/qsql_psql.cpp +++ b/src/plugins/sqldrivers/psql/qsql_psql.cpp @@ -692,40 +692,24 @@ QVariant QPSQLResult::data(int i) return dbl; } case QVariant::Date: - if (val[0] == '\0') { - return QVariant(QDate()); - } else { #if QT_CONFIG(datestring) - return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate)); + return QVariant(QDate::fromString(QString::fromLatin1(val), Qt::ISODate)); #else - return QVariant(QString::fromLatin1(val)); + return QVariant(QString::fromLatin1(val)); #endif - } - case QVariant::Time: { - const QString str = QString::fromLatin1(val); + case QVariant::Time: #if QT_CONFIG(datestring) - if (str.isEmpty()) - return QVariant(QTime()); - else - return QVariant(QTime::fromString(str, Qt::ISODate)); + return QVariant(QTime::fromString(QString::fromLatin1(val), Qt::ISODate)); #else - return QVariant(str); + return QVariant(QString::fromLatin1(val)); #endif - } - case QVariant::DateTime: { - QString dtval = QString::fromLatin1(val); + case QVariant::DateTime: #if QT_CONFIG(datestring) - if (dtval.length() < 10) { - return QVariant(QDateTime()); - } else { - QChar sign = dtval[dtval.size() - 3]; - if (sign == QLatin1Char('-') || sign == QLatin1Char('+')) dtval += QLatin1String(":00"); - return QVariant(QDateTime::fromString(dtval, Qt::ISODate).toLocalTime()); - } + return QVariant(QDateTime::fromString(QString::fromLatin1(val), + Qt::ISODate).toLocalTime()); #else - return QVariant(dtval); + return QVariant(QString::fromLatin1(val)); #endif - } case QVariant::ByteArray: { size_t len; unsigned char *data = PQunescapeBytea((const unsigned char*)val, &len); From 6bef90f3cfb886d74ac9ed38efeb8d80b7181011 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 26 Nov 2019 07:27:14 -0800 Subject: [PATCH 07/12] QTest::toString(QBitArray): fix Mismatched free() / delete / delete [] ==8015== Mismatched free() / delete / delete [] ==8015== at 0x483958B: operator delete[](void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==8015== by 0x48752D6: QTestResult::compare(bool, char const*, char*, char*, char const*, char const*, char const*, int) (qtestresult.cpp:356) ==8015== Address 0x602eb30 is 0 bytes inside a block of size 12 alloc'd ==8015== at 0x483777F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==8015== by 0x44AAE2: char* QTest::toString(QBitArray const&) (qtest.h:98) ==8015== by 0x44D212: bool QTest::qCompare(QBitArray const&, QBitArray const&, char const*, char const*, char const*, int) (qtestcase.h:352) Change-Id: Ia2aa807ffa8a4c798425fffd15dabfebfd63fdbd Reviewed-by: Giuseppe D'Angelo Reviewed-by: Edward Welbourne --- src/testlib/qtest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index 27fe08e8f44..3b1ffb389e4 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -95,7 +95,7 @@ template<> inline char *toString(const QByteArray &ba) template<> inline char *toString(const QBitArray &ba) { qsizetype size = ba.size(); - char *str = static_cast(malloc(size + 1)); + char *str = new char[size + 1]; for (qsizetype i = 0; i < size; ++i) str[i] = "01"[ba.testBit(i)]; str[size] = '\0'; From a77fdc9847bcf730862f262aac0a2c4e921f4411 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 8 Dec 2019 15:14:49 -0800 Subject: [PATCH 08/12] Doc: remove the claim that zero timers execute after GUI events This ties our hands on what we can do in our implementations. I don't care if you've depended on this in your code. It was wrong. Fixes: QTBUG-80600 Change-Id: I568dea4813b448fe9ba6fffd15de8865a27f0a35 Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/kernel/qtimer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp index 948f697dc59..4d8778ecf56 100644 --- a/src/corelib/kernel/qtimer.cpp +++ b/src/corelib/kernel/qtimer.cpp @@ -84,10 +84,10 @@ QT_BEGIN_NAMESPACE must start and stop the timer in its thread; it is not possible to start a timer from another thread. - As a special case, a QTimer with a timeout of 0 will time out as - soon as all the events in the window system's event queue have - been processed. This can be used to do heavy work while providing - a snappy user interface: + As a special case, a QTimer with a timeout of 0 will time out as soon as + possible, though the ordering between zero timers and other sources of + events is unspecified. Zero timers can be used to do some work while still + providing a snappy user interface: \snippet timers/timers.cpp 4 \snippet timers/timers.cpp 5 From a7108ec6cfb6411e40a4012f3e6d3b5d5fb9631d Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 11 Dec 2019 10:51:22 +0100 Subject: [PATCH 09/12] Fix CVE-2019-19244 in SQLite Fixes: QTBUG-80635 Change-Id: I718349e28ec76ea164dd50f2a985f2074dd6bdbd Reviewed-by: Jesus Fernandez --- .../0001-Fix-CVE-2019-19244-in-SQLite.patch | 26 +++++++++++++++++++ src/3rdparty/sqlite/sqlite3.c | 1 + 2 files changed, 27 insertions(+) create mode 100644 src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-19244-in-SQLite.patch diff --git a/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-19244-in-SQLite.patch b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-19244-in-SQLite.patch new file mode 100644 index 00000000000..99062928609 --- /dev/null +++ b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-19244-in-SQLite.patch @@ -0,0 +1,26 @@ +From 676425e522e08eb0e7dfaacdac79a5de27542322 Mon Sep 17 00:00:00 2001 +From: Andy Shaw +Date: Wed, 11 Dec 2019 10:51:22 +0100 +Subject: [PATCH 53/53] Fix CVE-2019-19244 in SQLite + +Fixes: QTBUG-80635 +Change-Id: I718349e28ec76ea164dd50f2a985f2074dd6bdbd +--- + src/3rdparty/sqlite/sqlite3.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c +index 8fd740b300..bd647ca1c2 100644 +--- a/src/3rdparty/sqlite/sqlite3.c ++++ b/src/3rdparty/sqlite/sqlite3.c +@@ -131679,6 +131679,7 @@ SQLITE_PRIVATE int sqlite3Select( + */ + if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct + && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 ++ && p->pWin==0 + ){ + p->selFlags &= ~SF_Distinct; + pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); +-- +2.21.0 (Apple Git-122.2) + diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c index 8fd740b3001..bd647ca1c27 100644 --- a/src/3rdparty/sqlite/sqlite3.c +++ b/src/3rdparty/sqlite/sqlite3.c @@ -131679,6 +131679,7 @@ SQLITE_PRIVATE int sqlite3Select( */ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 + && p->pWin==0 ){ p->selFlags &= ~SF_Distinct; pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); From 07838840e86a5fb7f81520bb2a31d16f80e51e4c Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sat, 30 Nov 2019 15:06:20 +0100 Subject: [PATCH 10/12] Doc/SQL: update sql driver creation instructions Update the instructions on how to build and distribute the mysql and postgresql drivers on windows. Change-Id: Ie4d50c1c34820680d7496b9544eb00fcee17f8e7 Reviewed-by: Andy Shaw --- src/plugins/sqldrivers/README | 4 +- .../doc/snippets/code/doc_src_sql-driver.qdoc | 57 ++++++++++++++++-- src/sql/doc/src/sql-driver.qdoc | 58 ++++++++++++++----- 3 files changed, 95 insertions(+), 24 deletions(-) diff --git a/src/plugins/sqldrivers/README b/src/plugins/sqldrivers/README index 7000fb5613e..26418c5e36d 100644 --- a/src/plugins/sqldrivers/README +++ b/src/plugins/sqldrivers/README @@ -1,5 +1,5 @@ -Please note that the DB2, Oracle and TDS client drivers are not distributed -with the Qt Open Source Editions. +Please note that the DB2, MySQL, Oracle and TDS client drivers are not +distributed with the Qt Open Source Editions. This is because the client libraries are distributed under a license which is not compatible with the GPL license. diff --git a/src/sql/doc/snippets/code/doc_src_sql-driver.qdoc b/src/sql/doc/snippets/code/doc_src_sql-driver.qdoc index 9709deeccbb..12a39d80b2e 100644 --- a/src/sql/doc/snippets/code/doc_src_sql-driver.qdoc +++ b/src/sql/doc/snippets/code/doc_src_sql-driver.qdoc @@ -70,7 +70,6 @@ BEGIN END //! [1] - //! [3] cd $QTDIR/qtbase/src/plugins/sqldrivers qmake -- MYSQL_PREFIX=/usr/local @@ -86,14 +85,15 @@ make install //! [5] cd %QTDIR%\qtbase\src\plugins\sqldrivers -qmake -- MYSQL_INCDIR=C:/MySQL/include "MYSQL_LIBDIR=C:/MYSQL/MySQL Server /lib/opt" +qmake -- MYSQL_INCDIR="C:/Program Files/MySQL/MySQL Connector C 6.1/include" MYSQL_LIBDIR="C:/Program Files/MySQL/MySQL Connector C 6.1/lib" nmake sub-mysql +nmake install //! [5] //! [6] cd $QTDIR/qtbase/src/plugins/sqldrivers -qmake -- "OCI_INCDIR=$ORACLE_HOME/rdbms/public" OCI_LIBDIR=$ORACLE_HOME/lib "OCI_LIBS=-lclntsh -lwtc9" +qmake -- OCI_INCDIR="$ORACLE_HOME/rdbms/public" OCI_LIBDIR="$ORACLE_HOME/lib" OCI_LIBS="-lclntsh -lwtc9" make sub-oci //! [6] @@ -142,6 +142,7 @@ make sub-psql cd %QTDIR%\qtbase\src\plugins\sqldrivers qmake -- PSQL_INCDIR=C:/psql/include PSQL_LIBDIR=C:/psql/lib/ms nmake sub-psql +nmake install //! [15] @@ -156,6 +157,7 @@ make sub-tds cd %QTDIR%\qtbase\src\plugins\sqldrivers qmake nmake sub-tds +nmake install //! [17] @@ -168,8 +170,9 @@ make sub-db2 //! [20] cd %QTDIR%\qtbase\src\plugins\sqldrivers -qmake -- "DB2_PREFIX=/sqllib" +qmake -- DB2_PREFIX="/sqllib" nmake sub-db2 +nmake install //! [20] @@ -184,6 +187,7 @@ make sub-sqlite cd %QTDIR%\qtbase\src\plugins\sqldrivers qmake -- -system-sqlite SQLITE3_PREFIX=C:/SQLITE nmake sub-sqlite +nmake install //! [23] @@ -205,6 +209,7 @@ make sub-ibase cd %QTDIR%\qtbase\src\plugins\sqldrivers qmake -- IBASE_INCDIR=C:/interbase/include nmake sub-ibase +nmake install //! [29] @@ -212,17 +217,18 @@ nmake sub-ibase cd %QTDIR%\qtbase\src\plugins\sqldrivers qmake -- IBASE_INCDIR=C:/interbase/include IBASE_LIBS=-lfbclient nmake sub-ibase +nmake install //! [30] //! [32] -configure OCI_INCDIR=/usr/include/oracle/10.1.0.3/client OCI_LIBDIR=/usr/lib/oracle/10.1.0.3/client/lib -R /usr/lib/oracle/10.1.0.3/client/lib "OCI_LIBS=-lclntsh -lnnz10" +configure OCI_INCDIR=/usr/include/oracle/10.1.0.3/client OCI_LIBDIR=/usr/lib/oracle/10.1.0.3/client/lib -R /usr/lib/oracle/10.1.0.3/client/lib OCI_LIBS="-lclntsh -lnnz10" make //! [32] //! [33] cd $QTDIR/qtbase/src/plugins/sqldrivers -qmake -- OCI_INCDIR=/usr/include/oracle/10.1.0.3/client OCI_LIBDIR=/usr/lib/oracle/10.1.0.3/client/lib "OCI_LIBS=-Wl,-rpath,/usr/lib/oracle/10.1.0.3/client/lib -lclntsh -lnnz10" +qmake -- OCI_INCDIR=/usr/include/oracle/10.1.0.3/client OCI_LIBDIR=/usr/lib/oracle/10.1.0.3/client/lib OCI_LIBS="-Wl,-rpath,/usr/lib/oracle/10.1.0.3/client/lib -lclntsh -lnnz10" make sub-oci //! [33] @@ -250,3 +256,42 @@ q.exec(QString("CREATE TABLE %1 (id INTEGER)").arg(tableString)); // Call toLower() on the string so that it can be matched QSqlRecord rec = database.record(tableString.toLower()); //! [40] + +//! [41] +C:\Qt5\5.13.2\Src\qtbase\src\plugins\sqldrivers>qmake -version +QMake version 3.1 +Using Qt version 5.13.2 in C:/Qt5/5.13.2/mingw73_64/lib +C:\Qt5\5.13.2\Src\qtbase\src\plugins\sqldrivers>qmake -- MYSQL_INCDIR="C:/Program Files/MySQL/MySQL Connector C 6.1/include" MYSQL_LIBDIR="C:/Program Files/MySQL/MySQL Connector C 6.1/lib" +Info: creating stash file C:\Qt5\5.13.2\Src\qtbase\src\plugins\sqldrivers\.qmake.stash + +Running configuration tests... +Checking for DB2 (IBM)... no +Checking for InterBase... no +Checking for MySQL... yes +Checking for OCI (Oracle)... no +Checking for ODBC... yes +Checking for PostgreSQL... no +Checking for SQLite (version 2)... no +Checking for TDS (Sybase)... no +Done running configuration tests. + +Configure summary: + +Qt Sql Drivers: + DB2 (IBM) .............................. no + InterBase .............................. no + MySql .................................. yes + OCI (Oracle) ........................... no + ODBC ................................... yes + PostgreSQL ............................. no + SQLite2 ................................ no + SQLite ................................. yes + Using system provided SQLite ......... no + TDS (Sybase) ........................... no + +Qt is now configured for building. Just run 'mingw32-make'. +Once everything is built, you must run 'mingw32-make install'. +Qt will be installed into 'C:\Qt5\5.13.2\mingw73_64'. + +Prior to reconfiguration, make sure you remove any leftovers from the previous build. +//! [41] diff --git a/src/sql/doc/src/sql-driver.qdoc b/src/sql/doc/src/sql-driver.qdoc index c6ac4d17ff3..9c26c4089cf 100644 --- a/src/sql/doc/src/sql-driver.qdoc +++ b/src/sql/doc/src/sql-driver.qdoc @@ -48,7 +48,7 @@ \header \li Driver name \li DBMS \row \li \l{#QDB2}{QDB2} \li IBM DB2 (version 7.1 and above) \row \li \l{#QIBASE}{QIBASE} \li Borland InterBase - \row \li \l{#QMYSQL}{QMYSQL} \li MySQL + \row \li \l{#QMYSQL}{QMYSQL} \li MySQL (version 5.0 and above) \row \li \l{#QOCI}{QOCI} \li Oracle Call Interface Driver \row \li \l{#QODBC}{QODBC} \li Open Database Connectivity (ODBC) - Microsoft SQL Server and other @@ -70,7 +70,8 @@ access to the API exposed by the DBMS, and is typically shipped with it. Most installation programs also allow you to install "development libraries", and these are what you need. These libraries are responsible - for the low-level communication with the DBMS. + for the low-level communication with the DBMS. Also make sure to install + the correct database libraries for your Qt architecture (32 or 64 bit). \note When using Qt under Open Source terms but with a proprietary database, verify the client library's license compatibility with @@ -91,11 +92,21 @@ may be necessary to specify these paths using the \c *_INCDIR=, \c *_LIBDIR=, or \c *_PREFIX= command-line options. For example, if your MySQL files are installed in \c /usr/local/mysql (or in - \c{C:\mysql} on Windows), then pass the following parameter to - configure: \c MYSQL_PREFIX=/usr/local/mysql - (or \c{MYSQL_PREFIX=C:\mysql} for Windows). + \c{C:/Program Files/MySQL/MySQL Connector C 6.1} on Windows), then pass the + following parameter to configure: \c MYSQL_PREFIX=/usr/local/mysql + (or \c{MYSQL_PREFIX="C:/Program Files/MySQL/MySQL Connector C 6.1"} for Windows). The particulars for each driver are explained below. + If something goes wrong and you want qmake to recheck your + available drivers, you must remove \e{config.cache} in + \e{/qtbase/src/plugins/sqldrivers} - otherwise qmake will not + search for the available drivers again. If you encounter an error during + the qmake stage, open \e{config.log} to see what went wrong. + + A typical qmake run (in this case to configure for MySQL) looks like this: + + \snippet code/doc_src_sql-driver.qdoc 41 + Due to the practicalities of dealing with external dependencies, only the SQLite3 plugin is shipped with binary builds of Qt. To be able to add additional drivers to the Qt installation @@ -112,11 +123,11 @@ \section1 Driver Specifics \target QMYSQL - \section2 QMYSQL for MySQL 4 and higher + \section2 QMYSQL for MySQL 5 and higher \section3 QMYSQL Stored Procedure Support - MySQL 5 introduces stored procedure support at the SQL level, but no + MySQL 5 has stored procedure support at the SQL level, but no API to control IN, OUT, and INOUT parameters. Therefore, parameters have to be set and read using SQL commands instead of QSqlQuery::bindValue(). @@ -159,16 +170,32 @@ \section3 How to Build the QMYSQL Plugin on Windows - You need to get the MySQL installation files. Run \c SETUP.EXE and - choose "Custom Install". Install the "Libs & Include Files" Module. - Build the plugin as follows (here it is assumed that MySQL is - installed in \c{C:\MySQL}): + You need to get the MySQL installation files (e.g. + \e{mysql-installer-web-community-8.0.18.0.msi}). Run the installer, + select custom installation and install the MySQL C Connector + which matches your Qt installation (x86 or x64). + After installation make sure that the needed files are there: + \list + \li \c {/lib/libmysql.lib} + \li \c {/lib/libmysql.dll} + \li \c {/include/mysql.h} + \endlist + + Build the plugin as follows (here it is assumed that the MySQL + C Connector is installed in + \c{C:/Program Files/MySQL/MySQL Connector C 6.1}): \snippet code/doc_src_sql-driver.qdoc 5 If you are not using a Microsoft compiler, replace \c nmake with \c mingw32-make in the line above. + When you distribute your application, remember to include libmysql.dll + in your installation package. It must be placed in the same folder + as the application executable. libmysql.dll additionally needs the + MSVC runtime libraries which can be installed with vcredist.exe + (\l {https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads}(vcredist.exe) + \target QOCI \section2 QOCI for the Oracle Call Interface (OCI) @@ -398,11 +425,6 @@ \snippet code/doc_src_sql-driver.qdoc 40 - \section3 QPSQL BLOB Support - - Binary Large Objects are supported through the \c BYTEA field type in - PostgreSQL server versions >= 7.1. - \section3 QPSQL Forward-only query support To use forward-only queries, you must build the QPSQL plugin with @@ -463,6 +485,10 @@ Users of MinGW may wish to consult the following online document: \l{http://www.postgresql.org/docs/current/static/installation-platform-notes.html#INSTALLATION-NOTES-MINGW}{PostgreSQL MinGW/Native Windows}. + When you distribute your application, remember to include libpq.dll + in your installation package. It must be placed in the same folder + as the application executable. + \target QTDS \section2 QTDS for Sybase Adaptive Server From c15d6a155c6121bad68a3ec60be62181fb92d0f8 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 10 Dec 2019 14:55:14 +0100 Subject: [PATCH 11/12] QVariant: introduce ShouldDeleteVariantData flag This flag is used in QSequentialIterable and QAssociativeIterable to indicate that the data pointer in VariantData should be deleted after the variant has been constructed. The use case for this is https://codereview.qt-project.org/c/qt/qtdeclarative/+/284151, where we have a proxy iterator and cannot easily return a pointer to already owned data, as it is hard to manage its lifetime in the iterator. In contrast, it is clear that we can release the memory in the QSequentialIterable functions, as it has already been copied into the QVariant there. Change-Id: I2b33497d991cd4f752153e0ebda767b82e4bb851 Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/kernel/qvariant.cpp | 35 +++---- src/corelib/kernel/qvariant_p.h | 5 + .../corelib/kernel/qvariant/tst_qvariant.cpp | 97 ++++++++++++++++++- 3 files changed, 117 insertions(+), 20 deletions(-) diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 84ad555f343..a1e1c71d12a 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -4520,15 +4520,24 @@ QSequentialIterable::const_iterator QSequentialIterable::end() const return it; } +static const QVariant variantFromVariantDataHelper(const QtMetaTypePrivate::VariantData &d) { + QVariant v; + if (d.metaTypeId == qMetaTypeId()) + v = *reinterpret_cast(d.data); + else + v = QVariant(d.metaTypeId, d.data, d.flags & ~QVariantConstructionFlags::ShouldDeleteVariantData); + if (d.flags & QVariantConstructionFlags::ShouldDeleteVariantData) + QMetaType::destroy(d.metaTypeId, const_cast(d.data)); + return v; +} + /*! Returns the element at position \a idx in the container. */ QVariant QSequentialIterable::at(int idx) const { const QtMetaTypePrivate::VariantData d = m_impl.at(idx); - if (d.metaTypeId == qMetaTypeId()) - return *reinterpret_cast(d.data); - return QVariant(d.metaTypeId, d.data, d.flags); + return variantFromVariantDataHelper(d); } /*! @@ -4605,9 +4614,7 @@ QSequentialIterable::const_iterator::operator=(const const_iterator &other) const QVariant QSequentialIterable::const_iterator::operator*() const { const QtMetaTypePrivate::VariantData d = m_impl.getCurrent(); - if (d.metaTypeId == qMetaTypeId()) - return *reinterpret_cast(d.data); - return QVariant(d.metaTypeId, d.data, d.flags); + return variantFromVariantDataHelper(d); } /*! @@ -4939,10 +4946,7 @@ QAssociativeIterable::const_iterator::operator=(const const_iterator &other) const QVariant QAssociativeIterable::const_iterator::operator*() const { const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue(); - QVariant v(d.metaTypeId, d.data, d.flags); - if (d.metaTypeId == qMetaTypeId()) - return *reinterpret_cast(d.data); - return v; + return variantFromVariantDataHelper(d); } /*! @@ -4951,10 +4955,7 @@ const QVariant QAssociativeIterable::const_iterator::operator*() const const QVariant QAssociativeIterable::const_iterator::key() const { const QtMetaTypePrivate::VariantData d = m_impl.getCurrentKey(); - QVariant v(d.metaTypeId, d.data, d.flags); - if (d.metaTypeId == qMetaTypeId()) - return *reinterpret_cast(d.data); - return v; + return variantFromVariantDataHelper(d); } /*! @@ -4962,11 +4963,7 @@ const QVariant QAssociativeIterable::const_iterator::key() const */ const QVariant QAssociativeIterable::const_iterator::value() const { - const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue(); - QVariant v(d.metaTypeId, d.data, d.flags); - if (d.metaTypeId == qMetaTypeId()) - return *reinterpret_cast(d.data); - return v; + return operator*(); } /*! diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 3d87beac835..b8b63b5e6fd 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -105,6 +105,11 @@ inline T *v_cast(QVariant::Private *d, T * = nullptr) #endif +enum QVariantConstructionFlags : uint { + Default = 0x0, + PointerType = 0x1, + ShouldDeleteVariantData = 0x2 // only used in Q*Iterable +}; //a simple template that avoids to allocate 2 memory chunks when creating a QVariant template class QVariantPrivateSharedEx : public QVariant::PrivateShared diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index edb15a8db22..e7198711286 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -276,7 +277,8 @@ private slots: void nullConvert(); void accessSequentialContainerKey(); - + void shouldDeleteVariantDataWorksForSequential(); + void shouldDeleteVariantDataWorksForAssociative(); void fromStdVariant(); void qt4UuidDataStream(); @@ -4990,6 +4992,99 @@ void tst_QVariant::accessSequentialContainerKey() QCOMPARE(nameResult, QStringLiteral("Seven")); } +void tst_QVariant::shouldDeleteVariantDataWorksForSequential() +{ + QCOMPARE(instanceCount, 0); + { + QtMetaTypePrivate::QSequentialIterableImpl iterator {}; + iterator._iteratorCapabilities = QtMetaTypePrivate::RandomAccessCapability | + QtMetaTypePrivate::BiDirectionalCapability | + QtMetaTypePrivate::ForwardCapability; + iterator._metaType_flags = QVariantConstructionFlags::ShouldDeleteVariantData; + + iterator._size = [](const void *) {return 1;}; + iterator._metaType_id = qMetaTypeId(); + iterator._moveToBegin = [](const void *, void **) {}; + iterator._moveToEnd = [](const void *, void **) {}; + iterator._advance = [](void **, int) {}; + iterator._destroyIter = [](void **){}; + iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/}; + iterator._destroyIter = [](void **){}; + iterator._at = [](const void *, int ) -> void const * { + MyType mytype {1, "eins"}; + return QMetaType::create(qMetaTypeId(), &mytype); + }; + iterator._get = [](void * const *, int, uint) -> QtMetaTypePrivate::VariantData { + MyType mytype {2, "zwei"}; + return {qMetaTypeId(), QMetaType::create(qMetaTypeId(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData}; + }; + QSequentialIterable iterable {iterator}; + QVariant value1 = iterable.at(0); + QVERIFY(value1.canConvert()); + QCOMPARE(value1.value().number, 1); + QVariant value2 = *iterable.begin(); + QVERIFY(value2.canConvert()); + QCOMPARE(value2.value().number, 2); + } + QCOMPARE(instanceCount, 0); +} + +void tst_QVariant::shouldDeleteVariantDataWorksForAssociative() +{ + QCOMPARE(instanceCount, 0); + { + QtMetaTypePrivate::QAssociativeIterableImpl iterator {}; + iterator._metaType_flags_key = QVariantConstructionFlags::ShouldDeleteVariantData; + iterator._metaType_flags_value = QVariantConstructionFlags::ShouldDeleteVariantData; + + iterator._size = [](const void *) {return 1;}; + iterator._metaType_id_value = qMetaTypeId(); + iterator._metaType_id_key = qMetaTypeId(); + iterator._begin = [](const void *, void **) {}; + iterator._end = [](const void *, void **) {}; + iterator._advance = [](void **, int) {}; + iterator._destroyIter = [](void **){}; + iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/}; + iterator._destroyIter = [](void **){}; + iterator._find = [](const void *, const void *, void **iterator ) -> void { + (*iterator) = reinterpret_cast(quintptr(42)); + }; + iterator._getKey = [](void * const *iterator, int, uint) -> QtMetaTypePrivate::VariantData { + MyType mytype {1, "key"}; + if (reinterpret_cast(*iterator) == 42) { + mytype.number = 42; + mytype.text = "find_key"; + } + return {qMetaTypeId(), QMetaType::create(qMetaTypeId(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData}; + }; + iterator._getValue = [](void * const *iterator, int, uint) -> QtMetaTypePrivate::VariantData { + MyType mytype {2, "value"}; + if (reinterpret_cast(*iterator) == 42) { + mytype.number = 42; + mytype.text = "find_value"; + } + return {qMetaTypeId(), QMetaType::create(qMetaTypeId(), &mytype), QVariantConstructionFlags::ShouldDeleteVariantData}; + }; + QAssociativeIterable iterable {iterator}; + auto it = iterable.begin(); + QVariant value1 = it.key(); + QVERIFY(value1.canConvert()); + QCOMPARE(value1.value().number, 1); + QCOMPARE(value1.value().text, "key"); + QVariant value2 = it.value(); + QVERIFY(value2.canConvert()); + QCOMPARE(value2.value().number, 2); + auto findIt = iterable.find(QVariant::fromValue(MyType {})); + value1 = findIt.key(); + QCOMPARE(value1.value().number, 42); + QCOMPARE(value1.value().text, "find_key"); + value2 = findIt.value(); + QCOMPARE(value2.value().number, 42); + QCOMPARE(value2.value().text, "find_value"); + } + QCOMPARE(instanceCount, 0); +} + void tst_QVariant::fromStdVariant() { #if __has_include() && __cplusplus >= 201703L From 3162345670ffe67a71a62abaeda0d8eb8ad0682e Mon Sep 17 00:00:00 2001 From: Michael Dippold Date: Wed, 4 Dec 2019 14:48:00 -0800 Subject: [PATCH 12/12] Support both qrc and qml files for qmlimportscanner Some projects can be configured to have both qrcFiles and qml-root-path included in the deployment settings file. The addition to qrc scanning prevented the qml root directory from being scanned. Change-Id: Idadb62f5572be45d0083294440bdb29740c2c47e Reviewed-by: Andy Shaw --- src/tools/androiddeployqt/main.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 550ed0832f7..6fd32c2d29b 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -1717,17 +1717,18 @@ bool scanImports(Options *options, QSet *usedDependencies) qmlImportScanner += QLatin1String(" -qrcFiles"); for (const QString &qrcFile : options->qrcFiles) qmlImportScanner += QLatin1Char(' ') + shellQuote(qrcFile); - } else { - if (rootPath.isEmpty()) - rootPath = QFileInfo(options->inputFileName).absolutePath(); - else - rootPath = QFileInfo(rootPath).absoluteFilePath(); - - if (!rootPath.endsWith(QLatin1Char('/'))) - rootPath += QLatin1Char('/'); - qmlImportScanner += QLatin1String(" -rootPath %1").arg(shellQuote(rootPath)); } + if (rootPath.isEmpty()) + rootPath = QFileInfo(options->inputFileName).absolutePath(); + else + rootPath = QFileInfo(rootPath).absoluteFilePath(); + + if (!rootPath.endsWith(QLatin1Char('/'))) + rootPath += QLatin1Char('/'); + + qmlImportScanner += QLatin1String(" -rootPath %1").arg(shellQuote(rootPath)); + QStringList importPaths; importPaths += shellQuote(options->qtInstallDirectory + QLatin1String("/qml")); if (!rootPath.isEmpty())