From da4a6cf78ff42a4f69c2775997ff174ef647f3f3 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 10 Aug 2024 15:31:43 -0700 Subject: [PATCH] moc: generate the uint data using the new constexpr functions We needed to go through two levels of indirection to generate it for classes. First, it needs to be inside the context of the class or namespace in question, hence the need for a qt_create_metaobjectdata() member function. This is necessary because moc does not know what context some property types are, like "Error": they may be a nested type in the class or local to this namespace. Doing this also gains us access to everything the class has access to. (The metatype array avoided this problem by declaring the types as template parameters in the context of the staticMetaObject, so it transitively gained the same access we now do) Second, because the class itself may be private, we need a way to call the function we've just defined without naming it, so we insert a template friend in Q_OBJECT & Q_GADGET, which we can define in the moc output. We multiplex it using the 1:1 association with the token type that moc already defines. The member function is a template so no compiler will attempt to export it. Moreover, our specialization is keyed to a type in an unnamed namespace. The changes to tst_QVariant are temporary, because it is abusing the Q_ENUM marker on a 64-bit enumeration. Task-number: QTBUG-111926 Change-Id: I8a96935cf6c742259c9dfffd17e951563adde9a1 Reviewed-by: Ahmad Samir Reviewed-by: Fabian Kosmale --- src/corelib/kernel/qobjectdefs.h | 4 + src/corelib/kernel/qtmetamacros.h | 5 + src/corelib/kernel/qtmocconstants.h | 1 + src/tools/moc/generator.cpp | 280 +++++++++++++++++- src/tools/moc/generator.h | 4 + .../corelib/kernel/qvariant/tst_qvariant.cpp | 20 +- tests/auto/tools/moc/tst_moc.cpp | 10 +- .../auto/tools/mochelpers/tst_mochelpers.cpp | 2 +- 8 files changed, 312 insertions(+), 14 deletions(-) diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 190901d5d19..b8b8ea62d9f 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -14,6 +14,10 @@ #include #include +// this function is implemented by moc for the user classes and thus +// intentionally outside of our namespace +template constexpr auto qt_call_create_metaobjectdata(); + QT_BEGIN_NAMESPACE class QByteArray; diff --git a/src/corelib/kernel/qtmetamacros.h b/src/corelib/kernel/qtmetamacros.h index 2ef5c0df9d6..69f390ea9a0 100644 --- a/src/corelib/kernel/qtmetamacros.h +++ b/src/corelib/kernel/qtmetamacros.h @@ -130,6 +130,8 @@ private: \ Q_OBJECT_NO_ATTRIBUTES_WARNING \ Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \ QT_WARNING_POP \ + template static constexpr auto qt_create_metaobjectdata(); \ + template friend constexpr auto ::qt_call_create_metaobjectdata(); \ QT_DEFINE_TAG_STRUCT(QPrivateSignal); \ QT_ANNOTATE_CLASS(qt_qobject, "") @@ -147,6 +149,8 @@ private: \ QT_WARNING_PUSH \ Q_OBJECT_NO_ATTRIBUTES_WARNING \ Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \ + template static constexpr auto qt_create_metaobjectdata(); \ + template friend constexpr auto ::qt_call_create_metaobjectdata(); \ QT_WARNING_POP \ QT_ANNOTATE_CLASS(qt_qgadget, "") \ /*end*/ @@ -157,6 +161,7 @@ private: \ /* qmake ignore Q_NAMESPACE_EXPORT */ #define Q_NAMESPACE_EXPORT(...) \ extern __VA_ARGS__ const QMetaObject staticMetaObject; \ + template static constexpr auto qt_create_metaobjectdata(); \ QT_ANNOTATE_CLASS(qt_qnamespace, "") \ /*end*/ diff --git a/src/corelib/kernel/qtmocconstants.h b/src/corelib/kernel/qtmocconstants.h index 3748bfc25ac..a3d7912d86b 100644 --- a/src/corelib/kernel/qtmocconstants.h +++ b/src/corelib/kernel/qtmocconstants.h @@ -50,6 +50,7 @@ enum PropertyFlags : uint { Required = 0x01000000, Bindable = 0x02000000, }; +inline constexpr PropertyFlags DefaultPropertyFlags { Readable | Designable | Scriptable | Stored }; enum MethodFlags : uint { AccessPrivate = 0x00, diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 9d437fe1e6d..6d8f98b51b4 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -286,6 +286,89 @@ void Generator::generateCode() // build the data array // + int initialMetaTypeOffset = 0; + + // We define a method inside the context of the class or namespace we're + // creating the meta object for, so we get access to everything it has + // access to and with the same contexts (for example, member enums and + // types). + fprintf(out, "#ifdef QT_MOC_HAS_UINTDATA\n" + "template <> constexpr inline auto %s::qt_create_metaobjectdata()\n" + "{\n" + " namespace QMC = QtMocConstants;\n" + " QtMocHelpers::UintData qt_properties {\n", + cdef->qualified.constData(), qualifiedClassNameIdentifier.constData()); + + addProperties(initialMetaTypeOffset); + fprintf(out, " };\n" + " QtMocHelpers::UintData qt_enums {\n"); + addEnums(initialMetaTypeOffset); + fprintf(out, " };\n" + " QtMocHelpers::UintData qt_methods {\n"); + + // we insert the metatype for the class itself here + ++initialMetaTypeOffset; + + // Build signals array first, otherwise the signal indices would be wrong + addFunctions(cdef->signalList, "Signal", initialMetaTypeOffset); + addFunctions(cdef->slotList, "Slot", initialMetaTypeOffset); + addFunctions(cdef->methodList, "Method", initialMetaTypeOffset); + fprintf(out, " };\n"); + + const char *uintDataParams = ""; + if (isConstructible || !cdef->classInfoList.isEmpty()) { + if (isConstructible) { + fprintf(out, " using Constructor = QtMocHelpers::NoType;\n" + " QtMocHelpers::UintData qt_constructors {\n"); + addFunctions(cdef->constructorList, "Constructor", initialMetaTypeOffset); + fprintf(out, " };\n"); + } else { + fputs(" QtMocHelpers::UintData qt_constructors {};\n", out); + } + + uintDataParams = ", qt_constructors"; + if (!cdef->classInfoList.isEmpty()) { + fprintf(out, " QtMocHelpers::ClassInfos qt_classinfo({\n"); + addClassInfos(); + fprintf(out, " });\n"); + uintDataParams = ", qt_constructors, qt_classinfo"; + } + } + + const char *metaObjectFlags = "QMC::MetaObjectFlag{}"; + if (cdef->hasQGadget || cdef->hasQNamespace) { + // Ideally, all the classes could have that flag. But this broke + // classes generated by qdbusxml2cpp which generate code that require + // that we call qt_metacall for properties. + metaObjectFlags = "QMC::PropertyAccessInStaticMetaCall"; + } + fprintf(out, " return QtMocHelpers::metaObjectData(%s, qt_methods, qt_properties, qt_enums%s);\n" + "}\n", metaObjectFlags, uintDataParams); + + if (cdef->hasQNamespace) { + // We can always access the function above if it's at namespace scope. + fprintf(out, "static constexpr auto qt_meta_data_%s_array =\n" + " %s::qt_create_metaobjectdata().data;\n", + qualifiedClassNameIdentifier.constData(), cdef->qualified.constData(), + qualifiedClassNameIdentifier.constData()); + } else { + // If this is a class, it might itself be private, so we need an extra + // level of indirection to access the function above. + fprintf(out, "template <> constexpr inline auto qt_call_create_metaobjectdata()\n" + "{\n" + " return %s::qt_create_metaobjectdata();\n" + "}\n" + "static constexpr auto qt_meta_data_%s_array =\n" + " qt_call_create_metaobjectdata().data;\n", + qualifiedClassNameIdentifier.constData(), cdef->qualified.constData(), + qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData(), + qualifiedClassNameIdentifier.constData()); + } + fprintf(out, "static constexpr const uint *qt_meta_data_%s =\n" + " qt_meta_data_%s_array.data();\n" + "#else // !QT_MOC_HAS_UINTDATA\n", + qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); + int index = MetaObjectPrivateFieldCount; fprintf(out, "Q_CONSTINIT static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); fprintf(out, "\n // content:\n"); @@ -347,7 +430,7 @@ void Generator::generateCode() || propEnumCount >= std::numeric_limits::max()) { parser->error("internal limit exceeded: number of property and enum metatypes is too big."); } - int initialMetaTypeOffset = int(propEnumCount); + initialMetaTypeOffset = int(propEnumCount); // // Build signals array first, otherwise the signal indices would be wrong @@ -401,7 +484,9 @@ void Generator::generateCode() // // Terminate data array // - fprintf(out, "\n 0 // eod\n};\n\n"); + fprintf(out, "\n 0 // eod\n};\n"); + + fprintf(out, "#endif // !QT_MOC_HAS_UINTDATA\n\n"); // // Build extra array @@ -672,6 +757,12 @@ void Generator::registerClassInfoStrings() } } +void Generator::addClassInfos() +{ + for (const ClassInfoDef &c : std::as_const(cdef->classInfoList)) + fprintf(out, " { %4d, %4d },\n", stridx(c.name), stridx(c.value)); +} + void Generator::generateClassInfos() { if (cdef->classInfoList.isEmpty()) @@ -705,6 +796,76 @@ void Generator::registerByteArrayVector(const QList &list) strreg(ba); } +void Generator::addFunctions(const QList &list, const char *functype, + int &initialMetatypeOffset) +{ + for (const FunctionDef &f : list) { + if (!f.isConstructor) + fprintf(out, " // %s '%s'\n", functype, f.name.constData()); + fprintf(out, " QtMocHelpers::%s%sData<", + f.revision > 0 ? "Revisioned" : "", functype); + + if (f.isConstructor) + fprintf(out, "Constructor("); + else + fprintf(out, "%s(", f.type.name.constData()); // return type + + const char *comma = ""; + for (const auto &argument : f.arguments) { + fprintf(out, "%s%s", comma, argument.type.name.constData()); + comma = ", "; + } + + fprintf(out, ")%s>(%d, %d, %d, ", f.isConst ? " const" : "", + stridx(f.name), stridx(f.tag), initialMetatypeOffset); + // flags + // access right is always present + if (f.access == FunctionDef::Private) + fprintf(out, "QMC::AccessPrivate"); + else if (f.access == FunctionDef::Public) + fprintf(out, "QMC::AccessPublic"); + else if (f.access == FunctionDef::Protected) + fprintf(out, "QMC::AccessProtected"); + if (f.isCompat) + fprintf(out, " | QMC::MethodCompatibility"); + if (f.wasCloned) + fprintf(out, " | QMC::MethodCloned"); + if (f.isScriptable) + fprintf(out, " | QMC::MethodScriptable"); + + // QtMocConstants::MethodRevisioned is implied by the call we're making + if (f.revision > 0) + fprintf(out, ", %#x", f.revision); + + // return type (if not a constructor) + const bool allowEmptyName = f.isConstructor; + fprintf(out, ", "); + generateTypeInfo(f.normalizedType, allowEmptyName); + + if (f.arguments.isEmpty()) { + fprintf(out, "),\n"); + } else { + // array of parameter types (or type names) and names + fprintf(out, ", {{"); + for (qsizetype i = 0; i < f.arguments.size(); ++i) { + if ((i % 4) == 0) + fprintf(out, "\n "); + const ArgumentDef &arg = f.arguments.at(i); + fprintf(out, " { "); + generateTypeInfo(arg.normalizedType); + fprintf(out, ", %d },", stridx(arg.name)); + } + + fprintf(out, "\n }}),\n"); + } + + // constructors don't have a return type + if (!f.isConstructor) + ++initialMetatypeOffset; + initialMetatypeOffset += int(f.arguments.size()); + } +} + void Generator::generateFunctions(const QList &list, const char *functype, int type, int ¶msIndex, int &initialMetatypeOffset) { @@ -825,6 +986,79 @@ void Generator::registerPropertyStrings() } } +void Generator::addProperties(int &initialMetaTypeOffset) +{ + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { + fprintf(out, " // property '%s'\n" + " QtMocHelpers::PropertyData(%d, ", + p.name.constData(), stridx(p.name)); + generateTypeInfo(p.type); + fputc(',', out); + + const char *separator = ""; + auto addFlag = [this, &separator](const char *text) { + fprintf(out, "%s QMC::%s", separator, text); + separator = " |"; + }; + bool readable = !p.read.isEmpty() || !p.member.isEmpty(); + bool designable = p.designable != "false"; + bool scriptable = p.scriptable != "false"; + bool stored = p.stored != "false"; + if (readable && designable && scriptable && stored) { + addFlag("DefaultPropertyFlags"); + if ((!p.member.isEmpty() && !p.constant) || !p.write.isEmpty()) + addFlag("Writable"); + } else { + if (readable) + addFlag("Readable"); + if ((!p.member.isEmpty() && !p.constant) || !p.write.isEmpty()) + addFlag("Writable"); + if (designable) + addFlag("Designable"); + if (scriptable) + addFlag("Scriptable"); + if (stored) + addFlag("Stored"); + } + if (!p.reset.isEmpty()) + addFlag("Resettable"); + if (!isBuiltinType(p.type)) + addFlag("EnumOrFlag"); + if (p.stdCppSet()) + addFlag("StdCppSet"); + if (p.constant) + addFlag("Constant"); + if (p.final) + addFlag("Final"); + if (p.user != "false") + addFlag("User"); + if (p.required) + addFlag("Required"); + if (!p.bind.isEmpty()) + addFlag("Bindable"); + + if (*separator == '\0') + addFlag("Invalid"); + + int notifyId = p.notifyId; + if (notifyId != -1 || p.revision > 0) { + fprintf(out, ", "); + if (p.notifyId < -1) { + // signal is in parent class + const int indexInStrings = int(strings.indexOf(p.notify)); + notifyId = indexInStrings; + fprintf(out, "%#x | ", IsUnresolvedSignal); + } + fprintf(out, "%d", notifyId); + if (p.revision > 0) + fprintf(out, ", %#x", p.revision); + } + + fprintf(out, "),\n"); + } + initialMetaTypeOffset += int(cdef->propertyList.size()); +} + void Generator::generateProperties() { // @@ -895,6 +1129,48 @@ void Generator::registerEnumStrings() } } +void Generator::addEnums(int &initialMetaTypeOffset) +{ + for (const EnumDef &e : std::as_const(cdef->enumList)) { + const QByteArray &typeName = e.enumName.isNull() ? e.name : e.enumName; + fprintf(out, " // %s '%s'\n" + " QtMocHelpers::EnumData<%s>(%d, %d,", + e.flags & EnumIsFlag ? "flag" : "enum", e.name.constData(), + e.name.constData(), stridx(e.name), stridx(typeName)); + + if (e.flags) { + const char *separator = ""; + auto addFlag = [this, &separator](const char *text) { + fprintf(out, "%s QMC::%s", separator, text); + separator = " |"; + }; + if (e.flags & EnumIsFlag) + addFlag("EnumIsFlag"); + if (e.flags & EnumIsScoped) + addFlag("EnumIsScoped"); + } else { + fprintf(out, " QMC::EnumFlags{}"); + } + + if (e.values.isEmpty()) { + fprintf(out, "),\n"); + continue; + } + + // add the enumerations + fprintf(out, ").add({\n"); + QByteArray prefix = (e.enumName.isNull() ? e.name : e.enumName); + for (const QByteArray &val : e.values) { + fprintf(out, " { %4d, %s::%s },\n", stridx(val), + prefix.constData(), val.constData()); + } + + fprintf(out, " }),\n"); + } + + initialMetaTypeOffset += int(cdef->enumList.size()); +} + void Generator::generateEnums(int index) { if (cdef->enumDeclarations.isEmpty()) diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index 2d4d69ca05f..c8b1861cdf1 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -29,6 +29,10 @@ private: void generateClassInfos(); void registerFunctionStrings(const QList &list); void registerByteArrayVector(const QList &list); + void addProperties(int &initialMetaTypeOffset); + void addEnums(int &initialMetaTypeOffset); + void addFunctions(const QList &list, const char *functype, int &initialMetatypeOffset); + void addClassInfos(); void generateFunctions(const QList &list, const char *functype, int type, int ¶msIndex, int &initialMetatypeOffset); void generateFunctionRevisions(const QList &list, const char *functype); diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index d79e0e82f8a..fbf695f4cbf 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -152,12 +152,12 @@ public: enum MetaEnumTest_Enum0 { MetaEnumTest_Enum0_dummy = 2, MetaEnumTest_Enum0_value = 42, MetaEnsureSignedEnum0 = -1 }; Q_ENUM(MetaEnumTest_Enum0) enum MetaEnumTest_Enum1 : qint64 { MetaEnumTest_Enum1_value = 42, MetaEnumTest_Enum1_bigValue = (Q_INT64_C(1) << 33) + 50 }; - Q_ENUM(MetaEnumTest_Enum1) + // Q_ENUM(MetaEnumTest_Enum1) enum MetaEnumTest_Enum3 : qint64 { MetaEnumTest_Enum3_value = -47, MetaEnumTest_Enum3_bigValue = (Q_INT64_C(1) << 56) + 5, MetaEnumTest_Enum3_bigNegValue = -(Q_INT64_C(1) << 56) - 3 }; - Q_ENUM(MetaEnumTest_Enum3) + // Q_ENUM(MetaEnumTest_Enum3) enum MetaEnumTest_Enum4 : quint64 { MetaEnumTest_Enum4_value = 47, MetaEnumTest_Enum4_bigValue = (Q_INT64_C(1) << 52) + 45 }; - Q_ENUM(MetaEnumTest_Enum4) + // Q_ENUM(MetaEnumTest_Enum4) enum MetaEnumTest_Enum5 : uint { MetaEnumTest_Enum5_value = 47 }; Q_ENUM(MetaEnumTest_Enum5) enum MetaEnumTest_Enum6 : uchar { MetaEnumTest_Enum6_value = 47 }; @@ -5390,13 +5390,13 @@ void tst_QVariant::metaEnums_data() QTest::newRow(#Value) << &testVariantMetaEnum << #Value; METAENUMS_TEST(MetaEnumTest_Enum0_value); - METAENUMS_TEST(MetaEnumTest_Enum1_value); - METAENUMS_TEST(MetaEnumTest_Enum1_bigValue); - METAENUMS_TEST(MetaEnumTest_Enum3_value); - METAENUMS_TEST(MetaEnumTest_Enum3_bigValue); - METAENUMS_TEST(MetaEnumTest_Enum3_bigNegValue); - METAENUMS_TEST(MetaEnumTest_Enum4_value); - METAENUMS_TEST(MetaEnumTest_Enum4_bigValue); + // METAENUMS_TEST(MetaEnumTest_Enum1_value); + // METAENUMS_TEST(MetaEnumTest_Enum1_bigValue); + // METAENUMS_TEST(MetaEnumTest_Enum3_value); + // METAENUMS_TEST(MetaEnumTest_Enum3_bigValue); + // METAENUMS_TEST(MetaEnumTest_Enum3_bigNegValue); + // METAENUMS_TEST(MetaEnumTest_Enum4_value); + // METAENUMS_TEST(MetaEnumTest_Enum4_bigValue); METAENUMS_TEST(MetaEnumTest_Enum5_value); METAENUMS_TEST(MetaEnumTest_Enum6_value); METAENUMS_TEST(MetaEnumTest_Enum8_value); diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 56d4dc62443..e37335cecf2 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -2541,7 +2541,8 @@ void tst_Moc::warnings() #endif } -class tst_Moc::PrivateClass : public QObject { +class tst_Moc::PrivateClass : public QObject +{ Q_PROPERTY(int someProperty READ someSlot WRITE someSlot2) Q_OBJECT Q_SIGNALS: @@ -2551,6 +2552,13 @@ public Q_SLOTS: void someSlot2(int) {} public: Q_INVOKABLE PrivateClass() {} +#ifdef QT_MOC_HAS_UINTDATA // access to private class' enums was fixed for Qt 6.9 + enum SomeEnum { + Value0, + Value1, + }; + Q_ENUM(SomeEnum) +#endif }; void tst_Moc::privateClass() diff --git a/tests/auto/tools/mochelpers/tst_mochelpers.cpp b/tests/auto/tools/mochelpers/tst_mochelpers.cpp index 0e12f1246b9..089acc73f57 100644 --- a/tests/auto/tools/mochelpers/tst_mochelpers.cpp +++ b/tests/auto/tools/mochelpers/tst_mochelpers.cpp @@ -470,7 +470,7 @@ void tst_MocHelpers::constructorUintData() QCOMPARE(result.header[1], 1U); QCOMPARE(result.header[3], 1U); QCOMPARE(result.header[4], AccessPublic | MethodConstructor); - QCOMPARE(result.header[5], 1U); + QCOMPARE(result.header[5], 0U); QCOMPARE(result.payload[0], NoType); QCOMPARE(result.payload[1], uint(QMetaType::QObjectStar)); QCOMPARE(result.payload[2], 2U);