diff --git a/qmake/main.cpp b/qmake/main.cpp index a4ef79227b..a598296898 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) { @@ -308,18 +306,18 @@ 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(); 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; } diff --git a/src/3rdparty/tinycbor/src/cbor.h b/src/3rdparty/tinycbor/src/cbor.h index 5c7ba74e39..3672bd0d98 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 52a4025be1..38804cfa6f 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 971230ea61..90a7d2ced6 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 2b9491d34d..bd10efc9c7 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 c33fb605aa..8b00cfec1f 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 f30c522601..458f55eb10 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 74c480bc51..2b10004faa 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/kernel/qelapsedtimer_win.cpp b/src/corelib/kernel/qelapsedtimer_win.cpp index d8a50a8277..8becb17461 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() { diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 3bcad8cf32..56217262f2 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/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index a2111a084f..dc0ab9f08a 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -817,7 +817,7 @@ bool QTranslatorPrivate::do_load(const uchar *data, qsizetype len, const QString data += MagicLength; QStringList dependencies; - while (data < end - 4) { + while (data < end - 5) { quint8 tag = read8(data++); quint32 blockLen = read32(data); data += 4; diff --git a/src/corelib/serialization/qcborstream.cpp b/src/corelib/serialization/qcborstream.cpp index 20a7e14dde..1392b4d8d6 100644 --- a/src/corelib/serialization/qcborstream.cpp +++ b/src/corelib/serialization/qcborstream.cpp @@ -101,6 +101,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); diff --git a/src/gui/util/qshadergenerator.cpp b/src/gui/util/qshadergenerator.cpp index ae45c03fd1..60cf5a2fc5 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 7485c179db..b05b710713 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 noexcept int QShaderGraph::Statement::portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept { - 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 7a047f35e9..a393e876e0 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 0b5294faf8..af565c906d 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), 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 3bdae239cd..296e301f07 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 2c49eb6440..bee23dfb3e 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; diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index ba441a1921..9de3268fc8 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; diff --git a/src/printsupport/kernel/qprintengine_win.cpp b/src/printsupport/kernel/qprintengine_win.cpp index e3a5c3d2e8..add57e9d95 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: diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index dd43c6d3e4..d4a6433c4d 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -1581,29 +1581,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); } } @@ -1878,7 +1855,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: { diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index 97ec1d3f19..ec5b6df6b3 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 d68bbfd03b..7942cafaf7 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(nullptr) {} + + static bool useFullScreenForPopup(); + mutable int layoutSpacingIndex; QStyle *proxyStyle; }; diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index 932affde07..17090efb56 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 7dc67c8efe..14964a696d 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 0fd0f9219c..ef152cd71b 100644 --- a/src/widgets/widgets/qmenu_p.h +++ b/src/widgets/widgets/qmenu_p.h @@ -349,6 +349,7 @@ public: void updateActionRects(const QRect &screen) const; QRect popupGeometry() const; QRect popupGeometry(int screen) const; + bool useFullScreenForPopup() const; int getLastVisibleAction() const; //selection diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index 3ee6a656b0..9c106c7f58 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() { diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 467d299526..350ed24c28 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 @@ -1208,6 +1215,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() diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp index 6e2220fa67..5a99d4e50c 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 ae3f084fdc..4ef4b25101 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 02c5e51cbe..53d0e7a694 100644 --- a/tests/auto/network/access/http2/tst_http2.cpp +++ b/tests/auto/network/access/http2/tst_http2.cpp @@ -60,6 +60,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 @@ -70,6 +73,7 @@ public: ~tst_Http2(); private slots: // Tests: + void singleRequest_data(); void singleRequest(); void multipleRequests(); void flowControlClientSide(); @@ -101,13 +105,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; @@ -144,6 +148,11 @@ struct ServerDeleter using ServerPtr = QScopedPointer; +H2Type defaultConnectionType() +{ + return clearTextHTTP2 ? H2Type::h2c : H2Type::h2Alpn; +} + } // unnamed namespace tst_Http2::tst_Http2() @@ -165,25 +174,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); @@ -208,7 +251,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); @@ -259,7 +302,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'); @@ -301,7 +344,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'); @@ -336,7 +379,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); @@ -344,7 +387,7 @@ void tst_Http2::pushPromise() QVERIFY(serverPort != 0); - auto url = requestUrl(); + auto url = requestUrl(defaultConnectionType()); url.setPath("/index.html"); QNetworkRequest request(url); @@ -410,14 +453,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) { @@ -457,7 +500,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(); @@ -467,7 +510,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); @@ -507,11 +550,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; @@ -536,7 +579,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); @@ -555,10 +598,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; }