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

source code of qtremoteobjects/tools/repc/repcodegenerator.cpp