From 3e6eb30a80de57d327d74896d13f82af2c76c82c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 10 Aug 2024 08:57:45 -0700 Subject: [PATCH] moc: add constexpr functions to generate the uint data These implement the current (revision 13) meta object uint data. The location of the data blocks is slightly different from what moc produces, which in turn is different from QMetaObjectBuilder, but it's compatible with any reader that doesn't make undue assumptions about just how data is laid out. In fact, revision 13 was created specifically so these functiosn could exist, as placing the method revisions where they were in revision 12 was too cumbersome. Change-Id: I8a96935cf6c742259c9dfffd17ea6930c947b768 Reviewed-by: Fabian Kosmale Reviewed-by: Ahmad Samir --- src/corelib/kernel/qtmochelpers.h | 354 +++++++++- .../auto/tools/mochelpers/tst_mochelpers.cpp | 610 ++++++++++++++++++ 2 files changed, 962 insertions(+), 2 deletions(-) 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"