summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qtmochelpers.h354
-rw-r--r--tests/auto/tools/mochelpers/tst_mochelpers.cpp610
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 <QtCore/qglobal.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qtmocconstants.h>
-#include <algorithm> // std::min
+#include <QtCore/q20algorithm.h> // std::min, std::copy_n
+#include <QtCore/q23type_traits.h> // std::is_scoped_enum
#include <limits>
#if 0
@@ -78,6 +80,354 @@ template <uint... Nx> constexpr auto stringData(const char (&...strings)[Nx])
# define QT_MOC_HAS_STRINGDATA 1
+struct NoType {};
+
+namespace detail {
+template <uint H, uint P> 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 <int Idx, typename T> 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 <typename Idx, typename... T> struct UintDataStorage;
+template <int... Idx, typename... T> struct UintDataStorage<std::integer_sequence<int, Idx...>, T...>
+ : UintDataEntry<Idx, T>...
+{
+ constexpr UintDataStorage(T &&... data)
+ : UintDataEntry<Idx, T>(std::move(data))...
+ {}
+
+ template <typename F> 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<const UintDataEntry<Idx, T> &>(*this))...
+ };
+ (void) dummy;
+ }
+};
+} // namespace detail
+
+template <typename... Block> 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 <typename Result>
+ 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 <typename F> constexpr void forEach(F &&f) const
+ {
+ data.forEach(std::forward<F>(f));
+ }
+
+private:
+ detail::UintDataStorage<std::make_integer_sequence<int, count()>, Block...> data;
+};
+
+template <int N> struct ClassInfos : detail::UintDataBlock<2 * N, 0>
+{
+ constexpr ClassInfos() = default;
+ constexpr ClassInfos(const std::array<uint, 2> (&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 <typename Enum, int N = 0>
+struct EnumData : detail::UintDataBlock<5, 2 * N>
+{
+private:
+ static_assert(sizeof(Enum) <= sizeof(uint), "Cannot store enumeration of this size");
+ template <typename T> struct RealEnum { using Type = T; };
+ template <typename T> struct RealEnum<QFlags<T>> { using Type = T; };
+public:
+ struct EnumEntry {
+ int nameIndex;
+ typename RealEnum<Enum>::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<Enum>::value)
+ this->header[2] |= QtMocConstants::EnumIsFlag;
+ if constexpr (q23::is_scoped_enum_v<Enum>)
+ this->header[2] |= QtMocConstants::EnumIsScoped;
+ }
+
+ template <int Added> constexpr auto add(const EnumEntry (&entries)[Added])
+ {
+ EnumData<Enum, N + Added> 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 <typename F, uint ExtraFlags> struct FunctionData;
+template <typename Ret, typename... Args, uint ExtraFlags>
+struct FunctionData<Ret (Args...), ExtraFlags>
+ : 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<FunctionParameter, sizeof...(Args)>;
+
+ 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 <typename Ret, typename... Args, uint ExtraFlags>
+struct FunctionData<Ret (Args...) const, ExtraFlags>
+ : FunctionData<Ret (Args...), ExtraFlags | QtMocConstants::MethodIsConst>
+{
+ using FunctionData<Ret (Args...), ExtraFlags | QtMocConstants::MethodIsConst>::FunctionData;
+};
+
+template <typename F> struct MethodData : FunctionData<F, QtMocConstants::MethodMethod>
+{
+ using FunctionData<F, QtMocConstants::MethodMethod>::FunctionData;
+};
+
+template <typename F> struct SignalData : FunctionData<F, QtMocConstants::MethodSignal>
+{
+ using FunctionData<F, QtMocConstants::MethodSignal>::FunctionData;
+};
+
+template <typename F> struct SlotData : FunctionData<F, QtMocConstants::MethodSlot>
+{
+ using FunctionData<F, QtMocConstants::MethodSlot>::FunctionData;
+};
+
+template <typename F> struct ConstructorData : FunctionData<F, QtMocConstants::MethodConstructor>
+{
+ using FunctionData<F, QtMocConstants::MethodConstructor>::FunctionData;
+};
+
+template <typename F> struct RevisionedMethodData :
+ FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodMethod>
+{
+ using FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodMethod>::FunctionData;
+};
+
+template <typename F> struct RevisionedSignalData :
+ FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodSignal>
+{
+ using FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodSignal>::FunctionData;
+};
+
+template <typename F> struct RevisionedSlotData :
+ FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodSlot>
+{
+ using FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodSlot>::FunctionData;
+};
+
+template <typename F> struct RevisionedConstructorData :
+ FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodConstructor>
+{
+ using FunctionData<F, QtMocConstants::MethodRevisioned | QtMocConstants::MethodConstructor>::FunctionData;
+};
+
+
+
+template <uint N> struct UintDataResult
+{
+ std::array<uint, N> data;
+};
+
+template <typename Methods, typename Properties, typename Enums,
+ typename Constructors = UintData<>, 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<TotalSize> 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 <QtCore/qtmochelpers.h>
+#include "qtmocconstants.h"
#include <QTest>
#include <QtCore/qobject.h>
#include <initializer_list>
+#include <q20algorithm.h>
+
+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 <int Count, size_t StringSize>
@@ -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 <size_t N> static void checkClassInfos(const std::array<uint, N> &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 <typename E, int N> void enumUintData_check(const E (&values)[N])
+{
+ using namespace QtMocHelpers;
+
+ // make an array of dummy offsets
+ typename EnumData<E>::EnumEntry namesAndOffsets[N];
+ for (int i = 0; i < N; ++i) {
+ namesAndOffsets[i].nameIndex = 2 * (i + 7);
+ namesAndOffsets[i].value = values[i];
+ }
+
+ auto result = EnumData<E>(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<E1>(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<QFlags<E1>>(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<E1>(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<QFlags<E1>>(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<E3>(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 <typename Data> 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 <size_t N> static void checkEnums(const std::array<uint, N> &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<E1>
+ 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<E1>(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }),
+ QtMocHelpers::EnumData<E3>(4, 5, EnumIsFlag | EnumIsScoped)
+ .add({ { 6, E3::V }, { 8, E3::V2 }, }),
+ QtMocHelpers::EnumData<E2>(7, 6, EnumIsFlag | EnumIsScoped)
+ .add({ { 7, E2::V0 }, { 10, E2::V1 }, }),
+ QtMocHelpers::EnumData<QFlags<E1>>(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 <size_t N> static void checkProperties(const std::array<uint, N> &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<void()>(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<void (const QString &) const>(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<void (const QString &)>(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 <size_t N> static void checkMethods(const std::array<uint, N> &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<void()>(1, 2, 6, QtMocConstants::AccessPublic, 0x509,
+ QMetaType::Void, {{ }}
+ ),
+ QtMocHelpers::SignalData<void (E1, Dummy)>(3, 2, 7, QtMocConstants::AccessPublic,
+ QMetaType::Void, {{ { 0x80000000 | 4, 6 }, { 0x80000000 | 5, 7 }} }
+ ),
+ QtMocHelpers::SlotData<bool (QString &) const>(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<QtMocHelpers::NoType()>(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<QtMocHelpers::NoType(QObject *)>(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 <size_t N> static void checkConstructors(const std::array<uint, N> &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<NoType(QObject *)>(0, 1, 1, QtMocConstants::AccessPublic,
+ 0x80000000 | 1, {{ { QMetaType::QObjectStar, 2 } }}
+ ),
+ QtMocHelpers::ConstructorData<NoType()>(0, 1, 2, QtMocConstants::AccessPublic | QtMocConstants::MethodCloned,
+ 0x80000000 | 1, {{ }}
+ ),
+ QtMocHelpers::ConstructorData<NoType(const QString &)>(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 <size_t N> static void checkUintArrayGeneric(const std::array<uint, N> &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<E1>(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }),
+ QtMocHelpers::EnumData<E3>(4, 5, EnumIsFlag | EnumIsScoped)
+ .add({ { 6, E3::V }, { 8, E3::V2 }, }),
+ QtMocHelpers::EnumData<E2>(7, 6, EnumIsFlag | EnumIsScoped)
+ .add({ { 7, E2::V0 }, { 10, E2::V1 }, }),
+ QtMocHelpers::EnumData<QFlags<E1>>(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<void()>(1, 2, 6, QtMocConstants::AccessPublic, 0x509,
+ QMetaType::Void, {{ }}
+ ),
+ QtMocHelpers::SignalData<void (E1, Dummy)>(3, 2, 7, QtMocConstants::AccessPublic,
+ QMetaType::Void, {{ { 0x80000000 | 4, 6 }, { 0x80000000 | 5, 7 }} }
+ ),
+ QtMocHelpers::SlotData<bool (QString &) const>(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<E1>(1, 1, 0x00).add({ { 3, E1::AnEnumValue } }),
+ QtMocHelpers::EnumData<E3>(4, 5, EnumIsFlag | EnumIsScoped)
+ .add({ { 6, E3::V }, { 8, E3::V2 }, }),
+ QtMocHelpers::EnumData<E2>(7, 6, EnumIsFlag | EnumIsScoped)
+ .add({ { 7, E2::V0 }, { 10, E2::V1 }, }),
+ QtMocHelpers::EnumData<QFlags<E1>>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }),
+ },
+ QtMocHelpers::UintData{
+ QtMocHelpers::ConstructorData<NoType(QObject *)>(0, 1, 1, QtMocConstants::AccessPublic,
+ 0x80000000 | 1, {{ { QMetaType::QObjectStar, 2 } }}
+ ),
+ QtMocHelpers::ConstructorData<NoType()>(0, 1, 2, QtMocConstants::AccessPublic | QtMocConstants::MethodCloned,
+ 0x80000000 | 1, {{ }}
+ ),
+ QtMocHelpers::ConstructorData<NoType(const QString &)>(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"