summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-08-21 11:19:44 +0200
committerUlf Hermann <ulf.hermann@qt.io>2023-08-22 12:34:19 +0200
commitba99613a0224e8b36e4e949eb517be197ae18854 (patch)
treeaf46ed5cbab9e6f4b1137a7f3e51684fb778c1d7
parentf95f4c51b9909000f1400a7bcdc6334f5be617e0 (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.txt2
-rw-r--r--tools/qscxmlc/generator.cpp206
-rw-r--r--tools/qscxmlc/moc.cpp2171
-rw-r--r--tools/qscxmlc/moc.h23
-rw-r--r--tools/qscxmlc/moc_patches/generator.cpp.patch51
-rw-r--r--tools/qscxmlc/moc_patches/generator.h.patch4
-rw-r--r--tools/qscxmlc/moc_patches/moc.cpp.patch29
-rw-r--r--tools/qscxmlc/moc_patches/moc.h.patch18
-rw-r--r--tools/qscxmlc/moc_patches/outputrevision.h.patch2
-rwxr-xr-xtools/qscxmlc/moc_patches/update_moc.sh2
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."