From 448177d857963a8d3ab1db8d1bc8332d552e6bed Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Thu, 4 Apr 2019 20:28:10 +0200 Subject: [PATCH 01/15] Let "qmake -install qinstall" set default permissions 0644 and 0755 ...like the install commands before Qt 5.9 did. This ensures consistent permissions. Also, we can throw away the code that took care of removing and re-adding the read-only flag on Windows. This reverts commit a0b5d6e60f96359d88352e0b1c000678cdc80988 with the addition of preserving permissions when copying directories to properly install app bundles (QTBUG-74912). Fixes: QTBUG-74733 Task-number: QTBUG-74912 Change-Id: Iee6d7c5e86787dd3ada5e5e9441209d418100b1f Reviewed-by: Eskil Abrahamsen Blomfeldt Reviewed-by: Simon Hausmann --- qmake/main.cpp | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/qmake/main.cpp b/qmake/main.cpp index a4ef79227bf..69dff1073e4 100644 --- a/qmake/main.cpp +++ b/qmake/main.cpp @@ -242,7 +242,8 @@ static int doLink(int argc, char **argv) #endif -static int installFile(const QString &source, const QString &target, bool exe = false) +static int installFile(const QString &source, const QString &target, bool exe = false, + bool preservePermissions = false) { QFile sourceFile(source); QFile targetFile(target); @@ -260,35 +261,32 @@ static int installFile(const QString &source, const QString &target, bool exe = return 3; } + QFileDevice::Permissions targetPermissions = preservePermissions + ? sourceFile.permissions() + : (QFileDevice::ReadOwner | QFileDevice::WriteOwner + | QFileDevice::ReadUser | QFileDevice::WriteUser + | QFileDevice::ReadGroup | QFileDevice::ReadOther); if (exe) { - if (!targetFile.setPermissions(sourceFile.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeUser | - QFileDevice::ExeGroup | QFileDevice::ExeOther)) { - fprintf(stderr, "Error setting execute permissions on %s: %s\n", - qPrintable(target), qPrintable(targetFile.errorString())); - return 3; - } + targetPermissions |= QFileDevice::ExeOwner | QFileDevice::ExeUser | + QFileDevice::ExeGroup | QFileDevice::ExeOther; + } + if (!targetFile.setPermissions(targetPermissions)) { + fprintf(stderr, "Error setting permissions on %s: %s\n", + qPrintable(target), qPrintable(targetFile.errorString())); + return 3; } // Copy file times QString error; -#ifdef Q_OS_WIN - const QFile::Permissions permissions = targetFile.permissions(); - const bool readOnly = !(permissions & QFile::WriteUser); - if (readOnly) - targetFile.setPermissions(permissions | QFile::WriteUser); -#endif if (!IoUtils::touchFile(target, sourceFile.fileName(), &error)) { fprintf(stderr, "%s", qPrintable(error)); return 3; } -#ifdef Q_OS_WIN - if (readOnly) - targetFile.setPermissions(permissions); -#endif return 0; } -static int installFileOrDirectory(const QString &source, const QString &target) +static int installFileOrDirectory(const QString &source, const QString &target, + bool preservePermissions = false) { QFileInfo fi(source); if (false) { @@ -314,12 +312,12 @@ static int installFileOrDirectory(const QString &source, const QString &target) const QFileInfo &entry = it.fileInfo(); const QString &entryTarget = target + QDir::separator() + entry.fileName(); - const int recursionResult = installFileOrDirectory(entry.filePath(), entryTarget); + const int recursionResult = installFileOrDirectory(entry.filePath(), entryTarget, true); if (recursionResult != 0) return recursionResult; } } else { - const int fileCopyResult = installFile(source, target); + const int fileCopyResult = installFile(source, target, /*exe*/ false, preservePermissions); if (fileCopyResult != 0) return fileCopyResult; } From 8045ccc382ac91c14849e10f37d9a8d0605dc562 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 3 Apr 2019 15:35:30 +0200 Subject: [PATCH 02/15] Windows QPA/File dialog: Pass up http[s] URLs The native Windows allows for typing in http[s] URLs directly (without checking existence). Pass these up. Fixes: QTBUG-71785 Change-Id: I60237bab596ca3f52e6f513f17544ff94e9080da Reviewed-by: Andre de la Rocha Reviewed-by: Oliver Wolff --- .../windows/qwindowsdialoghelpers.cpp | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index ba441a19214..9de3268fc83 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -1453,26 +1453,41 @@ static QString createTemporaryItemCopy(QWindowsShellItem &qItem, QString *errorM return result; } +static QUrl itemToDialogUrl(QWindowsShellItem &qItem, QString *errorMessage) +{ + QUrl url = qItem.url(); + if (url.isLocalFile() || url.scheme().startsWith(QLatin1String("http"))) + return url; + const QString path = qItem.path(); + if (path.isEmpty() && !qItem.isDir() && qItem.canStream()) { + const QString temporaryCopy = createTemporaryItemCopy(qItem, errorMessage); + if (temporaryCopy.isEmpty()) { + QDebug(errorMessage).noquote() << "Unable to create a local copy of" + << qItem << ": " << errorMessage; + return QUrl(); + } + return QUrl::fromLocalFile(temporaryCopy); + } + if (!url.isValid()) + QDebug(errorMessage).noquote() << "Invalid URL obtained from" << qItem; + return url; +} + QList QWindowsNativeOpenFileDialog::dialogResult() const { QList result; IShellItemArray *items = nullptr; if (SUCCEEDED(openFileDialog()->GetResults(&items)) && items) { + QString errorMessage; for (IShellItem *item : QWindowsShellItem::itemsFromItemArray(items)) { QWindowsShellItem qItem(item); - const QString path = qItem.path(); - if (path.isEmpty() && !qItem.isDir()) { - QString errorMessage; - const QString temporaryCopy = createTemporaryItemCopy(qItem, &errorMessage); - if (temporaryCopy.isEmpty()) { - qWarning().noquote() << "Unable to create a local copy of" << qItem - << ": " << errorMessage; - } else { - result.append(QUrl::fromLocalFile(temporaryCopy)); - } - } else { - result.append(qItem.url()); + const QUrl url = itemToDialogUrl(qItem, &errorMessage); + if (!url.isValid()) { + qWarning("%s", qPrintable(errorMessage)); + result.clear(); + break; } + result.append(url); } } return result; From 3a34ef636af43e249fba417419db14c42b98094a Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 21 Mar 2019 09:11:27 +0100 Subject: [PATCH 03/15] QMenu/QComboBox: Extract helper for determining the pop up geometry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the code returning whether a popup should use the full screen to QStylePrivate and use for QMenu and QComboBox. Task-number: QTBUG-73231 Change-Id: I1901ecedfa90edf16329ce3b13ef4abea5ab44e8 Reviewed-by: Morten Johan Sørvig Reviewed-by: Richard Moe Gustavsen --- src/widgets/styles/qstyle.cpp | 8 ++++++++ src/widgets/styles/qstyle_p.h | 3 +++ src/widgets/widgets/qcombobox.cpp | 12 ++++-------- src/widgets/widgets/qmenu.cpp | 30 ++++++++++++++---------------- src/widgets/widgets/qmenu_p.h | 1 + 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index 97ec1d3f191..ec5b6df6b32 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -46,6 +46,7 @@ #include "qstyleoption.h" #include "private/qstyle_p.h" #include "private/qguiapplication_p.h" +#include #ifndef QT_NO_DEBUG #include "qdebug.h" #endif @@ -2447,6 +2448,13 @@ void QStyle::setProxy(QStyle *style) d->proxyStyle = style; } +//Windows and KDE allow menus to cover the taskbar, while GNOME and macOS don't +bool QStylePrivate::useFullScreenForPopup() +{ + auto theme = QGuiApplicationPrivate::platformTheme(); + return theme && theme->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool(); +} + QT_END_NAMESPACE #include "moc_qstyle.cpp" diff --git a/src/widgets/styles/qstyle_p.h b/src/widgets/styles/qstyle_p.h index 94e4540d0b1..9643012c31d 100644 --- a/src/widgets/styles/qstyle_p.h +++ b/src/widgets/styles/qstyle_p.h @@ -67,6 +67,9 @@ class QStylePrivate: public QObjectPrivate public: inline QStylePrivate() : layoutSpacingIndex(-1), proxyStyle(0) {} + + static bool useFullScreenForPopup(); + mutable int layoutSpacingIndex; QStyle *proxyStyle; }; diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index e20a0892b4c..bdd2462c926 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -80,6 +80,7 @@ #if QT_CONFIG(effects) # include #endif +#include #ifndef QT_NO_ACCESSIBILITY #include "qaccessible.h" #endif @@ -261,16 +262,11 @@ void QComboBoxPrivate::_q_modelDestroyed() model = QAbstractItemModelPrivate::staticEmptyModel(); } - -//Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't QRect QComboBoxPrivate::popupGeometry(int screen) const { - bool useFullScreenForPopupMenu = false; - if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) - useFullScreenForPopupMenu = theme->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool(); - return useFullScreenForPopupMenu ? - QDesktopWidgetPrivate::screenGeometry(screen) : - QDesktopWidgetPrivate::availableGeometry(screen); + return QStylePrivate::useFullScreenForPopup() + ? QDesktopWidgetPrivate::screenGeometry(screen) + : QDesktopWidgetPrivate::availableGeometry(screen); } bool QComboBoxPrivate::updateHoverControl(const QPoint &pos) diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 48253b52b00..a44f0fb8a78 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -78,6 +78,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -307,29 +308,26 @@ int QMenuPrivate::scrollerHeight() const return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q)); } -//Windows and KDE allow menus to cover the taskbar, while GNOME and Mac don't +// Windows and KDE allow menus to cover the taskbar, while GNOME and macOS +// don't. Torn-off menus are again different +inline bool QMenuPrivate::useFullScreenForPopup() const +{ + return !tornoff && QStylePrivate::useFullScreenForPopup(); +} + QRect QMenuPrivate::popupGeometry() const { Q_Q(const QMenu); - if (!tornoff && // Torn-off menus are different - QGuiApplicationPrivate::platformTheme() && - QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) { - return QDesktopWidgetPrivate::screenGeometry(q); - } else { - return QDesktopWidgetPrivate::availableGeometry(q); - } + return useFullScreenForPopup() + ? QDesktopWidgetPrivate::screenGeometry(q) + : QDesktopWidgetPrivate::availableGeometry(q); } -//Windows and KDE allow menus to cover the taskbar, while GNOME and Mac don't QRect QMenuPrivate::popupGeometry(int screen) const { - if (!tornoff && // Torn-off menus are different - QGuiApplicationPrivate::platformTheme() && - QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) { - return QDesktopWidgetPrivate::screenGeometry(screen); - } else { - return QDesktopWidgetPrivate::availableGeometry(screen); - } + return useFullScreenForPopup() + ? QDesktopWidgetPrivate::screenGeometry(screen) + : QDesktopWidgetPrivate::availableGeometry(screen); } QVector > QMenuPrivate::calcCausedStack() const diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h index f740919dc74..d0fa8ff1130 100644 --- a/src/widgets/widgets/qmenu_p.h +++ b/src/widgets/widgets/qmenu_p.h @@ -343,6 +343,7 @@ public: void updateActionRects(const QRect &screen) const; QRect popupGeometry() const; QRect popupGeometry(int screen) const; + bool useFullScreenForPopup() const; int getLastVisibleAction() const; //selection From f7949df24319cda7363aed354ca6d60dc6512788 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 8 Apr 2019 09:58:08 +0200 Subject: [PATCH 04/15] tst_QUrl: Fix left-over temporary directory on Windows The test changes the current directory to the test directory in fromUserInputWithCwd(), but did not restore it, causing: Totals 898 passed, 0 failed, 1 skipped, 0 blacklisted, 368ms ********* Finished testing of tst_QUrl ********* QTemporaryDir Unable to remove "C:\\TEMP\\tst_qurl-ryVxqu" most likely due to the presence of read-only files. Restore the old directory at the end to fix this. Change-Id: I62669868f3c6d97dd38ebac76515428c14b7e1e7 Reviewed-by: David Faure --- tests/auto/corelib/io/qurl/tst_qurl.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index 4f173d2dfd8..d697dae9dda 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -47,6 +47,7 @@ class tst_QUrl : public QObject private slots: void initTestCase(); + void cleanupTestCase(); void effectiveTLDs_data(); void effectiveTLDs(); void getSetCheck(); @@ -188,6 +189,7 @@ private slots: private: void testThreadingHelper(); + const QString m_currentPath = QDir::currentPath(); QTemporaryDir m_tempDir; }; @@ -196,6 +198,12 @@ void tst_QUrl::initTestCase() QVERIFY2(m_tempDir.isValid(), qPrintable(m_tempDir.errorString())); } +void tst_QUrl::cleanupTestCase() +{ + // Restore working directory changed in fromUserInputWithCwd() + QDir::setCurrent(m_currentPath); +} + // Testing get/set functions void tst_QUrl::getSetCheck() { From c2a1360b3f0aa33d00882bd6cc4b1990c18e04b8 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 20 Sep 2018 20:30:46 -0700 Subject: [PATCH 05/15] Update TinyCBOR with bugfixes Updated to https://github.com/thiagomacieira/tinycbor commit ac9369d8ec8511bc7516266ae6b07f7da860c954. Fabrice Fontaine (1): fix undefined encode_half in json2cbor Pedro Oliveira (1): Fixed minor error in the example code. Svyatoslav Phirsov (2): Typo fixed in stdlib fread(...) usage typo in dumprecursive return type Thiago Macieira (9): Install the tinycbor-version.h header. Update version number for a possible but unlikely 0.5.3 release Fix #137: off-by-one error in UTF-8 decoding Update Travis CI to Ubuntu Xenial Pretty: fix use of uninitialised variable Validation: fix out-of-bounds access when content ends in a string Parser: apply the same memory-check update Tests: remove useless comment Tests: Catch an earlier QCOMPARE failure in compare() phirsov (6): Fix off-by-one causing buffer overflow in open_memstream Protect macro argument expansion using parentheses eliminating misleading messages in case .config file not yet created Run check silently in Travis Make AppVeyor test suit run silent as in Travis enhancement #149 implemented: access half-precision floating point data as single float Change-Id: I9e3d261ad9bf41cfb2b6fffd159088f1cc9b3b02 Reviewed-by: Edward Welbourne --- src/3rdparty/tinycbor/src/cbor.h | 4 +- src/3rdparty/tinycbor/src/cborencoder.c | 24 +- src/3rdparty/tinycbor/src/cborparser.c | 2 +- src/3rdparty/tinycbor/src/compilersupport_p.h | 2 +- src/3rdparty/tinycbor/tests/encoder/data.cpp | 36 ++ .../tinycbor/tests/encoder/tst_encoder.cpp | 125 +++++- .../tinycbor/tests/parser/tst_parser.cpp | 377 +++++++++++++----- src/corelib/serialization/qcborstream.cpp | 10 + 8 files changed, 467 insertions(+), 113 deletions(-) diff --git a/src/3rdparty/tinycbor/src/cbor.h b/src/3rdparty/tinycbor/src/cbor.h index 5c7ba74e396..3672bd0d98c 100644 --- a/src/3rdparty/tinycbor/src/cbor.h +++ b/src/3rdparty/tinycbor/src/cbor.h @@ -257,6 +257,7 @@ CBOR_INLINE_API CborError cbor_encode_undefined(CborEncoder *encoder) CBOR_INLINE_API CborError cbor_encode_half_float(CborEncoder *encoder, const void *value) { return cbor_encode_floating_point(encoder, CborHalfFloatType, value); } +CBOR_API CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value); CBOR_INLINE_API CborError cbor_encode_float(CborEncoder *encoder, float value) { return cbor_encode_floating_point(encoder, CborFloatType, &value); } CBOR_INLINE_API CborError cbor_encode_double(CborEncoder *encoder, double value) @@ -593,12 +594,13 @@ CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *s /* Floating point */ CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value) { return value->type == CborHalfFloatType; } +CBOR_API CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result); CBOR_INLINE_API CborError cbor_value_get_half_float(const CborValue *value, void *result) { assert(cbor_value_is_half_float(value)); assert((value->flags & CborIteratorFlag_IntegerValueTooLarge) == 0); - /* size has been computed already */ + /* size has already been computed */ memcpy(result, &value->extra, sizeof(value->extra)); return CborNoError; } diff --git a/src/3rdparty/tinycbor/src/cborencoder.c b/src/3rdparty/tinycbor/src/cborencoder.c index 52a4025be1a..38804cfa6f1 100644 --- a/src/3rdparty/tinycbor/src/cborencoder.c +++ b/src/3rdparty/tinycbor/src/cborencoder.c @@ -76,7 +76,7 @@ * \code * uint8_t buf[16]; * CborEncoder encoder, mapEncoder; - * cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); + * cbor_encoder_init(&encoder, buf, sizeof(buf), 0); * cbor_encoder_create_map(&encoder, &mapEncoder, 1); * cbor_encode_text_stringz(&mapEncoder, "foo"); * cbor_encode_boolean(&mapEncoder, some_value); @@ -115,7 +115,7 @@ * uint8_t buf[16]; * CborError err; * CborEncoder encoder, mapEncoder; - * cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); + * cbor_encoder_init(&encoder, buf, sizeof(buf), 0); * err = cbor_encoder_create_map(&encoder, &mapEncoder, 1); * if (!err) * return err; @@ -155,7 +155,7 @@ * goto error; * buf = nbuf; * - * cbor_encoder_init(&encoder, &buf, size, 0); + * cbor_encoder_init(&encoder, buf, size, 0); * err = cbor_encoder_create_array(&encoder, &arrayEncoder, n); * cbor_assert(err); // can't fail, the buffer is always big enough * @@ -411,7 +411,7 @@ CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value) * This function is useful for code that needs to pass through floating point * values but does not wish to have the actual floating-point code. * - * \sa cbor_encode_half_float, cbor_encode_float, cbor_encode_double + * \sa cbor_encode_half_float, cbor_encode_float_as_half_float, cbor_encode_float, cbor_encode_double */ CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value) { @@ -621,13 +621,25 @@ CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder * * \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double() */ +/** + * \fn CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value) + * + * Convert the IEEE 754 single-precision (32-bit) floating point value \a value + * to the IEEE 754 half-precision (16-bit) floating point value and append it + * to the CBOR stream provided by \a encoder. + * The \a value should be in the range of the IEEE 754 half-precision floating point type, + * INFINITY, -INFINITY, or NAN, otherwise the behavior of this function is undefined. + * + * \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double() + */ + /** * \fn CborError cbor_encode_float(CborEncoder *encoder, float value) * * Appends the IEEE 754 single-precision (32-bit) floating point value \a value * to the CBOR stream provided by \a encoder. * - * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_double() + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_double() */ /** @@ -636,7 +648,7 @@ CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder * * Appends the IEEE 754 double-precision (64-bit) floating point value \a value * to the CBOR stream provided by \a encoder. * - * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float() + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_float() */ /** diff --git a/src/3rdparty/tinycbor/src/cborparser.c b/src/3rdparty/tinycbor/src/cborparser.c index 971230ea615..90a7d2ced68 100644 --- a/src/3rdparty/tinycbor/src/cborparser.c +++ b/src/3rdparty/tinycbor/src/cborparser.c @@ -1520,7 +1520,7 @@ error: * floating point, this function takes a \c{void *} as a parameter for the * storage area, which must be at least 16 bits wide. * - * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_float() + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float_as_float(), cbor_value_get_float() */ /** @} */ diff --git a/src/3rdparty/tinycbor/src/compilersupport_p.h b/src/3rdparty/tinycbor/src/compilersupport_p.h index 2b9491d34db..bd10efc9c77 100644 --- a/src/3rdparty/tinycbor/src/compilersupport_p.h +++ b/src/3rdparty/tinycbor/src/compilersupport_p.h @@ -106,7 +106,7 @@ # define cbor_ntohs __builtin_bswap16 # define cbor_htons __builtin_bswap16 # else -# define cbor_ntohs(x) (((uint16_t)x >> 8) | ((uint16_t)x << 8)) +# define cbor_ntohs(x) (((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8)) # define cbor_htons cbor_ntohs # endif # else diff --git a/src/3rdparty/tinycbor/tests/encoder/data.cpp b/src/3rdparty/tinycbor/tests/encoder/data.cpp index c33fb605aa1..8b00cfec1f6 100644 --- a/src/3rdparty/tinycbor/tests/encoder/data.cpp +++ b/src/3rdparty/tinycbor/tests/encoder/data.cpp @@ -123,6 +123,42 @@ QVariant make_ilmap(const std::initializer_list> &list return QVariant::fromValue(IndeterminateLengthMap(list)); } +void addHalfFloat() +{ + QTest::addColumn("output"); + QTest::addColumn("rawInput"); + QTest::addColumn("floatInput"); + + QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; + QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; + + QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); + QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); + + QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); + QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); + + QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); + QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); + + QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; + QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; + + QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; + QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; + + QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); + QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); + + QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << myInf(); + QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << myNInf(); + + QTest::newRow("nan1") << raw("\x7c\x01") << 0x7c01U << myNaN(); + QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << myNaN(); + QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << myNaN(); + QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << myNaN(); +} + void addColumns() { QTest::addColumn("output"); diff --git a/src/3rdparty/tinycbor/tests/encoder/tst_encoder.cpp b/src/3rdparty/tinycbor/tests/encoder/tst_encoder.cpp index f30c5226014..458f55eb109 100644 --- a/src/3rdparty/tinycbor/tests/encoder/tst_encoder.cpp +++ b/src/3rdparty/tinycbor/tests/encoder/tst_encoder.cpp @@ -41,6 +41,13 @@ class tst_Encoder : public QObject { Q_OBJECT private slots: + void floatAsHalfFloat_data(); + void floatAsHalfFloat(); + void halfFloat_data(); + void halfFloat(); + void floatAsHalfFloatCloseToZero_data(); + void floatAsHalfFloatCloseToZero(); + void floatAsHalfFloatNaN(); void fixed_data(); void fixed(); void strings_data(); @@ -178,21 +185,127 @@ CborError encodeVariant(CborEncoder *encoder, const QVariant &v) return CborErrorUnknownType; } -void compare(const QVariant &input, const QByteArray &output) +template +void encodeOne(Input input, FnUnderTest fn_under_test, QByteArray &buffer, CborError &error) { - QByteArray buffer(output.length(), Qt::Uninitialized); uint8_t *bufptr = reinterpret_cast(buffer.data()); CborEncoder encoder; cbor_encoder_init(&encoder, bufptr, buffer.length(), 0); - QCOMPARE(encodeVariant(&encoder, input), CborNoError); - QCOMPARE(encoder.remaining, size_t(1)); - QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0)); + error = fn_under_test(&encoder, input); - buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr))); + if (error == CborNoError) { + QCOMPARE(encoder.remaining, size_t(1)); + QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0)); + + buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr))); + } +} + +template +void compare(Input input, FnUnderTest fn_under_test, const QByteArray &output) +{ + QByteArray buffer(output.length(), Qt::Uninitialized); + CborError error; + + encodeOne(input, fn_under_test, buffer, error); + if (QTest::currentTestFailed()) + return; + + QCOMPARE(error, CborNoError); QCOMPARE(buffer, output); } +void compare(const QVariant &input, const QByteArray &output) +{ + compare(input, encodeVariant, output); +} + +void tst_Encoder::floatAsHalfFloat_data() +{ + addHalfFloat(); +} + +void tst_Encoder::floatAsHalfFloat() +{ + QFETCH(unsigned, rawInput); + QFETCH(double, floatInput); + QFETCH(QByteArray, output); + + if (rawInput == 0U || rawInput == 0x8000U) + QSKIP("zero values are out of scope of this test case", QTest::SkipSingle); + + if (qIsNaN(floatInput)) + QSKIP("NaN values are out of scope of this test case", QTest::SkipSingle); + + output.prepend('\xf9'); + + compare((float)floatInput, cbor_encode_float_as_half_float, output); +} + +void tst_Encoder::halfFloat_data() +{ + addHalfFloat(); +} + +void tst_Encoder::halfFloat() +{ + QFETCH(unsigned, rawInput); + QFETCH(QByteArray, output); + + uint16_t v = (uint16_t)rawInput; + output.prepend('\xf9'); + + compare(&v, cbor_encode_half_float, output); +} + +void tst_Encoder::floatAsHalfFloatCloseToZero_data() +{ + QTest::addColumn("floatInput"); + + QTest::newRow("+0") << 0.0; + QTest::newRow("-0") << -0.0; + + QTest::newRow("below min.denorm") << ldexp(1.0, -14) * ldexp(1.0, -11); + QTest::newRow("above -min.denorm") << ldexp(-1.0, -14) * ldexp(1.0, -11); +} + +void tst_Encoder::floatAsHalfFloatCloseToZero() +{ + QFETCH(double, floatInput); + + QByteArray buffer(4, Qt::Uninitialized); + CborError error; + + encodeOne((float)floatInput, cbor_encode_float_as_half_float, buffer, error); + + QCOMPARE(error, CborNoError); + + QVERIFY2( + buffer == raw("\xf9\x00\x00") || buffer == raw("\xf9\x80\x00"), + "Got value " + QByteArray::number(floatInput) + " encoded to: " + buffer); +} + +void tst_Encoder::floatAsHalfFloatNaN() +{ + QByteArray buffer(4, Qt::Uninitialized); + CborError error; + + encodeOne(myNaNf(), cbor_encode_float_as_half_float, buffer, error); + + QCOMPARE(error, CborNoError); + QCOMPARE(buffer.size(), 3); + + uint8_t ini_byte = (uint8_t)buffer[0], + exp = (uint8_t)buffer[1] & 0x7cU, + manth = (uint8_t)buffer[1] & 0x03U, + mantl = (uint8_t)buffer[2]; + + QCOMPARE((unsigned)ini_byte, 0xf9U); + QCOMPARE((unsigned)exp, 0x7cU); + QVERIFY((manth | mantl) != 0); +} + void tst_Encoder::fixed_data() { addColumns(); diff --git a/src/3rdparty/tinycbor/tests/parser/tst_parser.cpp b/src/3rdparty/tinycbor/tests/parser/tst_parser.cpp index 74c480bc515..2b10004faad 100644 --- a/src/3rdparty/tinycbor/tests/parser/tst_parser.cpp +++ b/src/3rdparty/tinycbor/tests/parser/tst_parser.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 Intel Corporation +** Copyright (C) 2019 Intel Corporation ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal @@ -23,11 +23,22 @@ ****************************************************************************/ #define _XOPEN_SOURCE 700 +#define _DARWIN_C_SOURCE 1 /* need MAP_ANON */ #include #include "cbor.h" #include #include +#if defined(Q_OS_UNIX) +# include +# include +#elif defined(Q_OS_WIN) +# define WIN32_LEAN_AND_MEAN 1 +# define NOMINMAX 1 +# include +#endif + + namespace QTest { template<> char *toString(const CborError &err) { @@ -44,6 +55,8 @@ private slots: // parsing API void integers_data(); void integers(); + void halfFloat_data(); + void halfFloat(); void fixed_data(); void fixed(); void strings_data(); @@ -105,6 +118,100 @@ private slots: void recursionLimit(); }; +struct ParserWrapper +{ + void *realdata = nullptr; + uint8_t *data; + size_t len; + CborParser parser; + CborValue first; + + ~ParserWrapper() { freeMemory(); } + + CborError init(const QByteArray &ba, uint32_t flags = 0) + { + return init(ba.constData(), ba.size(), flags); + } + CborError init(const char *ptr, int n, uint32_t flags = 0) + { + freeMemory(); + data = allocateMemory(n); + memcpy(data, ptr, len); + return cbor_parser_init(data, len, flags, &parser, &first); + } + uint8_t *begin() { return data; } + uint8_t *end() { return data + len; } + + uint8_t *allocateMemory(size_t); + void freeMemory(); + + static const size_t PageSize = 4096; + static inline size_t mmapAllocation(size_t n) + { + // round up and add one page + return (n + 2*PageSize) & ~(PageSize - 1); + } + static bool shouldUseMmap(); +}; + +bool ParserWrapper::shouldUseMmap() +{ + static int v = qEnvironmentVariableIntValue("PARSER_NO_MMAP"); + return !v; +} + +uint8_t *ParserWrapper::allocateMemory(size_t n) +{ + len = n; + if (shouldUseMmap()) { + size_t alloc = mmapAllocation(n); +#if defined(Q_OS_UNIX) + realdata = mmap(nullptr, alloc, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + Q_ASSERT_X(realdata != MAP_FAILED, "allocateMemory", "mmap failed!"); + + // mark last page inaccessible + uint8_t *ptr = static_cast(realdata); + ptr += alloc - PageSize; + mprotect(ptr, PageSize, PROT_NONE); + + ptr -= n; + return ptr; +#elif defined(Q_OS_WIN) + DWORD flAllocationType = MEM_COMMIT | MEM_RESERVE; + DWORD flProtect = PAGE_READWRITE; + realdata = VirtualAlloc(nullptr, alloc, flAllocationType, flProtect); + Q_ASSERT_X(realdata, "allocateMemory", "VirtualAlloc failed!"); + + // mark last page inaccessible + uint8_t *ptr = static_cast(realdata); + ptr += alloc - PageSize; + VirtualProtect(ptr, PageSize, PAGE_NOACCESS, nullptr); + + ptr -= n; + return ptr; +#endif + } + realdata = malloc(n); + return static_cast(realdata); +} + +void ParserWrapper::freeMemory() +{ + if (shouldUseMmap()) { + if (realdata) { +#if defined(Q_OS_UNIX) + size_t alloc = mmapAllocation(len); + munmap(realdata, alloc); +#elif defined(Q_OS_WIN) + VirtualFree(realdata, 0, MEM_RELEASE); +#endif + } + return; + } + + free(realdata); +} + static CborError qstring_printf(void *out, const char *fmt, ...) { auto str = static_cast(out); @@ -166,47 +273,46 @@ bool compareFailed = true; void compareOne_real(const QByteArray &data, const QString &expected, int line, int n = -1) { compareFailed = true; - CborParser parser; - CborValue first; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\""); - if (cbor_value_get_type(&first) == CborArrayType) { + if (cbor_value_get_type(&w.first) == CborArrayType) { size_t len; if (n >= 0) { - QVERIFY(cbor_value_is_length_known(&first)); - QCOMPARE(cbor_value_get_array_length(&first, &len), CborNoError); + QVERIFY(cbor_value_is_length_known(&w.first)); + QCOMPARE(cbor_value_get_array_length(&w.first, &len), CborNoError); QCOMPARE(len, size_t(len)); } else { - QVERIFY(!cbor_value_is_length_known(&first)); - QCOMPARE(cbor_value_get_array_length(&first, &len), CborErrorUnknownLength); + QVERIFY(!cbor_value_is_length_known(&w.first)); + QCOMPARE(cbor_value_get_array_length(&w.first, &len), CborErrorUnknownLength); } - } else if (cbor_value_get_type(&first) == CborMapType) { + } else if (cbor_value_get_type(&w.first) == CborMapType) { size_t len; if (n >= 0) { - QVERIFY(cbor_value_is_length_known(&first)); - QCOMPARE(cbor_value_get_map_length(&first, &len), CborNoError); + QVERIFY(cbor_value_is_length_known(&w.first)); + QCOMPARE(cbor_value_get_map_length(&w.first, &len), CborNoError); QCOMPARE(len, size_t(len)); } else { - QVERIFY(!cbor_value_is_length_known(&first)); - QCOMPARE(cbor_value_get_map_length(&first, &len), CborErrorUnknownLength); + QVERIFY(!cbor_value_is_length_known(&w.first)); + QCOMPARE(cbor_value_get_map_length(&w.first, &len), CborErrorUnknownLength); } - } else if (cbor_value_is_text_string(&first) || cbor_value_is_byte_string(&first)) { + } else if (cbor_value_is_text_string(&w.first) || cbor_value_is_byte_string(&w.first)) { size_t len; - QCOMPARE(cbor_value_calculate_string_length(&first, &len), CborNoError); - if (cbor_value_is_length_known(&first)) { + QCOMPARE(cbor_value_calculate_string_length(&w.first, &len), CborNoError); + if (cbor_value_is_length_known(&w.first)) { size_t len2; - QCOMPARE(cbor_value_get_string_length(&first, &len2), CborNoError); + QCOMPARE(cbor_value_get_string_length(&w.first, &len2), CborNoError); QCOMPARE(len2, len); } else { - QCOMPARE(cbor_value_get_string_length(&first, &len), CborErrorUnknownLength); + QCOMPARE(cbor_value_get_string_length(&w.first, &len), CborErrorUnknownLength); } } - CborError err2 = cbor_value_validate_basic(&first); + CborError err2 = cbor_value_validate_basic(&w.first); QString decoded; - err = parseOne(&first, &decoded); + err = parseOne(&w.first, &decoded); QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\"; decoded stream:\n" + decoded.toLatin1()); QCOMPARE(decoded, expected); @@ -215,7 +321,7 @@ void compareOne_real(const QByteArray &data, const QString &expected, int line, QCOMPARE(err2, err); // check that we consumed everything - QCOMPARE((void*)cbor_value_get_next_byte(&first), (void*)data.constEnd()); + QCOMPARE((void*)cbor_value_get_next_byte(&w.first), (void*)w.end()); compareFailed = false; } @@ -237,39 +343,112 @@ void tst_Parser::integers() QFETCH(qint64, expectedValue); QFETCH(bool, inInt64Range); - CborParser parser; - CborValue first; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); - QVERIFY(cbor_value_is_integer(&first)); + QVERIFY(cbor_value_is_integer(&w.first)); uint64_t raw; - cbor_value_get_raw_integer(&first, &raw); + cbor_value_get_raw_integer(&w.first, &raw); QCOMPARE(quint64(raw), expectedRaw); if (isNegative) { - QVERIFY(cbor_value_is_negative_integer(&first)); - QVERIFY(!cbor_value_is_unsigned_integer(&first)); + QVERIFY(cbor_value_is_negative_integer(&w.first)); + QVERIFY(!cbor_value_is_unsigned_integer(&w.first)); } else { - QVERIFY(!cbor_value_is_negative_integer(&first)); - QVERIFY(cbor_value_is_unsigned_integer(&first)); + QVERIFY(!cbor_value_is_negative_integer(&w.first)); + QVERIFY(cbor_value_is_unsigned_integer(&w.first)); } int64_t value; if (inInt64Range) { - cbor_value_get_int64(&first, &value); + cbor_value_get_int64(&w.first, &value); QCOMPARE(qint64(value), expectedValue); } - err = cbor_value_get_int64_checked(&first, &value); + err = cbor_value_get_int64_checked(&w.first, &value); QCOMPARE(err, inInt64Range ? CborNoError : CborErrorDataTooLarge); int ivalue; bool inIntRange = inInt64Range && (expectedValue == int(expectedValue)); - err = cbor_value_get_int_checked(&first, &ivalue); + err = cbor_value_get_int_checked(&w.first, &ivalue); QCOMPARE(err, inIntRange ? CborNoError : CborErrorDataTooLarge); } +static void addHalfFloat() +{ + QTest::addColumn("data"); + QTest::addColumn("expectedRaw"); + QTest::addColumn("expectedValue"); + + QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; + QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; + + QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); + QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); + + QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); + QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); + + QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); + QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); + + QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; + QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; + + QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; + QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; + + QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); + QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); + + QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << double(INFINITY); + QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << double(-INFINITY); + + QTest::newRow("nan") << raw("\x7c\x01") << 0x7c01U << double(NAN); + QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << double(NAN); + QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << double(NAN); + QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << double(NAN); +} + +void tst_Parser::halfFloat_data() +{ + addHalfFloat(); +} + +void tst_Parser::halfFloat() +{ + QFETCH(QByteArray, data); + QFETCH(unsigned, expectedRaw); + QFETCH(double, expectedValue); + + CborParser parser; + CborValue first; + + data.prepend('\xf9'); + + CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); + QVERIFY(cbor_value_is_half_float(&first)); + + uint16_t raw; + cbor_value_get_half_float(&first, &raw); + QCOMPARE(raw, uint16_t(expectedRaw)); + + float value; + cbor_value_get_half_float_as_float(&first, &value); + + const double epsilon = ldexp(1.0, -25); + + if (qIsNaN(expectedValue)) { + QVERIFY(qIsNaN(value)); + } else if (qIsInf(expectedValue)) { + QVERIFY(value == (float)expectedValue); + } else { + QVERIFY(qAbs(value - (float)expectedValue) < epsilon); + } +} + void tst_Parser::fixed_data() { addColumns(); @@ -678,14 +857,13 @@ void tst_Parser::chunkedString_data() static void chunkedStringTest(const QByteArray &data, const QString &concatenated, QStringList &chunks, CborType ourType) { - CborParser parser; - CborValue first; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); CborValue value; - QVERIFY(cbor_value_is_array(&first)); - err = cbor_value_enter_container(&first, &value); + QVERIFY(cbor_value_is_array(&w.first)); + err = cbor_value_enter_container(&w.first, &value); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY(cbor_value_is_byte_string(&value) || cbor_value_is_text_string(&value)); @@ -748,9 +926,9 @@ static void chunkedStringTest(const QByteArray &data, const QString &concatenate // confirm EOF QVERIFY(cbor_value_at_end(&value)); - err = cbor_value_leave_container(&first, &value); + err = cbor_value_leave_container(&w.first, &value); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); - QCOMPARE((void*)cbor_value_get_next_byte(&first), (void*)data.constEnd()); + QCOMPARE((void*)cbor_value_get_next_byte(&w.first), (void*)w.end()); } void tst_Parser::chunkedString() @@ -840,18 +1018,17 @@ void tst_Parser::stringLength() QFETCH(QByteArray, data); QFETCH(int, expected); - CborParser parser; - CborValue value; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &value); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); size_t result; - err = cbor_value_calculate_string_length(&value, &result); + err = cbor_value_calculate_string_length(&w.first, &result); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QCOMPARE(result, size_t(expected)); - if (cbor_value_is_length_known(&value)) { - QCOMPARE(cbor_value_get_string_length(&value, &result), CborNoError); + if (cbor_value_is_length_known(&w.first)) { + QCOMPARE(cbor_value_get_string_length(&w.first, &result), CborNoError); QCOMPARE(result, size_t(expected)); } @@ -935,25 +1112,24 @@ void compareOneString(const QByteArray &data, const QString &string, bool expect { compareFailed = true; - CborParser parser; - CborValue value; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &value); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\""); bool result; QByteArray bastring = string.toUtf8(); - err = cbor_value_text_string_equals(&value, bastring.constData(), &result); + err = cbor_value_text_string_equals(&w.first, bastring.constData(), &result); QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\""); QCOMPARE(result, expected); if (expected) { size_t len; - cbor_value_skip_tag(&value); - if (cbor_value_is_length_known(&value)) { - QCOMPARE(cbor_value_get_string_length(&value, &len), CborNoError); + cbor_value_skip_tag(&w.first); + if (cbor_value_is_length_known(&w.first)) { + QCOMPARE(cbor_value_get_string_length(&w.first, &len), CborNoError); QCOMPARE(int(len), bastring.size()); } - QCOMPARE(cbor_value_calculate_string_length(&value, &len), CborNoError); + QCOMPARE(cbor_value_calculate_string_length(&w.first, &len), CborNoError); QCOMPARE(int(len), bastring.size()); } @@ -1042,13 +1218,12 @@ void tst_Parser::mapFind() QFETCH(QByteArray, data); QFETCH(bool, expected); - CborParser parser; - CborValue value; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &value); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); CborValue element; - err = cbor_value_map_find_value(&value, "needle", &element); + err = cbor_value_map_find_value(&w.first, "needle", &element); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); if (expected) { @@ -1119,13 +1294,12 @@ void tst_Parser::checkedIntegers() QFETCH(QVariant, result); int64_t expected = result.toLongLong(); - CborParser parser; - CborValue value; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &value); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); int64_t v; - err = cbor_value_get_int64_checked(&value, &v); + err = cbor_value_get_int64_checked(&w.first, &v); if (result.isNull()) { QCOMPARE(err, CborErrorDataTooLarge); } else { @@ -1133,7 +1307,7 @@ void tst_Parser::checkedIntegers() } int v2; - err = cbor_value_get_int_checked(&value, &v2); + err = cbor_value_get_int_checked(&w.first, &v2); if (result.isNull() || expected < std::numeric_limits::min() || expected > std::numeric_limits::max()) { QCOMPARE(err, CborErrorDataTooLarge); } else { @@ -1154,14 +1328,13 @@ void tst_Parser::validation() QFETCH(CborError, expectedError); QString decoded; - CborParser parser; - CborValue first; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), flags, &parser, &first); + ParserWrapper w; + CborError err = w.init(data, uint32_t(flags)); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); - CborError err2 = cbor_value_validate_basic(&first); - CborError err3 = cbor_value_validate(&first, CborValidateBasic); - err = parseOne(&first, &decoded); + CborError err2 = cbor_value_validate_basic(&w.first); + CborError err3 = cbor_value_validate(&w.first, CborValidateBasic); + err = parseOne(&w.first, &decoded); QCOMPARE(err, expectedError); if (!QByteArray(QTest::currentDataTag()).contains("utf8")) { QCOMPARE(err2, expectedError); @@ -1352,24 +1525,36 @@ void tst_Parser::strictValidation_data() QTest::newRow("overlong-_stringx2-0*8") << raw("\x7f\x60\x7b\0\0\0\0\0\0\0\0\xff") << int(CborValidateShortestNumbers) << CborErrorOverlongEncoding; // strict mode - QTest::newRow("invalid-utf8-1char") << raw("\x61\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; - QTest::newRow("invalid-utf8-2chars-1") << raw("\x62\xc2\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; - QTest::newRow("invalid-utf8-2chars-2") << raw("\x62\xc3\xdf") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; - QTest::newRow("invalid-utf8-2chars-3") << raw("\x62\xc7\xf0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; - QTest::newRow("invalid-utf8-3chars-1") << raw("\x63\xe0\xa0\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; - QTest::newRow("invalid-utf8-3chars-2") << raw("\x63\xe0\xc0\xa0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; - QTest::newRow("invalid-utf8-4chars-1") << raw("\x64\xf0\x90\x80\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; - QTest::newRow("invalid-utf8-4chars-2") << raw("\x64\xf0\x90\xc0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; - QTest::newRow("invalid-utf8-4chars-3") << raw("\x64\xf0\xc0\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + // UTF-8 sequences with invalid continuation bytes + QTest::newRow("invalid-utf8-bad-continuation-1char") << raw("\x61\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-bad-continuation-2chars-1") << raw("\x62\xc2\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-bad-continuation-2chars-2") << raw("\x62\xc3\xdf") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-bad-continuation-2chars-3") << raw("\x62\xc7\xf0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-bad-continuation-3chars-1") << raw("\x63\xe0\xa0\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-bad-continuation-3chars-2") << raw("\x63\xe0\xc0\xa0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-bad-continuation-4chars-1") << raw("\x64\xf0\x90\x80\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-bad-continuation-4chars-2") << raw("\x64\xf0\x90\xc0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-bad-continuation-4chars-3") << raw("\x64\xf0\xc0\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + // Too short UTF-8 sequences (in an array so there's a byte after that would make it valid UTF-8 if it were part of the string) + QTest::newRow("invalid-utf8-too-short-2chars") << raw("\x82\x61\xc2\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-too-short-3chars-1") << raw("\x82\x61\xe0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-too-short-3chars-2") << raw("\x82\x62\xe0\xa0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-too-short-4chars-1") << raw("\x82\x61\xf0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-too-short-4chars-2") << raw("\x82\x62\xf0\x90\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + QTest::newRow("invalid-utf8-too-short-4chars-3") << raw("\x82\x63\xf0\x90\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + // UTF-16 surrogages encoded in UTF-8 QTest::newRow("invalid-utf8-hi-surrogate") << raw("\x63\xed\xa0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-lo-surrogate") << raw("\x63\xed\xb0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-surrogate-pair") << raw("\x66\xed\xa0\x80\xed\xb0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + // Non-Unicode UTF-8 sequences QTest::newRow("invalid-utf8-non-unicode-1") << raw("\x64\xf4\x90\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-non-unicode-2") << raw("\x65\xf8\x88\x80\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-non-unicode-3") << raw("\x66\xfc\x84\x80\x80\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-non-unicode-4") << raw("\x66\xfd\xbf\xbf\xbf\xbf\xbf") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + // invalid bytes in UTF-8 QTest::newRow("invalid-utf8-fe") << raw("\x61\xfe") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-ff") << raw("\x61\xff") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; + // Overlong sequences QTest::newRow("invalid-utf8-overlong-1-2") << raw("\x62\xc1\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-overlong-1-3") << raw("\x63\xe0\x81\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-overlong-1-4") << raw("\x64\xf0\x80\x81\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; @@ -1525,12 +1710,11 @@ void tst_Parser::strictValidation() QFETCH(CborError, expectedError); QString decoded; - CborParser parser; - CborValue first; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); - err = cbor_value_validate(&first, flags); + err = cbor_value_validate(&w.first, flags); QCOMPARE(err, expectedError); } @@ -1549,12 +1733,11 @@ void tst_Parser::incompleteData() QFETCH(QString, expected); for (int len = 0; len < data.length() - 1; ++len) { - CborParser parser; - CborValue first; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), len, 0, &parser, &first); + ParserWrapper w; + CborError err = w.init(data.constData(), len); if (!err) { QString decoded; - err = parseOne(&first, &decoded); + err = parseOne(&w.first, &decoded); } if (err != CborErrorUnexpectedEOF) qDebug() << "Length is" << len; @@ -1583,14 +1766,13 @@ void tst_Parser::endPointer() QFETCH(int, offset); QString decoded; - CborParser parser; - CborValue first; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); - err = parseOne(&first, &decoded); + err = parseOne(&w.first, &decoded); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); - QCOMPARE(int(cbor_value_get_next_byte(&first) - reinterpret_cast(data.constBegin())), offset); + QCOMPARE(int(cbor_value_get_next_byte(&w.first) - w.begin()), offset); } void tst_Parser::recursionLimit_data() @@ -1638,24 +1820,23 @@ void tst_Parser::recursionLimit() { QFETCH(QByteArray, data); - CborParser parser; - CborValue first; - CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + ParserWrapper w; + CborError err = w.init(data); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); // check that it is valid: - CborValue it = first; + CborValue it = w.first; { QString dummy; err = parseOne(&it, &dummy); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); } - it = first; + it = w.first; err = cbor_value_advance(&it); QCOMPARE(err, CborErrorNestingTooDeep); - it = first; + it = w.first; if (cbor_value_is_map(&it)) { CborValue dummy; err = cbor_value_map_find_value(&it, "foo", &dummy); diff --git a/src/corelib/serialization/qcborstream.cpp b/src/corelib/serialization/qcborstream.cpp index d062cff0670..df38118805b 100644 --- a/src/corelib/serialization/qcborstream.cpp +++ b/src/corelib/serialization/qcborstream.cpp @@ -100,6 +100,16 @@ static CborError _cbor_value_dup_string(const CborValue *, void **, size_t *, Cb Q_UNREACHABLE(); return CborErrorInternalError; } +static CborError cbor_value_get_half_float_as_float(const CborValue *, float *) +{ + Q_UNREACHABLE(); + return CborErrorInternalError; +} +static CborError cbor_encode_float_as_half_float(CborEncoder *, float) +{ + Q_UNREACHABLE(); + return CborErrorInternalError; +} QT_WARNING_POP Q_DECLARE_TYPEINFO(CborEncoder, Q_PRIMITIVE_TYPE); From 4b445481c37836e2870a5717e05d575398341483 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 28 Mar 2019 16:14:59 +0100 Subject: [PATCH 06/15] tst_http2 - extend 'singleRequest' test case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to test different h2 modes: "h2c" (clear text with protocol upgrade), "h2" (encrypted, negotiating via ALPN extension), "h2-direct" (encrypted, no ALPN), "h2c-direct" (plain text, no protocol upgrade). This patch-set is an amendment to the recent fix in the protocol handler where we were crashing in "h2c-direct" mode. Change-Id: I3ff5ed1396a59b72b59a95f927d404ccd202d0b8 Reviewed-by: Timur Pocheptsov Reviewed-by: Mårten Nordheim --- tests/auto/network/access/http2/http2srv.cpp | 55 ++++++----- tests/auto/network/access/http2/http2srv.h | 15 ++- tests/auto/network/access/http2/tst_http2.cpp | 93 +++++++++++++++---- 3 files changed, 119 insertions(+), 44 deletions(-) diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp index 6e2220fa673..5a99d4e50c0 100644 --- a/tests/auto/network/access/http2/http2srv.cpp +++ b/tests/auto/network/access/http2/http2srv.cpp @@ -76,11 +76,15 @@ void fill_push_header(const HttpHeader &originalRequest, HttpHeader &promisedReq } -Http2Server::Http2Server(bool h2c, const Http2::RawSettings &ss, const Http2::RawSettings &cs) - : serverSettings(ss), - expectedClientSettings(cs), - clearTextHTTP2(h2c) +Http2Server::Http2Server(H2Type type, const Http2::RawSettings &ss, const Http2::RawSettings &cs) + : connectionType(type), + serverSettings(ss), + expectedClientSettings(cs) { +#if !QT_CONFIG(ssl) + Q_ASSERT(type != H2Type::h2Alpn && type != H2Type::h2Direct); +#endif + responseBody = "\n" "\n" "Sample \"Hello, World\" Application\n" @@ -129,15 +133,15 @@ void Http2Server::redirectOpenStream(quint16 port) targetPort = port; } +bool Http2Server::isClearText() const +{ + return connectionType == H2Type::h2c || connectionType == H2Type::h2cDirect; +} + void Http2Server::startServer() { -#ifdef QT_NO_SSL - // Let the test fail with timeout. - if (!clearTextHTTP2) - return; -#endif if (listen()) { - if (clearTextHTTP2) + if (isClearText()) authority = QStringLiteral("127.0.0.1:%1").arg(serverPort()).toLatin1(); emit serverStarted(serverPort()); } @@ -146,7 +150,7 @@ void Http2Server::startServer() bool Http2Server::sendProtocolSwitchReply() { Q_ASSERT(socket); - Q_ASSERT(clearTextHTTP2 && upgradeProtocol); + Q_ASSERT(connectionType == H2Type::h2c); // The first and the last HTTP/1.1 response we send: const char response[] = "HTTP/1.1 101 Switching Protocols\r\n" "Connection: Upgrade\r\n" @@ -262,25 +266,28 @@ void Http2Server::sendWINDOW_UPDATE(quint32 streamID, quint32 delta) void Http2Server::incomingConnection(qintptr socketDescriptor) { - if (clearTextHTTP2) { + if (isClearText()) { socket.reset(new QTcpSocket); const bool set = socket->setSocketDescriptor(socketDescriptor); Q_ASSERT(set); // Stop listening: close(); - upgradeProtocol = true; + upgradeProtocol = connectionType == H2Type::h2c; QMetaObject::invokeMethod(this, "connectionEstablished", Qt::QueuedConnection); } else { -#ifndef QT_NO_SSL +#if QT_CONFIG(ssl) socket.reset(new QSslSocket); QSslSocket *sslSocket = static_cast(socket.data()); - // Add HTTP2 as supported protocol: - auto conf = QSslConfiguration::defaultConfiguration(); - auto protos = conf.allowedNextProtocols(); - protos.prepend(QSslConfiguration::ALPNProtocolHTTP2); - conf.setAllowedNextProtocols(protos); - sslSocket->setSslConfiguration(conf); + + if (connectionType == H2Type::h2Alpn) { + // Add HTTP2 as supported protocol: + auto conf = QSslConfiguration::defaultConfiguration(); + auto protos = conf.allowedNextProtocols(); + protos.prepend(QSslConfiguration::ALPNProtocolHTTP2); + conf.setAllowedNextProtocols(protos); + sslSocket->setSslConfiguration(conf); + } // SSL-related setup ... sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone); sslSocket->setProtocol(QSsl::TlsV1_2OrLater); @@ -299,7 +306,7 @@ void Http2Server::incomingConnection(qintptr socketDescriptor) connect(sslSocket, SIGNAL(encrypted()), this, SLOT(connectionEstablished())); sslSocket->startServerEncryption(); #else - Q_UNREACHABLE(); + Q_ASSERT(0); #endif } } @@ -377,7 +384,7 @@ void Http2Server::connectionEstablished() { using namespace Http2; - if (testingGOAWAY && !clearTextHTTP2) + if (testingGOAWAY && !isClearText()) return triggerGOAWAYEmulation(); // For clearTextHTTP2 we first have to respond with 'protocol switch' @@ -392,7 +399,7 @@ void Http2Server::connectionEstablished() waitingClientSettings = false; settingsSent = false; - if (clearTextHTTP2) { + if (connectionType == H2Type::h2c) { requestLine.clear(); // Now we have to handle HTTP/1.1 request. We use Get/Post in our test, // so set requestType to something unsupported: @@ -818,7 +825,7 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody) Q_ASSERT(targetPort); header.push_back({":status", "308"}); const QString url("%1://localhost:%2/"); - header.push_back({"location", url.arg(clearTextHTTP2 ? QStringLiteral("http") : QStringLiteral("https"), + header.push_back({"location", url.arg(isClearText() ? QStringLiteral("http") : QStringLiteral("https"), QString::number(targetPort)).toLatin1()}); } else { diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h index ae3f084fdcd..4ef4b251017 100644 --- a/tests/auto/network/access/http2/http2srv.h +++ b/tests/auto/network/access/http2/http2srv.h @@ -62,21 +62,32 @@ public: Q_DECLARE_PRIVATE(Http11Reply) }; +enum class H2Type { + h2Alpn, // Secure connection, ALPN to negotiate h2. + h2c, // Clear text with protocol upgrade. + h2Direct, // Secure connection, ALPN not supported. + h2cDirect, // Clear text direct +}; + class Http2Server : public QTcpServer { Q_OBJECT public: - Http2Server(bool clearText, const Http2::RawSettings &serverSettings, + + Http2Server(H2Type type, const Http2::RawSettings &serverSettings, const Http2::RawSettings &clientSettings); ~Http2Server(); + // To be called before server started: void enablePushPromise(bool enabled, const QByteArray &path = QByteArray()); void setResponseBody(const QByteArray &body); void emulateGOAWAY(int timeout); void redirectOpenStream(quint16 targetPort); + bool isClearText() const; + // Invokables, since we can call them from the main thread, // but server (can) work on its own thread. Q_INVOKABLE void startServer(); @@ -129,6 +140,7 @@ private: QScopedPointer socket; + H2Type connectionType = H2Type::h2Alpn; // Connection preface: bool waitingClientPreface = false; bool waitingClientSettings = false; @@ -170,7 +182,6 @@ private: quint32 streamRecvWindowSize = Http2::defaultSessionWindowSize; QByteArray responseBody; - bool clearTextHTTP2 = false; bool pushPromiseEnabled = false; quint32 lastPromisedStream = 0; QByteArray pushPath; diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp index 52e98c1fc15..9ca7c892f77 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -59,6 +59,9 @@ const bool clearTextHTTP2 = false; const bool clearTextHTTP2 = true; #endif +Q_DECLARE_METATYPE(H2Type) +Q_DECLARE_METATYPE(QNetworkRequest::Attribute) + QT_BEGIN_NAMESPACE class tst_Http2 : public QObject @@ -69,6 +72,7 @@ public: ~tst_Http2(); private slots: // Tests: + void singleRequest_data(); void singleRequest(); void multipleRequests(); void flowControlClientSide(); @@ -100,13 +104,13 @@ private: // small payload. void runEventLoop(int ms = 5000); void stopEventLoop(); - Http2Server *newServer(const Http2::RawSettings &serverSettings, + Http2Server *newServer(const Http2::RawSettings &serverSettings, H2Type connectionType, const Http2::ProtocolParameters &clientSettings = {}); // Send a get or post request, depending on a payload (empty or not). void sendRequest(int streamNumber, QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority, const QByteArray &payload = QByteArray()); - QUrl requestUrl() const; + QUrl requestUrl(H2Type connnectionType) const; quint16 serverPort = 0; QThread *workerThread = nullptr; @@ -143,6 +147,11 @@ struct ServerDeleter using ServerPtr = QScopedPointer; +H2Type defaultConnectionType() +{ + return clearTextHTTP2 ? H2Type::h2c : H2Type::h2Alpn; +} + } // unnamed namespace tst_Http2::tst_Http2() @@ -164,25 +173,59 @@ tst_Http2::~tst_Http2() } } +void tst_Http2::singleRequest_data() +{ + QTest::addColumn("h2Attribute"); + QTest::addColumn("connectionType"); + + // 'Clear text' that should always work, either via the protocol upgrade + // or as direct. + QTest::addRow("h2c-upgrade") << QNetworkRequest::HTTP2AllowedAttribute << H2Type::h2c; + QTest::addRow("h2c-direct") << QNetworkRequest::Http2DirectAttribute << H2Type::h2cDirect; + + if (!clearTextHTTP2) { + // Qt with TLS where TLS-backend supports ALPN. + QTest::addRow("h2-ALPN") << QNetworkRequest::HTTP2AllowedAttribute << H2Type::h2Alpn; + } + +#if QT_CONFIG(ssl) + QTest::addRow("h2-direct") << QNetworkRequest::Http2DirectAttribute << H2Type::h2Direct; +#endif +} + void tst_Http2::singleRequest() { clearHTTP2State(); +#if QT_CONFIG(securetransport) + // Normally on macOS we use plain text only for SecureTransport + // does not support ALPN on the server side. With 'direct encrytped' + // we have to use TLS sockets (== private key) and thus suppress a + // keychain UI asking for permission to use a private key. + // Our CI has this, but somebody testing locally - will have a problem. + qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1")); + auto envRollback = qScopeGuard([](){ + qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN"); + }); +#endif + serverPort = 0; nRequests = 1; - ServerPtr srv(newServer(defaultServerSettings)); + QFETCH(const H2Type, connectionType); + ServerPtr srv(newServer(defaultServerSettings, connectionType)); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); runEventLoop(); QVERIFY(serverPort != 0); - auto url = requestUrl(); + auto url = requestUrl(connectionType); url.setPath("/index.html"); QNetworkRequest request(url); - request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); + QFETCH(const QNetworkRequest::Attribute, h2Attribute); + request.setAttribute(h2Attribute, QVariant(true)); auto reply = manager.get(request); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); @@ -207,7 +250,7 @@ void tst_Http2::multipleRequests() serverPort = 0; nRequests = 10; - ServerPtr srv(newServer(defaultServerSettings)); + ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType())); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); @@ -258,7 +301,7 @@ void tst_Http2::flowControlClientSide() manager.setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params)); const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, quint32(3)}}; - ServerPtr srv(newServer(serverSettings, params)); + ServerPtr srv(newServer(serverSettings, defaultConnectionType(), params)); const QByteArray respond(int(Http2::defaultSessionWindowSize * 10), 'x'); @@ -300,7 +343,7 @@ void tst_Http2::flowControlServerSide() const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, 7}}; - ServerPtr srv(newServer(serverSettings)); + ServerPtr srv(newServer(serverSettings, defaultConnectionType())); const QByteArray payload(int(Http2::defaultSessionWindowSize * 500), 'x'); @@ -335,7 +378,7 @@ void tst_Http2::pushPromise() params.settingsFrameData[Settings::ENABLE_PUSH_ID] = 1; manager.setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params)); - ServerPtr srv(newServer(defaultServerSettings, params)); + ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType(), params)); srv->enablePushPromise(true, QByteArray("/script.js")); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); @@ -343,7 +386,7 @@ void tst_Http2::pushPromise() QVERIFY(serverPort != 0); - auto url = requestUrl(); + auto url = requestUrl(defaultConnectionType()); url.setPath("/index.html"); QNetworkRequest request(url); @@ -409,14 +452,14 @@ void tst_Http2::goaway() serverPort = 0; nRequests = 3; - ServerPtr srv(newServer(defaultServerSettings)); + ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType())); srv->emulateGOAWAY(responseTimeoutMS); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); runEventLoop(); QVERIFY(serverPort != 0); - auto url = requestUrl(); + auto url = requestUrl(defaultConnectionType()); // We have to store these replies, so that we can check errors later. std::vector replies(nRequests); for (int i = 0; i < nRequests; ++i) { @@ -456,7 +499,7 @@ void tst_Http2::earlyResponse() serverPort = 0; nRequests = 1; - ServerPtr targetServer(newServer(defaultServerSettings)); + ServerPtr targetServer(newServer(defaultServerSettings, defaultConnectionType())); QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection); runEventLoop(); @@ -466,7 +509,7 @@ void tst_Http2::earlyResponse() const quint16 targetPort = serverPort; serverPort = 0; - ServerPtr redirector(newServer(defaultServerSettings)); + ServerPtr redirector(newServer(defaultServerSettings, defaultConnectionType())); redirector->redirectOpenStream(targetPort); QMetaObject::invokeMethod(redirector.data(), "startServer", Qt::QueuedConnection); @@ -506,11 +549,11 @@ void tst_Http2::stopEventLoop() eventLoop.exitLoop(); } -Http2Server *tst_Http2::newServer(const Http2::RawSettings &serverSettings, +Http2Server *tst_Http2::newServer(const Http2::RawSettings &serverSettings, H2Type connectionType, const Http2::ProtocolParameters &clientSettings) { using namespace Http2; - auto srv = new Http2Server(clearTextHTTP2, serverSettings, + auto srv = new Http2Server(connectionType, serverSettings, clientSettings.settingsFrameData); using Srv = Http2Server; @@ -535,7 +578,7 @@ void tst_Http2::sendRequest(int streamNumber, QNetworkRequest::Priority priority, const QByteArray &payload) { - auto url = requestUrl(); + auto url = requestUrl(defaultConnectionType()); url.setPath(QString("/stream%1.html").arg(streamNumber)); QNetworkRequest request(url); @@ -554,10 +597,24 @@ void tst_Http2::sendRequest(int streamNumber, connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); } -QUrl tst_Http2::requestUrl() const +QUrl tst_Http2::requestUrl(H2Type connectionType) const { +#if !QT_CONFIG(ssl) + Q_ASSERT(connectionType != H2Type::h2Alpn && connectionType != H2Type::h2Direct); +#endif static auto url = QUrl(QLatin1String(clearTextHTTP2 ? "http://127.0.0.1" : "https://127.0.0.1")); url.setPort(serverPort); + // Clear text may mean no-TLS-at-all or crappy-TLS-without-ALPN. + switch (connectionType) { + case H2Type::h2Alpn: + case H2Type::h2Direct: + url.setScheme(QStringLiteral("https")); + break; + case H2Type::h2c: + case H2Type::h2cDirect: + url.setScheme(QStringLiteral("http")); + break; + } return url; } From d92f63122b2980c20b9b5de88d3671b4eae6655b Mon Sep 17 00:00:00 2001 From: Paul Lemire Date: Fri, 29 Mar 2019 09:29:58 +0100 Subject: [PATCH 07/15] QShaderGenerator: stop abusing from auto everywhere Leads to a huge loss of context for people trying to understand and maintain that code. Replacing with proper types to ensure better readability and maintenance. Change-Id: I9900b743e8b7fe11bcc7db9ce3191c89f8718afc Reviewed-by: Mike Krus --- src/gui/util/qshadergenerator.cpp | 70 ++++++++++++------------ src/gui/util/qshadergraph.cpp | 42 +++++++-------- src/gui/util/qshadergraphloader.cpp | 76 +++++++++++++------------- src/gui/util/qshadernodesloader.cpp | 82 ++++++++++++++--------------- 4 files changed, 137 insertions(+), 133 deletions(-) diff --git a/src/gui/util/qshadergenerator.cpp b/src/gui/util/qshadergenerator.cpp index ae45c03fd15..60cf5a2fc53 100644 --- a/src/gui/util/qshadergenerator.cpp +++ b/src/gui/util/qshadergenerator.cpp @@ -260,21 +260,22 @@ namespace QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node, const QShaderFormat &format) { - auto result = original; + QByteArray result = original; - for (const auto ¶meterName : node.parameterNames()) { - const auto placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8()); - const auto parameter = node.parameter(parameterName); + const QStringList parameterNames = node.parameterNames(); + for (const QString ¶meterName : parameterNames) { + const QByteArray placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8()); + const QVariant parameter = node.parameter(parameterName); if (parameter.userType() == qMetaTypeId()) { - const auto qualifier = parameter.value(); - const auto value = toGlsl(qualifier, format); + const QShaderLanguage::StorageQualifier qualifier = parameter.value(); + const QByteArray value = toGlsl(qualifier, format); result.replace(placeholder, value); } else if (parameter.userType() == qMetaTypeId()) { - const auto type = parameter.value(); - const auto value = toGlsl(type); + const QShaderLanguage::VariableType type = parameter.value(); + const QByteArray value = toGlsl(type); result.replace(placeholder, value); } else { - const auto value = parameter.toString().toUtf8(); + const QByteArray value = parameter.toString().toUtf8(); result.replace(placeholder, value); } } @@ -288,20 +289,20 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) auto code = QByteArrayList(); if (format.isValid()) { - const auto isGLES = format.api() == QShaderFormat::OpenGLES; - const auto major = format.version().majorVersion(); - const auto minor = format.version().minorVersion(); + const bool isGLES = format.api() == QShaderFormat::OpenGLES; + const int major = format.version().majorVersion(); + const int minor = format.version().minorVersion(); - const auto version = major == 2 && isGLES ? 100 - : major == 3 && isGLES ? 300 - : major == 2 ? 100 + 10 * (minor + 1) - : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3) - : major * 100 + minor * 10; + const int version = major == 2 && isGLES ? 100 + : major == 3 && isGLES ? 300 + : major == 2 ? 100 + 10 * (minor + 1) + : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3) + : major * 100 + minor * 10; - const auto profile = isGLES && version > 100 ? QByteArrayLiteral(" es") - : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core") - : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility") - : QByteArray(); + const QByteArray profile = isGLES && version > 100 ? QByteArrayLiteral(" es") + : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core") + : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility") + : QByteArray(); code << (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile); code << QByteArray(); @@ -313,9 +314,11 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) [enabledLayers] (const QString &s) { return enabledLayers.contains(s); }); }; - for (const auto &node : graph.nodes()) { + const QVector nodes = graph.nodes(); + for (const QShaderNode &node : nodes) { if (intersectsEnabledLayers(node.layers())) { - for (const auto &snippet : node.rule(format).headerSnippets) { + const QByteArrayList headerSnippets = node.rule(format).headerSnippets; + for (const QByteArray &snippet : headerSnippets) { code << replaceParameters(snippet, node, format); } } @@ -325,17 +328,18 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) code << QByteArrayLiteral("void main()"); code << QByteArrayLiteral("{"); - for (const auto &statement : graph.createStatements(enabledLayers)) { - const auto node = statement.node; - auto line = node.rule(format).substitution; - for (const auto &port : node.ports()) { - const auto portName = port.name; - const auto portDirection = port.direction; - const auto isInput = port.direction == QShaderNodePort::Input; + for (const QShaderGraph::Statement &statement : graph.createStatements(enabledLayers)) { + const QShaderNode node = statement.node; + QByteArray line = node.rule(format).substitution; + const QVector ports = node.ports(); + for (const QShaderNodePort &port : ports) { + const QString portName = port.name; + const QShaderNodePort::Direction portDirection = port.direction; + const bool isInput = port.direction == QShaderNodePort::Input; - const auto portIndex = statement.portIndex(portDirection, portName); - const auto variableIndex = isInput ? statement.inputs.at(portIndex) - : statement.outputs.at(portIndex); + const int portIndex = statement.portIndex(portDirection, portName); + const int variableIndex = isInput ? statement.inputs.at(portIndex) + : statement.outputs.at(portIndex); if (variableIndex < 0) continue; diff --git a/src/gui/util/qshadergraph.cpp b/src/gui/util/qshadergraph.cpp index 828c709a126..40b85ac469c 100644 --- a/src/gui/util/qshadergraph.cpp +++ b/src/gui/util/qshadergraph.cpp @@ -82,8 +82,8 @@ namespace auto statement = QShaderGraph::Statement(); statement.node = node; - const auto ports = node.ports(); - for (const auto &port : ports) { + const QVector ports = node.ports(); + for (const QShaderNodePort &port : ports) { if (port.direction == QShaderNodePort::Input) { statement.inputs.append(-1); } else { @@ -99,19 +99,19 @@ namespace const QUuid &uuid) { auto targetStatement = idHash.value(uuid); - for (const auto &edge : edges) { + for (const QShaderGraph::Edge &edge : edges) { if (edge.targetNodeUuid != uuid) continue; - const auto sourceStatement = idHash.value(edge.sourceNodeUuid); - const auto sourcePortIndex = sourceStatement.portIndex(QShaderNodePort::Output, edge.sourcePortName); - const auto targetPortIndex = targetStatement.portIndex(QShaderNodePort::Input, edge.targetPortName); + const QShaderGraph::Statement sourceStatement = idHash.value(edge.sourceNodeUuid); + const int sourcePortIndex = sourceStatement.portIndex(QShaderNodePort::Output, edge.sourcePortName); + const int targetPortIndex = targetStatement.portIndex(QShaderNodePort::Input, edge.targetPortName); if (sourcePortIndex < 0 || targetPortIndex < 0) continue; - const auto &sourceOutputs = sourceStatement.outputs; - auto &targetInputs = targetStatement.inputs; + const QVector sourceOutputs = sourceStatement.outputs; + QVector &targetInputs = targetStatement.inputs; targetInputs[targetPortIndex] = sourceOutputs[sourcePortIndex]; } return targetStatement; @@ -125,9 +125,9 @@ QUuid QShaderGraph::Statement::uuid() const Q_DECL_NOTHROW int QShaderGraph::Statement::portIndex(QShaderNodePort::Direction direction, const QString &portName) const Q_DECL_NOTHROW { - const auto ports = node.ports(); + const QVector ports = node.ports(); int index = 0; - for (const auto &port : ports) { + for (const QShaderNodePort &port : ports) { if (port.name == portName && port.direction == direction) return index; else if (port.direction == direction) @@ -180,7 +180,7 @@ QVector QShaderGraph::createStatements(const QStringLis [enabledLayers] (const QString &s) { return enabledLayers.contains(s); }); }; - const auto enabledNodes = [this, intersectsEnabledLayers] { + const QVector enabledNodes = [this, intersectsEnabledLayers] { auto res = QVector(); std::copy_if(m_nodes.cbegin(), m_nodes.cend(), std::back_inserter(res), @@ -190,7 +190,7 @@ QVector QShaderGraph::createStatements(const QStringLis return res; }(); - const auto enabledEdges = [this, intersectsEnabledLayers] { + const QVector enabledEdges = [this, intersectsEnabledLayers] { auto res = QVector(); std::copy_if(m_edges.cbegin(), m_edges.cend(), std::back_inserter(res), @@ -200,18 +200,18 @@ QVector QShaderGraph::createStatements(const QStringLis return res; }(); - const auto idHash = [enabledNodes] { + const QHash idHash = [enabledNodes] { auto nextVarId = 0; auto res = QHash(); - for (const auto &node : enabledNodes) + for (const QShaderNode &node : enabledNodes) res.insert(node.uuid(), nodeToStatement(node, nextVarId)); return res; }(); auto result = QVector(); - auto currentEdges = enabledEdges; - auto currentUuids = [enabledNodes] { - const auto inputs = copyOutputNodes(enabledNodes); + QVector currentEdges = enabledEdges; + QVector currentUuids = [enabledNodes] { + const QVector inputs = copyOutputNodes(enabledNodes); auto res = QVector(); std::transform(inputs.cbegin(), inputs.cend(), std::back_inserter(res), @@ -226,14 +226,14 @@ QVector QShaderGraph::createStatements(const QStringLis // because we want to track the dependencies from the output nodes and not the // input nodes while (!currentUuids.isEmpty()) { - const auto uuid = currentUuids.takeFirst(); + const QUuid uuid = currentUuids.takeFirst(); result.append(completeStatement(idHash, enabledEdges, uuid)); - const auto outgoing = outgoingEdges(currentEdges, uuid); - for (const auto &outgoingEdge : outgoing) { + const QVector outgoing = outgoingEdges(currentEdges, uuid); + for (const QShaderGraph::Edge &outgoingEdge : outgoing) { currentEdges.removeAll(outgoingEdge); const QUuid nextUuid = outgoingEdge.sourceNodeUuid; - const auto incoming = incomingEdges(currentEdges, nextUuid); + const QVector incoming = incomingEdges(currentEdges, nextUuid); if (incoming.isEmpty()) { currentUuids.append(nextUuid); } diff --git a/src/gui/util/qshadergraphloader.cpp b/src/gui/util/qshadergraphloader.cpp index 99a9f7869e8..b9d8318655f 100644 --- a/src/gui/util/qshadergraphloader.cpp +++ b/src/gui/util/qshadergraphloader.cpp @@ -99,7 +99,7 @@ void QShaderGraphLoader::load() return; auto error = QJsonParseError(); - const auto document = QJsonDocument::fromJson(m_device->readAll(), &error); + const QJsonDocument document = QJsonDocument::fromJson(m_device->readAll(), &error); if (error.error != QJsonParseError::NoError) { qWarning() << "Invalid JSON document:" << error.errorString(); @@ -113,16 +113,16 @@ void QShaderGraphLoader::load() return; } - const auto root = document.object(); + const QJsonObject root = document.object(); - const auto nodesValue = root.value(QStringLiteral("nodes")); + const QJsonValue nodesValue = root.value(QStringLiteral("nodes")); if (!nodesValue.isArray()) { qWarning() << "Invalid nodes property, should be an array"; m_status = Error; return; } - const auto edgesValue = root.value(QStringLiteral("edges")); + const QJsonValue edgesValue = root.value(QStringLiteral("edges")); if (!edgesValue.isArray()) { qWarning() << "Invalid edges property, should be an array"; m_status = Error; @@ -131,7 +131,7 @@ void QShaderGraphLoader::load() bool hasError = false; - const auto prototypesValue = root.value(QStringLiteral("prototypes")); + const QJsonValue prototypesValue = root.value(QStringLiteral("prototypes")); if (!prototypesValue.isUndefined()) { if (prototypesValue.isObject()) { QShaderNodesLoader loader; @@ -144,60 +144,60 @@ void QShaderGraphLoader::load() } } - const auto nodes = nodesValue.toArray(); - for (const auto &nodeValue : nodes) { + const QJsonArray nodes = nodesValue.toArray(); + for (const QJsonValue &nodeValue : nodes) { if (!nodeValue.isObject()) { qWarning() << "Invalid node found"; hasError = true; continue; } - const auto nodeObject = nodeValue.toObject(); + const QJsonObject nodeObject = nodeValue.toObject(); - const auto uuidString = nodeObject.value(QStringLiteral("uuid")).toString(); - const auto uuid = QUuid(uuidString); + const QString uuidString = nodeObject.value(QStringLiteral("uuid")).toString(); + const QUuid uuid = QUuid(uuidString); if (uuid.isNull()) { qWarning() << "Invalid UUID found in node:" << uuidString; hasError = true; continue; } - const auto type = nodeObject.value(QStringLiteral("type")).toString(); + const QString type = nodeObject.value(QStringLiteral("type")).toString(); if (!m_prototypes.contains(type)) { qWarning() << "Unsupported node type found:" << type; hasError = true; continue; } - const auto layersArray = nodeObject.value(QStringLiteral("layers")).toArray(); + const QJsonArray layersArray = nodeObject.value(QStringLiteral("layers")).toArray(); auto layers = QStringList(); - for (const auto &layerValue : layersArray) { + for (const QJsonValue &layerValue : layersArray) { layers.append(layerValue.toString()); } - auto node = m_prototypes.value(type); + QShaderNode node = m_prototypes.value(type); node.setUuid(uuid); node.setLayers(layers); - const auto parametersValue = nodeObject.value(QStringLiteral("parameters")); + const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters")); if (parametersValue.isObject()) { - const auto parametersObject = parametersValue.toObject(); - for (const auto ¶meterName : parametersObject.keys()) { - const auto parameterValue = parametersObject.value(parameterName); + const QJsonObject parametersObject = parametersValue.toObject(); + for (const QString ¶meterName : parametersObject.keys()) { + const QJsonValue parameterValue = parametersObject.value(parameterName); if (parameterValue.isObject()) { - const auto parameterObject = parameterValue.toObject(); - const auto type = parameterObject.value(QStringLiteral("type")).toString(); - const auto typeId = QMetaType::type(type.toUtf8()); + const QJsonObject parameterObject = parameterValue.toObject(); + const QString type = parameterObject.value(QStringLiteral("type")).toString(); + const int typeId = QMetaType::type(type.toUtf8()); - const auto value = parameterObject.value(QStringLiteral("value")).toString(); + const QString value = parameterObject.value(QStringLiteral("value")).toString(); auto variant = QVariant(value); if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) { - const auto metaObject = QMetaType::metaObjectForType(typeId); - const auto className = metaObject->className(); - const auto enumName = type.mid(static_cast(qstrlen(className)) + 2).toUtf8(); - const auto metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName)); - const auto enumValue = metaEnum.keyToValue(value.toUtf8()); + const QMetaObject *metaObject = QMetaType::metaObjectForType(typeId); + const char *className = metaObject->className(); + const QByteArray enumName = type.mid(static_cast(qstrlen(className)) + 2).toUtf8(); + const QMetaEnum metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName)); + const int enumValue = metaEnum.keyToValue(value.toUtf8()); variant = QVariant(enumValue); variant.convert(typeId); } else { @@ -213,39 +213,39 @@ void QShaderGraphLoader::load() m_graph.addNode(node); } - const auto edges = edgesValue.toArray(); - for (const auto &edgeValue : edges) { + const QJsonArray edges = edgesValue.toArray(); + for (const QJsonValue &edgeValue : edges) { if (!edgeValue.isObject()) { qWarning() << "Invalid edge found"; hasError = true; continue; } - const auto edgeObject = edgeValue.toObject(); + const QJsonObject edgeObject = edgeValue.toObject(); - const auto sourceUuidString = edgeObject.value(QStringLiteral("sourceUuid")).toString(); - const auto sourceUuid = QUuid(sourceUuidString); + const QString sourceUuidString = edgeObject.value(QStringLiteral("sourceUuid")).toString(); + const QUuid sourceUuid = QUuid(sourceUuidString); if (sourceUuid.isNull()) { qWarning() << "Invalid source UUID found in edge:" << sourceUuidString; hasError = true; continue; } - const auto sourcePort = edgeObject.value(QStringLiteral("sourcePort")).toString(); + const QString sourcePort = edgeObject.value(QStringLiteral("sourcePort")).toString(); - const auto targetUuidString = edgeObject.value(QStringLiteral("targetUuid")).toString(); - const auto targetUuid = QUuid(targetUuidString); + const QString targetUuidString = edgeObject.value(QStringLiteral("targetUuid")).toString(); + const QUuid targetUuid = QUuid(targetUuidString); if (targetUuid.isNull()) { qWarning() << "Invalid target UUID found in edge:" << targetUuidString; hasError = true; continue; } - const auto targetPort = edgeObject.value(QStringLiteral("targetPort")).toString(); + const QString targetPort = edgeObject.value(QStringLiteral("targetPort")).toString(); - const auto layersArray = edgeObject.value(QStringLiteral("layers")).toArray(); + const QJsonArray layersArray = edgeObject.value(QStringLiteral("layers")).toArray(); auto layers = QStringList(); - for (const auto &layerValue : layersArray) { + for (const QJsonValue &layerValue : layersArray) { layers.append(layerValue.toString()); } diff --git a/src/gui/util/qshadernodesloader.cpp b/src/gui/util/qshadernodesloader.cpp index 692653ee446..9badbb94df7 100644 --- a/src/gui/util/qshadernodesloader.cpp +++ b/src/gui/util/qshadernodesloader.cpp @@ -84,7 +84,7 @@ void QShaderNodesLoader::load() return; auto error = QJsonParseError(); - const auto document = QJsonDocument::fromJson(m_device->readAll(), &error); + const QJsonDocument document = QJsonDocument::fromJson(m_device->readAll(), &error); if (error.error != QJsonParseError::NoError) { qWarning() << "Invalid JSON document:" << error.errorString(); @@ -98,7 +98,7 @@ void QShaderNodesLoader::load() return; } - const auto root = document.object(); + const QJsonObject root = document.object(); load(root); } @@ -106,22 +106,22 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject) { bool hasError = false; - for (const auto &property : prototypesObject.keys()) { - const auto nodeValue = prototypesObject.value(property); + for (const QString &property : prototypesObject.keys()) { + const QJsonValue nodeValue = prototypesObject.value(property); if (!nodeValue.isObject()) { qWarning() << "Invalid node found"; hasError = true; break; } - const auto nodeObject = nodeValue.toObject(); + const QJsonObject nodeObject = nodeValue.toObject(); auto node = QShaderNode(); - const auto inputsValue = nodeObject.value(QStringLiteral("inputs")); + const QJsonValue inputsValue = nodeObject.value(QStringLiteral("inputs")); if (inputsValue.isArray()) { - const auto inputsArray = inputsValue.toArray(); - for (const auto &inputValue : inputsArray) { + const QJsonArray inputsArray = inputsValue.toArray(); + for (const QJsonValue &inputValue : inputsArray) { if (!inputValue.isString()) { qWarning() << "Non-string value in inputs"; hasError = true; @@ -135,10 +135,10 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject) } } - const auto outputsValue = nodeObject.value(QStringLiteral("outputs")); + const QJsonValue outputsValue = nodeObject.value(QStringLiteral("outputs")); if (outputsValue.isArray()) { - const auto outputsArray = outputsValue.toArray(); - for (const auto &outputValue : outputsArray) { + const QJsonArray outputsArray = outputsValue.toArray(); + for (const QJsonValue &outputValue : outputsArray) { if (!outputValue.isString()) { qWarning() << "Non-string value in outputs"; hasError = true; @@ -152,25 +152,25 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject) } } - const auto parametersValue = nodeObject.value(QStringLiteral("parameters")); + const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters")); if (parametersValue.isObject()) { - const auto parametersObject = parametersValue.toObject(); - for (const auto ¶meterName : parametersObject.keys()) { - const auto parameterValue = parametersObject.value(parameterName); + const QJsonObject parametersObject = parametersValue.toObject(); + for (const QString ¶meterName : parametersObject.keys()) { + const QJsonValue parameterValue = parametersObject.value(parameterName); if (parameterValue.isObject()) { - const auto parameterObject = parameterValue.toObject(); - const auto type = parameterObject.value(QStringLiteral("type")).toString(); - const auto typeId = QMetaType::type(type.toUtf8()); + const QJsonObject parameterObject = parameterValue.toObject(); + const QString type = parameterObject.value(QStringLiteral("type")).toString(); + const int typeId = QMetaType::type(type.toUtf8()); - const auto value = parameterObject.value(QStringLiteral("value")).toString(); + const QString value = parameterObject.value(QStringLiteral("value")).toString(); auto variant = QVariant(value); if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) { - const auto metaObject = QMetaType::metaObjectForType(typeId); - const auto className = metaObject->className(); - const auto enumName = type.mid(static_cast(qstrlen(className)) + 2).toUtf8(); - const auto metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName)); - const auto enumValue = metaEnum.keyToValue(value.toUtf8()); + const QMetaObject *metaObject = QMetaType::metaObjectForType(typeId); + const char *className = metaObject->className(); + const QByteArray enumName = type.mid(static_cast(qstrlen(className)) + 2).toUtf8(); + const QMetaEnum metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName)); + const int enumValue = metaEnum.keyToValue(value.toUtf8()); variant = QVariant(enumValue); variant.convert(typeId); } else { @@ -183,36 +183,36 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject) } } - const auto rulesValue = nodeObject.value(QStringLiteral("rules")); + const QJsonValue rulesValue = nodeObject.value(QStringLiteral("rules")); if (rulesValue.isArray()) { - const auto rulesArray = rulesValue.toArray(); - for (const auto &ruleValue : rulesArray) { + const QJsonArray rulesArray = rulesValue.toArray(); + for (const QJsonValue &ruleValue : rulesArray) { if (!ruleValue.isObject()) { qWarning() << "Rules should be objects"; hasError = true; break; } - const auto ruleObject = ruleValue.toObject(); + const QJsonObject ruleObject = ruleValue.toObject(); - const auto formatValue = ruleObject.value(QStringLiteral("format")); + const QJsonValue formatValue = ruleObject.value(QStringLiteral("format")); if (!formatValue.isObject()) { qWarning() << "Format is mandatory in rules and should be an object"; hasError = true; break; } - const auto formatObject = formatValue.toObject(); + const QJsonObject formatObject = formatValue.toObject(); auto format = QShaderFormat(); - const auto apiValue = formatObject.value(QStringLiteral("api")); + const QJsonValue apiValue = formatObject.value(QStringLiteral("api")); if (!apiValue.isString()) { qWarning() << "Format API must be a string"; hasError = true; break; } - const auto api = apiValue.toString(); + const QString api = apiValue.toString(); format.setApi(api == QStringLiteral("OpenGLES") ? QShaderFormat::OpenGLES : api == QStringLiteral("OpenGLNoProfile") ? QShaderFormat::OpenGLNoProfile : api == QStringLiteral("OpenGLCoreProfile") ? QShaderFormat::OpenGLCoreProfile @@ -224,8 +224,8 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject) break; } - const auto majorValue = formatObject.value(QStringLiteral("major")); - const auto minorValue = formatObject.value(QStringLiteral("minor")); + const QJsonValue majorValue = formatObject.value(QStringLiteral("major")); + const QJsonValue minorValue = formatObject.value(QStringLiteral("minor")); if (!majorValue.isDouble() || !minorValue.isDouble()) { qWarning() << "Format major and minor version must be values"; hasError = true; @@ -233,28 +233,28 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject) } format.setVersion(QVersionNumber(majorValue.toInt(), minorValue.toInt())); - const auto extensionsValue = formatObject.value(QStringLiteral("extensions")); - const auto extensionsArray = extensionsValue.toArray(); + const QJsonValue extensionsValue = formatObject.value(QStringLiteral("extensions")); + const QJsonArray extensionsArray = extensionsValue.toArray(); auto extensions = QStringList(); std::transform(extensionsArray.constBegin(), extensionsArray.constEnd(), std::back_inserter(extensions), [] (const QJsonValue &extensionValue) { return extensionValue.toString(); }); format.setExtensions(extensions); - const auto vendor = formatObject.value(QStringLiteral("vendor")).toString(); + const QString vendor = formatObject.value(QStringLiteral("vendor")).toString(); format.setVendor(vendor); - const auto substitutionValue = ruleObject.value(QStringLiteral("substitution")); + const QJsonValue substitutionValue = ruleObject.value(QStringLiteral("substitution")); if (!substitutionValue.isString()) { qWarning() << "Substitution needs to be a string"; hasError = true; break; } - const auto substitution = substitutionValue.toString().toUtf8(); + const QByteArray substitution = substitutionValue.toString().toUtf8(); - const auto snippetsValue = ruleObject.value(QStringLiteral("headerSnippets")); - const auto snippetsArray = snippetsValue.toArray(); + const QJsonValue snippetsValue = ruleObject.value(QStringLiteral("headerSnippets")); + const QJsonArray snippetsArray = snippetsValue.toArray(); auto snippets = QByteArrayList(); std::transform(snippetsArray.constBegin(), snippetsArray.constEnd(), std::back_inserter(snippets), From e251f1184925ad4f0c2e18843343e9df690d8302 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Fri, 5 Apr 2019 15:31:14 +0200 Subject: [PATCH 08/15] printsupport: Fix clang-cl warning qprintengine_win.cpp(1117,14): warning: comparison of two values with different enumeration types in switch statement ('QPrint::DuplexMode' and 'QPrinter::DuplexMode') [-Wenum-compare-switch] case QPrinter::DuplexShortSide: Change-Id: Ib5028d80ecf7f6bb9eb0562e5c137acfeb709a14 Reviewed-by: Friedemann Kleint --- src/printsupport/kernel/qprintengine_win.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/printsupport/kernel/qprintengine_win.cpp b/src/printsupport/kernel/qprintengine_win.cpp index e3a5c3d2e84..add57e9d951 100644 --- a/src/printsupport/kernel/qprintengine_win.cpp +++ b/src/printsupport/kernel/qprintengine_win.cpp @@ -1105,16 +1105,16 @@ void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant & if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice.supportedDuplexModes().contains(mode)) break; switch (mode) { - case QPrinter::DuplexNone: + case QPrint::DuplexNone: d->devMode->dmDuplex = DMDUP_SIMPLEX; break; - case QPrinter::DuplexAuto: + case QPrint::DuplexAuto: d->devMode->dmDuplex = d->m_pageLayout.orientation() == QPageLayout::Landscape ? DMDUP_HORIZONTAL : DMDUP_VERTICAL; break; - case QPrinter::DuplexLongSide: + case QPrint::DuplexLongSide: d->devMode->dmDuplex = DMDUP_VERTICAL; break; - case QPrinter::DuplexShortSide: + case QPrint::DuplexShortSide: d->devMode->dmDuplex = DMDUP_HORIZONTAL; break; default: From 0adb78a8479417c902cde9ba050edc2f828ecd83 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Fri, 5 Apr 2019 11:13:30 +0200 Subject: [PATCH 09/15] QElapsedTimer: Remove unused static nanosecondsToTicks method Fixes clang-cl warning qelapsedtimer_win.cpp(80,22): warning: unused function 'nanosecondsToTicks' [-Wunused-function] Change-Id: I1ff334049fcf4b265fe97235b7daf06969331313 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qelapsedtimer_win.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/corelib/kernel/qelapsedtimer_win.cpp b/src/corelib/kernel/qelapsedtimer_win.cpp index a63290d2f82..3beb158b8ae 100644 --- a/src/corelib/kernel/qelapsedtimer_win.cpp +++ b/src/corelib/kernel/qelapsedtimer_win.cpp @@ -77,15 +77,6 @@ static inline qint64 ticksToNanoseconds(qint64 ticks) return ticks * 1000000; } -static inline qint64 nanosecondsToTicks(qint64 nsec) -{ - if (counterFrequency > 0) { - // QueryPerformanceCounter uses an arbitrary frequency - return double(nsec) * counterFrequency / 1000000000.; - } - // GetTickCount(64) uses milliseconds - return nsec / 1000000; -} static quint64 getTickCount() { From 9617791075e02f19675f60060cb5776eb0e09edd Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Wed, 3 Apr 2019 14:30:56 +0200 Subject: [PATCH 10/15] Let QINSTALL copy hidden files when installing directories When installing directories, QINSTALL must not ignore contained hidden files to be consistent with the old INSTALL_DIR. Fixes: QTBUG-66835 Change-Id: I3a7c952dcac9732d5b17c5a258f87ca277b388d2 Reviewed-by: Kai Koehne --- qmake/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qmake/main.cpp b/qmake/main.cpp index 69dff1073e4..a598296898a 100644 --- a/qmake/main.cpp +++ b/qmake/main.cpp @@ -306,7 +306,7 @@ static int installFileOrDirectory(const QString &source, const QString &target, } else if (fi.isDir()) { QDir::current().mkpath(target); - QDirIterator it(source, QDir::AllEntries | QDir::NoDotAndDotDot); + QDirIterator it(source, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden); while (it.hasNext()) { it.next(); const QFileInfo &entry = it.fileInfo(); From 6d4a456a28282973d2501e0b16f09cafb316bb0a Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 5 Apr 2019 11:25:13 +0200 Subject: [PATCH 11/15] Fix off-by-one error in QTranslatorPrivate::do_load() The central loop starts by reading five bytes; but the loop condition only checked that four were available. Change-Id: I244cecacabeffbac10ad94081f32847f912d95d9 Reviewed-by: Kai Koehne Reviewed-by: Thiago Macieira Reviewed-by: hjk --- src/corelib/kernel/qtranslator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index 929554f6bc5..63aeed07c3b 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -824,7 +824,7 @@ bool QTranslatorPrivate::do_load(const uchar *data, int len, const QString &dire data += MagicLength; QStringList dependencies; - while (data < end - 4) { + while (data < end - 5) { quint8 tag = read8(data++); quint32 blockLen = read32(data); data += 4; From 5f2afe18ccb0bbe258d4016ef65218cd3656cac2 Mon Sep 17 00:00:00 2001 From: Milian Wolff Date: Tue, 9 Apr 2019 13:38:06 +0200 Subject: [PATCH 12/15] Fix QMetaObject::newInstance on non-QObject meta object QMetaObject::newInstance returns a QObject, thus it's not possible to create a new instance of a Q_GADGET using this function. Previously, we returned a non-null QObject pointer for such scenarios, which then leads to crashes when one tries to use it. Now, we check whether the meta object inherits QObject's meta object, and error out early otherwise. Change-Id: I7b1fb6c8d48b3e98161894be2f281a491963345e Reviewed-by: Thiago Macieira --- src/corelib/kernel/qmetaobject.cpp | 6 ++++++ .../corelib/kernel/qmetaobject/tst_qmetaobject.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index b8b5c0de469..666bb6eace6 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -235,6 +235,12 @@ QObject *QMetaObject::newInstance(QGenericArgument val0, QGenericArgument val8, QGenericArgument val9) const { + if (!inherits(&QObject::staticMetaObject)) + { + qWarning("QMetaObject::newInstance: type %s does not inherit QObject", className()); + return nullptr; + } + QByteArray constructorName = className(); { int idx = constructorName.lastIndexOf(':'); diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 9855bec5208..cfe1443fd33 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -40,6 +40,13 @@ struct MyStruct int i; }; +class MyGadget +{ + Q_GADGET +public: + Q_INVOKABLE MyGadget() {} +}; + namespace MyNamespace { // Used in tst_QMetaObject::checkScope class MyClass : public QObject @@ -1207,6 +1214,12 @@ void tst_QMetaObject::invokeMetaConstructor() QCOMPARE(obj2->parent(), (QObject*)&obj); QVERIFY(qobject_cast(obj2) != 0); } + // gadget shouldn't return a valid pointer + { + QCOMPARE(MyGadget::staticMetaObject.constructorCount(), 1); + QTest::ignoreMessage(QtWarningMsg, "QMetaObject::newInstance: type MyGadget does not inherit QObject"); + QVERIFY(!MyGadget::staticMetaObject.newInstance()); + } } void tst_QMetaObject::invokeTypedefTypes() From 37c24c6b1b65c2ff9808e5c2e2a18cf496e4a9db Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Thu, 4 Apr 2019 20:16:18 +0200 Subject: [PATCH 13/15] QTableView: fix Ctrl+End behavior with disabled columns Ctrl+End moves the visual index to the bottom left index. This does not work correctly when the item in the bottom left is disabled. It should be fixed with 7863be311570fa219066df5fe8720d5b92ddb680 but does not due to a typo. Fix the typo by using the newly calculated visualColumn instead the initial column. There are cases where the algorithm still does not work as expected when there are more disabled items but fixing them would add a lot of complexity with no (much) gain. Fixes: QTBUG-72400 Change-Id: Ie90f6b3e41e00f54e826c2b4e7303e85ac1e4115 Reviewed-by: Richard Moe Gustavsen --- src/widgets/itemviews/qtableview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index 9c509583e69..e1280b250aa 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -1870,7 +1870,7 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi visualColumn = d->nextActiveVisualColumn(visualRow, right, -1, QTableViewPrivate::SearchDirection::Decreasing); if (modifiers & Qt::ControlModifier) - visualRow = d->nextActiveVisualRow(bottom, current.column(), -1, + visualRow = d->nextActiveVisualRow(bottom, visualColumn, -1, QTableViewPrivate::SearchDirection::Decreasing); break; case MovePageUp: { From 1029a2e01051cd35452941e7b49b36520481cdf0 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Fri, 29 Mar 2019 21:18:26 +0100 Subject: [PATCH 14/15] QTableView: don't draw additional grid lines top and left QTableView drew additional grid lines on the top and left side when the corresponding header was not visible and the ScrollMode was ScrollPerItem. After 8f2bacea41443af8564ff78f284016b6615a001b they were also drawn for ScrolPerPixel for consistency. But they are not needed at all and only create visual artifacts. Therefore remove the drawing of the additional lines completely. Fixes: QTBUG-74706 Change-Id: I5c77d53a2eeefab9b9bfe0efea6439f5afede4ac Reviewed-by: Friedemann Kleint Reviewed-by: Andy Shaw --- src/widgets/itemviews/qtableview.cpp | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index e1280b250aa..8fe5b703325 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -1573,29 +1573,6 @@ void QTableView::paintEvent(QPaintEvent *event) colp += columnWidth(col) - gridSize; painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom()); } - - //draw the top & left grid lines if the headers are not visible. - //We do update this line when subsequent scroll happen (see scrollContentsBy) - if (horizontalHeader->isHidden() && top == 0) { - const int row = verticalHeader->logicalIndex(top); - if (!verticalHeader->isSectionHidden(row)) { - const int rowY = rowViewportPosition(row) + offset.y(); - if (rowY == dirtyArea.top()) - painter.drawLine(dirtyArea.left(), rowY, dirtyArea.right(), rowY); - } - } - if (verticalHeader->isHidden() && left == 0) { - const int col = horizontalHeader->logicalIndex(left); - if (!horizontalHeader->isSectionHidden(col)) { - int colX = columnViewportPosition(col) + offset.x(); - if (!isLeftToRight()) - colX += columnWidth(left) - 1; - if (isLeftToRight() && colX == dirtyArea.left()) - painter.drawLine(colX, dirtyArea.top(), colX, dirtyArea.bottom()); - if (!isLeftToRight() && colX == dirtyArea.right()) - painter.drawLine(colX, dirtyArea.top(), colX, dirtyArea.bottom()); - } - } painter.setPen(old); } } From 0bdded64accc3f654b2d12d9bfaf64f842cedf26 Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 5 Apr 2019 09:50:16 +0200 Subject: [PATCH 15/15] eglfs_viv_wl: Clean up the wl_display Task-number: QTBUG-74879 Change-Id: Idb39a39a10bccb1822bdb80343fbe1b5c92560e9 Reviewed-by: Laszlo Agocs --- .../eglfs_viv_wl/qeglfsvivwlintegration.cpp | 5 +++++ .../deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp index 3bdae239cd5..296e301f078 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp @@ -66,6 +66,11 @@ void QEglFSVivWaylandIntegration::platformInit() mScreenSize.setWidth(width); } +void QEglFSVivWaylandIntegration::platformDestroy() +{ + wl_display_destroy(mWaylandDisplay); +} + QSize QEglFSVivWaylandIntegration::screenSize() const { return mScreenSize; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h index 2c49eb64407..bee23dfb3ec 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h @@ -49,6 +49,7 @@ class QEglFSVivWaylandIntegration : public QEglFSDeviceIntegration { public: void platformInit() override; + void platformDestroy() override; QSize screenSize() const override; EGLNativeWindowType createNativeWindow(QPlatformWindow *window, const QSize &size, const QSurfaceFormat &format) override; void destroyNativeWindow(EGLNativeWindowType window) override;