diff --git a/src/corelib/kernel/qtmochelpers.h b/src/corelib/kernel/qtmochelpers.h index 9d75177645e..e5454fbf504 100644 --- a/src/corelib/kernel/qtmochelpers.h +++ b/src/corelib/kernel/qtmochelpers.h @@ -15,9 +15,11 @@ // We mean it. // -#include +#include +#include -#include // std::min +#include // std::min, std::copy_n +#include // std::is_scoped_enum #include #if 0 @@ -78,6 +80,354 @@ template constexpr auto stringData(const char (&...strings)[Nx]) # define QT_MOC_HAS_STRINGDATA 1 +struct NoType {}; + +namespace detail { +template struct UintDataBlock +{ + static constexpr uint headerSize() { return H; } + static constexpr uint payloadSize() { return P; } + uint header[H ? H : 1] = {}; + uint payload[P ? P : 1] = {}; +}; + +template struct UintDataEntry +{ + T entry; + constexpr UintDataEntry(T &&entry_) : entry(std::move(entry_)) {} +}; + +// This storage type is designed similar to libc++'s std::tuple, in that it +// derives from a type unique to each of the types in the template parameter +// pack (even if they are the same type). That way, we can refer to each of +// entries uniquely by just casting *this to that unique type. +// +// Testing reveals this to compile MUCH faster than recursive approaches and +// avoids compiler constexpr-time limits. +template struct UintDataStorage; +template struct UintDataStorage, T...> + : UintDataEntry... +{ + constexpr UintDataStorage(T &&... data) + : UintDataEntry(std::move(data))... + {} + + template constexpr void forEach(F &&f) const + { + [[maybe_unused]] auto invoke = [&f](const auto &entry) { f(entry.entry); return 0; }; + int dummy[] = { + 0, + invoke(static_cast &>(*this))... + }; + (void) dummy; + } +}; +} // namespace detail + +template struct UintData +{ + constexpr UintData(Block &&... data_) + : data(std::move(data_)...) + {} + + static constexpr uint count() { return sizeof...(Block); } + static constexpr uint headerSize() + { + // same as: + // return (0 + ... + Block::headerSize()); + // but not using the fold expression to avoid exceeding compiler limits + // (calculation done using int to get compile-time overflow checking) + int total = 0; + int sizes[] = { 0, Block::headerSize()... }; + for (int n : sizes) + total += n; + return total; + } + static constexpr uint payloadSize() + { + // ditto + int total = 0; + int sizes[] = { 0, Block::payloadSize()... }; + for (int n : sizes) + total += n; + return total; + } + static constexpr uint dataSize() { return headerSize() + payloadSize(); } + + template + constexpr void copyTo(Result &result, size_t dataoffset) const + { + uint *ptr = result.data.data(); + size_t payloadoffset = dataoffset + headerSize(); + data.forEach([&](const auto &input) { + // copy the uint data + q20::copy_n(input.header, input.headerSize(), ptr + dataoffset); + q20::copy_n(input.payload, input.payloadSize(), ptr + payloadoffset); + input.adjustOffset(ptr, uint(dataoffset), uint(payloadoffset)); + + dataoffset += input.headerSize(); + payloadoffset += input.payloadSize(); + }); + } + + template constexpr void forEach(F &&f) const + { + data.forEach(std::forward(f)); + } + +private: + detail::UintDataStorage, Block...> data; +}; + +template struct ClassInfos : detail::UintDataBlock<2 * N, 0> +{ + constexpr ClassInfos() = default; + constexpr ClassInfos(const std::array (&infos)[N]) + { + uint *out = this->header; + for (int i = 0; i < N; ++i) { + *out++ = infos[i][0]; + *out++ = infos[i][1]; + } + } +}; + +struct PropertyData : detail::UintDataBlock<5, 0> +{ + constexpr PropertyData(uint nameIndex, uint typeIndex, uint flags, uint notifyId = uint(-1), uint revision = 0) + { + this->header[0] = nameIndex; + this->header[1] = typeIndex; + this->header[2] = flags; + this->header[3] = notifyId; + this->header[4] = revision; + } + + static constexpr void adjustOffset(uint *, uint, uint) noexcept {} +}; + +template +struct EnumData : detail::UintDataBlock<5, 2 * N> +{ +private: + static_assert(sizeof(Enum) <= sizeof(uint), "Cannot store enumeration of this size"); + template struct RealEnum { using Type = T; }; + template struct RealEnum> { using Type = T; }; +public: + struct EnumEntry { + int nameIndex; + typename RealEnum::Type value; + }; + + + constexpr EnumData(uint nameOffset, uint aliasOffset, uint flags) + { + this->header[0] = nameOffset; + this->header[1] = aliasOffset; + this->header[2] = flags; + this->header[3] = N; + this->header[4] = 0; // will be set in adjustOffsets() + + if (nameOffset != aliasOffset || QtPrivate::IsQFlags::value) + this->header[2] |= QtMocConstants::EnumIsFlag; + if constexpr (q23::is_scoped_enum_v) + this->header[2] |= QtMocConstants::EnumIsScoped; + } + + template constexpr auto add(const EnumEntry (&entries)[Added]) + { + EnumData result(this->header[0], this->header[1], this->header[2]); + + q20::copy_n(this->payload, this->payloadSize(), result.payload); + uint o = this->payloadSize(); + for (auto entry : entries) { + result.payload[o++] = uint(entry.nameIndex); + auto value = qToUnderlying(entry.value); + result.payload[o++] = uint(value); + } + return result; + } + + static constexpr void adjustOffset(uint *ptr, uint dataoffset, uint payloadoffset) noexcept + { + ptr[dataoffset + 4] += uint(payloadoffset); + } +}; + +template struct FunctionData; +template +struct FunctionData + : detail::UintDataBlock<6, 2 * sizeof...(Args) + 1 + (ExtraFlags & QtMocConstants::MethodRevisioned ? 1 : 0)> +{ + static constexpr bool IsRevisioned = (ExtraFlags & QtMocConstants::MethodRevisioned) != 0; + struct FunctionParameter { + uint typeIdx; // or static meta type ID + uint nameIdx; + }; + using ParametersArray = std::array; + + static constexpr void adjustOffset(uint *ptr, uint dataoffset, uint payloadoffset) noexcept + { + if constexpr (IsRevisioned) + ++payloadoffset; + ptr[dataoffset + 2] += uint(payloadoffset); + } + + constexpr + FunctionData(uint nameIndex, uint tagIndex, uint metaTypesIndex, uint flags, + uint returnType, ParametersArray params = {}) + { + this->header[0] = nameIndex; + this->header[1] = sizeof...(Args); + this->header[2] = 0; // will be set in adjustOffsets() + this->header[3] = tagIndex; + this->header[4] = flags | ExtraFlags; + this->header[5] = metaTypesIndex; + + uint *p = this->payload; + if constexpr (ExtraFlags & QtMocConstants::MethodRevisioned) + ++p; + *p++ = returnType; + for (uint i = 0; i < sizeof...(Args); ++i) + *p++ = params[i].typeIdx; + for (uint i = 0; i < sizeof...(Args); ++i) + *p++ = params[i].nameIdx; + } + + constexpr + FunctionData(uint nameIndex, uint tagIndex, uint metaTypesIndex, uint flags, uint revision, + uint returnType, ParametersArray params = {}) +#ifdef __cpp_concepts + requires(IsRevisioned) +#endif + : FunctionData(nameIndex, tagIndex, metaTypesIndex, flags, returnType, params) + { + // note: we place the revision differently from meta object revision 12 + this->payload[0] = revision; + } +}; +template +struct FunctionData + : FunctionData +{ + using FunctionData::FunctionData; +}; + +template struct MethodData : FunctionData +{ + using FunctionData::FunctionData; +}; + +template struct SignalData : FunctionData +{ + using FunctionData::FunctionData; +}; + +template struct SlotData : FunctionData +{ + using FunctionData::FunctionData; +}; + +template struct ConstructorData : FunctionData +{ + using FunctionData::FunctionData; +}; + +template struct RevisionedMethodData : + FunctionData +{ + using FunctionData::FunctionData; +}; + +template struct RevisionedSignalData : + FunctionData +{ + using FunctionData::FunctionData; +}; + +template struct RevisionedSlotData : + FunctionData +{ + using FunctionData::FunctionData; +}; + +template struct RevisionedConstructorData : + FunctionData +{ + using FunctionData::FunctionData; +}; + + + +template struct UintDataResult +{ + std::array data; +}; + +template , typename ClassInfo = detail::UintDataBlock<0, 0>> +constexpr auto metaObjectData(uint flags, const Methods &methods, const Properties &properties, + const Enums &enums, const Constructors &constructors = {}, + const ClassInfo &classInfo = {}) +{ + constexpr uint HeaderSize = 14; + constexpr uint TotalSize = HeaderSize + + Properties::dataSize() + + Enums::dataSize() + + Methods::dataSize() + + Constructors::dataSize() + + ClassInfo::headerSize() // + ClassInfo::payloadSize() + + 1; // empty EOD + UintDataResult result = {}; + uint dataoffset = HeaderSize; + + result.data[0] = QtMocConstants::OutputRevision; + result.data[1] = 0; // class name index (it's always 0) + + result.data[2] = ClassInfo::headerSize() / 2; + result.data[3] = ClassInfo::headerSize() ? dataoffset : 0; + q20::copy_n(classInfo.header, classInfo.headerSize(), result.data.data() + dataoffset); + dataoffset += ClassInfo::headerSize(); + + result.data[6] = properties.count(); + result.data[7] = properties.count() ? dataoffset : 0; + properties.copyTo(result, dataoffset); + dataoffset += properties.dataSize(); + + result.data[8] = enums.count(); + result.data[9] = enums.count() ? dataoffset : 0; + enums.copyTo(result, dataoffset); + dataoffset += enums.dataSize(); + + result.data[4] = methods.count(); + result.data[5] = methods.count() ? dataoffset : 0; + methods.copyTo(result, dataoffset); + dataoffset += methods.dataSize(); + + result.data[10] = constructors.count(); + result.data[11] = constructors.count() ? dataoffset : 0; + constructors.copyTo(result, dataoffset); + dataoffset += constructors.dataSize(); + + result.data[12] = flags; + + // count the number of signals + if constexpr (Methods::count()) { + constexpr uint MethodHeaderSize = Methods::headerSize() / Methods::count(); + const uint *ptr = &result.data[result.data[5]]; + const uint *end = &result.data[result.data[5] + MethodHeaderSize * Methods::count()]; + for ( ; ptr < end; ptr += MethodHeaderSize) { + if ((ptr[4] & QtMocConstants::MethodSignal) == 0) + break; + ++result.data[13]; + } + } + + return result; +} + +#define QT_MOC_HAS_UINTDATA 1 + } // namespace QtMocHelpers QT_END_NAMESPACE diff --git a/tests/auto/tools/mochelpers/tst_mochelpers.cpp b/tests/auto/tools/mochelpers/tst_mochelpers.cpp index 6ceade78c14..0e12f1246b9 100644 --- a/tests/auto/tools/mochelpers/tst_mochelpers.cpp +++ b/tests/auto/tools/mochelpers/tst_mochelpers.cpp @@ -4,18 +4,40 @@ // Testing qtmochelpers.h is probably pointless... if there's a problem with it // then you most likely can't compile this test in the first place. #include +#include "qtmocconstants.h" #include #include #include +#include + +QT_BEGIN_NAMESPACE +namespace QtMocHelpers { +} QT_END_NAMESPACE class tst_MocHelpers : public QObject { Q_OBJECT private slots: void stringData(); + + // parts of the uint array + void classinfoData(); + void classinfoDataGroup(); + void enumUintData(); + void enumUintGroup(); + void propertyUintData(); + void propertyUintGroup(); + void methodUintData(); + void methodUintGroup(); + void constructorUintData(); + void constructorUintGroup(); + + void emptyUintArray(); + void uintArrayNoMethods(); + void uintArray(); }; template @@ -46,5 +68,593 @@ void tst_MocHelpers::stringData() #undef CHECK } +void tst_MocHelpers::classinfoData() +{ + { + auto result = QtMocHelpers::ClassInfos({{1, 2}}); + QCOMPARE(result.headerSize(), 2U); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 2U); + } + { + auto result = QtMocHelpers::ClassInfos({{1, 2}, {3, 4}}); + QCOMPARE(result.headerSize(), 4U); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 2U); + QCOMPARE(result.header[2], 3U); + QCOMPARE(result.header[3], 4U); + } +} + +template static void checkClassInfos(const std::array &data) +{ + QCOMPARE(data[2], 2U); + QCOMPARE_GE(data[3], 14U); + + const uint *classinfos = data.data() + data[3]; + QCOMPARE(classinfos[0], 1U); + QCOMPARE(classinfos[1], 2U); + QCOMPARE(classinfos[2], 3U); + QCOMPARE(classinfos[3], 4U); +} + +void tst_MocHelpers::classinfoDataGroup() +{ + constexpr auto data = QtMocHelpers::metaObjectData(0, + QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, + QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, + QtMocHelpers::ClassInfos({{1, 2}, {3, 4}})); + checkClassInfos(data.data); +} + +template void enumUintData_check(const E (&values)[N]) +{ + using namespace QtMocHelpers; + + // make an array of dummy offsets + typename EnumData::EnumEntry namesAndOffsets[N]; + for (int i = 0; i < N; ++i) { + namesAndOffsets[i].nameIndex = 2 * (i + 7); + namesAndOffsets[i].value = values[i]; + } + + auto result = EnumData(0, 0, 0).add(namesAndOffsets); + for (uint i = 0; i < N; ++i) { + QCOMPARE(result.payload[2 * i + 0], uint(namesAndOffsets[i].nameIndex)); + QCOMPARE(result.payload[2 * i + 1], uint(values[i])); + } +} + +enum E1 { AnEnumValue }; +enum class E2 { V0 = INT_MAX, V1 = INT_MIN }; +enum class E3 : int { V = 0x1111'2222, V2 = -V }; +void tst_MocHelpers::enumUintData() +{ + using namespace QtMocHelpers; + using namespace QtMocConstants; + { + auto result = QtMocHelpers::EnumData(1, 1, EnumFlags{}); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 1U); + QCOMPARE(result.header[2], EnumFlags{}); + QCOMPARE(result.header[3], 0U); + QCOMPARE(result.payloadSize(), 0U); + } + { + auto result = QtMocHelpers::EnumData>(1, 2, EnumIsFlag); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 2U); + QCOMPARE(result.header[2], EnumIsFlag); + QCOMPARE(result.header[3], 0U); + QCOMPARE(result.payloadSize(), 0U); + } + { + auto result = QtMocHelpers::EnumData(1, 1, EnumFlags{}).add({ { 1, E1::AnEnumValue } }); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 1U); + QCOMPARE(result.header[2], EnumFlags{}); + QCOMPARE(result.header[3], 1U); + QCOMPARE(result.payload[0], 1U); + QCOMPARE(result.payload[1], uint(E1::AnEnumValue)); + } + { + auto result = QtMocHelpers::EnumData>(1, 2, EnumFlags{}).add({ { 1, E1::AnEnumValue } }); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 2U); + QCOMPARE(result.header[2], uint(EnumIsFlag)); + QCOMPARE(result.header[3], 1U); + QCOMPARE(result.payload[0], 1U); + QCOMPARE(result.payload[1], uint(E1::AnEnumValue)); + } + { + constexpr auto result = QtMocHelpers::EnumData(1, 1, EnumIsScoped) + .add({ { 2, E3::V }, {3, E3::V2 }, }); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 1U); + QCOMPARE(result.header[2], EnumIsScoped); + QCOMPARE(result.header[3], 2U); + QCOMPARE(result.payload[0], 2U); + QCOMPARE(result.payload[1], uint(E3::V)); + QCOMPARE(result.payload[2], 3U); + QCOMPARE(result.payload[3], uint(E3::V2)); + } + + QTest::setThrowOnFail(true); + { + enum E { E0, E1 = -1, E2 = 123, E3 = INT_MIN }; + enumUintData_check({E0, E1, E2, E3}); + } +} + +template void testUintData(const Data &data) +{ + uint count = 0; + size_t headerSize = 0; + size_t payloadSize = 0; + data.forEach([&](const auto &block) { + ++count; + headerSize += block.headerSize(); + payloadSize += block.payloadSize(); + }); + + QCOMPARE(data.count(), count); + QCOMPARE(data.headerSize(), headerSize); + QCOMPARE(data.payloadSize(), payloadSize); +} + +template static void checkEnums(const std::array &data) +{ + using namespace QtMocConstants; + QCOMPARE(data[8], 4U); + QCOMPARE_NE(data[9], 0U); + + const uint *header = data.data() + data[9]; + + // E1: + QCOMPARE(header[0 + 0], 1U); + QCOMPARE(header[0 + 1], 1U); + QCOMPARE(header[0 + 2], 0U); + QCOMPARE(header[0 + 3], 1U); + QCOMPARE_GE(header[0 + 4], 14U); + const uint *payload = data.data() + header[0 + 4]; + QCOMPARE(payload[0], 3U); + QCOMPARE(payload[1], uint(E1::AnEnumValue)); + + // E3: + QCOMPARE(header[5 + 0], 4U); + QCOMPARE(header[5 + 1], 5U); + QCOMPARE(header[5 + 2], EnumIsFlag | EnumIsScoped); + QCOMPARE(header[5 + 3], 2U); + QCOMPARE_GE(header[5 + 4], 14U); + payload = data.data() + header[5 + 4]; + QCOMPARE(payload[0], 6U); + QCOMPARE(payload[1], uint(E3::V)); + QCOMPARE(payload[2], 8U); + QCOMPARE(payload[3], uint(E3::V2)); + + // E2: + QCOMPARE(header[10 + 0], 7U); + QCOMPARE(header[10 + 1], 6U); + QCOMPARE(header[10 + 2], EnumIsFlag | EnumIsScoped); + QCOMPARE(header[10 + 3], 2U); + QCOMPARE_GE(header[10 + 4], 14U); + payload = data.data() + header[10 + 4]; + QCOMPARE(payload[0], 7U); + QCOMPARE(payload[1], uint(E2::V0)); + QCOMPARE(payload[2], 10U); + QCOMPARE(payload[3], uint(E2::V1)); + + // QFlags + QCOMPARE(header[15 + 0], 11U); + QCOMPARE(header[15 + 1], 1U); + QCOMPARE(header[15 + 2], EnumIsFlag); + QCOMPARE(header[15 + 3], 1U); + QCOMPARE_GE(header[15 + 4], 14U); + payload = data.data() + header[15 + 4]; + QCOMPARE(payload[0], 3U); + QCOMPARE(payload[1], uint(E1::AnEnumValue)); +} + +void tst_MocHelpers::enumUintGroup() +{ + using namespace QtMocConstants; + QTest::setThrowOnFail(true); + constexpr QtMocHelpers::UintData enums = { + QtMocHelpers::EnumData(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }), + QtMocHelpers::EnumData(4, 5, EnumIsFlag | EnumIsScoped) + .add({ { 6, E3::V }, { 8, E3::V2 }, }), + QtMocHelpers::EnumData(7, 6, EnumIsFlag | EnumIsScoped) + .add({ { 7, E2::V0 }, { 10, E2::V1 }, }), + QtMocHelpers::EnumData>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }), + }; + testUintData(enums); + + constexpr auto data = QtMocHelpers::metaObjectData(0, + QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, enums); + checkEnums(data.data); +} + +void tst_MocHelpers::propertyUintData() +{ + using namespace QtMocHelpers; + { + auto result = PropertyData(3, QMetaType::Int, 0x3, 13, 0x101); + QCOMPARE(result.payloadSize(), 0U); + QCOMPARE(result.header[0], 3U); + QCOMPARE(result.header[1], uint(QMetaType::Int)); + QCOMPARE(result.header[2], 0x3U); + QCOMPARE(result.header[3], 13U); + QCOMPARE(result.header[4], 0x0101U); + } + { + // check that QMetaType doesn't override if it's an alias + auto result = PropertyData(3, 0x80000000 | 4, 0x03); + QCOMPARE(result.header[1], 0x80000000U | 4); + } + { + // Or derived from + struct Dummy : QString {}; + auto result = PropertyData(3, 0x80000000 | 4, 0x03); + QCOMPARE(result.header[1], 0x80000000U | 4); + } +} + +template static void checkProperties(const std::array &data) +{ + QCOMPARE(data[6], 3U); + QCOMPARE_NE(data[7], 0U); + + const uint *header = data.data() + data[7]; + + QCOMPARE(header[0 + 0], 3U); + QCOMPARE(header[0 + 1], uint(QMetaType::Int)); + QCOMPARE(header[0 + 2], 0x3U); + QCOMPARE(header[0 + 3], 13U); + QCOMPARE(header[0 + 4], 0x0101U); + + QCOMPARE(header[5 + 0], 4U); + QCOMPARE(header[5 + 1], 0x80000000U | 5); + QCOMPARE(header[5 + 2], 0x3U); + QCOMPARE(header[5 + 3], uint(-1)); + QCOMPARE(header[5 + 4], 0U); + + QCOMPARE(header[10 + 0], 6U); + QCOMPARE(header[10 + 1], 0x80000000U | 7); + QCOMPARE(header[10 + 2], 0x3U); + QCOMPARE(header[10 + 3], uint(-1)); + QCOMPARE(header[10 + 4], 0U); +} + +void tst_MocHelpers::propertyUintGroup() +{ + QTest::setThrowOnFail(true); + constexpr QtMocHelpers::UintData properties = { + QtMocHelpers::PropertyData(3, QMetaType::Int, 0x3, 13, 0x101), + QtMocHelpers::PropertyData(4, 0x80000000 | 5, 0x03), + QtMocHelpers::PropertyData(6, 0x80000000 | 7, 0x03) + }; + testUintData(properties); + + constexpr auto data = QtMocHelpers::metaObjectData(0, QtMocHelpers::UintData{}, properties, QtMocHelpers::UintData{}); + checkProperties(data.data); +} + +void tst_MocHelpers::methodUintData() +{ + using namespace QtMocHelpers; + using namespace QtMocConstants; + { + auto result = SignalData(1, 2, 0, AccessPublic, QMetaType::Void, {}); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 0U); + QCOMPARE(result.header[3], 2U); + QCOMPARE(result.header[4], AccessPublic | MethodSignal); + QCOMPARE(result.header[5], 0U); + QCOMPARE(result.payload[0], uint(QMetaType::Void)); + } + { + auto result = SlotData(1, 2, 0, AccessPublic, + QMetaType::Void, { { { QMetaType::QString, 1000 } } }); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 1U); + QCOMPARE(result.header[3], 2U); + QCOMPARE(result.header[4], AccessPublic | MethodSlot | MethodIsConst); + QCOMPARE(result.header[5], 0U); + QCOMPARE(result.payload[0], uint(QMetaType::Void)); + QCOMPARE(result.payload[1], uint(QMetaType::QString)); + QCOMPARE(result.payload[2], 1000U); + } + { + auto result = RevisionedSlotData(1, 2, 0, AccessPublic, 0xff01, + QMetaType::Void, { { { QMetaType::QString, 1000 } } }); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 1U); + QCOMPARE(result.header[3], 2U); + QCOMPARE(result.header[4], AccessPublic | MethodSlot | MethodRevisioned); + QCOMPARE(result.header[5], 0U); + QCOMPARE(result.payload[0], 0xff01U); + QCOMPARE(result.payload[1], uint(QMetaType::Void)); + QCOMPARE(result.payload[2], uint(QMetaType::QString)); + QCOMPARE(result.payload[3], 1000U); + } +} + +template static void checkMethods(const std::array &data) +{ + using namespace QtMocConstants; + QCOMPARE(data[4], 3U); + QCOMPARE_NE(data[5], 0U); + + const uint *header = data.data() + data[5]; + + // signals: void signal() + QCOMPARE(header[0], 1U); + QCOMPARE(header[1], 0U); + QCOMPARE_NE(header[2], 0U); + QCOMPARE(header[3], 2U); + QCOMPARE(header[4], AccessPublic | MethodSignal | MethodRevisioned); + QCOMPARE(header[5], 6U); // ### + const uint *payload = data.data() + header[2]; + QCOMPARE(payload[-1], 0x0509U); + QCOMPARE(payload[0], uint(QMetaType::Void)); + + // signals: void signal(E1, Dummy) [Dummy = QString] + header += 6; + QCOMPARE(header[0], 3U); + QCOMPARE(header[1], 2U); + QCOMPARE_NE(header[2], 0U); + QCOMPARE(header[3], 2U); + QCOMPARE(header[4], AccessPublic | MethodSignal); + QCOMPARE(header[5], 7U); // ### + payload = data.data() + header[2]; + QCOMPARE(payload[0], uint(QMetaType::Void)); + QCOMPARE(payload[1], 0x80000000U | 4); // not a builtin type + QCOMPARE(payload[2], 0x80000000U | 5); + QCOMPARE(payload[3], 6U); + QCOMPARE(payload[4], 7U); + + // public slots: bool slot(QString &) const + header += 6; + QCOMPARE(header[0], 8U); + QCOMPARE(header[1], 1U); + QCOMPARE_NE(header[2], 0U); + QCOMPARE(header[3], 2U); + QCOMPARE(header[4], AccessPublic | MethodSlot | MethodIsConst); + QCOMPARE(header[5], 9U); // ### + payload = data.data() + header[2]; + QCOMPARE(payload[0], uint(QMetaType::Bool)); + QCOMPARE(payload[1], 0x80000000U | 10); // not a builtin type + QCOMPARE(payload[2], 11U); +} + +void tst_MocHelpers::methodUintGroup() +{ + QTest::setThrowOnFail(true); + using Dummy = QString; + constexpr QtMocHelpers::UintData methods = { + QtMocHelpers::RevisionedSignalData(1, 2, 6, QtMocConstants::AccessPublic, 0x509, + QMetaType::Void, {{ }} + ), + QtMocHelpers::SignalData(3, 2, 7, QtMocConstants::AccessPublic, + QMetaType::Void, {{ { 0x80000000 | 4, 6 }, { 0x80000000 | 5, 7 }} } + ), + QtMocHelpers::SlotData(8, 2, 9, QtMocConstants::AccessPublic, + QMetaType::Bool, {{ { 0x80000000 | 10, 11 } }} + ) + }; + testUintData(methods); + + constexpr auto data = QtMocHelpers::metaObjectData(0, methods, QtMocHelpers::UintData{}, + QtMocHelpers::UintData{}); + checkMethods(data.data); +} + +void tst_MocHelpers::constructorUintData() +{ + constexpr uint NoType = 0x80000000 | 1; + using namespace QtMocHelpers; + using namespace QtMocConstants; + { + auto result = ConstructorData(1, 2, 0, AccessPublic, NoType, {}); + QCOMPARE(result.header[0], 1U); + QCOMPARE(result.header[1], 0U); + QCOMPARE(result.header[3], 2U); + QCOMPARE(result.header[4], AccessPublic | MethodConstructor); + QCOMPARE(result.header[5], 0U); + QCOMPARE(result.payload[0], NoType); + } + { + auto result = ConstructorData(0, 1, 0, AccessPublic, NoType, + {{ { QMetaType::QObjectStar, 2 } }}); + QCOMPARE(result.header[0], 0U); + QCOMPARE(result.header[1], 1U); + QCOMPARE(result.header[3], 1U); + QCOMPARE(result.header[4], AccessPublic | MethodConstructor); + QCOMPARE(result.header[5], 1U); + QCOMPARE(result.payload[0], NoType); + QCOMPARE(result.payload[1], uint(QMetaType::QObjectStar)); + QCOMPARE(result.payload[2], 2U); + } +} + +template static void checkConstructors(const std::array &data) +{ + using namespace QtMocConstants; + QCOMPARE(data[10], 3U); + QCOMPARE_NE(data[11], 0U); + + const uint *header = data.data() + data[11]; + + // Constructor(QObject *) + QCOMPARE(header[0], 0U); + QCOMPARE(header[1], 1U); + QCOMPARE_NE(header[2], 0U); + QCOMPARE(header[3], 1U); + QCOMPARE(header[4], AccessPublic | MethodConstructor); + QCOMPARE_GT(header[5], 0U); + const uint *payload = data.data() + header[2]; + QCOMPARE(payload[0], 0x80000000U | 1); + QCOMPARE(payload[1], uint(QMetaType::QObjectStar)); + + // Constructor() [cloned from the previous with a default argument] + header += 6; + QCOMPARE(header[0], 0U); + QCOMPARE(header[1], 0U); + QCOMPARE_NE(header[2], 0U); + QCOMPARE(header[3], 1U); + QCOMPARE(header[4], AccessPublic | MethodConstructor | MethodCloned); + QCOMPARE_GT(header[5], 0U); + payload = data.data() + header[2]; + QCOMPARE(payload[0], 0x80000000U | 1); + + // Constructor(const QString &) + header += 6; + QCOMPARE(header[0], 0U); + QCOMPARE(header[1], 1U); + QCOMPARE_NE(header[2], 0U); + QCOMPARE(header[3], 1U); + QCOMPARE(header[4], AccessPublic | MethodConstructor); + QCOMPARE_GT(header[5], 0U); + payload = data.data() + header[2]; + QCOMPARE(payload[0], 0x80000000U | 1); + QCOMPARE(payload[1], uint(QMetaType::QString)); +} + +void tst_MocHelpers::constructorUintGroup() +{ + using QtMocHelpers::NoType; + QTest::setThrowOnFail(true); + constexpr QtMocHelpers::UintData constructors = { + QtMocHelpers::ConstructorData(0, 1, 1, QtMocConstants::AccessPublic, + 0x80000000 | 1, {{ { QMetaType::QObjectStar, 2 } }} + ), + QtMocHelpers::ConstructorData(0, 1, 2, QtMocConstants::AccessPublic | QtMocConstants::MethodCloned, + 0x80000000 | 1, {{ }} + ), + QtMocHelpers::ConstructorData(0, 1, 2, QtMocConstants::AccessPublic, + 0x80000000 | 1, {{ { QMetaType::QString, 3 }, }} + ) + }; + testUintData(constructors); + + constexpr auto data = QtMocHelpers::metaObjectData(0, + QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, + QtMocHelpers::UintData{}, constructors); + checkConstructors(data.data); +} + +template static void checkUintArrayGeneric(const std::array &data, uint flags = 0) +{ + using namespace QtMocConstants; + QCOMPARE(data[0], uint(OutputRevision)); + QCOMPARE(data[1], 0U); + QCOMPARE(data[12], flags); + QCOMPARE(data[N-1], 0U); + + // check the offsets are valid + QCOMPARE_LT(data[2], N); // classinfos + QCOMPARE_LT(data[4], N); // methods + QCOMPARE_LT(data[6], N); // properties + QCOMPARE_LT(data[8], N); // enums + QCOMPARE_LT(data[10], N); // constructors +} + +void tst_MocHelpers::emptyUintArray() +{ + using namespace QtMocConstants; + constexpr auto data = QtMocHelpers::metaObjectData(MetaObjectFlag{}, + QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, QtMocHelpers::UintData{}); + QTest::setThrowOnFail(true); + checkUintArrayGeneric(data.data, MetaObjectFlag{}); + QTest::setThrowOnFail(false); + + // check it says nothing was added + QCOMPARE(data.data[2], 0U); // classinfos + QCOMPARE(data.data[4], 0U); // methods + QCOMPARE(data.data[6], 0U); // properties + QCOMPARE(data.data[8], 0U); // enums + QCOMPARE(data.data[10], 0U); // constructors + QCOMPARE(data.data[13], 0U); // signals +} + +void tst_MocHelpers::uintArrayNoMethods() +{ + using namespace QtMocConstants; + constexpr auto data = QtMocHelpers::metaObjectData(PropertyAccessInStaticMetaCall, + QtMocHelpers::UintData{}, + QtMocHelpers::UintData{ + QtMocHelpers::PropertyData(3, QMetaType::Int, 0x3, 13, 0x101), + QtMocHelpers::PropertyData(4, 0x80000000 | 5, 0x03), + QtMocHelpers::PropertyData(6, 0x80000000 | 7, 0x03), + }, QtMocHelpers::UintData{ + QtMocHelpers::EnumData(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }), + QtMocHelpers::EnumData(4, 5, EnumIsFlag | EnumIsScoped) + .add({ { 6, E3::V }, { 8, E3::V2 }, }), + QtMocHelpers::EnumData(7, 6, EnumIsFlag | EnumIsScoped) + .add({ { 7, E2::V0 }, { 10, E2::V1 }, }), + QtMocHelpers::EnumData>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }), + }, QtMocHelpers::UintData{}, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}})); + + QTest::setThrowOnFail(true); + checkUintArrayGeneric(data.data, PropertyAccessInStaticMetaCall); + checkClassInfos(data.data); + checkProperties(data.data); + checkEnums(data.data); + QTest::setThrowOnFail(false); + QCOMPARE(data.data[4], 0U); // methods + QCOMPARE(data.data[10], 0U); // constructors + QCOMPARE(data.data[13], 0U); // signals +} + +void tst_MocHelpers::uintArray() +{ + using Dummy = QString; + using QtMocHelpers::NoType; + using namespace QtMocConstants; + constexpr auto data = QtMocHelpers::metaObjectData(PropertyAccessInStaticMetaCall, + QtMocHelpers::UintData{ + QtMocHelpers::RevisionedSignalData(1, 2, 6, QtMocConstants::AccessPublic, 0x509, + QMetaType::Void, {{ }} + ), + QtMocHelpers::SignalData(3, 2, 7, QtMocConstants::AccessPublic, + QMetaType::Void, {{ { 0x80000000 | 4, 6 }, { 0x80000000 | 5, 7 }} } + ), + QtMocHelpers::SlotData(8, 2, 9, QtMocConstants::AccessPublic, + QMetaType::Bool, {{ { 0x80000000 | 10, 11 } }} + ) + }, + QtMocHelpers::UintData{ + QtMocHelpers::PropertyData(3, QMetaType::Int, 0x3, 13, 0x101), + QtMocHelpers::PropertyData(4, 0x80000000 | 5, 0x03), + QtMocHelpers::PropertyData(6, 0x80000000 | 7, 0x03), + }, QtMocHelpers::UintData{ + QtMocHelpers::EnumData(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }), + QtMocHelpers::EnumData(4, 5, EnumIsFlag | EnumIsScoped) + .add({ { 6, E3::V }, { 8, E3::V2 }, }), + QtMocHelpers::EnumData(7, 6, EnumIsFlag | EnumIsScoped) + .add({ { 7, E2::V0 }, { 10, E2::V1 }, }), + QtMocHelpers::EnumData>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }), + }, + QtMocHelpers::UintData{ + QtMocHelpers::ConstructorData(0, 1, 1, QtMocConstants::AccessPublic, + 0x80000000 | 1, {{ { QMetaType::QObjectStar, 2 } }} + ), + QtMocHelpers::ConstructorData(0, 1, 2, QtMocConstants::AccessPublic | QtMocConstants::MethodCloned, + 0x80000000 | 1, {{ }} + ), + QtMocHelpers::ConstructorData(0, 1, 3, QtMocConstants::AccessPublic, + 0x80000000 | 1, {{ { QMetaType::QString, 3 }, }} + ) + }, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}})); + + QTest::setThrowOnFail(true); + checkUintArrayGeneric(data.data, PropertyAccessInStaticMetaCall); + checkClassInfos(data.data); + checkProperties(data.data); + checkEnums(data.data); + checkMethods(data.data); + checkConstructors(data.data); +} + QTEST_MAIN(tst_MocHelpers) #include "tst_mochelpers.moc"