QMetaObjectBuilder: add support for 64-bit flags and enums

Task-number: QTBUG-111926
Change-Id: I8a96935cf6c742259c9dfffd17e9519aa2540313
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2024-08-06 19:34:17 -07:00
parent 21f04d5d29
commit f85a17abeb
3 changed files with 189 additions and 38 deletions

View File

@ -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<QByteArray> keys;
QList<int> values;
QList<quint64> values;
QFlags<EnumFlags> 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<quint64> 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.

View File

@ -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<quint64> 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:

View File

@ -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;
}