diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2023-08-21 11:19:44 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2023-08-22 12:34:19 +0200 |
commit | ba99613a0224e8b36e4e949eb517be197ae18854 (patch) | |
tree | af46ed5cbab9e6f4b1137a7f3e51684fb778c1d7 | |
parent | f95f4c51b9909000f1400a7bcdc6334f5be617e0 (diff) |
Update the moc copy
Fixes: QTBUG-116255
Change-Id: I12697eb144d3d7333220cbc38c96a5760224c936
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r-- | tools/qscxmlc/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tools/qscxmlc/generator.cpp | 206 | ||||
-rw-r--r-- | tools/qscxmlc/moc.cpp | 2171 | ||||
-rw-r--r-- | tools/qscxmlc/moc.h | 23 | ||||
-rw-r--r-- | tools/qscxmlc/moc_patches/generator.cpp.patch | 51 | ||||
-rw-r--r-- | tools/qscxmlc/moc_patches/generator.h.patch | 4 | ||||
-rw-r--r-- | tools/qscxmlc/moc_patches/moc.cpp.patch | 29 | ||||
-rw-r--r-- | tools/qscxmlc/moc_patches/moc.h.patch | 18 | ||||
-rw-r--r-- | tools/qscxmlc/moc_patches/outputrevision.h.patch | 2 | ||||
-rwxr-xr-x | tools/qscxmlc/moc_patches/update_moc.sh | 2 |
10 files changed, 2347 insertions, 161 deletions
diff --git a/tools/qscxmlc/CMakeLists.txt b/tools/qscxmlc/CMakeLists.txt index 006b2fd..98b7b3e 100644 --- a/tools/qscxmlc/CMakeLists.txt +++ b/tools/qscxmlc/CMakeLists.txt @@ -19,7 +19,7 @@ qt_internal_add_tool(${target_name} ../../src/scxml/qscxmltabledata.cpp ../../src/scxml/qscxmltabledata.h generator.cpp generator.h main.cpp - moc.h + moc.cpp moc.h outputrevision.h qscxmlc.cpp qscxmlc.h scxmlcppdumper.cpp scxmlcppdumper.h diff --git a/tools/qscxmlc/generator.cpp b/tools/qscxmlc/generator.cpp index 2d6865e..b0b1a32 100644 --- a/tools/qscxmlc/generator.cpp +++ b/tools/qscxmlc/generator.cpp @@ -98,11 +98,11 @@ Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, } // -- QtScxml -static inline int lengthOfEscapeSequence(const QByteArray &s, int i) +static inline qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i) { if (s.at(i) != '\\' || i >= s.size() - 1) return 1; - const int startPos = i; + const qsizetype startPos = i; ++i; char ch = s.at(i); if (ch == 'x') { @@ -139,18 +139,18 @@ static inline uint lengthOfEscapedString(const QByteArray &str) static void printStringWithIndentation(QIODevice &out, const QByteArray &s) // -- QtScxml { static constexpr int ColumnWidth = 72; - int len = s.size(); - int idx = 0; + const qsizetype len = s.size(); + qsizetype idx = 0; do { - int spanLen = qMin(ColumnWidth - 2, len - idx); + qsizetype spanLen = qMin(ColumnWidth - 2, len - idx); // don't cut escape sequences at the end of a line - int backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); + const qsizetype backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); if (backSlashPos >= idx) { - int escapeLen = lengthOfEscapeSequence(s, backSlashPos); + const qsizetype escapeLen = lengthOfEscapeSequence(s, backSlashPos); spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, len - idx); } - fprintf(out, "\n \"%.*s\"", spanLen, s.constData() + idx); + fprintf(out, "\n \"%.*s\"", int(spanLen), s.constData() + idx); idx += spanLen; } while (idx < len); } @@ -174,8 +174,8 @@ int Generator::stridx(const QByteArray &s) static int aggregateParameterCount(const QList<FunctionDef> &list) { int sum = 0; - for (int i = 0; i < list.size(); ++i) - sum += list.at(i).arguments.size() + 1; // +1 for return type + for (const FunctionDef &def : list) + sum += int(def.arguments.size()) + 1; // +1 for return type return sum; } @@ -214,7 +214,7 @@ bool Generator::registerableMetaType(const QByteArray &propertyType) for (const QByteArray &oneArgTemplateType : oneArgTemplates) { QByteArray ba = oneArgTemplateType + "<"; if (propertyType.startsWith(ba) && propertyType.endsWith(">")) { - const int argumentSize = propertyType.size() - oneArgTemplateType.size() - 1 + const qsizetype argumentSize = propertyType.size() - oneArgTemplateType.size() - 1 // The closing '>' - 1 // templates inside templates have an extra whitespace char to strip. @@ -232,7 +232,7 @@ static bool qualifiedNameEquals(const QByteArray &qualifiedName, const QByteArra { if (qualifiedName == name) return true; - int index = qualifiedName.indexOf("::"); + const qsizetype index = qualifiedName.indexOf("::"); if (index == -1) return false; return qualifiedNameEquals(qualifiedName.mid(index+2), name); @@ -262,8 +262,7 @@ void Generator::generateCode() // filter out undeclared enumerators and sets { QList<EnumDef> enumList; - for (int i = 0; i < cdef->enumList.size(); ++i) { - EnumDef def = cdef->enumList.at(i); + for (EnumDef def : std::as_const(cdef->enumList)) { if (cdef->enumDeclarations.contains(def.name)) { enumList += def; } @@ -331,7 +330,7 @@ void Generator::generateCode() for (int i = 0; i < strings.size(); ++i) { int thisLength = lengthOfEscapedString(strings.at(i)) + 1; // -- QtScxml - fprintf(out, " unsigned char stringdata%d[%d];\n", i, thisLength); + fprintf(out, " char stringdata%d[%d];\n", i, thisLength); // -- QtScxml } fprintf(out, "};\n"); @@ -371,7 +370,7 @@ void Generator::generateCode() const QByteArray s = strings.at(i); const qsizetype len = s.size(); for (qsizetype charPos = 0; charPos < len; ++charPos) - fprintf(out, "0x%.2x,", static_cast<quint8>(s.at(charPos))); + fprintf(out, "char(0x%.2x),", static_cast<quint8>(s.at(charPos))); const bool isLast = (i == end - 1); fprintf(out, "0%s // %d: %s", isLast ? "}" : "},", i, s.constData()); } @@ -397,8 +396,8 @@ void Generator::generateCode() fprintf(out, " %4d, %4d, // classinfo\n", int(cdef->classInfoList.size()), int(cdef->classInfoList.size() ? index : 0)); index += cdef->classInfoList.size() * 2; - int methodCount = cdef->signalList.size() + cdef->slotList.size() + cdef->methodList.size(); - fprintf(out, " %4d, %4d, // methods\n", methodCount, methodCount ? index : 0); + const qsizetype methodCount = cdef->signalList.size() + cdef->slotList.size() + cdef->methodList.size(); + fprintf(out, " %4" PRIdQSIZETYPE ", %4d, // methods\n", methodCount, methodCount ? index : 0); index += methodCount * QMetaObjectPrivate::IntsPerMethod; if (cdef->revisionedMethods) index += methodCount; @@ -416,8 +415,9 @@ void Generator::generateCode() fprintf(out, " %4d, %4d, // enums/sets\n", int(cdef->enumList.size()), cdef->enumList.size() ? index : 0); int enumsIndex = index; - for (int i = 0; i < cdef->enumList.size(); ++i) - index += 5 + (cdef->enumList.at(i).values.size() * 2); + for (const EnumDef &def : std::as_const(cdef->enumList)) + index += QMetaObjectPrivate::IntsPerEnum + (def.values.size() * 2); + fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.size()) : 0, isConstructible ? index : 0); @@ -436,8 +436,8 @@ void Generator::generateCode() // generateClassInfos(); - // all property metatypes, + 1 for the type of the current class itself - int initialMetaTypeOffset = cdef->propertyList.size() + 1; + // all property metatypes + all enum metatypes + 1 for the type of the current class itself + int initialMetaTypeOffset = cdef->propertyList.size() + cdef->enumList.size() + 1; // // Build signals array first, otherwise the signal indices would be wrong @@ -500,15 +500,14 @@ void Generator::generateCode() QMultiHash<QByteArray, QByteArray> knownExtraMetaObject(knownGadgets); knownExtraMetaObject.unite(knownQObjectClasses); - for (int i = 0; i < cdef->propertyList.size(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { if (isBuiltinType(p.type)) continue; if (p.type.contains('*') || p.type.contains('<') || p.type.contains('>')) continue; - int s = p.type.lastIndexOf("::"); + const qsizetype s = p.type.lastIndexOf("::"); if (s <= 0) continue; @@ -519,7 +518,7 @@ void Generator::generateCode() QByteArray thisScope = cdef->qualified; do { - int s = thisScope.lastIndexOf("::"); + const qsizetype s = thisScope.lastIndexOf("::"); thisScope = thisScope.left(s); QByteArray currentScope = thisScope.isEmpty() ? unqualifiedScope : thisScope + "::" + unqualifiedScope; scopeIt = knownExtraMetaObject.constFind(currentScope); @@ -545,7 +544,7 @@ void Generator::generateCode() for (auto it = cdef->enumDeclarations.keyBegin(), end = cdef->enumDeclarations.keyEnd(); it != end; ++it) { const QByteArray &enumKey = *it; - int s = enumKey.lastIndexOf("::"); + const qsizetype s = enumKey.lastIndexOf("::"); if (s > 0) { QByteArray scope = enumKey.left(s); if (scope != "Qt" && !qualifiedNameEquals(cdef->qualified, scope) && !extraList.contains(scope)) @@ -560,9 +559,9 @@ void Generator::generateCode() if (!extraList.isEmpty()) { fprintf(out, "Q_CONSTINIT static const QMetaObject::SuperData qt_meta_extradata_%s[] = {\n", qualifiedClassNameIdentifier.constData()); - for (int i = 0; i < extraList.size(); ++i) { - fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", extraList.at(i).constData()); - } + for (const QByteArray &ba : std::as_const(extraList)) + fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", ba.constData()); + fprintf(out, " nullptr\n};\n\n"); } @@ -608,13 +607,19 @@ void Generator::generateCode() fprintf(out, " qt_metaTypeArray<"); } // metatypes for properties - for (int i = 0; i < cdef->propertyList.size(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { fprintf(out, "%s\n // property '%s'\n %s", comma, p.name.constData(), stringForType(p.type, true).constData()); comma = ","; } + // metatypes for enums + for (const EnumDef &e : std::as_const(cdef->enumList)) { + fprintf(out, "%s\n // enum '%s'\n %s", + comma, e.name.constData(), stringForType(e.qualifiedType(cdef), true).constData()); + comma = ","; + } + // type name for the Q_OJBECT/GADGET itself, void for namespaces auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void"; fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s", @@ -625,8 +630,7 @@ void Generator::generateCode() // because we definitely printed something above, this section doesn't need comma control for (const QList<FunctionDef> &methodContainer : { cdef->signalList, cdef->slotList, cdef->methodList }) { - for (int i = 0; i< methodContainer.size(); ++i) { - const FunctionDef& fdef = methodContainer.at(i); + for (const FunctionDef &fdef : methodContainer) { fprintf(out, ",\n // method '%s'\n %s", fdef.name.constData(), stringForType(fdef.type.name, false).constData()); for (const auto &argument: fdef.arguments) @@ -635,8 +639,7 @@ void Generator::generateCode() } // but constructors have no return types, so this needs comma control again - for (int i = 0; i< cdef->constructorList.size(); ++i) { - const FunctionDef& fdef = cdef->constructorList.at(i); + for (const FunctionDef &fdef : std::as_const(cdef->constructorList)) { if (fdef.arguments.isEmpty()) continue; @@ -670,24 +673,28 @@ void Generator::generateCode() // fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData()); fprintf(out, " if (!_clname) return nullptr;\n"); -// -- QtScxml - fprintf(out, " if (!strcmp(_clname, reinterpret_cast<const char *>(\n" - " qt_meta_stringdata_%s.stringdata0)))\n" - " return static_cast<void*>(const_cast< %s*>(this));\n", - qualifiedClassNameIdentifier.constData(), cdef->qualified.constData()); -// -- QtScxml - for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one - if (cdef->superclassList.at(i).second == FunctionDef::Private) - continue; - const char *cname = cdef->superclassList.at(i).first.constData(); - fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(this);\n", - cname, cname); + fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n" + " return static_cast<void*>(this);\n", + qualifiedClassNameIdentifier.constData()); + + // for all superclasses but the first one + if (cdef->superclassList.size() > 1) { + auto it = cdef->superclassList.cbegin() + 1; + const auto end = cdef->superclassList.cend(); + for (; it != end; ++it) { + const auto &[className, access] = *it; + if (access == FunctionDef::Private) + continue; + const char *cname = className.constData(); + fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(this);\n", + cname, cname); + } } - for (int i = 0; i < cdef->interfaceList.size(); ++i) { - const QList<ClassDef::Interface> &iface = cdef->interfaceList.at(i); - for (int j = 0; j < iface.size(); ++j) { + + for (const QList<ClassDef::Interface> &iface : std::as_const(cdef->interfaceList)) { + for (qsizetype j = 0; j < iface.size(); ++j) { fprintf(out, " if (!strcmp(_clname, %s))\n return ", iface.at(j).interfaceId.constData()); - for (int k = j; k >= 0; --k) + for (qsizetype k = j; k >= 0; --k) fprintf(out, "static_cast< %s*>(", iface.at(k).className.constData()); fprintf(out, "this%s;\n", QByteArray(j + 1, ')').constData()); } @@ -735,8 +742,7 @@ void Generator::generateCode() void Generator::registerClassInfoStrings() { - for (int i = 0; i < cdef->classInfoList.size(); ++i) { - const ClassInfoDef &c = cdef->classInfoList.at(i); + for (const ClassInfoDef &c : std::as_const(cdef->classInfoList)) { strreg(c.name); strreg(c.value); } @@ -749,25 +755,19 @@ void Generator::generateClassInfos() fprintf(out, "\n // classinfo: key, value\n"); - for (int i = 0; i < cdef->classInfoList.size(); ++i) { - const ClassInfoDef &c = cdef->classInfoList.at(i); + for (const ClassInfoDef &c : std::as_const(cdef->classInfoList)) fprintf(out, " %4d, %4d,\n", stridx(c.name), stridx(c.value)); - } } void Generator::registerFunctionStrings(const QList<FunctionDef> &list) { - for (int i = 0; i < list.size(); ++i) { - const FunctionDef &f = list.at(i); - + for (const FunctionDef &f : list) { strreg(f.name); if (!isBuiltinType(f.normalizedType)) strreg(f.normalizedType); strreg(f.tag); - int argsCount = f.arguments.size(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); + for (const ArgumentDef &a : f.arguments) { if (!isBuiltinType(a.normalizedType)) strreg(a.normalizedType); strreg(a.name); @@ -788,9 +788,7 @@ void Generator::generateFunctions(const QList<FunctionDef> &list, const char *fu return; fprintf(out, "\n // %ss: name, argc, parameters, tag, flags, initial metatype offsets\n", functype); - for (int i = 0; i < list.size(); ++i) { - const FunctionDef &f = list.at(i); - + for (const FunctionDef &f : list) { QByteArray comment; uint flags = type; if (f.access == FunctionDef::Private) { @@ -839,10 +837,8 @@ void Generator::generateFunctionRevisions(const QList<FunctionDef> &list, const { if (list.size()) fprintf(out, "\n // %ss: revision\n", functype); - for (int i = 0; i < list.size(); ++i) { - const FunctionDef &f = list.at(i); + for (const FunctionDef &f : list) fprintf(out, " %4d,\n", f.revision); - } } void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const char *functype) @@ -850,8 +846,7 @@ void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const if (list.isEmpty()) return; fprintf(out, "\n // %ss: parameters\n", functype); - for (int i = 0; i < list.size(); ++i) { - const FunctionDef &f = list.at(i); + for (const FunctionDef &f : list) { fprintf(out, " "); // Types @@ -865,10 +860,8 @@ void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const } // Parameter names - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &arg = f.arguments.at(j); + for (const ArgumentDef &arg : f.arguments) fprintf(out, " %4d,", stridx(arg.name)); - } fprintf(out, "\n"); } @@ -901,8 +894,7 @@ void Generator::generateTypeInfo(const QByteArray &typeName, bool allowEmptyName void Generator::registerPropertyStrings() { - for (int i = 0; i < cdef->propertyList.size(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { strreg(p.name); if (!isBuiltinType(p.type)) strreg(p.type); @@ -917,8 +909,7 @@ void Generator::generateProperties() if (cdef->propertyList.size()) fprintf(out, "\n // properties: name, type, flags\n"); - for (int i = 0; i < cdef->propertyList.size(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { uint flags = Invalid; if (!isBuiltinType(p.type)) flags |= EnumOrFlag; @@ -971,13 +962,12 @@ void Generator::generateProperties() void Generator::registerEnumStrings() { - for (int i = 0; i < cdef->enumList.size(); ++i) { - const EnumDef &e = cdef->enumList.at(i); + for (const EnumDef &e : std::as_const(cdef->enumList)) { strreg(e.name); if (!e.enumName.isNull()) strreg(e.enumName); - for (int j = 0; j < e.values.size(); ++j) - strreg(e.values.at(j)); + for (const QByteArray &val : e.values) + strreg(val); } } @@ -987,7 +977,7 @@ void Generator::generateEnums(int index) return; fprintf(out, "\n // enums: name, alias, flags, count, data\n"); - index += 5 * cdef->enumList.size(); + index += QMetaObjectPrivate::IntsPerEnum * cdef->enumList.size(); int i; for (i = 0; i < cdef->enumList.size(); ++i) { const EnumDef &e = cdef->enumList.at(i); @@ -1006,10 +996,8 @@ void Generator::generateEnums(int index) } fprintf(out, "\n // enum data: key, value\n"); - for (i = 0; i < cdef->enumList.size(); ++i) { - const EnumDef &e = cdef->enumList.at(i); - for (int j = 0; j < e.values.size(); ++j) { - const QByteArray &val = e.values.at(j); + for (const EnumDef &e : std::as_const(cdef->enumList)) { + for (const QByteArray &val : e.values) { QByteArray code = cdef->qualified.constData(); if (e.isEnumClass) code += "::" + (e.enumName.isNull() ? e.name : e.enumName); @@ -1122,10 +1110,11 @@ void Generator::generateStaticMetacall() Q_ASSERT(!f.isPrivateSignal); // That would be a strange ctor indeed int offset = 1; - int argsCount = f.arguments.size(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); - if (j) + const auto begin = f.arguments.cbegin(); + const auto end = f.arguments.cend(); + for (auto it = begin; it != end; ++it) { + const ArgumentDef &a = *it; + if (it != begin) fprintf(out, ","); fprintf(out, "(*reinterpret_cast<%s>(_a[%d]))", a.typeNameForCast.constData(), offset++); @@ -1204,16 +1193,17 @@ void Generator::generateStaticMetacall() if (f.isRawSlot) { fprintf(out, "QMethodRawArguments{ _a }"); } else { - int argsCount = f.arguments.size(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); - if (j) + const auto begin = f.arguments.cbegin(); + const auto end = f.arguments.cend(); + for (auto it = begin; it != end; ++it) { + const ArgumentDef &a = *it; + if (it != begin) fprintf(out, ","); fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++); isUsed_a = true; } if (f.isPrivateSignal) { - if (argsCount > 0) + if (!f.arguments.isEmpty()) fprintf(out, ", "); fprintf(out, "%s", "QPrivateSignal()"); } @@ -1278,15 +1268,16 @@ void Generator::generateStaticMetacall() fprintf(out, " {\n"); fprintf(out, " using _t = %s (%s::*)(",f.type.rawName.constData() , cdef->classname.constData()); - int argsCount = f.arguments.size(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); - if (j) + const auto begin = f.arguments.cbegin(); + const auto end = f.arguments.cend(); + for (auto it = begin; it != end; ++it) { + const ArgumentDef &a = *it; + if (it != begin) fprintf(out, ", "); fprintf(out, "%s", QByteArray(a.type.name + ' ' + a.rightType).constData()); } if (f.isPrivateSignal) { - if (argsCount > 0) + if (!f.arguments.isEmpty()) fprintf(out, ", "); fprintf(out, "%s", "QPrivateSignal"); } @@ -1337,8 +1328,7 @@ void Generator::generateStaticMetacall() bool needSet = false; bool needReset = false; bool hasBindableProperties = false; - for (int i = 0; i < cdef->propertyList.size(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { needGet |= !p.read.isEmpty() || !p.member.isEmpty(); if (!p.read.isEmpty() || !p.member.isEmpty()) needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec @@ -1551,9 +1541,11 @@ void Generator::generateSignal(FunctionDef *def,int index) } int offset = 1; - for (int j = 0; j < def->arguments.size(); ++j) { - const ArgumentDef &a = def->arguments.at(j); - if (j) + const auto begin = def->arguments.cbegin(); + const auto end = def->arguments.cend(); + for (auto it = begin; it != end; ++it) { + const ArgumentDef &a = *it; + if (it != begin) fputs(", ", out); if (a.type.name.size()) fputs(a.type.name.constData(), out); @@ -1723,7 +1715,7 @@ void Generator::generatePluginMetaData() }; // 'Use' all namespaces. - int pos = cdef->qualified.indexOf("::"); + qsizetype pos = cdef->qualified.indexOf("::"); for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) ) fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData()); diff --git a/tools/qscxmlc/moc.cpp b/tools/qscxmlc/moc.cpp new file mode 100644 index 0000000..2d01a29 --- /dev/null +++ b/tools/qscxmlc/moc.cpp @@ -0,0 +1,2171 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "moc.h" +#include "generator.h" +#include "qdatetime.h" +#include "utils.h" +#include "outputrevision.h" +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> +#include <QtCore/qjsondocument.h> + +// -- QScxml +#include <QtCore/qjsonobject.h> +// -- QScxml + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +#if 0 // -- QtScxml +// only moc needs this function +static QByteArray normalizeType(const QByteArray &ba) +{ + return ba.size() ? normalizeTypeInternal(ba.constBegin(), ba.constEnd()) : ba; +} + +bool Moc::parseClassHead(ClassDef *def) +{ + // figure out whether this is a class declaration, or only a + // forward or variable declaration. + int i = 0; + Token token; + do { + token = lookup(i++); + if (token == COLON || token == LBRACE) + break; + if (token == SEMIC || token == RANGLE) + return false; + } while (token); + + // support attributes like "class [[deprecated]]] name" + skipCxxAttributes(); + + if (!test(IDENTIFIER)) // typedef struct { ... } + return false; + QByteArray name = lexem(); + + // support "class IDENT name" and "class IDENT(IDENT) name" + // also support "class IDENT name (final|sealed|Q_DECL_FINAL)" + if (test(LPAREN)) { + until(RPAREN); + if (!test(IDENTIFIER)) + return false; + name = lexem(); + } else if (test(IDENTIFIER)) { + const QByteArray lex = lexem(); + if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL") + name = lex; + } + + def->qualified += name; + while (test(SCOPE)) { + def->qualified += lexem(); + if (test(IDENTIFIER)) { + name = lexem(); + def->qualified += name; + } + } + def->classname = name; + + if (test(IDENTIFIER)) { + const QByteArray lex = lexem(); + if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL") + return false; + } + + if (test(COLON)) { + do { + test(VIRTUAL); + FunctionDef::Access access = FunctionDef::Public; + if (test(PRIVATE)) + access = FunctionDef::Private; + else if (test(PROTECTED)) + access = FunctionDef::Protected; + else + test(PUBLIC); + test(VIRTUAL); + const QByteArray type = parseType().name; + // ignore the 'class Foo : BAR(Baz)' case + if (test(LPAREN)) { + until(RPAREN); + } else { + def->superclassList += qMakePair(type, access); + } + } while (test(COMMA)); + + if (!def->superclassList.isEmpty() + && knownGadgets.contains(def->superclassList.constFirst().first)) { + // Q_GADGET subclasses are treated as Q_GADGETs + knownGadgets.insert(def->classname, def->qualified); + knownGadgets.insert(def->qualified, def->qualified); + } + } + if (!test(LBRACE)) + return false; + def->begin = index - 1; + bool foundRBrace = until(RBRACE); + def->end = index; + index = def->begin + 1; + return foundRBrace; +} + +Type Moc::parseType() +{ + Type type; + bool hasSignedOrUnsigned = false; + bool isVoid = false; + type.firstToken = lookup(); + for (;;) { + skipCxxAttributes(); + switch (next()) { + case SIGNED: + case UNSIGNED: + hasSignedOrUnsigned = true; + Q_FALLTHROUGH(); + case CONST: + case VOLATILE: + type.name += lexem(); + type.name += ' '; + if (lookup(0) == VOLATILE) + type.isVolatile = true; + continue; + case Q_MOC_COMPAT_TOKEN: + case Q_INVOKABLE_TOKEN: + case Q_SCRIPTABLE_TOKEN: + case Q_SIGNALS_TOKEN: + case Q_SLOTS_TOKEN: + case Q_SIGNAL_TOKEN: + case Q_SLOT_TOKEN: + type.name += lexem(); + return type; + case NOTOKEN: + return type; + default: + prev(); + break; + } + break; + } + + skipCxxAttributes(); + test(ENUM) || test(CLASS) || test(STRUCT); + for(;;) { + skipCxxAttributes(); + switch (next()) { + case IDENTIFIER: + // void mySlot(unsigned myArg) + if (hasSignedOrUnsigned) { + prev(); + break; + } + Q_FALLTHROUGH(); + case CHAR: + case SHORT: + case INT: + case LONG: + type.name += lexem(); + // preserve '[unsigned] long long', 'short int', 'long int', 'long double' + if (test(LONG) || test(INT) || test(DOUBLE)) { + type.name += ' '; + prev(); + continue; + } + break; + case FLOAT: + case DOUBLE: + case VOID: + case BOOL: + case AUTO: + type.name += lexem(); + isVoid |= (lookup(0) == VOID); + break; + case NOTOKEN: + return type; + default: + prev(); + ; + } + if (test(LANGLE)) { + if (type.name.isEmpty()) { + // '<' cannot start a type + return type; + } + type.name += lexemUntil(RANGLE); + } + if (test(SCOPE)) { + type.name += lexem(); + type.isScoped = true; + } else { + break; + } + } + while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED) + || test(STAR) || test(AND) || test(ANDAND)) { + type.name += ' '; + type.name += lexem(); + if (lookup(0) == AND) + type.referenceType = Type::Reference; + else if (lookup(0) == ANDAND) + type.referenceType = Type::RValueReference; + else if (lookup(0) == STAR) + type.referenceType = Type::Pointer; + } + type.rawName = type.name; + // transform stupid things like 'const void' or 'void const' into 'void' + if (isVoid && type.referenceType == Type::NoReference) { + type.name = "void"; + } + return type; +} + +enum class IncludeState { + IncludeBegin, + IncludeEnd, + NoInclude, +}; + +bool Moc::parseEnum(EnumDef *def) +{ + bool isTypdefEnum = false; // typedef enum { ... } Foo; + + if (test(CLASS) || test(STRUCT)) + def->isEnumClass = true; + + if (test(IDENTIFIER)) { + def->name = lexem(); + } else { + if (lookup(-1) != TYPEDEF) + return false; // anonymous enum + isTypdefEnum = true; + } + if (test(COLON)) { // C++11 strongly typed enum + // enum Foo : unsigned long { ... }; + def->type = normalizeType(parseType().name); + } + if (!test(LBRACE)) + return false; + auto handleInclude = [this]() -> IncludeState { + bool hadIncludeBegin = false; + if (test(MOC_INCLUDE_BEGIN)) { + currentFilenames.push(symbol().unquotedLexem()); + // we do not return early to handle empty headers in one go + hadIncludeBegin = true; + } + if (test(NOTOKEN)) { + next(MOC_INCLUDE_END); + currentFilenames.pop(); + return IncludeState::IncludeEnd; + } + if (hadIncludeBegin) + return IncludeState::IncludeBegin; + else + return IncludeState::NoInclude; + }; + do { + handleInclude(); + if (lookup() == RBRACE) // accept trailing comma + break; + next(IDENTIFIER); + def->values += lexem(); + handleInclude(); + skipCxxAttributes(); + } while (test(EQ) ? until(COMMA) : test(COMMA)); + next(RBRACE); + if (isTypdefEnum) { + if (!test(IDENTIFIER)) + return false; + def->name = lexem(); + } + return true; +} + +void Moc::parseFunctionArguments(FunctionDef *def) +{ + Q_UNUSED(def); + while (hasNext()) { + ArgumentDef arg; + arg.type = parseType(); + if (arg.type.name == "void") + break; + if (test(IDENTIFIER)) + arg.name = lexem(); + while (test(LBRACK)) { + arg.rightType += lexemUntil(RBRACK); + } + if (test(CONST) || test(VOLATILE)) { + arg.rightType += ' '; + arg.rightType += lexem(); + } + arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType)); + arg.typeNameForCast = QByteArray("std::add_pointer_t<"+arg.normalizedType+">"); + if (test(EQ)) + arg.isDefault = true; + def->arguments += arg; + if (!until(COMMA)) + break; + } + + if (!def->arguments.isEmpty() + && def->arguments.constLast().normalizedType == "QPrivateSignal") { + def->arguments.removeLast(); + def->isPrivateSignal = true; + } + if (def->arguments.size() == 1 + && def->arguments.constLast().normalizedType == "QMethodRawArguments") { + def->arguments.removeLast(); + def->isRawSlot = true; + } +} + +bool Moc::testFunctionAttribute(FunctionDef *def) +{ + if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) { + ++index; + return true; + } + return false; +} + +bool Moc::testFunctionAttribute(Token tok, FunctionDef *def) +{ + switch (tok) { + case Q_MOC_COMPAT_TOKEN: + def->isCompat = true; + return true; + case Q_INVOKABLE_TOKEN: + def->isInvokable = true; + return true; + case Q_SIGNAL_TOKEN: + def->isSignal = true; + return true; + case Q_SLOT_TOKEN: + def->isSlot = true; + return true; + case Q_SCRIPTABLE_TOKEN: + def->isInvokable = def->isScriptable = true; + return true; + default: break; + } + return false; +} + +bool Moc::skipCxxAttributes() +{ + auto rewind = index; + if (test(LBRACK) && test(LBRACK) && until(RBRACK) && test(RBRACK)) + return true; + index = rewind; + return false; +} + +QTypeRevision Moc::parseRevision() +{ + next(LPAREN); + QByteArray revisionString = lexemUntil(RPAREN); + revisionString.remove(0, 1); + revisionString.chop(1); + const QList<QByteArray> majorMinor = revisionString.split(','); + switch (majorMinor.size()) { + case 1: { + bool ok = false; + const int revision = revisionString.toInt(&ok); + if (!ok || !QTypeRevision::isValidSegment(revision)) + error("Invalid revision"); + return QTypeRevision::fromMinorVersion(revision); + } + case 2: { // major.minor + bool ok = false; + const int major = majorMinor[0].toInt(&ok); + if (!ok || !QTypeRevision::isValidSegment(major)) + error("Invalid major version"); + const int minor = majorMinor[1].toInt(&ok); + if (!ok || !QTypeRevision::isValidSegment(minor)) + error("Invalid minor version"); + return QTypeRevision::fromVersion(major, minor); + } + default: + error("Invalid revision"); + return QTypeRevision(); + } +} + +bool Moc::testFunctionRevision(FunctionDef *def) +{ + + if (test(Q_REVISION_TOKEN)) { + def->revision = parseRevision().toEncodedVersion<int>(); + return true; + } + + return false; +} + +// returns false if the function should be ignored +bool Moc::parseFunction(FunctionDef *def, bool inMacro) +{ + def->isVirtual = false; + def->isStatic = false; + //skip modifiers and attributes + while (testForFunctionModifiers(def) + || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {} + bool templateFunction = (lookup() == TEMPLATE); + def->type = parseType(); + if (def->type.name.isEmpty()) { + if (templateFunction) + error("Template function as signal or slot"); + else + error(); + } + bool scopedFunctionName = false; + if (test(LPAREN)) { + def->name = def->type.name; + scopedFunctionName = def->type.isScoped; + def->type = Type("int"); + } else { + // we might have modifiers and attributes after a tag + // note that testFunctionAttribute is handled further below, + // and revisions and attributes must come first + while (testForFunctionModifiers(def)) {} + Type tempType = parseType();; + while (!tempType.name.isEmpty() && lookup() != LPAREN) { + if (testFunctionAttribute(def->type.firstToken, def)) + ; // fine + else if (def->type.firstToken == Q_SIGNALS_TOKEN) + error(); + else if (def->type.firstToken == Q_SLOTS_TOKEN) + error(); + else { + if (!def->tag.isEmpty()) + def->tag += ' '; + def->tag += def->type.name; + } + def->type = tempType; + tempType = parseType(); + } + next(LPAREN, "Not a signal or slot declaration"); + def->name = tempType.name; + scopedFunctionName = tempType.isScoped; + } + + if (!test(RPAREN)) { + parseFunctionArguments(def); + next(RPAREN); + } + + // support optional macros with compiler specific options + while (test(IDENTIFIER)) + ; + + def->isConst = test(CONST); + + while (test(IDENTIFIER)) + ; + + if (inMacro) { + next(RPAREN); + prev(); + } else { + if (test(THROW)) { + next(LPAREN); + until(RPAREN); + } + + if (def->type.name == "auto" && test(ARROW)) + def->type = parseType(); // Parse trailing return-type + + if (test(SEMIC)) + ; + else if ((def->inlineCode = test(LBRACE))) + until(RBRACE); + else if ((def->isAbstract = test(EQ))) + until(SEMIC); + else if (skipCxxAttributes()) + until(SEMIC); + else + error(); + } + if (scopedFunctionName) { + const QByteArray msg = "Function declaration " + def->name + + " contains extra qualification. Ignoring as signal or slot."; + warning(msg.constData()); + return false; + } + + QList<QByteArray> typeNameParts = normalizeType(def->type.name).split(' '); + if (typeNameParts.contains("auto")) { + // We expected a trailing return type but we haven't seen one + error("Function declared with auto as return type but missing trailing return type. " + "Return type deduction is not supported."); + } + + // we don't support references as return types, it's too dangerous + if (def->type.referenceType == Type::Reference) { + QByteArray rawName = def->type.rawName; + def->type = Type("void"); + def->type.rawName = rawName; + } + + def->normalizedType = normalizeType(def->type.name); + return true; +} + +bool Moc::testForFunctionModifiers(FunctionDef *def) +{ + return test(EXPLICIT) || test(INLINE) || + (test(STATIC) && (def->isStatic = true)) || + (test(VIRTUAL) && (def->isVirtual = true)); +} + +// like parseFunction, but never aborts with an error +bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def) +{ + def->isVirtual = false; + def->isStatic = false; + //skip modifiers and attributes + while (testForFunctionModifiers(def) + || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {} + bool tilde = test(TILDE); + def->type = parseType(); + if (def->type.name.isEmpty()) + return false; + bool scopedFunctionName = false; + if (test(LPAREN)) { + def->name = def->type.name; + scopedFunctionName = def->type.isScoped; + if (def->name == cdef->classname) { + def->isDestructor = tilde; + def->isConstructor = !tilde; + def->type = Type(); + } else { + def->type = Type("int"); + } + } else { + // ### TODO: The condition before testForFunctionModifiers shoulnd't be necessary, + // but otherwise we end up with misparses + if (def->isSlot || def->isSignal || def->isInvokable) + while (testForFunctionModifiers(def)) {} + Type tempType = parseType();; + while (!tempType.name.isEmpty() && lookup() != LPAREN) { + if (testFunctionAttribute(def->type.firstToken, def)) + ; // fine + else if (def->type.name == "Q_SIGNAL") + def->isSignal = true; + else if (def->type.name == "Q_SLOT") + def->isSlot = true; + else { + if (!def->tag.isEmpty()) + def->tag += ' '; + def->tag += def->type.name; + } + def->type = tempType; + tempType = parseType(); + } + if (!test(LPAREN)) + return false; + def->name = tempType.name; + scopedFunctionName = tempType.isScoped; + } + + // we don't support references as return types, it's too dangerous + if (def->type.referenceType == Type::Reference) { + QByteArray rawName = def->type.rawName; + def->type = Type("void"); + def->type.rawName = rawName; + } + + def->normalizedType = normalizeType(def->type.name); + + if (!test(RPAREN)) { + parseFunctionArguments(def); + if (!test(RPAREN)) + return false; + } + def->isConst = test(CONST); + if (scopedFunctionName + && (def->isSignal || def->isSlot || def->isInvokable)) { + const QByteArray msg = "parsemaybe: Function declaration " + def->name + + " contains extra qualification. Ignoring as signal or slot."; + warning(msg.constData()); + return false; + } + return true; +} + +inline void handleDefaultArguments(QList<FunctionDef> *functionList, FunctionDef &function) +{ + // support a function with a default argument by pretending there is an + // overload without the argument (the original function is the overload with + // all arguments present) + while (function.arguments.size() > 0 && function.arguments.constLast().isDefault) { + function.wasCloned = true; + function.arguments.removeLast(); + *functionList += function; + } +} + +void Moc::prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const +{ + auto it = namespaceList.crbegin(); + const auto rend = namespaceList.crend(); + for (; it != rend; ++it) { + if (inNamespace(&*it)) + def.qualified.prepend(it->classname + "::"); + } +} + +void Moc::parse() +{ + QList<NamespaceDef> namespaceList; + bool templateClass = false; + while (hasNext()) { + Token t = next(); + switch (t) { + case NAMESPACE: { + qsizetype rewind = index; + if (test(IDENTIFIER)) { + QByteArray nsName = lexem(); + QByteArrayList nested; + while (test(SCOPE)) { + /* treat (C++20's) namespace A::inline B {} as A::B + this is mostly to not break compilation when encountering such + a construct in a header; the interaction of Qt's meta-macros with + inline namespaces is still rather poor. + */ + test(INLINE); + next(IDENTIFIER); + nested.append(nsName); + nsName = lexem(); + } + if (test(EQ)) { + // namespace Foo = Bar::Baz; + until(SEMIC); + } else if (test(LPAREN)) { + // Ignore invalid code such as: 'namespace __identifier("x")' (QTBUG-56634) + until(RPAREN); + } else if (!test(SEMIC)) { + NamespaceDef def; + def.classname = nsName; + def.doGenerate = currentFilenames.size() <= 1; + + next(LBRACE); + def.begin = index - 1; + until(RBRACE); + def.end = index; + index = def.begin + 1; + + prependNamespaces(def, namespaceList); + + for (const QByteArray &ns : nested) { + NamespaceDef parentNs; + parentNs.classname = ns; + parentNs.qualified = def.qualified; + def.qualified += ns + "::"; + parentNs.begin = def.begin; + parentNs.end = def.end; + namespaceList += parentNs; + } + + while (inNamespace(&def) && hasNext()) { + switch (next()) { + case NAMESPACE: + if (test(IDENTIFIER)) { + while (test(SCOPE)) + next(IDENTIFIER); + if (test(EQ)) { + // namespace Foo = Bar::Baz; + until(SEMIC); + } else if (!test(SEMIC)) { + until(RBRACE); + } + } + break; + case Q_NAMESPACE_TOKEN: + def.hasQNamespace = true; + break; + case Q_NAMESPACE_EXPORT_TOKEN: + next(LPAREN); + while (test(IDENTIFIER)) + {} + next(RPAREN); + def.hasQNamespace = true; + break; + case Q_ENUMS_TOKEN: + case Q_ENUM_NS_TOKEN: + parseEnumOrFlag(&def, false); + break; + case Q_ENUM_TOKEN: + error("Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead"); + break; + case Q_FLAGS_TOKEN: + case Q_FLAG_NS_TOKEN: + parseEnumOrFlag(&def, true); + break; + case Q_FLAG_TOKEN: + error("Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead"); + break; + case Q_DECLARE_FLAGS_TOKEN: + parseFlag(&def); + break; + case Q_CLASSINFO_TOKEN: + parseClassInfo(&def); + break; + case Q_MOC_INCLUDE_TOKEN: + // skip it, the namespace is parsed twice + next(LPAREN); + lexemUntil(RPAREN); + break; + case ENUM: { + EnumDef enumDef; + if (parseEnum(&enumDef)) + def.enumList += enumDef; + } break; + case CLASS: + case STRUCT: { + ClassDef classdef; + if (!parseClassHead(&classdef)) + continue; + while (inClass(&classdef) && hasNext()) + next(); // consume all Q_XXXX macros from this class + } break; + default: break; + } + } + namespaceList += def; + index = rewind; + if (!def.hasQNamespace && (!def.classInfoList.isEmpty() || !def.enumDeclarations.isEmpty())) + error("Namespace declaration lacks Q_NAMESPACE macro."); + } + } + break; + } + case SEMIC: + case RBRACE: + templateClass = false; + break; + case TEMPLATE: + templateClass = true; + break; + case MOC_INCLUDE_BEGIN: + currentFilenames.push(symbol().unquotedLexem()); + break; + case MOC_INCLUDE_END: + currentFilenames.pop(); + break; + case Q_DECLARE_INTERFACE_TOKEN: + parseDeclareInterface(); + break; + case Q_DECLARE_METATYPE_TOKEN: + parseDeclareMetatype(); + break; + case Q_MOC_INCLUDE_TOKEN: + parseMocInclude(); + break; + case USING: + if (test(NAMESPACE)) { + while (test(SCOPE) || test(IDENTIFIER)) + ; + // Ignore invalid code such as: 'using namespace __identifier("x")' (QTBUG-63772) + if (test(LPAREN)) + until(RPAREN); + next(SEMIC); + } + break; + case CLASS: + case STRUCT: { + if (currentFilenames.size() <= 1) + break; + + ClassDef def; + if (!parseClassHead(&def)) + continue; + + while (inClass(&def) && hasNext()) { + switch (next()) { + case Q_OBJECT_TOKEN: + def.hasQObject = true; + break; + case Q_GADGET_EXPORT_TOKEN: + next(LPAREN); + while (test(IDENTIFIER)) + {} + next(RPAREN); + Q_FALLTHROUGH(); + case Q_GADGET_TOKEN: + def.hasQGadget = true; + break; + default: break; + } + } + + if (!def.hasQObject && !def.hasQGadget) + continue; + + prependNamespaces(def, namespaceList); + + QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets; + classHash.insert(def.classname, def.qualified); + classHash.insert(def.qualified, def.qualified); + + continue; } + default: break; + } + if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1) + continue; + ClassDef def; + if (parseClassHead(&def)) { + prependNamespaces(def, namespaceList); + + FunctionDef::Access access = FunctionDef::Private; + while (inClass(&def) && hasNext()) { + switch ((t = next())) { + case PRIVATE: + access = FunctionDef::Private; + if (test(Q_SIGNALS_TOKEN)) + error("Signals cannot have access specifier"); + break; + case PROTECTED: + access = FunctionDef::Protected; + if (test(Q_SIGNALS_TOKEN)) + error("Signals cannot have access specifier"); + break; + case PUBLIC: + access = FunctionDef::Public; + if (test(Q_SIGNALS_TOKEN)) + error("Signals cannot have access specifier"); + break; + case CLASS: { + ClassDef nestedDef; + if (parseClassHead(&nestedDef)) { + while (inClass(&nestedDef) && inClass(&def)) { + t = next(); + if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END) + error("Meta object features not supported for nested classes"); + } + } + } break; + case Q_SIGNALS_TOKEN: + parseSignals(&def); + break; + case Q_SLOTS_TOKEN: + switch (lookup(-1)) { + case PUBLIC: + case PROTECTED: + case PRIVATE: + parseSlots(&def, access); + break; + default: + error("Missing access specifier for slots"); + } + break; + case Q_OBJECT_TOKEN: + def.hasQObject = true; + if (templateClass) + error("Template classes not supported by Q_OBJECT"); + if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty()) + error("Class contains Q_OBJECT macro but does not inherit from QObject"); + break; + case Q_GADGET_EXPORT_TOKEN: + next(LPAREN); + while (test(IDENTIFIER)) + {} + next(RPAREN); + Q_FALLTHROUGH(); + case Q_GADGET_TOKEN: + def.hasQGadget = true; + if (templateClass) + error("Template classes not supported by Q_GADGET"); + break; + case Q_PROPERTY_TOKEN: + parseProperty(&def, Named); + break; + case QT_ANONYMOUS_PROPERTY_TOKEN: + parseProperty(&def, Anonymous); + break; + case Q_PLUGIN_METADATA_TOKEN: + parsePluginData(&def); + break; + case Q_ENUMS_TOKEN: + case Q_ENUM_TOKEN: + parseEnumOrFlag(&def, false); + break; + case Q_ENUM_NS_TOKEN: + error("Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead"); + break; + case Q_FLAGS_TOKEN: + case Q_FLAG_TOKEN: + parseEnumOrFlag(&def, true); + break; + case Q_FLAG_NS_TOKEN: + error("Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead"); + break; + case Q_DECLARE_FLAGS_TOKEN: + parseFlag(&def); + break; + case Q_CLASSINFO_TOKEN: + parseClassInfo(&def); + break; + case Q_MOC_INCLUDE_TOKEN: + parseMocInclude(); + break; + case Q_INTERFACES_TOKEN: + parseInterfaces(&def); + break; + case Q_PRIVATE_SLOT_TOKEN: + parseSlotInPrivate(&def, access); + break; + case Q_PRIVATE_PROPERTY_TOKEN: + parsePrivateProperty(&def, Named); + break; + case QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN: + parsePrivateProperty(&def, Anonymous); + break; + case ENUM: { + EnumDef enumDef; + if (parseEnum(&enumDef)) + def.enumList += enumDef; + } break; + case SEMIC: + case COLON: + break; + default: + FunctionDef funcDef; + funcDef.access = access; + qsizetype rewind = index--; + if (parseMaybeFunction(&def, &funcDef)) { + if (funcDef.isConstructor) { + if ((access == FunctionDef::Public) && funcDef.isInvokable) { + def.constructorList += funcDef; + handleDefaultArguments(&def.constructorList, funcDef); + } + } else if (funcDef.isDestructor) { + // don't care about destructors + } else { + if (access == FunctionDef::Public) + def.publicList += funcDef; + if (funcDef.isSlot) { + def.slotList += funcDef; + handleDefaultArguments(&def.slotList, funcDef); + if (funcDef.revision > 0) + ++def.revisionedMethods; + } else if (funcDef.isSignal) { + def.signalList += funcDef; + handleDefaultArguments(&def.signalList, funcDef); + if (funcDef.revision > 0) + ++def.revisionedMethods; + } else if (funcDef.isInvokable) { + def.methodList += funcDef; + handleDefaultArguments(&def.methodList, funcDef); + if (funcDef.revision > 0) + ++def.revisionedMethods; + } + } + } else { + index = rewind; + } + } + } + + next(RBRACE); + + if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty() + && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty()) + continue; // no meta object code required + + + if (!def.hasQObject && !def.hasQGadget) + error("Class declaration lacks Q_OBJECT macro."); + + // Add meta tags to the plugin meta data: + if (!def.pluginData.iid.isEmpty()) + def.pluginData.metaArgs = metaArgs; + + if (def.hasQObject && !def.superclassList.isEmpty()) + checkSuperClasses(&def); + + checkProperties(&def); + + classList += def; + QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets; + classHash.insert(def.classname, def.qualified); + classHash.insert(def.qualified, def.qualified); + } + } + for (const auto &n : std::as_const(namespaceList)) { + if (!n.hasQNamespace) + continue; + ClassDef def; + static_cast<BaseDef &>(def) = static_cast<BaseDef>(n); + def.qualified += def.classname; + def.hasQNamespace = true; + auto it = std::find_if(classList.begin(), classList.end(), [&def](const ClassDef &val) { + return def.classname == val.classname && def.qualified == val.qualified; + }); + + if (it != classList.end()) { + it->classInfoList += def.classInfoList; + it->enumDeclarations.insert(def.enumDeclarations); + it->enumList += def.enumList; + it->flagAliases.insert(def.flagAliases); + } else { + knownGadgets.insert(def.classname, def.qualified); + knownGadgets.insert(def.qualified, def.qualified); + if (n.doGenerate) + classList += def; + } + } +} + +static bool any_type_contains(const QList<PropertyDef> &properties, const QByteArray &pattern) +{ + for (const auto &p : properties) { + if (p.type.contains(pattern)) + return true; + } + return false; +} + +static bool any_arg_contains(const QList<FunctionDef> &functions, const QByteArray &pattern) +{ + for (const auto &f : functions) { + for (const auto &arg : f.arguments) { + if (arg.normalizedType.contains(pattern)) + return true; + } + } + return false; +} + +static QByteArrayList make_candidates() +{ + QByteArrayList result; + result +#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER + QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER) +#undef STREAM_SMART_POINTER +#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME + QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE) +#undef STREAM_1ARG_TEMPLATE + ; + return result; +} + +static QByteArrayList requiredQtContainers(const QList<ClassDef> &classes) +{ + static const QByteArrayList candidates = make_candidates(); + + QByteArrayList required; + required.reserve(candidates.size()); + + bool needsQProperty = false; + + for (const auto &candidate : candidates) { + const QByteArray pattern = candidate + '<'; + + for (const auto &c : classes) { + for (const auto &p : c.propertyList) + needsQProperty |= !p.bind.isEmpty(); + if (any_type_contains(c.propertyList, pattern) || + any_arg_contains(c.slotList, pattern) || + any_arg_contains(c.signalList, pattern) || + any_arg_contains(c.methodList, pattern)) { + required.push_back(candidate); + break; + } + } + } + + if (needsQProperty) + required.push_back("QProperty"); + + return required; +} + +void Moc::generate(FILE *out, FILE *jsonOutput) +{ + QByteArrayView fn = QByteArrayView(filename); + + auto isSlash = [](char ch) { return ch == '/' || ch == '\\'; }; + auto rit = std::find_if(fn.crbegin(), fn.crend(), isSlash); + if (rit != fn.crend()) + fn = fn.last(rit - fn.crbegin()); + + fprintf(out, "/****************************************************************************\n" + "** Meta object code from reading C++ file '%s'\n**\n" , fn.constData()); + fprintf(out, "** Created by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , mocOutputRevision, QT_VERSION_STR); + fprintf(out, "** WARNING! All changes made in this file will be lost!\n" + "*****************************************************************************/\n\n"); + + // include header(s) of user class definitions at _first_ to allow + // for preprocessor definitions possibly affecting standard headers. + // see https://codereview.qt-project.org/c/qt/qtbase/+/445937 + if (!noInclude) { + if (includePath.size() && !includePath.endsWith('/')) + includePath += '/'; + for (QByteArray inc : std::as_const(includeFiles)) { + if (!inc.isEmpty() && inc.at(0) != '<' && inc.at(0) != '"') { + if (includePath.size() && includePath != "./") + inc.prepend(includePath); + inc = '\"' + inc + '\"'; + } + fprintf(out, "#include %s\n", inc.constData()); + } + } + if (classList.size() && classList.constFirst().classname == "Qt") + fprintf(out, "#include <QtCore/qobject.h>\n"); + + fprintf(out, "#include <QtCore/qmetatype.h>\n"); // For QMetaType::Type + if (mustIncludeQPluginH) + fprintf(out, "#include <QtCore/qplugin.h>\n"); + + const auto qtContainers = requiredQtContainers(classList); + for (const QByteArray &qtContainer : qtContainers) + fprintf(out, "#include <QtCore/%s>\n", qtContainer.constData()); + + fprintf(out, "\n%s#include <QtCore/qtmochelpers.h>\n%s\n", +#if QT_VERSION <= QT_VERSION_CHECK(6, 9, 0) + "#if __has_include(<QtCore/qtmochelpers.h>)\n", + "#else\n" + "QT_BEGIN_MOC_NAMESPACE\n" + "#endif\n" +#else + "", "" +#endif + ); + + fprintf(out, "\n#include <memory>\n\n"); // For std::addressof + + fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n" + "#error \"The header file '%s' doesn't include <QObject>.\"\n", fn.constData()); + fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision); + fprintf(out, "#error \"This file was generated using the moc from %s." + " It\"\n#error \"cannot be used with the include files from" + " this version of Qt.\"\n#error \"(The moc has changed too" + " much.)\"\n", QT_VERSION_STR); + fprintf(out, "#endif\n\n"); + +#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0) + fprintf(out, "#ifndef Q_CONSTINIT\n" + "#define Q_CONSTINIT\n" + "#endif\n\n"); +#endif + + fprintf(out, "QT_WARNING_PUSH\n"); + fprintf(out, "QT_WARNING_DISABLE_DEPRECATED\n"); + fprintf(out, "QT_WARNING_DISABLE_GCC(\"-Wuseless-cast\")\n"); + + fputs("", out); + for (ClassDef &def : classList) { + Generator generator(&def, metaTypes, knownQObjectClasses, knownGadgets, out, + requireCompleteTypes); + generator.generateCode(); + } + fputs("", out); + + fprintf(out, "QT_WARNING_POP\n"); + + if (jsonOutput) { + QJsonObject mocData; + mocData["outputRevision"_L1] = mocOutputRevision; + mocData["inputFile"_L1] = QLatin1StringView(fn.constData()); + + QJsonArray classesJsonFormatted; + + for (const ClassDef &cdef: std::as_const(classList)) + classesJsonFormatted.append(cdef.toJson()); + + if (!classesJsonFormatted.isEmpty()) + mocData["classes"_L1] = classesJsonFormatted; + + QJsonDocument jsonDoc(mocData); + fputs(jsonDoc.toJson().constData(), jsonOutput); + } +} + +void Moc::parseSlots(ClassDef *def, FunctionDef::Access access) +{ + QTypeRevision defaultRevision; + if (test(Q_REVISION_TOKEN)) + defaultRevision = parseRevision(); + + next(COLON); + while (inClass(def) && hasNext()) { + switch (next()) { + case PUBLIC: + case PROTECTED: + case PRIVATE: + case Q_SIGNALS_TOKEN: + case Q_SLOTS_TOKEN: + prev(); + return; + case SEMIC: + continue; + case FRIEND: + until(SEMIC); + continue; + case USING: + error("'using' directive not supported in 'slots' section"); + default: + prev(); + } + + FunctionDef funcDef; + funcDef.access = access; + if (!parseFunction(&funcDef)) + continue; + if (funcDef.revision > 0) { + ++def->revisionedMethods; + } else if (defaultRevision.isValid()) { + funcDef.revision = defaultRevision.toEncodedVersion<int>(); + ++def->revisionedMethods; + } + def->slotList += funcDef; + handleDefaultArguments(&def->slotList, funcDef); + } +} + +void Moc::parseSignals(ClassDef *def) +{ + QTypeRevision defaultRevision; + if (test(Q_REVISION_TOKEN)) + defaultRevision = parseRevision(); + + next(COLON); + while (inClass(def) && hasNext()) { + switch (next()) { + case PUBLIC: + case PROTECTED: + case PRIVATE: + case Q_SIGNALS_TOKEN: + case Q_SLOTS_TOKEN: + prev(); + return; + case SEMIC: + continue; + case FRIEND: + until(SEMIC); + continue; + case USING: + error("'using' directive not supported in 'signals' section"); + default: + prev(); + } + FunctionDef funcDef; + funcDef.access = FunctionDef::Public; + parseFunction(&funcDef); + if (funcDef.isVirtual) + warning("Signals cannot be declared virtual"); + if (funcDef.inlineCode) + error("Not a signal declaration"); + if (funcDef.revision > 0) { + ++def->revisionedMethods; + } else if (defaultRevision.isValid()) { + funcDef.revision = defaultRevision.toEncodedVersion<int>(); + ++def->revisionedMethods; + } + def->signalList += funcDef; + handleDefaultArguments(&def->signalList, funcDef); + } +} + +void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex, Moc::PropertyMode mode) +{ + propDef.location = index; + propDef.relativeIndex = propertyIndex; + + QByteArray type = parseType().name; + if (type.isEmpty()) + error(); + propDef.designable = propDef.scriptable = propDef.stored = "true"; + propDef.user = "false"; + /* + The Q_PROPERTY construct cannot contain any commas, since + commas separate macro arguments. We therefore expect users + to type "QMap" instead of "QMap<QString, QVariant>". For + coherence, we also expect the same for + QValueList<QVariant>, the other template class supported by + QVariant. + */ + type = normalizeType(type); + if (type == "QMap") + type = "QMap<QString,QVariant>"; + else if (type == "QValueList") + type = "QValueList<QVariant>"; + else if (type == "LongLong") + type = "qlonglong"; + else if (type == "ULongLong") + type = "qulonglong"; + + propDef.type = type; + + if (mode == Moc::Named) { + next(); + propDef.name = lexem(); + } + + parsePropertyAttributes(propDef); +} + +void Moc::parsePropertyAttributes(PropertyDef &propDef) +{ + auto checkIsFunction = [&](const QByteArray &def, const char *name) { + if (def.endsWith(')')) { + QByteArray msg = "Providing a function for "; + msg += name; + msg += " in a property declaration is not be supported in Qt 6."; + error(msg.constData()); + } + }; + + while (test(IDENTIFIER)) { + const Symbol &lsym = symbol(); + const QByteArray l = lsym.lexem(); + if (l[0] == 'C' && l == "CONSTANT") { + propDef.constant = true; + continue; + } else if (l[0] == 'F' && l == "FINAL") { + propDef.final = true; + continue; + } else if (l[0] == 'N' && l == "NAME") { + next(IDENTIFIER); + propDef.name = lexem(); + continue; + } else if (l[0] == 'R' && l == "REQUIRED") { + propDef.required = true; + continue; + } else if (l[0] == 'R' && l == "REVISION" && test(LPAREN)) { + prev(); + propDef.revision = parseRevision().toEncodedVersion<int>(); + continue; + } + + QByteArray v, v2; + if (test(LPAREN)) { + v = lexemUntil(RPAREN); + v = v.mid(1, v.size() - 2); // removes the '(' and ')' + } else if (test(INTEGER_LITERAL)) { + v = lexem(); + if (l != "REVISION") + error(lsym); + } else if (test(DEFAULT)) { + v = lexem(); + if (l != "READ" && l != "WRITE") + error(lsym); + } else { + next(IDENTIFIER); + v = lexem(); + if (test(LPAREN)) + v2 = lexemUntil(RPAREN); + else if (v != "true" && v != "false") + v2 = "()"; + } + switch (l[0]) { + case 'M': + if (l == "MEMBER") + propDef.member = v; + else + error(lsym); + break; + case 'R': + if (l == "READ") + propDef.read = v; + else if (l == "RESET") + propDef.reset = v; + else if (l == "REVISION") { + bool ok = false; + const int minor = v.toInt(&ok); + if (!ok || !QTypeRevision::isValidSegment(minor)) + error(lsym); + propDef.revision = QTypeRevision::fromMinorVersion(minor).toEncodedVersion<int>(); + } else + error(lsym); + break; + case 'S': + if (l == "SCRIPTABLE") { + propDef.scriptable = v + v2; + checkIsFunction(propDef.scriptable, "SCRIPTABLE"); + } else if (l == "STORED") { + propDef.stored = v + v2; + checkIsFunction(propDef.stored, "STORED"); + } else + error(lsym); + break; + case 'W': if (l != "WRITE") error(lsym); + propDef.write = v; + break; + case 'B': if (l != "BINDABLE") error(lsym); + propDef.bind = v; + break; + case 'D': if (l != "DESIGNABLE") error(lsym); + propDef.designable = v + v2; + checkIsFunction(propDef.designable, "DESIGNABLE"); + break; + case 'N': if (l != "NOTIFY") error(lsym); + propDef.notify = v; + break; + case 'U': if (l != "USER") error(lsym); + propDef.user = v + v2; + checkIsFunction(propDef.user, "USER"); + break; + default: + error(lsym); + } + } + if (propDef.constant && !propDef.write.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is both WRITEable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if (propDef.constant && !propDef.notify.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is both NOTIFYable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if (propDef.constant && !propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is both BINDable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if (propDef.read == "default" && propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is not BINDable but default-READable. READ will be ignored."; + propDef.read = ""; + warning(msg.constData()); + } + if (propDef.write == "default" && propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is not BINDable but default-WRITEable. WRITE will be ignored."; + propDef.write = ""; + warning(msg.constData()); + } +} + +void Moc::parseProperty(ClassDef *def, Moc::PropertyMode mode) +{ + next(LPAREN); + PropertyDef propDef; + createPropertyDef(propDef, int(def->propertyList.size()), mode); + next(RPAREN); + + def->propertyList += propDef; +} + +void Moc::parsePluginData(ClassDef *def) +{ + next(LPAREN); + QByteArray metaData; + while (test(IDENTIFIER)) { + QByteArray l = lexem(); + if (l == "IID") { + next(STRING_LITERAL); + def->pluginData.iid = unquotedLexem(); + } else if (l == "URI") { + next(STRING_LITERAL); + def->pluginData.uri = unquotedLexem(); + } else if (l == "FILE") { + next(STRING_LITERAL); + QByteArray metaDataFile = unquotedLexem(); + QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top())).dir(), + QString::fromLocal8Bit(metaDataFile)); + for (const IncludePath &p : std::as_const(includes)) { + if (fi.exists()) + break; + if (p.isFrameworkPath) + continue; + + fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(metaDataFile.constData())); + // try again, maybe there's a file later in the include paths with the same name + if (fi.isDir()) { + fi = QFileInfo(); + continue; + } + } + if (!fi.exists()) { + const QByteArray msg = "Plugin Metadata file " + lexem() + + " does not exist. Declaration will be ignored"; + error(msg.constData()); + return; + } + QFile file(fi.canonicalFilePath()); + if (!file.open(QFile::ReadOnly)) { + QByteArray msg = "Plugin Metadata file " + lexem() + " could not be opened: " + + file.errorString().toUtf8(); + error(msg.constData()); + return; + } + parsedPluginMetadataFiles.append(fi.canonicalFilePath()); + metaData = file.readAll(); + } + } + + if (!metaData.isEmpty()) { + def->pluginData.metaData = QJsonDocument::fromJson(metaData); + if (!def->pluginData.metaData.isObject()) { + const QByteArray msg = "Plugin Metadata file " + lexem() + + " does not contain a valid JSON object. Declaration will be ignored"; + warning(msg.constData()); + def->pluginData.iid = QByteArray(); + def->pluginData.uri = QByteArray(); + return; + } + } + + mustIncludeQPluginH = true; + next(RPAREN); +} + +QByteArray Moc::parsePropertyAccessor() +{ + int nesting = 0; + QByteArray accessor; + while (1) { + Token t = peek(); + if (!nesting && (t == RPAREN || t == COMMA)) + break; + t = next(); + if (t == LPAREN) + ++nesting; + if (t == RPAREN) + --nesting; + accessor += lexem(); + } + return accessor; +} + +void Moc::parsePrivateProperty(ClassDef *def, Moc::PropertyMode mode) +{ + next(LPAREN); + PropertyDef propDef; + propDef.inPrivateClass = parsePropertyAccessor(); + + next(COMMA); + + createPropertyDef(propDef, int(def->propertyList.size()), mode); + + def->propertyList += propDef; +} + +void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag) +{ + next(LPAREN); + QByteArray identifier; + while (test(IDENTIFIER)) { + identifier = lexem(); + while (test(SCOPE) && test(IDENTIFIER)) { + identifier += "::"; + identifier += lexem(); + } + def->enumDeclarations[identifier] = isFlag; + } + next(RPAREN); +} + +void Moc::parseFlag(BaseDef *def) +{ + next(LPAREN); + QByteArray flagName, enumName; + while (test(IDENTIFIER)) { + flagName = lexem(); + while (test(SCOPE) && test(IDENTIFIER)) { + flagName += "::"; + flagName += lexem(); + } + } + next(COMMA); + while (test(IDENTIFIER)) { + enumName = lexem(); + while (test(SCOPE) && test(IDENTIFIER)) { + enumName += "::"; + enumName += lexem(); + } + } + + def->flagAliases.insert(enumName, flagName); + next(RPAREN); +} + +Moc::EncounteredQmlMacro Moc::parseClassInfo(BaseDef *def) +{ + bool encounteredQmlMacro = false; + next(LPAREN); + ClassInfoDef infoDef; + next(STRING_LITERAL); + infoDef.name = symbol().unquotedLexem(); + if (infoDef.name.startsWith("QML.")) + encounteredQmlMacro = true; + next(COMMA); + if (test(STRING_LITERAL)) { + infoDef.value = symbol().unquotedLexem(); + } else if (test(Q_REVISION_TOKEN)) { + infoDef.value = QByteArray::number(parseRevision().toEncodedVersion<quint16>()); + } else { + // support Q_CLASSINFO("help", QT_TR_NOOP("blah")) + next(IDENTIFIER); + next(LPAREN); + next(STRING_LITERAL); + infoDef.value = symbol().unquotedLexem(); + next(RPAREN); + } + next(RPAREN); + def->classInfoList += infoDef; + return encounteredQmlMacro ? EncounteredQmlMacro::Yes : EncounteredQmlMacro::No; +} + +void Moc::parseClassInfo(ClassDef *def) +{ + if (parseClassInfo(static_cast<BaseDef *>(def)) == EncounteredQmlMacro::Yes) + def->requireCompleteMethodTypes = true; +} + +void Moc::parseInterfaces(ClassDef *def) +{ + next(LPAREN); + while (test(IDENTIFIER)) { + QList<ClassDef::Interface> iface; + iface += ClassDef::Interface(lexem()); + while (test(SCOPE)) { + iface.last().className += lexem(); + next(IDENTIFIER); + iface.last().className += lexem(); + } + while (test(COLON)) { + next(IDENTIFIER); + iface += ClassDef::Interface(lexem()); + while (test(SCOPE)) { + iface.last().className += lexem(); + next(IDENTIFIER); + iface.last().className += lexem(); + } + } + // resolve from classnames to interface ids + for (qsizetype i = 0; i < iface.size(); ++i) { + const QByteArray iid = interface2IdMap.value(iface.at(i).className); + if (iid.isEmpty()) + error("Undefined interface"); + + iface[i].interfaceId = iid; + } + def->interfaceList += iface; + } + next(RPAREN); +} + +void Moc::parseDeclareInterface() +{ + next(LPAREN); + QByteArray interface; + next(IDENTIFIER); + interface += lexem(); + while (test(SCOPE)) { + interface += lexem(); + next(IDENTIFIER); + interface += lexem(); + } + next(COMMA); + QByteArray iid; + if (test(STRING_LITERAL)) { + iid = lexem(); + } else { + next(IDENTIFIER); + iid = lexem(); + } + interface2IdMap.insert(interface, iid); + next(RPAREN); +} + +void Moc::parseDeclareMetatype() +{ + next(LPAREN); + QByteArray typeName = lexemUntil(RPAREN); + typeName.remove(0, 1); + typeName.chop(1); + metaTypes.append(typeName); +} + +void Moc::parseMocInclude() +{ + next(LPAREN); + QByteArray include = lexemUntil(RPAREN); + // remove parentheses + include.remove(0, 1); + include.chop(1); + includeFiles.append(include); +} + +void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access) +{ + next(LPAREN); + FunctionDef funcDef; + next(IDENTIFIER); + funcDef.inPrivateClass = lexem(); + // also allow void functions + if (test(LPAREN)) { + next(RPAREN); + funcDef.inPrivateClass += "()"; + } + next(COMMA); + funcDef.access = access; + parseFunction(&funcDef, true); + def->slotList += funcDef; + handleDefaultArguments(&def->slotList, funcDef); + if (funcDef.revision > 0) + ++def->revisionedMethods; + +} + +QByteArray Moc::lexemUntil(Token target) +{ + qsizetype from = index; + until(target); + QByteArray s; + while (from <= index) { + QByteArray n = symbols.at(from++-1).lexem(); + if (s.size() && n.size()) { + char prev = s.at(s.size()-1); + char next = n.at(0); + if ((is_ident_char(prev) && is_ident_char(next)) + || (prev == '<' && next == ':') + || (prev == '>' && next == '>')) + s += ' '; + } + s += n; + } + return s; +} + +bool Moc::until(Token target) { + int braceCount = 0; + int brackCount = 0; + int parenCount = 0; + int angleCount = 0; + if (index) { + switch(symbols.at(index-1).token) { + case LBRACE: ++braceCount; break; + case LBRACK: ++brackCount; break; + case LPAREN: ++parenCount; break; + case LANGLE: ++angleCount; break; + default: break; + } + } + + //when searching commas within the default argument, we should take care of template depth (anglecount) + // unfortunately, we do not have enough semantic information to know if '<' is the operator< or + // the beginning of a template type. so we just use heuristics. + qsizetype possible = -1; + + while (index < symbols.size()) { + Token t = symbols.at(index++).token; + switch (t) { + case LBRACE: ++braceCount; break; + case RBRACE: --braceCount; break; + case LBRACK: ++brackCount; break; + case RBRACK: --brackCount; break; + case LPAREN: ++parenCount; break; + case RPAREN: --parenCount; break; + case LANGLE: + if (parenCount == 0 && braceCount == 0) + ++angleCount; + break; + case RANGLE: + if (parenCount == 0 && braceCount == 0) + --angleCount; + break; + case GTGT: + if (parenCount == 0 && braceCount == 0) { + angleCount -= 2; + t = RANGLE; + } + break; + default: break; + } + if (t == target + && braceCount <= 0 + && brackCount <= 0 + && parenCount <= 0 + && (target != RANGLE || angleCount <= 0)) { + if (target != COMMA || angleCount <= 0) + return true; + possible = index; + } + + if (target == COMMA && t == EQ && possible != -1) { + index = possible; + return true; + } + + if (braceCount < 0 || brackCount < 0 || parenCount < 0 + || (target == RANGLE && angleCount < 0)) { + --index; + break; + } + + if (braceCount <= 0 && t == SEMIC) { + // Abort on semicolon. Allow recovering bad template parsing (QTBUG-31218) + break; + } + } + + if (target == COMMA && angleCount != 0 && possible != -1) { + index = possible; + return true; + } + + return false; +} + +void Moc::checkSuperClasses(ClassDef *def) +{ + Q_ASSERT(!def->superclassList.isEmpty()); + const QByteArray &firstSuperclass = def->superclassList.at(0).first; + + if (!knownQObjectClasses.contains(firstSuperclass)) { + // enable once we /require/ include paths +#if 0 + const QByteArray msg + = "Class " + + def->className + + " contains the Q_OBJECT macro and inherits from " + + def->superclassList.value(0) + + " but that is not a known QObject subclass. You may get compilation errors."; + warning(msg.constData()); +#endif + return; + } + + auto isRegisteredInterface = [&def](QByteArrayView super) { + auto matchesSuperClass = [&super](const auto &ifaces) { + return !ifaces.isEmpty() && ifaces.first().className == super; + }; + return std::any_of(def->interfaceList.cbegin(), def->interfaceList.cend(), matchesSuperClass); + }; + + const auto end = def->superclassList.cend(); + auto it = def->superclassList.cbegin() + 1; + for (; it != end; ++it) { + const QByteArray &superClass = it->first; + if (knownQObjectClasses.contains(superClass)) { + const QByteArray msg + = "Class " + + def->classname + + " inherits from two QObject subclasses " + + firstSuperclass + + " and " + + superClass + + ". This is not supported!"; + warning(msg.constData()); + } + + if (interface2IdMap.contains(superClass)) { + if (!isRegisteredInterface(superClass)) { + const QByteArray msg + = "Class " + + def->classname + + " implements the interface " + + superClass + + " but does not list it in Q_INTERFACES. qobject_cast to " + + superClass + + " will not work!"; + warning(msg.constData()); + } + } + } +} + +void Moc::checkProperties(ClassDef *cdef) +{ + // + // specify get function, for compatibility we accept functions + // returning pointers, or const char * for QByteArray. + // + QDuplicateTracker<QByteArray> definedProperties(cdef->propertyList.size()); + for (int i = 0; i < cdef->propertyList.size(); ++i) { + PropertyDef &p = cdef->propertyList[i]; + if (definedProperties.hasSeen(p.name)) { + QByteArray msg = "The property '" + p.name + "' is defined multiple times in class " + cdef->classname + "."; + warning(msg.constData()); + } + + if (p.read.isEmpty() && p.member.isEmpty() && p.bind.isEmpty()) { + const qsizetype rewind = index; + if (p.location >= 0) + index = p.location; + QByteArray msg = "Property declaration " + p.name + " has neither an associated QProperty<> member" + ", nor a READ accessor function nor an associated MEMBER variable. The property will be invalid."; + warning(msg.constData()); + index = rewind; + if (p.write.isEmpty()) { + cdef->propertyList.removeAt(i); + --i; + } + continue; + } + + for (const FunctionDef &f : std::as_const(cdef->publicList)) { + if (f.name != p.read) + continue; + if (!f.isConst) // get functions must be const + continue; + if (f.arguments.size()) // and must not take any arguments + continue; + PropertyDef::Specification spec = PropertyDef::ValueSpec; + QByteArray tmp = f.normalizedType; + if (p.type == "QByteArray" && tmp == "const char *") + tmp = "QByteArray"; + if (tmp.left(6) == "const ") + tmp = tmp.mid(6); + if (p.type != tmp && tmp.endsWith('*')) { + tmp.chop(1); + spec = PropertyDef::PointerSpec; + } else if (f.type.name.endsWith('&')) { // raw type, not normalized type + spec = PropertyDef::ReferenceSpec; + } + if (p.type != tmp) + continue; + p.gspec = spec; + break; + } + if (!p.notify.isEmpty()) { + int notifyId = -1; + for (int j = 0; j < cdef->signalList.size(); ++j) { + const FunctionDef &f = cdef->signalList.at(j); + if (f.name != p.notify) { + continue; + } else { + notifyId = j /* Signal indexes start from 0 */; + break; + } + } + p.notifyId = notifyId; + if (notifyId == -1) { + int index = cdef->nonClassSignalList.indexOf(p.notify); + if (index == -1) { + cdef->nonClassSignalList << p.notify; + p.notifyId = -1 - cdef->nonClassSignalList.size(); + } else { + p.notifyId = int(-2 - index); + } + } + } + } +} +#endif // -- QScxml + +QJsonObject ClassDef::toJson() const +{ + QJsonObject cls; + cls["className"_L1] = QString::fromUtf8(classname.constData()); + cls["qualifiedClassName"_L1] = QString::fromUtf8(qualified.constData()); + + QJsonArray classInfos; + for (const auto &info: std::as_const(classInfoList)) { + QJsonObject infoJson; + infoJson["name"_L1] = QString::fromUtf8(info.name); + infoJson["value"_L1] = QString::fromUtf8(info.value); + classInfos.append(infoJson); + } + + if (classInfos.size()) + cls["classInfos"_L1] = classInfos; + + const auto appendFunctions = [&cls](const QString &type, const QList<FunctionDef> &funcs) { + QJsonArray jsonFuncs; + + for (const FunctionDef &fdef: funcs) + jsonFuncs.append(fdef.toJson()); + + if (!jsonFuncs.isEmpty()) + cls[type] = jsonFuncs; + }; + + appendFunctions("signals"_L1, signalList); + appendFunctions("slots"_L1, slotList); + appendFunctions("constructors"_L1, constructorList); + appendFunctions("methods"_L1, methodList); + + QJsonArray props; + + for (const PropertyDef &propDef: std::as_const(propertyList)) + props.append(propDef.toJson()); + + if (!props.isEmpty()) + cls["properties"_L1] = props; + + if (hasQObject) + cls["object"_L1] = true; + if (hasQGadget) + cls["gadget"_L1] = true; + if (hasQNamespace) + cls["namespace"_L1] = true; + + QJsonArray superClasses; + + for (const auto &super: std::as_const(superclassList)) { + const auto name = super.first; + const auto access = super.second; + QJsonObject superCls; + superCls["name"_L1] = QString::fromUtf8(name); + FunctionDef::accessToJson(&superCls, access); + superClasses.append(superCls); + } + + if (!superClasses.isEmpty()) + cls["superClasses"_L1] = superClasses; + + QJsonArray enums; + for (const EnumDef &enumDef: std::as_const(enumList)) + enums.append(enumDef.toJson(*this)); + if (!enums.isEmpty()) + cls["enums"_L1] = enums; + + QJsonArray ifaces; + for (const QList<Interface> &ifaceList : interfaceList) { + QJsonArray jsonList; + for (const Interface &iface: ifaceList) { + QJsonObject ifaceJson; + ifaceJson["id"_L1] = QString::fromUtf8(iface.interfaceId); + ifaceJson["className"_L1] = QString::fromUtf8(iface.className); + jsonList.append(ifaceJson); + } + ifaces.append(jsonList); + } + if (!ifaces.isEmpty()) + cls["interfaces"_L1] = ifaces; + + return cls; +} + +QJsonObject FunctionDef::toJson() const +{ + QJsonObject fdef; + fdef["name"_L1] = QString::fromUtf8(name); + if (!tag.isEmpty()) + fdef["tag"_L1] = QString::fromUtf8(tag); + fdef["returnType"_L1] = QString::fromUtf8(normalizedType); + + QJsonArray args; + for (const ArgumentDef &arg: arguments) + args.append(arg.toJson()); + + if (!args.isEmpty()) + fdef["arguments"_L1] = args; + + accessToJson(&fdef, access); + + if (revision > 0) + fdef["revision"_L1] = revision; + + if (wasCloned) + fdef["isCloned"_L1] = true; + + return fdef; +} + +void FunctionDef::accessToJson(QJsonObject *obj, FunctionDef::Access acs) +{ + switch (acs) { + case Private: (*obj)["access"_L1] = "private"_L1; break; + case Public: (*obj)["access"_L1] = "public"_L1; break; + case Protected: (*obj)["access"_L1] = "protected"_L1; break; + } +} + +QJsonObject ArgumentDef::toJson() const +{ + QJsonObject arg; + arg["type"_L1] = QString::fromUtf8(normalizedType); + if (!name.isEmpty()) + arg["name"_L1] = QString::fromUtf8(name); + return arg; +} + +QJsonObject PropertyDef::toJson() const +{ + QJsonObject prop; + prop["name"_L1] = QString::fromUtf8(name); + prop["type"_L1] = QString::fromUtf8(type); + + const auto jsonify = [&prop](const char *str, const QByteArray &member) { + if (!member.isEmpty()) + prop[QLatin1StringView(str)] = QString::fromUtf8(member); + }; + + jsonify("member", member); + jsonify("read", read); + jsonify("write", write); + jsonify("bindable", bind); + jsonify("reset", reset); + jsonify("notify", notify); + jsonify("privateClass", inPrivateClass); + + const auto jsonifyBoolOrString = [&prop](const char *str, const QByteArray &boolOrString) { + QJsonValue value; + if (boolOrString == "true") + value = true; + else if (boolOrString == "false") + value = false; + else + value = QString::fromUtf8(boolOrString); // function name to query at run-time + prop[QLatin1StringView(str)] = value; + }; + + jsonifyBoolOrString("designable", designable); + jsonifyBoolOrString("scriptable", scriptable); + jsonifyBoolOrString("stored", stored); + jsonifyBoolOrString("user", user); + + prop["constant"_L1] = constant; + prop["final"_L1] = final; + prop["required"_L1] = required; + prop["index"_L1] = relativeIndex; + if (revision > 0) + prop["revision"_L1] = revision; + + return prop; +} + +QJsonObject EnumDef::toJson(const ClassDef &cdef) const +{ + QJsonObject def; + def["name"_L1] = QString::fromUtf8(name); + if (!enumName.isEmpty()) + def["alias"_L1] = QString::fromUtf8(enumName); + if (!type.isEmpty()) + def["type"_L1] = QString::fromUtf8(type); + def["isFlag"_L1] = cdef.enumDeclarations.value(name); + def["isClass"_L1] = isEnumClass; + + QJsonArray valueArr; + for (const QByteArray &value: values) + valueArr.append(QString::fromUtf8(value)); + if (!valueArr.isEmpty()) + def["values"_L1] = valueArr; + + return def; +} + +QByteArray EnumDef::qualifiedType(const ClassDef *cdef) const +{ + if (name == cdef->classname) { + // The name of the enclosing namespace is the same as the enum class name + if (cdef->qualified.contains("::")) { + // QTBUG-112996, fully qualify by using cdef->qualified to disambiguate enum + // class name and enclosing namespace, e.g.: + // namespace A { namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } } + return cdef->qualified % "::" % name; + } else { + // Just "B"; otherwise the compiler complains about the type "B::B" inside + // "B::staticMetaObject" in the generated code; e.g.: + // namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } + return name; + } + } + return cdef->classname % "::" % name; +} + +QT_END_NAMESPACE diff --git a/tools/qscxmlc/moc.h b/tools/qscxmlc/moc.h index dabc6c0..0d33b6e 100644 --- a/tools/qscxmlc/moc.h +++ b/tools/qscxmlc/moc.h @@ -42,10 +42,12 @@ struct EnumDef { QByteArray name; QByteArray enumName; + QByteArray type; QList<QByteArray> values; bool isEnumClass; // c++11 enum class EnumDef() : isEnumClass(false) {} QJsonObject toJson(const ClassDef &cdef) const; + QByteArray qualifiedType(const ClassDef *cdef) const; }; Q_DECLARE_TYPEINFO(EnumDef, Q_RELOCATABLE_TYPE); @@ -107,6 +109,8 @@ Q_DECLARE_TYPEINFO(FunctionDef, Q_RELOCATABLE_TYPE); struct PropertyDef { bool stdCppSet() const { + if (name.isEmpty()) + return false; QByteArray s("set"); s += QtMiscUtils::toAsciiUpper(name[0]); s += name.mid(1); @@ -123,7 +127,7 @@ struct PropertyDef bool required = false; int relativeIndex = -1; // property index in current metaobject - int location = -1; // token index, used for error reporting + qsizetype location = -1; // token index, used for error reporting QJsonObject toJson() const; @@ -157,8 +161,8 @@ struct BaseDef { QMap<QByteArray, bool> enumDeclarations; QList<EnumDef> enumList; QMap<QByteArray, QByteArray> flagAliases; - int begin = 0; - int end = 0; + qsizetype begin = 0; + qsizetype end = 0; }; struct ClassDef : BaseDef { @@ -207,6 +211,8 @@ Q_DECLARE_TYPEINFO(NamespaceDef, Q_RELOCATABLE_TYPE); class Moc : public Parser { public: + enum PropertyMode { Named, Anonymous }; + Moc() : noInclude(false), mustIncludeQPluginH(false), requireCompleteTypes(false) {} @@ -239,6 +245,8 @@ public: return index > def->begin && index < def->end - 1; } + void prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const; + Type parseType(); bool parseEnum(EnumDef *def); @@ -248,9 +256,11 @@ public: void parseSlots(ClassDef *def, FunctionDef::Access access); void parseSignals(ClassDef *def); - void parseProperty(ClassDef *def); + void parseProperty(ClassDef *def, PropertyMode mode); void parsePluginData(ClassDef *def); - void createPropertyDef(PropertyDef &def, int propertyIndex); + + void createPropertyDef(PropertyDef &def, int propertyIndex, PropertyMode mode); + void parsePropertyAttributes(PropertyDef &propDef); void parseEnumOrFlag(BaseDef *def, bool isFlag); void parseFlag(BaseDef *def); @@ -263,7 +273,7 @@ public: void parseMocInclude(); void parseSlotInPrivate(ClassDef *def, FunctionDef::Access access); QByteArray parsePropertyAccessor(); - void parsePrivateProperty(ClassDef *def); + void parsePrivateProperty(ClassDef *def, PropertyMode mode); void parseFunctionArguments(FunctionDef *def); @@ -281,6 +291,7 @@ public: void checkSuperClasses(ClassDef *def); void checkProperties(ClassDef* cdef); + bool testForFunctionModifiers(FunctionDef *def); }; #endif // -- QtScxml diff --git a/tools/qscxmlc/moc_patches/generator.cpp.patch b/tools/qscxmlc/moc_patches/generator.cpp.patch index a9da0f7..6c8a74e 100644 --- a/tools/qscxmlc/moc_patches/generator.cpp.patch +++ b/tools/qscxmlc/moc_patches/generator.cpp.patch @@ -1,5 +1,5 @@ ---- .upstream/generator.cpp 2023-02-02 09:58:24.431073637 +0100 -+++ generator.cpp 2023-02-02 12:55:41.000517603 +0100 +--- .upstream/generator.cpp 2023-08-11 10:37:23.940392525 +0200 ++++ generator.cpp 2023-08-21 11:25:19.407307372 +0200 @@ -4,7 +4,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 @@ -75,7 +75,7 @@ } +// -- QtScxml - static inline int lengthOfEscapeSequence(const QByteArray &s, int i) + static inline qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i) { @@ -108,7 +136,7 @@ @@ -85,19 +85,18 @@ +static void printStringWithIndentation(QIODevice &out, const QByteArray &s) // -- QtScxml { static constexpr int ColumnWidth = 72; - int len = s.size(); -@@ -302,7 +330,9 @@ + const qsizetype len = s.size(); +@@ -301,7 +329,9 @@ fprintf(out, " uint offsetsAndSizes[%d];\n", int(strings.size() * 2)); for (int i = 0; i < strings.size(); ++i) { int thisLength = lengthOfEscapedString(strings.at(i)) + 1; -- fprintf(out, " char stringdata%d[%d];\n", i, thisLength); +// -- QtScxml -+ fprintf(out, " unsigned char stringdata%d[%d];\n", i, thisLength); + fprintf(out, " char stringdata%d[%d];\n", i, thisLength); +// -- QtScxml } fprintf(out, "};\n"); -@@ -333,10 +363,19 @@ +@@ -332,10 +362,19 @@ // // Build stringdata arrays // @@ -112,7 +111,7 @@ + const QByteArray s = strings.at(i); + const qsizetype len = s.size(); + for (qsizetype charPos = 0; charPos < len; ++charPos) -+ fprintf(out, "0x%.2x,", static_cast<quint8>(s.at(charPos))); ++ fprintf(out, "char(0x%.2x),", static_cast<quint8>(s.at(charPos))); + const bool isLast = (i == end - 1); + fprintf(out, "0%s // %d: %s", isLast ? "}" : "},", i, s.constData()); } @@ -120,23 +119,7 @@ // Terminate stringdata struct fprintf(out, "\n};\n"); -@@ -631,9 +670,12 @@ - // - fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData()); - fprintf(out, " if (!_clname) return nullptr;\n"); -- fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n" -- " return static_cast<void*>(this);\n", -- qualifiedClassNameIdentifier.constData()); -+// -- QtScxml -+ fprintf(out, " if (!strcmp(_clname, reinterpret_cast<const char *>(\n" -+ " qt_meta_stringdata_%s.stringdata0)))\n" -+ " return static_cast<void*>(const_cast< %s*>(this));\n", -+ qualifiedClassNameIdentifier.constData(), cdef->qualified.constData()); -+// -- QtScxml - for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one - if (cdef->superclassList.at(i).second == FunctionDef::Private) - continue; -@@ -672,7 +714,9 @@ +@@ -682,7 +721,9 @@ // // Generate plugin meta data // @@ -146,7 +129,7 @@ // // Generate function to make sure the non-class signals exist in the parent classes -@@ -1142,6 +1186,13 @@ +@@ -1134,6 +1175,13 @@ const FunctionDef &f = methodList.at(methodindex); Q_ASSERT(!f.normalizedType.isEmpty()); fprintf(out, " case %d: ", methodindex); @@ -160,7 +143,7 @@ if (f.normalizedType != "void") fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); fprintf(out, "_t->"); -@@ -1219,6 +1270,10 @@ +@@ -1212,6 +1260,10 @@ const FunctionDef &f = cdef->signalList.at(methodindex); if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic) continue; @@ -171,7 +154,7 @@ anythingUsed = true; fprintf(out, " {\n"); fprintf(out, " using _t = %s (%s::*)(",f.type.rawName.constData() , cdef->classname.constData()); -@@ -1240,7 +1295,7 @@ +@@ -1234,7 +1286,7 @@ else fprintf(out, ");\n"); fprintf(out, " if (_t _q_method = &%s::%s; *reinterpret_cast<_t *>(_a[1]) == _q_method) {\n", @@ -180,7 +163,7 @@ fprintf(out, " *result = %d;\n", methodindex); fprintf(out, " return;\n"); fprintf(out, " }\n }\n"); -@@ -1336,8 +1391,11 @@ +@@ -1329,8 +1381,11 @@ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s().value(); break;\n", propindex, p.type.constData(), prefix.constData(), p.bind.constData()); else if (!p.read.isEmpty()) @@ -194,7 +177,7 @@ else fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n", propindex, p.type.constData(), prefix.constData(), p.member.constData()); -@@ -1469,6 +1527,10 @@ +@@ -1462,6 +1517,10 @@ { if (def->wasCloned || def->isAbstract) return; @@ -205,7 +188,7 @@ fprintf(out, "\n// SIGNAL %d\n%s %s::%s(", index, def->type.name.constData(), cdef->qualified.constData(), def->name.constData()); -@@ -1515,10 +1577,8 @@ +@@ -1510,10 +1569,8 @@ if (def->normalizedType == "void") { fprintf(out, "nullptr"); } else { @@ -218,7 +201,7 @@ } int i; for (i = 1; i < offset; ++i) -@@ -1533,6 +1593,36 @@ +@@ -1528,6 +1585,36 @@ fprintf(out, "}\n"); } @@ -255,7 +238,7 @@ static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v); static CborError jsonObjectToCbor(CborEncoder *parent, const QJsonObject &o) { -@@ -1668,7 +1758,11 @@ +@@ -1663,7 +1750,11 @@ #define CBOR_ENCODER_WRITER_CONTROL 1 #define CBOR_ENCODER_WRITE_FUNCTION CborDevice::callback diff --git a/tools/qscxmlc/moc_patches/generator.h.patch b/tools/qscxmlc/moc_patches/generator.h.patch index fb6c117..86c45e5 100644 --- a/tools/qscxmlc/moc_patches/generator.h.patch +++ b/tools/qscxmlc/moc_patches/generator.h.patch @@ -1,5 +1,5 @@ ---- .upstream/generator.h 2022-07-14 10:20:51.543950028 +0200 -+++ generator.h 2023-02-02 10:46:55.601993965 +0100 +--- .upstream/generator.h 2023-06-06 10:15:35.263646156 +0200 ++++ generator.h 2023-08-21 11:17:37.759825035 +0200 @@ -6,20 +6,33 @@ #include "moc.h" diff --git a/tools/qscxmlc/moc_patches/moc.cpp.patch b/tools/qscxmlc/moc_patches/moc.cpp.patch new file mode 100644 index 0000000..b82f6cf --- /dev/null +++ b/tools/qscxmlc/moc_patches/moc.cpp.patch @@ -0,0 +1,29 @@ +--- .upstream/moc.cpp 2023-08-11 10:37:23.940392525 +0200 ++++ moc.cpp 2023-08-21 11:21:53.873272207 +0200 +@@ -12,14 +12,15 @@ + #include <QtCore/qdir.h> + #include <QtCore/qjsondocument.h> + +-// for normalizeTypeInternal +-#include <private/qmetaobject_moc_p.h> +-#include <private/qduplicatetracker_p.h> ++// -- QScxml ++#include <QtCore/qjsonobject.h> ++// -- QScxml + + QT_BEGIN_NAMESPACE + + using namespace Qt::StringLiterals; + ++#if 0 // -- QtScxml + // only moc needs this function + static QByteArray normalizeType(const QByteArray &ba) + { +@@ -1953,6 +1954,7 @@ + } + } + } ++#endif // -- QScxml + + QJsonObject ClassDef::toJson() const + { diff --git a/tools/qscxmlc/moc_patches/moc.h.patch b/tools/qscxmlc/moc_patches/moc.h.patch index eda5d3e..ba5c083 100644 --- a/tools/qscxmlc/moc_patches/moc.h.patch +++ b/tools/qscxmlc/moc_patches/moc.h.patch @@ -1,5 +1,5 @@ ---- .upstream/moc.h 2022-12-22 11:12:45.832773413 +0100 -+++ moc.h 2023-02-02 11:05:36.004931550 +0100 +--- .upstream/moc.h 2023-08-11 10:37:23.940392525 +0200 ++++ moc.h 2023-08-21 11:17:37.751825117 +0200 @@ -4,15 +4,12 @@ #ifndef MOC_H #define MOC_H @@ -43,7 +43,7 @@ ReferenceType referenceType; }; Q_DECLARE_TYPEINFO(Type, Q_RELOCATABLE_TYPE); -@@ -81,8 +80,9 @@ +@@ -83,8 +82,9 @@ bool inlineCode = false; bool wasCloned = false; @@ -54,7 +54,7 @@ bool isCompat = false; bool isInvokable = false; bool isScriptable = false; -@@ -96,6 +96,11 @@ +@@ -98,6 +98,11 @@ QJsonObject toJson() const; static void accessToJson(QJsonObject *obj, Access acs); @@ -66,8 +66,8 @@ }; Q_DECLARE_TYPEINFO(FunctionDef, Q_RELOCATABLE_TYPE); -@@ -121,6 +126,10 @@ - int location = -1; // token index, used for error reporting +@@ -125,6 +130,10 @@ + qsizetype location = -1; // token index, used for error reporting QJsonObject toJson() const; + @@ -77,7 +77,7 @@ }; Q_DECLARE_TYPEINFO(PropertyDef, Q_RELOCATABLE_TYPE); -@@ -194,6 +203,7 @@ +@@ -198,6 +207,7 @@ }; Q_DECLARE_TYPEINFO(NamespaceDef, Q_RELOCATABLE_TYPE); @@ -85,9 +85,9 @@ class Moc : public Parser { public: -@@ -272,6 +282,7 @@ - void checkSuperClasses(ClassDef *def); +@@ -283,6 +293,7 @@ void checkProperties(ClassDef* cdef); + bool testForFunctionModifiers(FunctionDef *def); }; +#endif // -- QtScxml diff --git a/tools/qscxmlc/moc_patches/outputrevision.h.patch b/tools/qscxmlc/moc_patches/outputrevision.h.patch index 6f6daff..febe36f 100644 --- a/tools/qscxmlc/moc_patches/outputrevision.h.patch +++ b/tools/qscxmlc/moc_patches/outputrevision.h.patch @@ -1,5 +1,5 @@ --- .upstream/outputrevision.h 2022-07-14 10:20:51.543950028 +0200 -+++ outputrevision.h 2023-02-02 10:46:55.593994063 +0100 ++++ outputrevision.h 2023-08-21 11:17:37.747825158 +0200 @@ -4,7 +4,13 @@ #ifndef OUTPUTREVISION_H #define OUTPUTREVISION_H diff --git a/tools/qscxmlc/moc_patches/update_moc.sh b/tools/qscxmlc/moc_patches/update_moc.sh index b4585c1..410c9da 100755 --- a/tools/qscxmlc/moc_patches/update_moc.sh +++ b/tools/qscxmlc/moc_patches/update_moc.sh @@ -79,7 +79,7 @@ update_moc() { MODE="$1" MOC_DIR="$2" -FILES=( "outputrevision.h" "moc.h" "generator.h" "generator.cpp" ) +FILES=( "outputrevision.h" "moc.cpp" "moc.h" "generator.h" "generator.cpp" ) [[ -f moc_patches/update_moc.sh ]] || usage "Error: script must be run from the tools/qscxmlc/ directory." [[ -n "$MOC_DIR" ]] || usage "Error: You did not specify a moc directory." |