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 ¶m : 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 ¶m : func.params) {
144 ret += param.name.toLatin1();
145 ret += typeData(param.type, specialTypes);
146 ret += QByteArray(reinterpret_cast<const char *>(¶m.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 ¶mNames = 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