diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index e511f14686c..e98e113413a 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -153,17 +153,24 @@ class QMetaEnumBuilderPrivate { public: QMetaEnumBuilderPrivate(const QByteArray &_name) - : name(_name), enumName(_name), isFlag(false), isScoped(false) + : name(_name), enumName(_name) { } QByteArray name; QByteArray enumName; QMetaType metaType; - bool isFlag; - bool isScoped; QList keys; - QList values; + QList values; + QFlags flags = {}; + + int addKey(const QByteArray &name, quint64 value) + { + int index = keys.size(); + keys += name; + values += value; + return index; + } }; Q_DECLARE_TYPEINFO(QMetaEnumBuilderPrivate, Q_RELOCATABLE_TYPE); @@ -592,7 +599,9 @@ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum &prototype) en.setIsScoped(prototype.isScoped()); int count = prototype.keyCount(); for (int index = 0; index < count; ++index) - en.addKey(prototype.key(index), prototype.value(index)); + en.addKey(prototype.key(index), prototype.value64(index).value_or(0)); + // reset the is64Bit() flag if necessary + en.setIs64Bit(prototype.is64Bit()); return en; } @@ -1206,8 +1215,11 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Allocate space for the enumerator key names and values. enumIndex = dataIndex; - for (const auto &enumerator : d->enumerators) + for (const auto &enumerator : d->enumerators) { dataIndex += 2 * enumerator.keys.size(); + if (enumerator.flags & EnumIs64Bit) + dataIndex += enumerator.keys.size(); + } // Zero terminator at the end of the data offset table. ++dataIndex; @@ -1334,26 +1346,30 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, for (const auto &enumerator : d->enumerators) { [[maybe_unused]] int name = strings.enter(enumerator.name); [[maybe_unused]] int enumName = strings.enter(enumerator.enumName); - [[maybe_unused]] int isFlag = enumerator.isFlag ? EnumIsFlag : EnumFlags{}; - [[maybe_unused]] int isScoped = enumerator.isScoped ? EnumIsScoped : EnumFlags{}; int count = enumerator.keys.size(); - int enumOffset = enumIndex; if constexpr (mode == Construct) { data[dataIndex] = name; data[dataIndex + 1] = enumName; - data[dataIndex + 2] = isFlag | isScoped; + data[dataIndex + 2] = enumerator.flags.toInt(); data[dataIndex + 3] = count; - data[dataIndex + 4] = enumOffset; + data[dataIndex + 4] = enumIndex; } for (int key = 0; key < count; ++key) { [[maybe_unused]] int keyIndex = strings.enter(enumerator.keys[key]); if constexpr (mode == Construct) { - data[enumOffset++] = keyIndex; - data[enumOffset++] = enumerator.values[key]; + data[enumIndex + 0] = keyIndex; + data[enumIndex + 1] = uint(enumerator.values[key]); } + enumIndex += 2; + } + bool is64Bit = enumerator.flags.testAnyFlags(EnumIs64Bit); + for (int key = 0; is64Bit && key < count; ++key) { + if constexpr (mode == Construct) { + data[enumIndex] = uint(enumerator.values[key] >> 32); + } + ++enumIndex; } dataIndex += QMetaObjectPrivate::IntsPerEnum; - enumIndex += 2 * count; } // Output the constructors in the class. @@ -2312,7 +2328,8 @@ QMetaType QMetaEnumBuilder::metaType() const } /*! - Sets this enumerator to have the given \c metaType. + Sets this enumerator to have the given \c metaType. The is64Bit() flag will + be set to match \a metaType's size. \since 6.6 \sa metaType() @@ -2320,8 +2337,10 @@ QMetaType QMetaEnumBuilder::metaType() const void QMetaEnumBuilder::setMetaType(QMetaType metaType) { QMetaEnumBuilderPrivate *d = d_func(); - if (d) + if (d) { d->metaType = metaType; + setIs64Bit(metaType.sizeOf() > 4); + } } /*! @@ -2334,7 +2353,7 @@ bool QMetaEnumBuilder::isFlag() const { QMetaEnumBuilderPrivate *d = d_func(); if (d) - return d->isFlag; + return d->flags.toInt() & EnumIsFlag; else return false; } @@ -2348,7 +2367,7 @@ void QMetaEnumBuilder::setIsFlag(bool value) { QMetaEnumBuilderPrivate *d = d_func(); if (d) - d->isFlag = value; + d->flags.setFlag(EnumIsFlag, value); } /*! @@ -2360,7 +2379,7 @@ bool QMetaEnumBuilder::isScoped() const { QMetaEnumBuilderPrivate *d = d_func(); if (d) - return d->isScoped; + return d->flags.toInt() & EnumIsScoped; return false; } @@ -2373,7 +2392,37 @@ void QMetaEnumBuilder::setIsScoped(bool value) { QMetaEnumBuilderPrivate *d = d_func(); if (d) - d->isScoped = value; + d->flags.setFlag(EnumIsScoped, value); +} + +/*! + Return \c true if this enumerations in this enumerator are 64-bit. + + This flag is autoamtically enabled if a 64-bit value is added with addKey(). + + \sa setIs64Bit() +*/ +bool QMetaEnumBuilder::is64Bit() const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + return d->flags.toInt() & EnumIs64Bit; + return false; +} + +/*! + Sets this enumerator to be 64-bit wide if \a value is true. If \a value is + false, any stored 64-bit keys will be truncated to 32 bits. + + This flag is autoamtically enabled if a 64-bit value is added with addKey(). + + \sa is64Bit() +*/ +void QMetaEnumBuilder::setIs64Bit(bool value) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + d->flags.setFlag(EnumIs64Bit, value); } /*! @@ -2409,15 +2458,38 @@ QByteArray QMetaEnumBuilder::key(int index) const Returns the value with the given \a index; or returns -1 if there is no such value. - \sa keyCount(), addKey(), key() + If this is a 64-bit enumeration (see is64Bit()), this function returns the + low 32-bit portion of the value. Use value64() to obtain the full value + instead. + + \sa value64(), keyCount(), addKey(), key(), is64Bit() */ int QMetaEnumBuilder::value(int index) const +{ + return value64(index).value_or(-1); +} + +/*! + \since 6.9 + + Returns the value with the given \a index if it exists; or returns a + disengaged \c{std::optional} if it doesn't. + + \include qmetaobject.cpp qmetaenum-32bit-zeroextend-64bit + + \sa keyCount(), key(), addKey() +*/ +std::optional QMetaEnumBuilder::value64(int index) const { QMetaEnumBuilderPrivate *d = d_func(); - if (d && index >= 0 && index < d->keys.size()) - return d->values[index]; - else - return -1; + if (d && index >= 0 && index < d->keys.size()) { + quint64 v = d->values[index]; + if (d->flags & EnumIs64Bit) + return v; + return uint(v); // return only the low 32 bits + } else { + return std::nullopt; + } } /*! @@ -2430,15 +2502,33 @@ int QMetaEnumBuilder::addKey(const QByteArray &name, int value) { QMetaEnumBuilderPrivate *d = d_func(); if (d) { - int index = d->keys.size(); - d->keys += name; - d->values += value; - return index; + return d->addKey(name, uint(value)); } else { return -1; } } +/*! + \since 6.9 + + Adds a new key called \a name to this enumerator, associated + with \a value. Returns the index of the new key. + + Using the 64-bit version of this function automatically makes this + enumeration be stored as 64-bit. + + \sa keyCount(), key(), value(), removeKey(), is64Bit() +*/ +int QMetaEnumBuilder::addKey(const QByteArray &name, quint64 value) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) { + setIs64Bit(true); + return d->addKey(name, value); + } + return -1; +} + /*! Removes the key at \a index from this enumerator. diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index b52487986ce..697398aa2b3 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -267,11 +267,16 @@ public: bool isScoped() const; void setIsScoped(bool value); + bool is64Bit() const; + void setIs64Bit(bool value); + int keyCount() const; QByteArray key(int index) const; int value(int index) const; + std::optional value64(int index) const; - int addKey(const QByteArray& name, int value); + int addKey(const QByteArray &name, int value); + int addKey(const QByteArray &name, quint64 value); void removeKey(int index); private: diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 18e4e2a4a90..fa927536b77 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -75,6 +75,8 @@ class SomethingOfEverything : public QObject Q_PROPERTY(QLocale::Language language READ language) Q_ENUMS(SomethingEnum) Q_FLAGS(SomethingFlag) + Q_ENUMS(SomethingEnum64) + Q_FLAGS(SomethingFlag64) public: Q_INVOKABLE SomethingOfEverything() {} ~SomethingOfEverything() {} @@ -85,6 +87,12 @@ public: JKL = 10 }; + enum SomethingEnum64 : qint64 + { + MNO = -1, + PQR = 0x1'2345'5678, + }; + enum SomethingFlagEnum { XYZ = 1, @@ -92,6 +100,13 @@ public: }; Q_DECLARE_FLAGS(SomethingFlag, SomethingFlagEnum) + enum SomethingFlagEnum64 : quint64 + { + RST = Q_UINT64_C(1) << 31, + OPQ = Q_UINT64_C(1) << 63, + }; + Q_DECLARE_FLAGS(SomethingFlag64, SomethingFlagEnum64) + Q_INVOKABLE Q_SCRIPTABLE void method1() {} QString prop() const { return QString(); } @@ -806,6 +821,7 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum1.name(), QByteArray("foo")); QVERIFY(!enum1.isFlag()); QVERIFY(!enum1.isScoped()); + QVERIFY(!enum1.is64Bit()); QCOMPARE(enum1.keyCount(), 0); QCOMPARE(enum1.index(), 0); QCOMPARE(builder.enumeratorCount(), 1); @@ -815,6 +831,7 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(!enum2.isFlag()); QVERIFY(!enum2.isScoped()); + QVERIFY(!enum2.is64Bit()); QCOMPARE(enum2.keyCount(), 0); QCOMPARE(enum2.index(), 1); QCOMPARE(builder.enumeratorCount(), 2); @@ -831,9 +848,12 @@ void tst_QMetaObjectBuilder::enumerator() enum1.setIsScoped(true); enum1.setEnumName(QByteArrayLiteral("fooFlag")); enum1.setMetaType(QMetaType(&fooFlagMetaType)); + QVERIFY(enum1.is64Bit()); + enum1.setIs64Bit(false); + QVERIFY(!enum1.is64Bit()); QCOMPARE(enum1.addKey("ABC", 0), 0); QCOMPARE(enum1.addKey("DEF", 1), 1); - QCOMPARE(enum1.addKey("GHI", -1), 2); + QCOMPARE(enum1.addKey("GHI", -2), 2); // Check that enum1 is changed, but enum2 is not. QCOMPARE(enum1.name(), QByteArray("foo")); @@ -849,7 +869,10 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum1.key(3), QByteArray()); QCOMPARE(enum1.value(0), 0); QCOMPARE(enum1.value(1), 1); - QCOMPARE(enum1.value(2), -1); + QCOMPARE(enum1.value(2), -2); + QCOMPARE(enum1.value64(0), 0); + QCOMPARE(enum1.value64(1), 1); + QCOMPARE(enum1.value64(2), uint(-2)); QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(!enum2.isFlag()); QVERIFY(!enum2.isScoped()); @@ -859,12 +882,14 @@ void tst_QMetaObjectBuilder::enumerator() // Modify the attributes on enum2. enum2.setIsFlag(true); QCOMPARE(enum2.addKey("XYZ", 10), 0); - QCOMPARE(enum2.addKey("UVW", 19), 1); + QCOMPARE(enum2.addKey("UVW", quint64(1) << 32), 1); + QVERIFY(enum2.is64Bit()); // This time check that only method2 changed. QCOMPARE(enum1.name(), QByteArray("foo")); QVERIFY(enum1.isFlag()); QVERIFY(enum1.isScoped()); + QVERIFY(!enum1.is64Bit()); QCOMPARE(enum1.keyCount(), 3); QCOMPARE(enum1.index(), 0); QCOMPARE(enum1.key(0), QByteArray("ABC")); @@ -873,17 +898,35 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum1.key(3), QByteArray()); QCOMPARE(enum1.value(0), 0); QCOMPARE(enum1.value(1), 1); - QCOMPARE(enum1.value(2), -1); + QCOMPARE(enum1.value(2), -2); + QCOMPARE(enum1.value64(0), 0); + QCOMPARE(enum1.value64(1), 1); + QCOMPARE(enum1.value64(2), uint(-2)); QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(enum2.isFlag()); QVERIFY(!enum2.isScoped()); + QVERIFY(enum2.is64Bit()); QCOMPARE(enum2.keyCount(), 2); QCOMPARE(enum2.index(), 1); QCOMPARE(enum2.key(0), QByteArray("XYZ")); QCOMPARE(enum2.key(1), QByteArray("UVW")); QCOMPARE(enum2.key(2), QByteArray()); QCOMPARE(enum2.value(0), 10); - QCOMPARE(enum2.value(1), 19); + QCOMPARE(enum2.value(1), 0); // truncated! + QCOMPARE(enum2.value64(0), 10); + QCOMPARE(enum2.value64(1), quint64(1) << 32); + + // Reset enum2 to 32 bits + enum2.setIs64Bit(false); + QCOMPARE(enum2.value(0), 10); + QCOMPARE(enum2.value(1), 0); + QCOMPARE(enum2.value64(0), 10); + QCOMPARE(enum2.value64(1), 0); + + // Reset back restores it + enum2.setIs64Bit(true); + QCOMPARE(enum2.value64(0), 10); + QCOMPARE(enum2.value64(1), quint64(1) << 32); // Remove enum1 key enum1.removeKey(2); @@ -898,6 +941,9 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum1.value(0), 0); QCOMPARE(enum1.value(1), 1); QCOMPARE(enum1.value(2), -1); + QCOMPARE(enum1.value64(0), 0); + QCOMPARE(enum1.value64(1), 1); + QCOMPARE(enum1.value64(2), std::nullopt); QCOMPARE(enum2.name(), QByteArray("bar")); QVERIFY(enum2.isFlag()); QVERIFY(!enum2.isScoped()); @@ -907,7 +953,9 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum2.key(1), QByteArray("UVW")); QCOMPARE(enum2.key(2), QByteArray()); QCOMPARE(enum2.value(0), 10); - QCOMPARE(enum2.value(1), 19); + QCOMPARE(enum2.value(1), 0); // truncated! + QCOMPARE(enum2.value64(0), 10); + QCOMPARE(enum2.value64(1), quint64(1) << 32); // Remove enum1 and check that enum2 becomes index 0. builder.removeEnumerator(0); @@ -922,7 +970,9 @@ void tst_QMetaObjectBuilder::enumerator() QCOMPARE(enum2.key(1), QByteArray("UVW")); QCOMPARE(enum2.key(2), QByteArray()); QCOMPARE(enum2.value(0), 10); - QCOMPARE(enum2.value(1), 19); + QCOMPARE(enum2.value(1), 0); // truncated! + QCOMPARE(enum2.value64(0), 10); + QCOMPARE(enum2.value64(1), quint64(1) << 32); // Perform index-based lookup again. QCOMPARE(builder.indexOfEnumerator("foo"), -1); @@ -1185,13 +1235,19 @@ static bool sameEnumerator(const QMetaEnum& enum1, const QMetaEnum& enum2) for (int index = 0; index < enum1.keyCount(); ++index) { if (QByteArray(enum1.key(index)) != QByteArray(enum2.key(index))) return false; - if (enum1.value(index) != enum2.value(index)) + if (enum1.value64(index) != enum2.value64(index)) return false; } if (QByteArray(enum1.scope()) != QByteArray(enum2.scope())) return false; + if (enum1.isScoped() != enum2.isScoped()) + return false; + + if (enum1.is64Bit() != enum2.is64Bit()) + return false; + return true; }