1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 Ford Motor Company
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtRemoteObjects module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "repcodegenerator.h"
30 
31 #include "repparser.h"
32 
33 #include <QFileInfo>
34 #include <QMetaType>
35 #include <QTextStream>
36 #include <QCryptographicHash>
37 #include <QRegExp>
38 
39 QT_BEGIN_NAMESPACE
40 
41 template <typename C>
accumulatedSizeOfNames(const C & c)42 static int accumulatedSizeOfNames(const C &c)
43 {
44     int result = 0;
45     for (const auto &e : c)
46         result += e.name.size();
47     return result;
48 }
49 
50 template <typename C>
accumulatedSizeOfTypes(const C & c)51 static int accumulatedSizeOfTypes(const C &c)
52 {
53     int result = 0;
54     for (const auto &e : c)
55         result += e.type.size();
56     return result;
57 }
58 
cap(QString name)59 static QString cap(QString name)
60 {
61     if (!name.isEmpty())
62         name[0] = name[0].toUpper();
63     return name;
64 }
65 
isClassEnum(const ASTClass & classContext,const QString & typeName)66 static bool isClassEnum(const ASTClass &classContext, const QString &typeName)
67 {
68     for (const ASTEnum &astEnum : classContext.enums) {
69         if (astEnum.name == typeName) {
70             return true;
71         }
72     }
73 
74     return false;
75 }
76 
fullyQualifiedTypeName(const ASTClass & classContext,const QString & className,const QString & typeName)77 static QString fullyQualifiedTypeName(const ASTClass& classContext, const QString &className, const QString &typeName)
78 {
79     if (isClassEnum(classContext, typeName)) {
80         // type was defined in this class' context, prefix typeName with class name
81         return className + QStringLiteral("::") + typeName;
82     }
83     return typeName;
84 }
85 
86 // for enums we need to transform signal/slot arguments to include the class scope
transformEnumParams(const ASTClass & classContext,const QVector<ASTFunction> & methodList,const QString & typeName)87 static QVector<ASTFunction> transformEnumParams(const ASTClass& classContext, const QVector<ASTFunction> &methodList, const QString &typeName) {
88     QVector<ASTFunction> localList = methodList;
89     for (ASTFunction &astFunction : localList) {
90         for (ASTDeclaration &astParam : astFunction.params) {
91             for (const ASTEnum &astEnum : classContext.enums) {
92                 if (astEnum.name == astParam.type) {
93                     astParam.type = typeName + QStringLiteral("::") + astParam.type;
94                 }
95             }
96         }
97     }
98     return localList;
99 }
100 
101 /*
102   Returns \c true if the type is a built-in type.
103 */
isBuiltinType(const QString & type)104 static bool isBuiltinType(const QString &type)
105  {
106     int id = QMetaType::type(type.toLatin1().constData());
107     if (id == QMetaType::UnknownType)
108         return false;
109     return (id < QMetaType::User);
110 }
111 
RepCodeGenerator(QIODevice * outputDevice)112 RepCodeGenerator::RepCodeGenerator(QIODevice *outputDevice)
113     : m_outputDevice(outputDevice)
114 {
115     Q_ASSERT(m_outputDevice);
116 }
117 
enumSignature(const ASTEnum & e)118 static QByteArray enumSignature(const ASTEnum &e)
119 {
120     QByteArray ret;
121     ret += e.name.toLatin1();
122     for (const ASTEnumParam &param : e.params)
123         ret += param.name.toLatin1() + QByteArray::number(param.value);
124     return ret;
125 }
126 
typeData(const QString & type,const QHash<QString,QByteArray> & specialTypes)127 static QByteArray typeData(const QString &type, const QHash<QString, QByteArray> &specialTypes)
128 {
129     QHash<QString, QByteArray>::const_iterator it = specialTypes.find(type);
130     if (it != specialTypes.end())
131         return it.value();
132     int pos = type.lastIndexOf(QLatin1String("::"));
133     if (pos > 0)
134             return typeData(type.mid(pos + 2), specialTypes);
135     return type.toLatin1();
136 }
137 
functionsData(const QVector<ASTFunction> & functions,const QHash<QString,QByteArray> & specialTypes)138 static QByteArray functionsData(const QVector<ASTFunction> &functions, const QHash<QString, QByteArray> &specialTypes)
139 {
140     QByteArray ret;
141     for (const ASTFunction &func : functions) {
142         ret += func.name.toLatin1();
143         for (const ASTDeclaration &param : func.params) {
144             ret += param.name.toLatin1();
145             ret += typeData(param.type, specialTypes);
146             ret += QByteArray(reinterpret_cast<const char *>(&param.variableType), sizeof(param.variableType));
147         }
148         ret += typeData(func.returnType, specialTypes);
149     }
150     return ret;
151 }
152 
classSignature(const ASTClass & ac)153 QByteArray RepCodeGenerator::classSignature(const ASTClass &ac)
154 {
155     QCryptographicHash checksum(QCryptographicHash::Sha1);
156     QHash<QString, QByteArray> localTypes = m_globalEnumsPODs;
157     for (const ASTEnum &e : ac.enums) // add local enums
158         localTypes[e.name] = enumSignature(e);
159 
160     checksum.addData(ac.name.toLatin1());
161 
162     // Checksum properties
163     for (const ASTProperty &p : ac.properties) {
164         checksum.addData(p.name.toLatin1());
165         checksum.addData(typeData(p.type, localTypes));
166         checksum.addData(reinterpret_cast<const char *>(&p.modifier), sizeof(p.modifier));
167     }
168 
169     // Checksum signals
170     checksum.addData(functionsData(ac.signalsList, localTypes));
171 
172     // Checksum slots
173     checksum.addData(functionsData(ac.slotsList, localTypes));
174 
175     return checksum.result().toHex();
176 }
177 
generate(const AST & ast,Mode mode,QString fileName)178 void RepCodeGenerator::generate(const AST &ast, Mode mode, QString fileName)
179 {
180     QTextStream stream(m_outputDevice);
181     if (fileName.isEmpty())
182         stream << "#pragma once" << Qt::endl << Qt::endl;
183     else {
184         fileName = QFileInfo(fileName).fileName();
185         fileName = fileName.toUpper();
186         fileName.replace(QLatin1Char('.'), QLatin1Char('_'));
187         stream << "#ifndef " << fileName << Qt::endl;
188         stream << "#define " << fileName << Qt::endl << Qt::endl;
189     }
190 
191     generateHeader(mode, stream, ast);
192     for (const ASTEnum &en : ast.enums)
193         generateENUM(stream, en);
194     for (const POD &pod : ast.pods)
195         generatePOD(stream, pod);
196 
197     QSet<QString> metaTypes;
198     for (const POD &pod : ast.pods) {
199         metaTypes << pod.name;
200         for (const PODAttribute &attribute : pod.attributes)
201             metaTypes << attribute.type;
202     }
203     const QString metaTypeRegistrationCode = generateMetaTypeRegistration(metaTypes)
204                                            + generateMetaTypeRegistrationForEnums(ast.enumUses);
205 
206     for (const ASTClass &astClass : ast.classes) {
207         QSet<QString> classMetaTypes;
208         QSet<QString> pendingMetaTypes;
209         for (const ASTProperty &property : astClass.properties) {
210             if (property.isPointer)
211                 continue;
212             classMetaTypes << property.type;
213         }
214         const auto extractClassMetaTypes = [&](const ASTFunction &function) {
215             classMetaTypes << function.returnType;
216             pendingMetaTypes << function.returnType;
217             for (const ASTDeclaration &decl : function.params) {
218                 classMetaTypes << decl.type;
219             }
220         };
221         for (const ASTFunction &function : astClass.signalsList)
222             extractClassMetaTypes(function);
223         for (const ASTFunction &function : astClass.slotsList)
224             extractClassMetaTypes(function);
225 
226         const QString classMetaTypeRegistrationCode = metaTypeRegistrationCode
227                 + generateMetaTypeRegistration(classMetaTypes);
228         const QString replicaMetaTypeRegistrationCode = classMetaTypeRegistrationCode
229                 + generateMetaTypeRegistrationForPending(pendingMetaTypes);
230 
231         if (mode == MERGED) {
232             generateClass(REPLICA, stream, astClass, replicaMetaTypeRegistrationCode);
233             generateClass(SOURCE, stream, astClass, classMetaTypeRegistrationCode);
234             generateClass(SIMPLE_SOURCE, stream, astClass, classMetaTypeRegistrationCode);
235             generateSourceAPI(stream, astClass);
236         } else {
237             generateClass(mode, stream, astClass, mode == REPLICA ? replicaMetaTypeRegistrationCode : classMetaTypeRegistrationCode);
238             if (mode == SOURCE) {
239                 generateClass(SIMPLE_SOURCE, stream, astClass, classMetaTypeRegistrationCode);
240                 generateSourceAPI(stream, astClass);
241             }
242         }
243     }
244 
245     generateStreamOperatorsForEnums(stream, ast.enumUses);
246 
247     stream << Qt::endl;
248     if (!fileName.isEmpty())
249         stream << "#endif // " << fileName << Qt::endl;
250 }
251 
generateHeader(Mode mode,QTextStream & out,const AST & ast)252 void RepCodeGenerator::generateHeader(Mode mode, QTextStream &out, const AST &ast)
253 {
254     out << "// This is an autogenerated file.\n"
255            "// Do not edit this file, any changes made will be lost the next time it is generated.\n"
256            "\n"
257            "#include <QtCore/qobject.h>\n"
258            "#include <QtCore/qdatastream.h>\n"
259            "#include <QtCore/qvariant.h>\n"
260            "#include <QtCore/qmetatype.h>\n";
261     bool hasModel = false;
262     for (auto c : ast.classes)
263     {
264         if (c.modelMetadata.count() > 0)
265         {
266             hasModel = true;
267             break;
268         }
269     }
270     if (hasModel)
271         out << "#include <QtCore/qabstractitemmodel.h>\n";
272     out << "\n"
273            "#include <QtRemoteObjects/qremoteobjectnode.h>\n";
274 
275     if (mode == MERGED) {
276         out << "#include <QtRemoteObjects/qremoteobjectpendingcall.h>\n";
277         out << "#include <QtRemoteObjects/qremoteobjectreplica.h>\n";
278         out << "#include <QtRemoteObjects/qremoteobjectsource.h>\n";
279         if (hasModel)
280             out << "#include <QtRemoteObjects/qremoteobjectabstractitemmodelreplica.h>\n";
281     } else if (mode == REPLICA) {
282         out << "#include <QtRemoteObjects/qremoteobjectpendingcall.h>\n";
283         out << "#include <QtRemoteObjects/qremoteobjectreplica.h>\n";
284         if (hasModel)
285             out << "#include <QtRemoteObjects/qremoteobjectabstractitemmodelreplica.h>\n";
286     } else
287         out << "#include <QtRemoteObjects/qremoteobjectsource.h>\n";
288     out << "\n";
289 
290     out << ast.preprocessorDirectives.join(QLatin1Char('\n'));
291     out << "\n";
292 }
293 
formatTemplateStringArgTypeNameCapitalizedName(int numberOfTypeOccurrences,int numberOfNameOccurrences,QString templateString,const POD & pod)294 static QString formatTemplateStringArgTypeNameCapitalizedName(int numberOfTypeOccurrences, int numberOfNameOccurrences,
295                                                               QString templateString, const POD &pod)
296 {
297     QString out;
298     const int LengthOfPlaceholderText = 2;
299     Q_ASSERT(templateString.count(QRegExp(QStringLiteral("%\\d"))) == numberOfNameOccurrences + numberOfTypeOccurrences);
300     const int expectedOutSize
301             = numberOfNameOccurrences * accumulatedSizeOfNames(pod.attributes)
302             + numberOfTypeOccurrences * accumulatedSizeOfTypes(pod.attributes)
303             + pod.attributes.size() * (templateString.size() - (numberOfNameOccurrences + numberOfTypeOccurrences) * LengthOfPlaceholderText);
304     out.reserve(expectedOutSize);
305     for (const PODAttribute &a : pod.attributes)
306         out += templateString.arg(a.type, a.name, cap(a.name));
307     return out;
308 }
309 
formatQPropertyDeclarations(const POD & pod)310 QString RepCodeGenerator::formatQPropertyDeclarations(const POD &pod)
311 {
312     return formatTemplateStringArgTypeNameCapitalizedName(1, 3, QStringLiteral("    Q_PROPERTY(%1 %2 READ %2 WRITE set%3)\n"), pod);
313 }
314 
formatConstructors(const POD & pod)315 QString RepCodeGenerator::formatConstructors(const POD &pod)
316 {
317     QString initializerString = QStringLiteral(": ");
318     QString defaultInitializerString = initializerString;
319     QString argString;
320     for (const PODAttribute &a : pod.attributes) {
321         initializerString += QString::fromLatin1("m_%1(%1), ").arg(a.name);
322         defaultInitializerString += QString::fromLatin1("m_%1(), ").arg(a.name);
323         argString += QString::fromLatin1("%1 %2, ").arg(a.type, a.name);
324     }
325     argString.chop(2);
326     initializerString.chop(2);
327     defaultInitializerString.chop(2);
328 
329     return QString::fromLatin1("    %1() %2 {}\n"
330                    "    explicit %1(%3) %4 {}\n")
331             .arg(pod.name, defaultInitializerString, argString, initializerString);
332 }
333 
formatPropertyGettersAndSetters(const POD & pod)334 QString RepCodeGenerator::formatPropertyGettersAndSetters(const POD &pod)
335 {
336     // MSVC doesn't like adjacent string literal concatenation within QStringLiteral, so keep it in one line:
337     QString templateString
338             = QStringLiteral("    %1 %2() const { return m_%2; }\n    void set%3(%1 %2) { if (%2 != m_%2) { m_%2 = %2; } }\n");
339     return formatTemplateStringArgTypeNameCapitalizedName(2, 8, qMove(templateString), pod);
340 }
341 
formatDataMembers(const POD & pod)342 QString RepCodeGenerator::formatDataMembers(const POD &pod)
343 {
344     QString out;
345     const QString prefix = QStringLiteral("    ");
346     const QString infix  = QStringLiteral(" m_");
347     const QString suffix = QStringLiteral(";\n");
348     const int expectedOutSize
349             = accumulatedSizeOfNames(pod.attributes)
350             + accumulatedSizeOfTypes(pod.attributes)
351             + pod.attributes.size() * (prefix.size() + infix.size() + suffix.size());
352     out.reserve(expectedOutSize);
353     for (const PODAttribute &a : pod.attributes) {
354         out += prefix;
355         out += a.type;
356         out += infix;
357         out += a.name;
358         out += suffix;
359     }
360     Q_ASSERT(out.size() == expectedOutSize);
361     return out;
362 }
363 
formatMarshallingOperators(const POD & pod)364 QString RepCodeGenerator::formatMarshallingOperators(const POD &pod)
365 {
366     return QLatin1String("inline QDataStream &operator<<(QDataStream &ds, const ") + pod.name + QLatin1String(" &obj) {\n"
367            "    QtRemoteObjects::copyStoredProperties(&obj, ds);\n"
368            "    return ds;\n"
369            "}\n"
370            "\n"
371            "inline QDataStream &operator>>(QDataStream &ds, ") + pod.name + QLatin1String(" &obj) {\n"
372            "    QtRemoteObjects::copyStoredProperties(ds, &obj);\n"
373            "    return ds;\n"
374            "}\n")
375            ;
376 }
377 
typeForMode(const ASTProperty & property,RepCodeGenerator::Mode mode)378 QString RepCodeGenerator::typeForMode(const ASTProperty &property, RepCodeGenerator::Mode mode)
379 {
380     if (!property.isPointer)
381         return property.type;
382 
383     if (property.type.startsWith(QStringLiteral("QAbstractItemModel")))
384         return mode == REPLICA ? property.type + QStringLiteral("Replica*") : property.type + QStringLiteral("*");
385 
386     switch (mode) {
387     case REPLICA: return property.type + QStringLiteral("Replica*");
388     case SIMPLE_SOURCE:
389         Q_FALLTHROUGH();
390     case SOURCE: return property.type + QStringLiteral("Source*");
391     default: qCritical("Invalid mode");
392     }
393 
394     return QStringLiteral("InvalidPropertyName");
395 }
396 
generateSimpleSetter(QTextStream & out,const ASTProperty & property,bool generateOverride)397 void RepCodeGenerator::generateSimpleSetter(QTextStream &out, const ASTProperty &property, bool generateOverride)
398 {
399     if (!generateOverride)
400         out << "    virtual ";
401     else
402         out << "    ";
403     out << "void set" << cap(property.name) << "(" << typeForMode(property, SIMPLE_SOURCE) << " " << property.name << ")";
404     if (generateOverride)
405         out << " override";
406     out << Qt::endl;
407     out << "    {" << Qt::endl;
408     out << "        if (" << property.name << " != m_" << property.name << ") {" << Qt::endl;
409     out << "            m_" << property.name << " = " << property.name << ";" << Qt::endl;
410     out << "            Q_EMIT " << property.name << "Changed(m_" << property.name << ");" << Qt::endl;
411     out << "        }" << Qt::endl;
412     out << "    }" << Qt::endl;
413 }
414 
generatePOD(QTextStream & out,const POD & pod)415 void RepCodeGenerator::generatePOD(QTextStream &out, const POD &pod)
416 {
417     QByteArray podData = pod.name.toLatin1();
418     QStringList equalityCheck;
419     for (const PODAttribute &attr : pod.attributes) {
420         equalityCheck << QStringLiteral("left.%1() == right.%1()").arg(attr.name);
421         podData += attr.name.toLatin1() + typeData(attr.type, m_globalEnumsPODs);
422     }
423     m_globalEnumsPODs[pod.name] = podData;
424     out << "class " << pod.name << "\n"
425            "{\n"
426            "    Q_GADGET\n"
427         << "\n"
428         <<      formatQPropertyDeclarations(pod)
429         << "public:\n"
430         <<      formatConstructors(pod)
431         <<      formatPropertyGettersAndSetters(pod)
432         << "private:\n"
433         <<      formatDataMembers(pod)
434         << "};\n"
435         << "\n"
436         << "inline bool operator==(const " << pod.name << " &left, const " << pod.name << " &right) Q_DECL_NOTHROW {\n"
437         << "    return " << equalityCheck.join(QStringLiteral(" && ")) << ";\n"
438         << "}\n"
439         << "inline bool operator!=(const " << pod.name << " &left, const " << pod.name << " &right) Q_DECL_NOTHROW {\n"
440         << "    return !(left == right);\n"
441         << "}\n"
442         << "\n"
443         << formatMarshallingOperators(pod)
444         << "\n"
445            "\n"
446            ;
447 }
448 
getEnumType(const ASTEnum & en)449 QString getEnumType(const ASTEnum &en)
450 {
451     if (en.isSigned) {
452         if (en.max < 0x7F)
453             return QStringLiteral("qint8");
454         if (en.max < 0x7FFF)
455             return QStringLiteral("qint16");
456         return QStringLiteral("qint32");
457     } else {
458         if (en.max < 0xFF)
459             return QStringLiteral("quint8");
460         if (en.max < 0xFFFF)
461             return QStringLiteral("quint16");
462         return QStringLiteral("quint32");
463     }
464 }
465 
generateDeclarationsForEnums(QTextStream & out,const QVector<ASTEnum> & enums,bool generateQENUM)466 void RepCodeGenerator::generateDeclarationsForEnums(QTextStream &out, const QVector<ASTEnum> &enums, bool generateQENUM)
467 {
468     if (!generateQENUM) {
469         out << "    // You need to add this enum as well as Q_ENUM to your" << Qt::endl;
470         out << "    // QObject class in order to use .rep enums over QtRO for" << Qt::endl;
471         out << "    // non-repc generated QObjects." << Qt::endl;
472     }
473     for (const ASTEnum &en : enums) {
474         m_globalEnumsPODs[en.name] = enumSignature(en);
475         out << "    enum " << en.name << " {" << Qt::endl;
476         for (const ASTEnumParam &p : en.params)
477             out << "        " << p.name << " = " << p.value << "," << Qt::endl;
478 
479         out << "    };" << Qt::endl;
480 
481         if (generateQENUM)
482             out << "    Q_ENUM(" << en.name << ")" << Qt::endl;
483     }
484 }
485 
generateENUMs(QTextStream & out,const QVector<ASTEnum> & enums,const QString & className)486 void RepCodeGenerator::generateENUMs(QTextStream &out, const QVector<ASTEnum> &enums, const QString &className)
487 {
488     out << "class " << className << "\n"
489            "{\n"
490            "    Q_GADGET\n"
491            "    " << className << "();\n"
492            "\n"
493            "public:\n";
494 
495     generateDeclarationsForEnums(out, enums);
496     generateConversionFunctionsForEnums(out, enums);
497 
498     out << "};\n\n";
499 
500     generateStreamOperatorsForEnums(out, enums, className);
501 }
502 
generateConversionFunctionsForEnums(QTextStream & out,const QVector<ASTEnum> & enums)503 void RepCodeGenerator::generateConversionFunctionsForEnums(QTextStream &out, const QVector<ASTEnum> &enums)
504 {
505     for (const ASTEnum &en : enums)
506     {
507         const QString type = getEnumType(en);
508         out << "    static inline " << en.name << " to" << en.name << "(" << type << " i, bool *ok = nullptr)\n"
509                "    {\n"
510                "        if (ok)\n"
511                "            *ok = true;\n"
512                "        switch (i) {\n";
513             for (const ASTEnumParam &p : en.params)
514                 out << "        case " << p.value << ": return " << p.name << ";\n";
515         out << "        default:\n"
516                "            if (ok)\n"
517                "                *ok = false;\n"
518                "            return " << en.params.at(0).name << ";\n"
519                "        }\n"
520                "    }\n";
521     }
522 }
523 
generateStreamOperatorsForEnums(QTextStream & out,const QVector<ASTEnum> & enums,const QString & className)524 void RepCodeGenerator::generateStreamOperatorsForEnums(QTextStream &out, const QVector<ASTEnum> &enums, const QString &className)
525 {
526     for (const ASTEnum &en : enums)
527     {
528         const QString type = getEnumType(en);
529         out <<  "inline QDataStream &operator<<(QDataStream &ds, const " << className << "::" << en.name << " &obj)\n"
530                 "{\n"
531                 "    " << type << " val = obj;\n"
532                 "    ds << val;\n"
533                 "    return ds;\n"
534                 "}\n\n"
535 
536                 "inline QDataStream &operator>>(QDataStream &ds, " << className << "::" << en.name << " &obj) {\n"
537                 "    bool ok;\n"
538                 "    " << type << " val;\n"
539                 "    ds >> val;\n"
540                 "    obj = " << className << "::to" << en.name << "(val, &ok);\n"
541                 "    if (!ok)\n        qWarning() << \"QtRO received an invalid enum value for type" << en.name << ", value =\" << val;\n"
542                 "    return ds;\n"
543                 "}\n\n";
544     }
545 }
546 
generateENUM(QTextStream & out,const ASTEnum & en)547 void RepCodeGenerator::generateENUM(QTextStream &out, const ASTEnum &en)
548 {
549     generateENUMs(out, (QVector<ASTEnum>() << en), QStringLiteral("%1Enum").arg(en.name));
550 }
551 
generateMetaTypeRegistration(const QSet<QString> & metaTypes)552 QString RepCodeGenerator::generateMetaTypeRegistration(const QSet<QString> &metaTypes)
553 {
554     QString out;
555     const QString qRegisterMetaType = QStringLiteral("        qRegisterMetaType<");
556     const QString qRegisterMetaTypeStreamOperators = QStringLiteral("        qRegisterMetaTypeStreamOperators<");
557     const QString lineEnding = QStringLiteral(">();\n");
558     for (const QString &metaType : metaTypes) {
559         if (isBuiltinType(metaType))
560             continue;
561 
562         out += qRegisterMetaType;
563         out += metaType;
564         out += lineEnding;
565 
566         out += qRegisterMetaTypeStreamOperators;
567         out += metaType;
568         out += lineEnding;
569     }
570     return out;
571 }
572 
generateMetaTypeRegistrationForPending(const QSet<QString> & metaTypes)573 QString RepCodeGenerator::generateMetaTypeRegistrationForPending(const QSet<QString> &metaTypes)
574 {
575     QString out;
576     if (!metaTypes.isEmpty())
577         out += QLatin1String("        qRegisterMetaType<QRemoteObjectPendingCall>();\n");
578     const QString qRegisterMetaType = QStringLiteral("        qRegisterMetaType<QRemoteObjectPendingReply<%1>>();\n");
579     const QString qRegisterConverterConditional = QStringLiteral("        if (!QMetaType::hasRegisteredConverterFunction<QRemoteObjectPendingReply<%1>, QRemoteObjectPendingCall>())\n");
580     const QString qRegisterConverter = QStringLiteral("            QMetaType::registerConverter<QRemoteObjectPendingReply<%1>, QRemoteObjectPendingCall>();\n");
581     for (const QString &metaType : metaTypes) {
582         out += qRegisterMetaType.arg(metaType);
583         out += qRegisterConverterConditional.arg(metaType);
584         out += qRegisterConverter.arg(metaType);
585     }
586     return out;
587 }
588 
589 
generateMetaTypeRegistrationForEnums(const QVector<QString> & enumUses)590 QString RepCodeGenerator::generateMetaTypeRegistrationForEnums(const QVector<QString> &enumUses)
591 {
592     QString out;
593 
594     for (const QString &enumName : enumUses) {
595         out += QLatin1String("        qRegisterMetaTypeStreamOperators<") + enumName + QLatin1String(">(\"") + enumName + QLatin1String("\");\n");
596     }
597 
598     return out;
599 }
600 
generateStreamOperatorsForEnums(QTextStream & out,const QVector<QString> & enumUses)601 void RepCodeGenerator::generateStreamOperatorsForEnums(QTextStream &out, const QVector<QString> &enumUses)
602 {
603     out << "QT_BEGIN_NAMESPACE" << Qt::endl;
604     for (const QString &enumName : enumUses) {
605         out << "inline QDataStream &operator<<(QDataStream &out, " << enumName << " value)" << Qt::endl;
606         out << "{" << Qt::endl;
607         out << "    out << static_cast<qint32>(value);" << Qt::endl;
608         out << "    return out;" << Qt::endl;
609         out << "}" << Qt::endl;
610         out << Qt::endl;
611         out << "inline QDataStream &operator>>(QDataStream &in, " << enumName << " &value)" << Qt::endl;
612         out << "{" << Qt::endl;
613         out << "    qint32 intValue = 0;" << Qt::endl;
614         out << "    in >> intValue;" << Qt::endl;
615         out << "    value = static_cast<" << enumName << ">(intValue);" << Qt::endl;
616         out << "    return in;" << Qt::endl;
617         out << "}" << Qt::endl;
618         out << Qt::endl;
619     }
620     out << "QT_END_NAMESPACE" << Qt::endl << Qt::endl;
621 }
622 
generateClass(Mode mode,QTextStream & out,const ASTClass & astClass,const QString & metaTypeRegistrationCode)623 void RepCodeGenerator::generateClass(Mode mode, QTextStream &out, const ASTClass &astClass, const QString &metaTypeRegistrationCode)
624 {
625     const QString className = (astClass.name + (mode == REPLICA ? QStringLiteral("Replica") : mode == SOURCE ? QStringLiteral("Source") : QStringLiteral("SimpleSource")));
626     if (mode == REPLICA)
627         out << "class " << className << " : public QRemoteObjectReplica" << Qt::endl;
628     else if (mode == SIMPLE_SOURCE)
629         out << "class " << className << " : public " << astClass.name << "Source" << Qt::endl;
630     else
631         out << "class " << className << " : public QObject" << Qt::endl;
632 
633     out << "{" << Qt::endl;
634     out << "    Q_OBJECT" << Qt::endl;
635     if (mode != SIMPLE_SOURCE) {
636         out << "    Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_TYPE, \"" << astClass.name << "\")" << Qt::endl;
637         out << "    Q_CLASSINFO(QCLASSINFO_REMOTEOBJECT_SIGNATURE, \"" << QLatin1String(classSignature(astClass)) << "\")" << Qt::endl;
638         for (int i = 0; i < astClass.modelMetadata.count(); i++) {
639             const auto model = astClass.modelMetadata.at(i);
640             const auto modelName = astClass.properties.at(model.propertyIndex).name;
641             if (!model.roles.isEmpty()) {
642                 QStringList list;
643                 for (auto role : model.roles)
644                     list << role.name;
645                 out << QString::fromLatin1("    Q_CLASSINFO(\"%1_ROLES\", \"%2\")").arg(modelName.toUpper(), list.join(QChar::fromLatin1('|'))) << Qt::endl;
646             }
647         }
648 
649 
650         //First output properties
651         for (const ASTProperty &property : astClass.properties) {
652             out << "    Q_PROPERTY(" << typeForMode(property, mode) << " " << property.name << " READ " << property.name;
653             if (property.modifier == ASTProperty::Constant) {
654                 if (mode == REPLICA) // We still need to notify when we get the initial value
655                     out << " NOTIFY " << property.name << "Changed";
656                 else
657                     out << " CONSTANT";
658             } else if (property.modifier == ASTProperty::ReadOnly)
659                 out << " NOTIFY " << property.name << "Changed";
660             else if (property.modifier == ASTProperty::ReadWrite)
661                 out << " WRITE set" << cap(property.name) << " NOTIFY " << property.name << "Changed";
662             else if (property.modifier == ASTProperty::ReadPush || property.modifier == ASTProperty::SourceOnlySetter) {
663                 if (mode == REPLICA) // The setter slot isn't known to the PROP
664                     out << " NOTIFY " << property.name << "Changed";
665                 else // The Source can use the setter, since non-asynchronous
666                     out << " WRITE set" << cap(property.name) << " NOTIFY " << property.name << "Changed";
667             }
668             out << ")" << Qt::endl;
669         }
670 
671         if (!astClass.enums.isEmpty()) {
672             out << "" << Qt::endl;
673             out << "public:" << Qt::endl;
674             generateDeclarationsForEnums(out, astClass.enums);
675         }
676     }
677 
678     out << "" << Qt::endl;
679     out << "public:" << Qt::endl;
680 
681     if (mode == REPLICA) {
682         out << "    " << className << "() : QRemoteObjectReplica() { initialize(); }" << Qt::endl;
683         out << "    static void registerMetatypes()" << Qt::endl;
684         out << "    {" << Qt::endl;
685         out << "        static bool initialized = false;" << Qt::endl;
686         out << "        if (initialized)" << Qt::endl;
687         out << "            return;" << Qt::endl;
688         out << "        initialized = true;" << Qt::endl;
689 
690         if (!metaTypeRegistrationCode.isEmpty())
691             out << metaTypeRegistrationCode << Qt::endl;
692 
693         out << "    }" << Qt::endl;
694 
695         if (astClass.hasPointerObjects())
696         {
697             out << "    void setNode(QRemoteObjectNode *node) override" << Qt::endl;
698             out << "    {" << Qt::endl;
699             out << "        QRemoteObjectReplica::setNode(node);" << Qt::endl;
700             for (int index = 0; index < astClass.properties.count(); ++index) {
701                 const ASTProperty &property = astClass.properties.at(index);
702                 if (!property.isPointer)
703                     continue;
704                 if (astClass.subClassPropertyIndices.contains(index))
705                     out << QString::fromLatin1("        setChild(%1, QVariant::fromValue(node->acquire<%2Replica>(QRemoteObjectStringLiterals::CLASS().arg(\"%3\"))));")
706                                               .arg(QString::number(index), property.type, property.name) << Qt::endl;
707                 else
708                     out << QString::fromLatin1("        setChild(%1, QVariant::fromValue(node->acquireModel(QRemoteObjectStringLiterals::MODEL().arg(\"%2\"))));")
709                                               .arg(QString::number(index), property.name) << Qt::endl;
710                 out << "        Q_EMIT " << property.name << "Changed(" << property.name << "()" << ");" << Qt::endl;
711 
712             }
713             out << "    }" << Qt::endl;
714         }
715         out << "" << Qt::endl;
716         out << "private:" << Qt::endl;
717         out << "    " << className << "(QRemoteObjectNode *node, const QString &name = QString())" << Qt::endl;
718         out << "        : QRemoteObjectReplica(ConstructWithNode)" << Qt::endl;
719         out << "    {" << Qt::endl;
720         out << "        initializeNode(node, name);" << Qt::endl;
721         for (int index = 0; index < astClass.properties.count(); ++index) {
722             const ASTProperty &property = astClass.properties.at(index);
723             if (!property.isPointer)
724                 continue;
725             if (astClass.subClassPropertyIndices.contains(index))
726                 out << QString::fromLatin1("        setChild(%1, QVariant::fromValue(node->acquire<%2Replica>(QRemoteObjectStringLiterals::CLASS().arg(\"%3\"))));")
727                                           .arg(QString::number(index), property.type, property.name) << Qt::endl;
728             else
729                 out << QString::fromLatin1("        setChild(%1, QVariant::fromValue(node->acquireModel(QRemoteObjectStringLiterals::MODEL().arg(\"%2\"))));")
730                                           .arg(QString::number(index), property.name) << Qt::endl;
731         }
732         out << "    }" << Qt::endl;
733 
734         out << "" << Qt::endl;
735 
736         out << "    void initialize() override" << Qt::endl;
737         out << "    {" << Qt::endl;
738         out << "        " << className << "::registerMetatypes();" << Qt::endl;
739         out << "        QVariantList properties;" << Qt::endl;
740         out << "        properties.reserve(" << astClass.properties.size() << ");" << Qt::endl;
741         for (const ASTProperty &property : astClass.properties) {
742             if (property.isPointer)
743                 out << "        properties << QVariant::fromValue((" << typeForMode(property, mode) << ")" << property.defaultValue << ");" << Qt::endl;
744             else
745                 out << "        properties << QVariant::fromValue(" << typeForMode(property, mode) << "(" << property.defaultValue << "));" << Qt::endl;
746         }
747         int nPersisted = 0;
748         if (astClass.hasPersisted) {
749             out << "        QVariantList stored = retrieveProperties(\"" << astClass.name << "\", \"" << classSignature(astClass) << "\");" << Qt::endl;
750             out << "        if (!stored.isEmpty()) {" << Qt::endl;
751             for (int i = 0; i < astClass.properties.size(); i++) {
752                 if (astClass.properties.at(i).persisted) {
753                     out << "            properties[" << i << "] = stored.at(" << nPersisted << ");" << Qt::endl;
754                     nPersisted++;
755                 }
756             }
757             out << "        }" << Qt::endl;
758         }
759         out << "        setProperties(properties);" << Qt::endl;
760         out << "    }" << Qt::endl;
761     } else if (mode == SOURCE) {
762         out << "    explicit " << className << "(QObject *parent = nullptr) : QObject(parent)" << Qt::endl;
763         out << "    {" << Qt::endl;
764         if (!metaTypeRegistrationCode.isEmpty())
765             out << metaTypeRegistrationCode << Qt::endl;
766         out << "    }" << Qt::endl;
767     } else {
768         QVector<int> constIndices;
769         for (int index = 0; index < astClass.properties.count(); ++index) {
770             const ASTProperty &property = astClass.properties.at(index);
771             if (property.modifier == ASTProperty::Constant)
772                 constIndices.append(index);
773         }
774         if (constIndices.isEmpty()) {
775             out << "    explicit " << className << "(QObject *parent = nullptr) : " << astClass.name << "Source(parent)" << Qt::endl;
776         } else {
777             QStringList parameters;
778             for (int index : constIndices) {
779                 const ASTProperty &property = astClass.properties.at(index);
780                 parameters.append(QString::fromLatin1("%1 %2 = %3").arg(typeForMode(property, SOURCE), property.name, property.defaultValue));
781             }
782             parameters.append(QStringLiteral("QObject *parent = nullptr"));
783             out << "    explicit " << className << "(" << parameters.join(QStringLiteral(", ")) << ") : " << astClass.name << "Source(parent)" << Qt::endl;
784         }
785         for (const ASTProperty &property : astClass.properties) {
786             if (property.modifier == ASTProperty::Constant)
787                 out << "    , m_" << property.name << "(" << property.name << ")" << Qt::endl;
788             else
789                 out << "    , m_" << property.name << "(" << property.defaultValue << ")" << Qt::endl;
790         }
791         out << "    {" << Qt::endl;
792         out << "    }" << Qt::endl;
793     }
794 
795     out << "" << Qt::endl;
796     out << "public:" << Qt::endl;
797 
798     if (mode == REPLICA && astClass.hasPersisted) {
799         out << "    ~" << className << "() override {" << Qt::endl;
800         out << "        QVariantList persisted;" << Qt::endl;
801         for (int i = 0; i < astClass.properties.size(); i++) {
802             if (astClass.properties.at(i).persisted) {
803                 out << "        persisted << propAsVariant(" << i << ");" << Qt::endl;
804             }
805         }
806         out << "        persistProperties(\"" << astClass.name << "\", \"" << classSignature(astClass) << "\", persisted);" << Qt::endl;
807         out << "    }" << Qt::endl;
808     } else {
809         out << "    ~" << className << "() override = default;" << Qt::endl;
810     }
811     out << "" << Qt::endl;
812 
813     if (mode != SIMPLE_SOURCE)
814         generateConversionFunctionsForEnums(out, astClass.enums);
815 
816     //Next output getter/setter
817     if (mode == REPLICA) {
818         int i = 0;
819         for (const ASTProperty &property : astClass.properties) {
820             auto type = typeForMode(property, mode);
821             if (type == QLatin1String("QVariant")) {
822                 out << "    " << type << " " << property.name << "() const" << Qt::endl;
823                 out << "    {" << Qt::endl;
824                 out << "        return propAsVariant(" << i << ");" << Qt::endl;
825                 out << "    }" << Qt::endl;
826             } else {
827                 out << "    " << type << " " << property.name << "() const" << Qt::endl;
828                 out << "    {" << Qt::endl;
829                 out << "        const QVariant variant = propAsVariant(" << i << ");" << Qt::endl;
830                 out << "        if (!variant.canConvert<" << type << ">()) {" << Qt::endl;
831                 out << "            qWarning() << \"QtRO cannot convert the property " << property.name << " to type " << type << "\";" << Qt::endl;
832                 out << "        }" << Qt::endl;
833                 out << "        return variant.value<" << type << " >();" << Qt::endl;
834                 out << "    }" << Qt::endl;
835             }
836             i++;
837             if (property.modifier == ASTProperty::ReadWrite) {
838                 out << "" << Qt::endl;
839                 out << "    void set" << cap(property.name) << "(" << property.type << " " << property.name << ")" << Qt::endl;
840                 out << "    {" << Qt::endl;
841                 out << "        static int __repc_index = " << className << "::staticMetaObject.indexOfProperty(\"" << property.name << "\");" << Qt::endl;
842                 out << "        QVariantList __repc_args;" << Qt::endl;
843                 out << "        __repc_args << QVariant::fromValue(" << property.name << ");" << Qt::endl;
844                 out << "        send(QMetaObject::WriteProperty, __repc_index, __repc_args);" << Qt::endl;
845                 out << "    }" << Qt::endl;
846             }
847             out << "" << Qt::endl;
848         }
849     } else if (mode == SOURCE) {
850         for (const ASTProperty &property : astClass.properties)
851             out << "    virtual " << typeForMode(property, mode) << " " << property.name << "() const = 0;" << Qt::endl;
852         for (const ASTProperty &property : astClass.properties) {
853             if (property.modifier == ASTProperty::ReadWrite ||
854                     property.modifier == ASTProperty::ReadPush ||
855                     property.modifier == ASTProperty::SourceOnlySetter)
856                 out << "    virtual void set" << cap(property.name) << "(" << typeForMode(property, mode) << " " << property.name << ") = 0;" << Qt::endl;
857         }
858     } else {
859         for (const ASTProperty &property : astClass.properties)
860             out << "    " << typeForMode(property, mode) << " " << property.name << "() const override { return m_"
861                 << property.name << "; }" << Qt::endl;
862         for (const ASTProperty &property : astClass.properties) {
863             if (property.modifier == ASTProperty::ReadWrite ||
864                     property.modifier == ASTProperty::ReadPush ||
865                     property.modifier == ASTProperty::SourceOnlySetter) {
866                 generateSimpleSetter(out, property);
867             }
868         }
869     }
870 
871     if (mode != SIMPLE_SOURCE) {
872         //Next output property signals
873         if (!astClass.properties.isEmpty() || !astClass.signalsList.isEmpty()) {
874             out << "" << Qt::endl;
875             out << "Q_SIGNALS:" << Qt::endl;
876             for (const ASTProperty &property : astClass.properties) {
877                 if (property.modifier != ASTProperty::Constant)
878                     out << "    void " << property.name << "Changed(" << fullyQualifiedTypeName(astClass, className, typeForMode(property, mode)) << " " << property.name << ");" << Qt::endl;
879             }
880 
881             const QVector<ASTFunction> signalsList = transformEnumParams(astClass, astClass.signalsList, className);
882             for (const ASTFunction &signal : signalsList)
883                 out << "    void " << signal.name << "(" << signal.paramsAsString() << ");" << Qt::endl;
884 
885             // CONSTANT source properties still need an onChanged signal on the Replica side to
886             // update (once) when the value is initialized.  Put these last, so they don't mess
887             // up the signal index order
888             for (const ASTProperty &property : astClass.properties) {
889                 if (mode == REPLICA && property.modifier == ASTProperty::Constant)
890                     out << "    void " << property.name << "Changed(" << fullyQualifiedTypeName(astClass, className, typeForMode(property, mode)) << " " << property.name << ");" << Qt::endl;
891             }
892         }
893         bool hasWriteSlots = false;
894         for (const ASTProperty &property : astClass.properties) {
895             if (property.modifier == ASTProperty::ReadPush) {
896                 hasWriteSlots = true;
897                 break;
898             }
899         }
900         if (hasWriteSlots || !astClass.slotsList.isEmpty()) {
901             out << "" << Qt::endl;
902             out << "public Q_SLOTS:" << Qt::endl;
903             for (const ASTProperty &property : astClass.properties) {
904                 if (property.modifier == ASTProperty::ReadPush) {
905                     const auto type = fullyQualifiedTypeName(astClass, className, property.type);
906                     if (mode != REPLICA) {
907                         out << "    virtual void push" << cap(property.name) << "(" << type << " " << property.name << ")" << Qt::endl;
908                         out << "    {" << Qt::endl;
909                         out << "        set" << cap(property.name) << "(" << property.name << ");" << Qt::endl;
910                         out << "    }" << Qt::endl;
911                     } else {
912                         out << "    void push" << cap(property.name) << "(" << type << " " << property.name << ")" << Qt::endl;
913                         out << "    {" << Qt::endl;
914                         out << "        static int __repc_index = " << className << "::staticMetaObject.indexOfSlot(\"push" << cap(property.name) << "(" << type << ")\");" << Qt::endl;
915                         out << "        QVariantList __repc_args;" << Qt::endl;
916                         out << "        __repc_args << QVariant::fromValue(" << property.name << ");" << Qt::endl;
917                         out << "        send(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args);" << Qt::endl;
918                         out << "    }" << Qt::endl;
919                     }
920                 }
921             }
922             const QVector<ASTFunction> slotsList = transformEnumParams(astClass, astClass.slotsList, className);
923             for (const ASTFunction &slot : slotsList) {
924                 const auto returnType = fullyQualifiedTypeName(astClass, className, slot.returnType);
925                 if (mode != REPLICA) {
926                     out << "    virtual " << returnType << " " << slot.name << "(" << slot.paramsAsString() << ") = 0;" << Qt::endl;
927                 } else {
928                     // TODO: Discuss whether it is a good idea to special-case for void here,
929                     const bool isVoid = slot.returnType == QStringLiteral("void");
930 
931                     if (isVoid)
932                         out << "    void " << slot.name << "(" << slot.paramsAsString() << ")" << Qt::endl;
933                     else
934                         out << "    QRemoteObjectPendingReply<" << returnType << "> " << slot.name << "(" << slot.paramsAsString()<< ")" << Qt::endl;
935                     out << "    {" << Qt::endl;
936                     out << "        static int __repc_index = " << className << "::staticMetaObject.indexOfSlot(\"" << slot.name << "(" << slot.paramsAsString(ASTFunction::Normalized) << ")\");" << Qt::endl;
937                     out << "        QVariantList __repc_args;" << Qt::endl;
938                     const auto &paramNames = slot.paramNames();
939                     if (!paramNames.isEmpty()) {
940                         out << "        __repc_args" << Qt::endl;
941                         for (const QString &name : paramNames)
942                             out << "            << " << "QVariant::fromValue(" << name << ")" << Qt::endl;
943                         out << "        ;" << Qt::endl;
944                     }
945                     if (isVoid)
946                         out << "        send(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args);" << Qt::endl;
947                     else
948                         out << "        return QRemoteObjectPendingReply<" << returnType << ">(sendWithReply(QMetaObject::InvokeMetaMethod, __repc_index, __repc_args));" << Qt::endl;
949                     out << "    }" << Qt::endl;
950                 }
951             }
952         }
953     } else {
954         if (!astClass.properties.isEmpty()) {
955             bool addProtected = true;
956             for (const ASTProperty &property : astClass.properties) {
957                 if (property.modifier == ASTProperty::ReadOnly) {
958                     if (addProtected) {
959                         out << "" << Qt::endl;
960                         out << "protected:" << Qt::endl;
961                         addProtected = false;
962                     }
963                     generateSimpleSetter(out, property, false);
964                 }
965             }
966         }
967     }
968 
969     out << "" << Qt::endl;
970     out << "private:" << Qt::endl;
971 
972     //Next output data members
973     if (mode == SIMPLE_SOURCE) {
974         for (const ASTProperty &property : astClass.properties)
975             out << "    " << typeForMode(property, SOURCE) << " " << "m_" << property.name << ";" << Qt::endl;
976     }
977 
978     if (mode != SIMPLE_SOURCE)
979         out << "    friend class QT_PREPEND_NAMESPACE(QRemoteObjectNode);" << Qt::endl;
980 
981     out << "};" << Qt::endl;
982     out << "" << Qt::endl;
983 
984     if (mode != SIMPLE_SOURCE)
985         generateStreamOperatorsForEnums(out, astClass.enums, className);
986 
987     out << "" << Qt::endl;
988 }
989 
generateSourceAPI(QTextStream & out,const ASTClass & astClass)990 void RepCodeGenerator::generateSourceAPI(QTextStream &out, const ASTClass &astClass)
991 {
992     const QString className = astClass.name + QStringLiteral("SourceAPI");
993     out << QStringLiteral("template <class ObjectType>") << Qt::endl;
994     out << QString::fromLatin1("struct %1 : public SourceApiMap").arg(className) << Qt::endl;
995     out << QStringLiteral("{") << Qt::endl;
996     if (!astClass.enums.isEmpty()) {
997         // Include enum definition in SourceAPI
998         generateDeclarationsForEnums(out, astClass.enums, false);
999     }
1000     out << QString::fromLatin1("    %1(ObjectType *object, const QString &name = QLatin1String(\"%2\"))").arg(className, astClass.name) << Qt::endl;
1001     out << QStringLiteral("        : SourceApiMap(), m_name(name)") << Qt::endl;
1002     out << QStringLiteral("    {") << Qt::endl;
1003     if (!astClass.hasPointerObjects())
1004         out << QStringLiteral("        Q_UNUSED(object);") << Qt::endl;
1005 
1006     const int enumCount = astClass.enums.count();
1007     for (int i : astClass.subClassPropertyIndices) {
1008         const ASTProperty &child = astClass.properties.at(i);
1009         out << QString::fromLatin1("        using %1_type_t = typename std::remove_pointer<decltype(object->%1())>::type;")
1010                                   .arg(child.name) << Qt::endl;
1011     }
1012     out << QString::fromLatin1("        m_enums[0] = %1;").arg(enumCount) << Qt::endl;
1013     for (int i = 0; i < enumCount; ++i) {
1014         const auto enumerator = astClass.enums.at(i);
1015         out << QString::fromLatin1("        m_enums[%1] = ObjectType::staticMetaObject.indexOfEnumerator(\"%2\");")
1016                              .arg(i+1).arg(enumerator.name) << Qt::endl;
1017     }
1018     const int propCount = astClass.properties.count();
1019     out << QString::fromLatin1("        m_properties[0] = %1;").arg(propCount) << Qt::endl;
1020     QList<ASTProperty> onChangeProperties;
1021     QList<int> propertyChangeIndex;
1022     for (int i = 0; i < propCount; ++i) {
1023         const ASTProperty &prop = astClass.properties.at(i);
1024         const QString propTypeName = fullyQualifiedTypeName(astClass, QStringLiteral("typename ObjectType"), typeForMode(prop, SOURCE));
1025         out << QString::fromLatin1("        m_properties[%1] = QtPrivate::qtro_property_index<ObjectType>(&ObjectType::%2, "
1026                               "static_cast<%3 (QObject::*)()>(0),\"%2\");")
1027                              .arg(QString::number(i+1), prop.name, propTypeName) << Qt::endl;
1028         if (prop.modifier == prop.ReadWrite) //Make sure we have a setter function
1029             out << QStringLiteral("        QtPrivate::qtro_method_test<ObjectType>(&ObjectType::set%1, static_cast<void (QObject::*)(%2)>(0));")
1030                                  .arg(cap(prop.name), propTypeName) << Qt::endl;
1031         if (prop.modifier != prop.Constant) { //Make sure we have an onChange signal
1032             out << QStringLiteral("        QtPrivate::qtro_method_test<ObjectType>(&ObjectType::%1Changed, static_cast<void (QObject::*)()>(0));")
1033                                  .arg(prop.name) << Qt::endl;
1034             onChangeProperties << prop;
1035             propertyChangeIndex << i + 1; //m_properties[0] is the count, so index is one higher
1036         }
1037     }
1038     const int signalCount = astClass.signalsList.count();
1039     const int changedCount = onChangeProperties.size();
1040     out << QString::fromLatin1("        m_signals[0] = %1;").arg(signalCount+onChangeProperties.size()) << Qt::endl;
1041     for (int i = 0; i < changedCount; ++i)
1042         out << QString::fromLatin1("        m_signals[%1] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::%2Changed, "
1043                               "static_cast<void (QObject::*)(%3)>(0),m_signalArgCount+%4,&m_signalArgTypes[%4]);")
1044                              .arg(QString::number(i+1), onChangeProperties.at(i).name,
1045                                   fullyQualifiedTypeName(astClass, QStringLiteral("typename ObjectType"), typeForMode(onChangeProperties.at(i), SOURCE)),
1046                                   QString::number(i)) << Qt::endl;
1047 
1048     QVector<ASTFunction> signalsList = transformEnumParams(astClass, astClass.signalsList, QStringLiteral("typename ObjectType"));
1049     for (int i = 0; i < signalCount; ++i) {
1050         const ASTFunction &sig = signalsList.at(i);
1051         out << QString::fromLatin1("        m_signals[%1] = QtPrivate::qtro_signal_index<ObjectType>(&ObjectType::%2, "
1052                               "static_cast<void (QObject::*)(%3)>(0),m_signalArgCount+%4,&m_signalArgTypes[%4]);")
1053                              .arg(QString::number(changedCount+i+1), sig.name, sig.paramsAsString(ASTFunction::Normalized), QString::number(changedCount+i)) << Qt::endl;
1054     }
1055     const int slotCount = astClass.slotsList.count();
1056     QVector<ASTProperty> pushProps;
1057     for (const ASTProperty &property : astClass.properties) {
1058         if (property.modifier == ASTProperty::ReadPush)
1059             pushProps << property;
1060     }
1061     const int pushCount = pushProps.count();
1062     const int methodCount = slotCount + pushCount;
1063     out << QString::fromLatin1("        m_methods[0] = %1;").arg(methodCount) << Qt::endl;
1064     for (int i = 0; i < pushCount; ++i) {
1065         const ASTProperty &prop = pushProps.at(i);
1066         const QString propTypeName = fullyQualifiedTypeName(astClass, QStringLiteral("typename ObjectType"), prop.type);
1067         out << QString::fromLatin1("        m_methods[%1] = QtPrivate::qtro_method_index<ObjectType>(&ObjectType::push%2, "
1068                               "static_cast<void (QObject::*)(%3)>(0),\"push%2(%4)\",m_methodArgCount+%5,&m_methodArgTypes[%5]);")
1069                              .arg(QString::number(i+1), cap(prop.name), propTypeName,
1070                                   QString(propTypeName).remove(QStringLiteral("typename ObjectType::")), // we don't want this in the string signature
1071                                   QString::number(i)) << Qt::endl;
1072     }
1073 
1074     QVector<ASTFunction> slotsList = transformEnumParams(astClass, astClass.slotsList, QStringLiteral("typename ObjectType"));
1075     for (int i = 0; i < slotCount; ++i) {
1076         const ASTFunction &slot = slotsList.at(i);
1077         const QString params = slot.paramsAsString(ASTFunction::Normalized);
1078         out << QString::fromLatin1("        m_methods[%1] = QtPrivate::qtro_method_index<ObjectType>(&ObjectType::%2, "
1079                               "static_cast<void (QObject::*)(%3)>(0),\"%2(%4)\",m_methodArgCount+%5,&m_methodArgTypes[%5]);")
1080                              .arg(QString::number(i+pushCount+1), slot.name, params,
1081                                   QString(params).remove(QStringLiteral("typename ObjectType::")), // we don't want this in the string signature
1082                                   QString::number(i+pushCount)) << Qt::endl;
1083     }
1084     for (const auto &model : astClass.modelMetadata) {
1085         const ASTProperty &property = astClass.properties.at(model.propertyIndex);
1086         out << QString::fromLatin1("        m_models << ModelInfo({object->%1(),").arg(property.name) << Qt::endl;
1087         out << QString::fromLatin1("                               QStringLiteral(\"%1\"),").arg(property.name) << Qt::endl;
1088         QStringList list;
1089         if (!model.roles.isEmpty()) {
1090             for (auto role : model.roles)
1091                 list << role.name;
1092         }
1093         out << QString::fromLatin1("                               QByteArrayLiteral(\"%1\")});").arg(list.join(QChar::fromLatin1('|'))) << Qt::endl;
1094     }
1095     for (int i : astClass.subClassPropertyIndices) {
1096         const ASTProperty &child = astClass.properties.at(i);
1097         out << QString::fromLatin1("        m_subclasses << new %2SourceAPI<%1_type_t>(object->%1(), QStringLiteral(\"%1\"));")
1098                                    .arg(child.name, child.type) << Qt::endl;
1099     }
1100     out << QStringLiteral("    }") << Qt::endl;
1101     out << QStringLiteral("") << Qt::endl;
1102     out << QString::fromLatin1("    QString name() const override { return m_name; }") << Qt::endl;
1103     out << QString::fromLatin1("    QString typeName() const override { return QStringLiteral(\"%1\"); }").arg(astClass.name) << Qt::endl;
1104     out << QStringLiteral("    int enumCount() const override { return m_enums[0]; }") << Qt::endl;
1105     out << QStringLiteral("    int propertyCount() const override { return m_properties[0]; }") << Qt::endl;
1106     out << QStringLiteral("    int signalCount() const override { return m_signals[0]; }") << Qt::endl;
1107     out << QStringLiteral("    int methodCount() const override { return m_methods[0]; }") << Qt::endl;
1108     out << QStringLiteral("    int sourceEnumIndex(int index) const override") << Qt::endl;
1109     out << QStringLiteral("    {") << Qt::endl;
1110     out << QStringLiteral("        if (index < 0 || index >= m_enums[0])") << Qt::endl;
1111     out << QStringLiteral("            return -1;") << Qt::endl;
1112     out << QStringLiteral("        return m_enums[index+1];") << Qt::endl;
1113     out << QStringLiteral("    }") << Qt::endl;
1114     out << QStringLiteral("    int sourcePropertyIndex(int index) const override") << Qt::endl;
1115     out << QStringLiteral("    {") << Qt::endl;
1116     out << QStringLiteral("        if (index < 0 || index >= m_properties[0])") << Qt::endl;
1117     out << QStringLiteral("            return -1;") << Qt::endl;
1118     out << QStringLiteral("        return m_properties[index+1];") << Qt::endl;
1119     out << QStringLiteral("    }") << Qt::endl;
1120     out << QStringLiteral("    int sourceSignalIndex(int index) const override") << Qt::endl;
1121     out << QStringLiteral("    {") << Qt::endl;
1122     out << QStringLiteral("        if (index < 0 || index >= m_signals[0])") << Qt::endl;
1123     out << QStringLiteral("            return -1;") << Qt::endl;
1124     out << QStringLiteral("        return m_signals[index+1];") << Qt::endl;
1125     out << QStringLiteral("    }") << Qt::endl;
1126     out << QStringLiteral("    int sourceMethodIndex(int index) const override") << Qt::endl;
1127     out << QStringLiteral("    {") << Qt::endl;
1128     out << QStringLiteral("        if (index < 0 || index >= m_methods[0])") << Qt::endl;
1129     out << QStringLiteral("            return -1;") << Qt::endl;
1130     out << QStringLiteral("        return m_methods[index+1];") << Qt::endl;
1131     out << QStringLiteral("    }") << Qt::endl;
1132     if (signalCount+changedCount > 0) {
1133         out << QStringLiteral("    int signalParameterCount(int index) const override") << Qt::endl;
1134         out << QStringLiteral("    {") << Qt::endl;
1135         out << QStringLiteral("        if (index < 0 || index >= m_signals[0])") << Qt::endl;
1136         out << QStringLiteral("            return -1;") << Qt::endl;
1137         out << QStringLiteral("        return m_signalArgCount[index];") << Qt::endl;
1138         out << QStringLiteral("    }") << Qt::endl;
1139         out << QStringLiteral("    int signalParameterType(int sigIndex, int paramIndex) const override") << Qt::endl;
1140         out << QStringLiteral("    {") << Qt::endl;
1141         out << QStringLiteral("        if (sigIndex < 0 || sigIndex >= m_signals[0] || paramIndex < 0 || paramIndex >= m_signalArgCount[sigIndex])") << Qt::endl;
1142         out << QStringLiteral("            return -1;") << Qt::endl;
1143         out << QStringLiteral("        return m_signalArgTypes[sigIndex][paramIndex];") << Qt::endl;
1144         out << QStringLiteral("    }") << Qt::endl;
1145     } else {
1146         out << QStringLiteral("    int signalParameterCount(int index) const override { Q_UNUSED(index); return -1; }") << Qt::endl;
1147         out << QStringLiteral("    int signalParameterType(int sigIndex, int paramIndex) const override") << Qt::endl;
1148         out << QStringLiteral("    { Q_UNUSED(sigIndex); Q_UNUSED(paramIndex); return -1; }") << Qt::endl;
1149     }
1150     if (methodCount > 0) {
1151         out << QStringLiteral("    int methodParameterCount(int index) const override") << Qt::endl;
1152         out << QStringLiteral("    {") << Qt::endl;
1153         out << QStringLiteral("        if (index < 0 || index >= m_methods[0])") << Qt::endl;
1154         out << QStringLiteral("            return -1;") << Qt::endl;
1155         out << QStringLiteral("        return m_methodArgCount[index];") << Qt::endl;
1156         out << QStringLiteral("    }") << Qt::endl;
1157         out << QStringLiteral("    int methodParameterType(int methodIndex, int paramIndex) const override") << Qt::endl;
1158         out << QStringLiteral("    {") << Qt::endl;
1159         out << QStringLiteral("        if (methodIndex < 0 || methodIndex >= m_methods[0] || paramIndex < 0 || paramIndex >= m_methodArgCount[methodIndex])") << Qt::endl;
1160         out << QStringLiteral("            return -1;") << Qt::endl;
1161         out << QStringLiteral("        return m_methodArgTypes[methodIndex][paramIndex];") << Qt::endl;
1162         out << QStringLiteral("    }") << Qt::endl;
1163     } else {
1164         out << QStringLiteral("    int methodParameterCount(int index) const override { Q_UNUSED(index); return -1; }") << Qt::endl;
1165         out << QStringLiteral("    int methodParameterType(int methodIndex, int paramIndex) const override") << Qt::endl;
1166         out << QStringLiteral("    { Q_UNUSED(methodIndex); Q_UNUSED(paramIndex); return -1; }") << Qt::endl;
1167     }
1168     //propertyIndexFromSignal method
1169     out << QStringLiteral("    int propertyIndexFromSignal(int index) const override") << Qt::endl;
1170     out << QStringLiteral("    {") << Qt::endl;
1171     if (!propertyChangeIndex.isEmpty()) {
1172         out << QStringLiteral("        switch (index) {") << Qt::endl;
1173         for (int i = 0; i < propertyChangeIndex.size(); ++i)
1174             out << QString::fromLatin1("        case %1: return m_properties[%2];").arg(i).arg(propertyChangeIndex.at(i)) << Qt::endl;
1175         out << QStringLiteral("        }") << Qt::endl;
1176     } else
1177         out << QStringLiteral("        Q_UNUSED(index);") << Qt::endl;
1178     out << QStringLiteral("        return -1;") << Qt::endl;
1179     out << QStringLiteral("    }") << Qt::endl;
1180     //propertyRawIndexFromSignal method
1181     out << QStringLiteral("    int propertyRawIndexFromSignal(int index) const override") << Qt::endl;
1182     out << QStringLiteral("    {") << Qt::endl;
1183     if (!propertyChangeIndex.isEmpty()) {
1184         out << QStringLiteral("        switch (index) {") << Qt::endl;
1185         for (int i = 0; i < propertyChangeIndex.size(); ++i)
1186             out << QString::fromLatin1("        case %1: return %2;").arg(i).arg(propertyChangeIndex.at(i)-1) << Qt::endl;
1187         out << QStringLiteral("        }") << Qt::endl;
1188     } else
1189         out << QStringLiteral("        Q_UNUSED(index);") << Qt::endl;
1190     out << QStringLiteral("        return -1;") << Qt::endl;
1191     out << QStringLiteral("    }") << Qt::endl;
1192 
1193     //signalSignature method
1194     out << QStringLiteral("    const QByteArray signalSignature(int index) const override") << Qt::endl;
1195     out << QStringLiteral("    {") << Qt::endl;
1196     if (signalCount+changedCount > 0) {
1197         out << QStringLiteral("        switch (index) {") << Qt::endl;
1198         for (int i = 0; i < changedCount; ++i) {
1199             const ASTProperty &prop = onChangeProperties.at(i);
1200             if (isClassEnum(astClass, prop.type))
1201                 out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"%2Changed($1)\").replace(\"$1\", QtPrivate::qtro_enum_signature<ObjectType>(\"%3\"));")
1202                     .arg(QString::number(i), prop.name, prop.type) << Qt::endl;
1203             else
1204                 out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"%2Changed(%3)\");")
1205                     .arg(QString::number(i), prop.name, typeForMode(prop, SOURCE)) << Qt::endl;
1206         }
1207         for (int i = 0; i < signalCount; ++i)
1208         {
1209             const ASTFunction &sig = astClass.signalsList.at(i);
1210             auto paramsAsString = sig.paramsAsString(ASTFunction::Normalized);
1211             const auto paramsAsList = paramsAsString.split(QLatin1String(","));
1212             int enumCount = 0;
1213             QString enumString;
1214             for (int j = 0; j < paramsAsList.count(); j++) {
1215                 auto const p = paramsAsList.at(j);
1216                 if (isClassEnum(astClass, p)) {
1217                     paramsAsString.replace(paramsAsString.indexOf(p), p.size(), QStringLiteral("$%1").arg(enumCount));
1218                     enumString.append(QString::fromLatin1(".replace(\"$%1\", QtPrivate::qtro_enum_signature<ObjectType>(\"%2\"))").arg(enumCount++).arg(paramsAsList.at(j)));
1219                 }
1220             }
1221             out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"%2(%3)\")%4;")
1222                                     .arg(QString::number(i+changedCount), sig.name, paramsAsString, enumString) << Qt::endl;
1223         }
1224         out << QStringLiteral("        }") << Qt::endl;
1225     } else
1226         out << QStringLiteral("        Q_UNUSED(index);") << Qt::endl;
1227     out << QStringLiteral("        return QByteArrayLiteral(\"\");") << Qt::endl;
1228     out << QStringLiteral("    }") << Qt::endl;
1229 
1230     //signalParameterNames method
1231     out << QStringLiteral("    QList<QByteArray> signalParameterNames(int index) const override") << Qt::endl;
1232     out << QStringLiteral("    {") << Qt::endl;
1233     out << QStringLiteral("        if (index < 0 || index >= m_signals[0])") << Qt::endl;
1234     out << QStringLiteral("            return QList<QByteArray>();") << Qt::endl;
1235     out << QStringLiteral("        return ObjectType::staticMetaObject.method(m_signals[index + 1]).parameterNames();") << Qt::endl;
1236     out << QStringLiteral("    }") << Qt::endl;
1237 
1238     //methodSignature method
1239     out << QStringLiteral("    const QByteArray methodSignature(int index) const override") << Qt::endl;
1240     out << QStringLiteral("    {") << Qt::endl;
1241     if (methodCount > 0) {
1242         out << QStringLiteral("        switch (index) {") << Qt::endl;
1243         for (int i = 0; i < pushCount; ++i)
1244         {
1245             const ASTProperty &prop = pushProps.at(i);
1246             if (isClassEnum(astClass, prop.type))
1247                 out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"push%2($1)\").replace(\"$1\", QtPrivate::qtro_enum_signature<ObjectType>(\"%3\"));")
1248                     .arg(QString::number(i), prop.name, prop.type) << Qt::endl;
1249             else
1250                 out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"push%2(%3)\");")
1251                                         .arg(QString::number(i), cap(prop.name), prop.type) << Qt::endl;
1252         }
1253         for (int i = 0; i < slotCount; ++i)
1254         {
1255             const ASTFunction &slot = astClass.slotsList.at(i);
1256             auto paramsAsString = slot.paramsAsString(ASTFunction::Normalized);
1257             const auto paramsAsList = paramsAsString.split(QLatin1String(","));
1258             int enumCount = 0;
1259             QString enumString;
1260             for (int j = 0; j < paramsAsList.count(); j++) {
1261                 auto const p = paramsAsList.at(j);
1262                 if (isClassEnum(astClass, p)) {
1263                     paramsAsString.replace(paramsAsString.indexOf(p), p.size(), QStringLiteral("$%1").arg(enumCount));
1264                     enumString.append(QString::fromLatin1(".replace(\"$%1\", QtPrivate::qtro_enum_signature<ObjectType>(\"%2\"))").arg(enumCount++).arg(paramsAsList.at(j)));
1265                 }
1266             }
1267             out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"%2(%3)\")%4;")
1268                                     .arg(QString::number(i+pushCount), slot.name, paramsAsString, enumString) << Qt::endl;
1269         }
1270         out << QStringLiteral("        }") << Qt::endl;
1271     } else
1272         out << QStringLiteral("        Q_UNUSED(index);") << Qt::endl;
1273     out << QStringLiteral("        return QByteArrayLiteral(\"\");") << Qt::endl;
1274     out << QStringLiteral("    }") << Qt::endl;
1275 
1276     //methodType method
1277     out << QStringLiteral("    QMetaMethod::MethodType methodType(int) const override") << Qt::endl;
1278     out << QStringLiteral("    {") << Qt::endl;
1279     out << QStringLiteral("        return QMetaMethod::Slot;") << Qt::endl;
1280     out << QStringLiteral("    }") << Qt::endl;
1281 
1282     //methodParameterNames method
1283     out << QStringLiteral("    QList<QByteArray> methodParameterNames(int index) const override") << Qt::endl;
1284     out << QStringLiteral("    {") << Qt::endl;
1285     out << QStringLiteral("        if (index < 0 || index >= m_methods[0])") << Qt::endl;
1286     out << QStringLiteral("            return QList<QByteArray>();") << Qt::endl;
1287     out << QStringLiteral("        return ObjectType::staticMetaObject.method(m_methods[index + 1]).parameterNames();") << Qt::endl;
1288     out << QStringLiteral("    }") << Qt::endl;
1289 
1290     //typeName method
1291     out << QStringLiteral("    const QByteArray typeName(int index) const override") << Qt::endl;
1292     out << QStringLiteral("    {") << Qt::endl;
1293     if (methodCount > 0) {
1294         out << QStringLiteral("        switch (index) {") << Qt::endl;
1295         for (int i = 0; i < pushCount; ++i)
1296         {
1297             out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"void\");")
1298                                       .arg(QString::number(i)) << Qt::endl;
1299         }
1300         for (int i = 0; i < slotCount; ++i)
1301         {
1302             const ASTFunction &slot = astClass.slotsList.at(i);
1303             if (isClassEnum(astClass, slot.returnType))
1304                 out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"$1\").replace(\"$1\", QtPrivate::qtro_enum_signature<ObjectType>(\"%2\"));")
1305                                           .arg(QString::number(i+pushCount), slot.returnType) << Qt::endl;
1306             else
1307                 out << QString::fromLatin1("        case %1: return QByteArrayLiteral(\"%2\");")
1308                                           .arg(QString::number(i+pushCount), slot.returnType) << Qt::endl;
1309         }
1310         out << QStringLiteral("        }") << Qt::endl;
1311     } else
1312         out << QStringLiteral("        Q_UNUSED(index);") << Qt::endl;
1313     out << QStringLiteral("        return QByteArrayLiteral(\"\");") << Qt::endl;
1314     out << QStringLiteral("    }") << Qt::endl;
1315 
1316     //objectSignature method
1317     out << QStringLiteral("    QByteArray objectSignature() const override { return QByteArray{\"")
1318         << QLatin1String(classSignature(astClass))
1319         << QStringLiteral("\"}; }") << Qt::endl;
1320 
1321     out << QStringLiteral("") << Qt::endl;
1322     out << QString::fromLatin1("    int m_enums[%1];").arg(enumCount + 1) << Qt::endl;
1323     out << QString::fromLatin1("    int m_properties[%1];").arg(propCount+1) << Qt::endl;
1324     out << QString::fromLatin1("    int m_signals[%1];").arg(signalCount+changedCount+1) << Qt::endl;
1325     out << QString::fromLatin1("    int m_methods[%1];").arg(methodCount+1) << Qt::endl;
1326     out << QString::fromLatin1("    const QString m_name;") << Qt::endl;
1327     if (signalCount+changedCount > 0) {
1328         out << QString::fromLatin1("    int m_signalArgCount[%1];").arg(signalCount+changedCount) << Qt::endl;
1329         out << QString::fromLatin1("    const int* m_signalArgTypes[%1];").arg(signalCount+changedCount) << Qt::endl;
1330     }
1331     if (methodCount > 0) {
1332         out << QString::fromLatin1("    int m_methodArgCount[%1];").arg(methodCount) << Qt::endl;
1333         out << QString::fromLatin1("    const int* m_methodArgTypes[%1];").arg(methodCount) << Qt::endl;
1334     }
1335     out << QStringLiteral("};") << Qt::endl;
1336     out << "" << Qt::endl;
1337 }
1338 
1339 QT_END_NAMESPACE
1340