Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
generator.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
3// Copyright (C) 2018 Intel Corporation.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
5
6#include "generator.h"
7#include "cbordevice.h"
9#include "utils.h"
10#include <QtCore/qmetatype.h>
11#include <QtCore/qjsondocument.h>
12#include <QtCore/qjsonobject.h>
13#include <QtCore/qjsonvalue.h>
14#include <QtCore/qjsonarray.h>
15#include <QtCore/qplugin.h>
16#include <QtCore/qstringview.h>
17#include <QtCore/qtmocconstants.h>
18
19#include <math.h>
20#include <stdio.h>
21
22#include <private/qmetaobject_p.h> //for the flags.
23#include <private/qplugin_p.h> //for the flags.
24
25QT_BEGIN_NAMESPACE
26
27using namespace QtMiscUtils;
28
29uint nameToBuiltinType(const QByteArray &name)
30{
31 if (name.isEmpty())
32 return 0;
33
34 uint tp = qMetaTypeTypeInternal(name.constData());
35 return tp < uint(QMetaType::User) ? tp : uint(QMetaType::UnknownType);
36}
37
38/*
39 Returns \c true if the type is a built-in type.
40*/
41bool isBuiltinType(const QByteArray &type)
42 {
43 int id = qMetaTypeTypeInternal(type.constData());
44 if (id == QMetaType::UnknownType)
45 return false;
46 return (id < QMetaType::User);
47}
48
49static const char *metaTypeEnumValueString(int type)
50 {
51#define RETURN_METATYPENAME_STRING(MetaTypeName, MetaTypeId, RealType)
52 case QMetaType::MetaTypeName: return #MetaTypeName;
53
54 switch (type) {
55QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING)
56 }
57#undef RETURN_METATYPENAME_STRING
58 return nullptr;
59 }
60
61 Generator::Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes,
62 const QHash<QByteArray, QByteArray> &knownQObjectClasses,
63 const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile,
64 bool requireCompleteTypes)
65 : parser(moc),
66 out(outfile),
67 cdef(classDef),
71 requireCompleteTypes(requireCompleteTypes)
72 {
73 if (cdef->superclassList.size())
74 purestSuperClass = cdef->superclassList.constFirst().classname;
75}
76
77static inline qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i)
78{
79 if (s.at(i) != '\\' || i >= s.size() - 1)
80 return 1;
81 const qsizetype startPos = i;
82 ++i;
83 char ch = s.at(i);
84 if (ch == 'x') {
85 ++i;
86 while (i < s.size() && isHexDigit(s.at(i)))
87 ++i;
88 } else if (isOctalDigit(ch)) {
89 while (i < startPos + 4
90 && i < s.size()
91 && isOctalDigit(s.at(i))) {
92 ++i;
93 }
94 } else { // single character escape sequence
95 i = qMin(i + 1, s.size());
96 }
97 return i - startPos;
98}
99
100// Prints \a s to \a out, breaking it into lines of at most ColumnWidth. The
101// opening and closing quotes are NOT included (it's up to the caller).
102static void printStringWithIndentation(FILE *out, const QByteArray &s)
103{
104 static constexpr int ColumnWidth = 72;
105 const qsizetype len = s.size();
106 qsizetype idx = 0;
107
108 do {
109 qsizetype spanLen = qMin(ColumnWidth - 2, len - idx);
110 // don't cut escape sequences at the end of a line
111 const qsizetype backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1);
112 if (backSlashPos >= idx) {
113 const qsizetype escapeLen = lengthOfEscapeSequence(s, backSlashPos);
114 spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, len - idx);
115 }
116 fprintf(out, "\n \"%.*s\"", int(spanLen), s.constData() + idx);
117 idx += spanLen;
118 } while (idx < len);
119}
120
121void Generator::strreg(const QByteArray &s)
122{
123 if (!strings.contains(s))
124 strings.append(s);
125}
126
127int Generator::stridx(const QByteArray &s)
128{
129 int i = int(strings.indexOf(s));
130 Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings");
131 return i;
132}
133
134// Returns the sum of all parameters (including return type) for the given
135// \a list of methods. This is needed for calculating the size of the methods'
136// parameter type/name meta-data.
138{
139 int sum = 0;
140 for (const FunctionDef &def : list)
141 sum += int(def.arguments.size()) + 1; // +1 for return type
142 return sum;
143}
144
145bool Generator::registerableMetaType(const QByteArray &propertyType)
146{
147 if (metaTypes.contains(propertyType))
148 return true;
149
150 if (propertyType.endsWith('*')) {
151 QByteArray objectPointerType = propertyType;
152 // The objects container stores class names, such as 'QState', 'QLabel' etc,
153 // not 'QState*', 'QLabel*'. The propertyType does contain the '*', so we need
154 // to chop it to find the class type in the known QObjects list.
155 objectPointerType.chop(1);
156 if (knownQObjectClasses.contains(objectPointerType))
157 return true;
158 }
159
160 static const QList<QByteArray> smartPointers = QList<QByteArray>()
161#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER
162 QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER)
163#undef STREAM_SMART_POINTER
164 ;
165
166 for (const QByteArray &smartPointer : smartPointers) {
167 QByteArray ba = smartPointer + "<";
168 if (propertyType.startsWith(ba) && !propertyType.endsWith("&"))
169 return knownQObjectClasses.contains(propertyType.mid(smartPointer.size() + 1, propertyType.size() - smartPointer.size() - 1 - 1));
170 }
171
172 static const QList<QByteArray> oneArgTemplates = QList<QByteArray>()
173#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME
174 QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE)
175#undef STREAM_1ARG_TEMPLATE
176 ;
177 for (const QByteArray &oneArgTemplateType : oneArgTemplates) {
178 const QByteArray ba = oneArgTemplateType + "<";
179 if (propertyType.startsWith(ba) && propertyType.endsWith(">")) {
180 const qsizetype argumentSize = propertyType.size() - ba.size()
181 // The closing '>'
182 - 1
183 // templates inside templates have an extra whitespace char to strip.
184 - (propertyType.at(propertyType.size() - 2) == ' ' ? 1 : 0 );
185 const QByteArray templateArg = propertyType.sliced(ba.size(), argumentSize);
186 return isBuiltinType(templateArg) || registerableMetaType(templateArg);
187 }
188 }
189 return false;
190}
191
192/* returns \c true if name and qualifiedName refers to the same name.
193 * If qualified name is "A::B::C", it returns \c true for "C", "B::C" or "A::B::C" */
194static bool qualifiedNameEquals(const QByteArray &qualifiedName, const QByteArray &name)
195{
196 if (qualifiedName == name)
197 return true;
198 const qsizetype index = qualifiedName.indexOf("::");
199 if (index == -1)
200 return false;
201 return qualifiedNameEquals(qualifiedName.mid(index+2), name);
202}
203
204static QByteArray generateQualifiedClassNameIdentifier(const QByteArray &identifier)
205{
206 QByteArray qualifiedClassNameIdentifier = identifier;
207
208 // Remove ':'s in the name, but be sure not to create any illegal
209 // identifiers in the process. (Don't replace with '_', because
210 // that will create problems with things like NS_::_class.)
211 qualifiedClassNameIdentifier.replace("::", "SCOPE");
212
213 // Also, avoid any leading/trailing underscores (we'll concatenate
214 // the generated name with other prefixes/suffixes, and these latter
215 // may already include an underscore, leading to two underscores)
216 qualifiedClassNameIdentifier = "CLASS" + qualifiedClassNameIdentifier + "ENDCLASS";
217 return qualifiedClassNameIdentifier;
218}
219
221{
222 bool isQObject = (cdef->classname == "QObject");
223 bool isConstructible = !cdef->constructorList.isEmpty();
224
225 // filter out undeclared enumerators and sets
226 {
227 QList<EnumDef> enumList;
228 for (EnumDef def : std::as_const(cdef->enumList)) {
229 if (cdef->enumDeclarations.contains(def.name)) {
230 enumList += def;
231 }
232 def.enumName = def.name;
233 QByteArray alias = cdef->flagAliases.value(def.name);
234 if (cdef->enumDeclarations.contains(alias)) {
235 def.name = alias;
236 enumList += def;
237 }
238 }
239 cdef->enumList = enumList;
240 }
241
242//
243// Register all strings used in data section
244//
245 strreg(cdef->qualified);
246 registerClassInfoStrings();
247 registerFunctionStrings(cdef->signalList);
248 registerFunctionStrings(cdef->slotList);
249 registerFunctionStrings(cdef->methodList);
250 registerFunctionStrings(cdef->constructorList);
251 registerByteArrayVector(cdef->nonClassSignalList);
252 registerPropertyStrings();
253 registerEnumStrings();
254
255 const bool hasStaticMetaCall =
256 (cdef->hasQObject || !cdef->methodList.isEmpty()
257 || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty());
258
259 const QByteArray qualifiedClassNameIdentifier = generateQualifiedClassNameIdentifier(cdef->qualified);
260
261 // ensure the qt_meta_stringdata_XXXX_t type is local
262 fprintf(out, "namespace {\n");
263
264//
265// Build the strings using QtMocHelpers::StringData
266//
267
268 fprintf(out, "\n#ifdef QT_MOC_HAS_STRINGDATA\n"
269 "struct qt_meta_stringdata_%s_t {};\n"
270 "constexpr auto qt_meta_stringdata_%s = QtMocHelpers::stringData(",
271 qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData());
272 {
273 char comma = 0;
274 for (const QByteArray &str : strings) {
275 if (comma)
276 fputc(comma, out);
277 printStringWithIndentation(out, str);
278 comma = ',';
279 }
280 }
281 fprintf(out, "\n);\n"
282 "#else // !QT_MOC_HAS_STRINGDATA\n");
283 fprintf(out, "#error \"qtmochelpers.h not found or too old.\"\n");
284 fprintf(out, "#endif // !QT_MOC_HAS_STRINGDATA\n");
285 fprintf(out, "} // unnamed namespace\n\n");
286
287//
288// build the data array
289//
290
291 int index = MetaObjectPrivateFieldCount;
292 fprintf(out, "Q_CONSTINIT static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData());
293 fprintf(out, "\n // content:\n");
294 fprintf(out, " %4d, // revision\n", int(QtMocConstants::OutputRevision));
295 fprintf(out, " %4d, // classname\n", stridx(cdef->qualified));
296 fprintf(out, " %4d, %4d, // classinfo\n", int(cdef->classInfoList.size()), int(cdef->classInfoList.size() ? index : 0));
297 index += cdef->classInfoList.size() * 2;
298
299 qsizetype methodCount = 0;
300 if (qAddOverflow(cdef->signalList.size(), cdef->slotList.size(), &methodCount)
301 || qAddOverflow(cdef->methodList.size(), methodCount, &methodCount)) {
302 parser->error("internal limit exceeded: the total number of member functions"
303 " (including signals and slots) is too big.");
304 }
305
306 fprintf(out, " %4" PRIdQSIZETYPE ", %4d, // methods\n", methodCount, methodCount ? index : 0);
307 index += methodCount * QMetaObjectPrivate::IntsPerMethod;
308 if (cdef->revisionedMethods)
309 index += methodCount;
310 int paramsIndex = index;
311 int totalParameterCount = aggregateParameterCount(cdef->signalList)
312 + aggregateParameterCount(cdef->slotList)
313 + aggregateParameterCount(cdef->methodList)
314 + aggregateParameterCount(cdef->constructorList);
315 index += totalParameterCount * 2 // types and parameter names
316 - methodCount // return "parameters" don't have names
317 - int(cdef->constructorList.size()); // "this" parameters don't have names
318
319 fprintf(out, " %4d, %4d, // properties\n", int(cdef->propertyList.size()), int(cdef->propertyList.size() ? index : 0));
320 index += cdef->propertyList.size() * QMetaObjectPrivate::IntsPerProperty;
321 fprintf(out, " %4d, %4d, // enums/sets\n", int(cdef->enumList.size()), cdef->enumList.size() ? index : 0);
322
323 int enumsIndex = index;
324 for (const EnumDef &def : std::as_const(cdef->enumList))
325 index += QMetaObjectPrivate::IntsPerEnum + (def.values.size() * 2);
326
327 fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.size()) : 0,
328 isConstructible ? index : 0);
329
330 int flags = 0;
331 if (cdef->hasQGadget || cdef->hasQNamespace) {
332 // Ideally, all the classes could have that flag. But this broke classes generated
333 // by qdbusxml2cpp which generate code that require that we call qt_metacall for properties
334 flags |= PropertyAccessInStaticMetaCall;
335 }
336 fprintf(out, " %4d, // flags\n", flags);
337 fprintf(out, " %4d, // signalCount\n", int(cdef->signalList.size()));
338
339
340//
341// Build classinfo array
342//
343 generateClassInfos();
344
345 qsizetype propEnumCount = 0;
346 // all property metatypes + all enum metatypes + 1 for the type of the current class itself
347 if (qAddOverflow(cdef->propertyList.size(), cdef->enumList.size(), &propEnumCount)
348 || qAddOverflow(propEnumCount, qsizetype(1), &propEnumCount)
349 || propEnumCount >= std::numeric_limits<int>::max()) {
350 parser->error("internal limit exceeded: number of property and enum metatypes is too big.");
351 }
352 int initialMetaTypeOffset = int(propEnumCount);
353
354//
355// Build signals array first, otherwise the signal indices would be wrong
356//
357 generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex, initialMetaTypeOffset);
358
359//
360// Build slots array
361//
362 generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex, initialMetaTypeOffset);
363
364//
365// Build method array
366//
367 generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex, initialMetaTypeOffset);
368
369//
370// Build method version arrays
371//
372 if (cdef->revisionedMethods) {
373 generateFunctionRevisions(cdef->signalList, "signal");
374 generateFunctionRevisions(cdef->slotList, "slot");
375 generateFunctionRevisions(cdef->methodList, "method");
376 }
377
378//
379// Build method parameters array
380//
381 generateFunctionParameters(cdef->signalList, "signal");
382 generateFunctionParameters(cdef->slotList, "slot");
383 generateFunctionParameters(cdef->methodList, "method");
384 if (isConstructible)
385 generateFunctionParameters(cdef->constructorList, "constructor");
386
387//
388// Build property array
389//
390 generateProperties();
391
392//
393// Build enums array
394//
395 generateEnums(enumsIndex);
396
397//
398// Build constructors array
399//
400 if (isConstructible)
401 generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex, initialMetaTypeOffset);
402
403//
404// Terminate data array
405//
406 fprintf(out, "\n 0 // eod\n};\n\n");
407
408//
409// Build extra array
410//
411 QList<QByteArray> extraList;
412 QMultiHash<QByteArray, QByteArray> knownExtraMetaObject(knownGadgets);
413 knownExtraMetaObject.unite(knownQObjectClasses);
414
415 for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
416 if (isBuiltinType(p.type))
417 continue;
418
419 if (p.type.contains('*') || p.type.contains('<') || p.type.contains('>'))
420 continue;
421
422 const qsizetype s = p.type.lastIndexOf("::");
423 if (s <= 0)
424 continue;
425
426 QByteArray unqualifiedScope = p.type.left(s);
427
428 // The scope may be a namespace for example, so it's only safe to include scopes that are known QObjects (QTBUG-2151)
429 QMultiHash<QByteArray, QByteArray>::ConstIterator scopeIt;
430
431 QByteArray thisScope = cdef->qualified;
432 do {
433 const qsizetype s = thisScope.lastIndexOf("::");
434 thisScope = thisScope.left(s);
435 QByteArray currentScope = thisScope.isEmpty() ? unqualifiedScope : thisScope + "::" + unqualifiedScope;
436 scopeIt = knownExtraMetaObject.constFind(currentScope);
437 } while (!thisScope.isEmpty() && scopeIt == knownExtraMetaObject.constEnd());
438
439 if (scopeIt == knownExtraMetaObject.constEnd())
440 continue;
441
442 const QByteArray &scope = *scopeIt;
443
444 if (scope == "Qt")
445 continue;
446 if (qualifiedNameEquals(cdef->qualified, scope))
447 continue;
448
449 if (!extraList.contains(scope))
450 extraList += scope;
451 }
452
453 // QTBUG-20639 - Accept non-local enums for QML signal/slot parameters.
454 // Look for any scoped enum declarations, and add those to the list
455 // of extra/related metaobjects for this object.
456 for (auto it = cdef->enumDeclarations.keyBegin(),
457 end = cdef->enumDeclarations.keyEnd(); it != end; ++it) {
458 const QByteArray &enumKey = *it;
459 const qsizetype s = enumKey.lastIndexOf("::");
460 if (s > 0) {
461 QByteArray scope = enumKey.left(s);
462 if (scope != "Qt" && !qualifiedNameEquals(cdef->qualified, scope) && !extraList.contains(scope))
463 extraList += scope;
464 }
465 }
466
467//
468// Generate meta object link to parent meta objects
469//
470
471 if (!extraList.isEmpty()) {
472 fprintf(out, "Q_CONSTINIT static const QMetaObject::SuperData qt_meta_extradata_%s[] = {\n",
473 qualifiedClassNameIdentifier.constData());
474 for (const QByteArray &ba : std::as_const(extraList))
475 fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", ba.constData());
476
477 fprintf(out, " nullptr\n};\n\n");
478 }
479
480//
481// Finally create and initialize the static meta object
482//
483 fprintf(out, "Q_CONSTINIT const QMetaObject %s::staticMetaObject = { {\n",
484 cdef->qualified.constData());
485
486 if (isQObject)
487 fprintf(out, " nullptr,\n");
488 else if (cdef->superclassList.size() && !cdef->hasQGadget && !cdef->hasQNamespace) // for qobject, we know the super class must have a static metaobject
489 fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", purestSuperClass.constData());
490 else if (cdef->superclassList.size()) // for gadgets we need to query at compile time for it
491 fprintf(out, " QtPrivate::MetaObjectForType<%s>::value,\n", purestSuperClass.constData());
492 else
493 fprintf(out, " nullptr,\n");
494 fprintf(out, " qt_meta_stringdata_%s.offsetsAndSizes,\n"
495 " qt_meta_data_%s,\n", qualifiedClassNameIdentifier.constData(),
496 qualifiedClassNameIdentifier.constData());
497 if (hasStaticMetaCall)
498 fprintf(out, " qt_static_metacall,\n");
499 else
500 fprintf(out, " nullptr,\n");
501
502 if (extraList.isEmpty())
503 fprintf(out, " nullptr,\n");
504 else
505 fprintf(out, " qt_meta_extradata_%s,\n", qualifiedClassNameIdentifier.constData());
506
507 const char *comma = "";
508 const bool requireCompleteness = requireCompleteTypes || cdef->requireCompleteMethodTypes;
509 auto stringForType = [requireCompleteness](const QByteArray &type, bool forceComplete) -> QByteArray {
510 const char *forceCompleteType = forceComplete ? ", std::true_type>" : ", std::false_type>";
511 if (requireCompleteness)
512 return type;
513 return "QtPrivate::TypeAndForceComplete<" % type % forceCompleteType;
514 };
515 if (!requireCompleteness) {
516 fprintf(out, " qt_incomplete_metaTypeArray<qt_meta_stringdata_%s_t", qualifiedClassNameIdentifier.constData());
517 comma = ",";
518 } else {
519 fprintf(out, " qt_metaTypeArray<");
520 }
521 // metatypes for properties
522 for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
523 fprintf(out, "%s\n // property '%s'\n %s",
524 comma, p.name.constData(), stringForType(p.type, true).constData());
525 comma = ",";
526 }
527
528 // metatypes for enums
529 for (const EnumDef &e : std::as_const(cdef->enumList)) {
530 fprintf(out, "%s\n // enum '%s'\n %s",
531 comma, e.name.constData(), stringForType(e.qualifiedType(cdef), true).constData());
532 comma = ",";
533 }
534
535 // type name for the Q_OJBECT/GADGET itself, void for namespaces
536 auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void";
537 fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s",
538 comma, stringForType(ownType, true).constData());
539 comma = ",";
540
541 // metatypes for all exposed methods
542 // because we definitely printed something above, this section doesn't need comma control
543 const auto allMethods = {&cdef->signalList, &cdef->slotList, &cdef->methodList};
544 for (const QList<FunctionDef> *methodContainer : allMethods) {
545 for (const FunctionDef &fdef : *methodContainer) {
546 fprintf(out, ",\n // method '%s'\n %s",
547 fdef.name.constData(), stringForType(fdef.type.name, false).constData());
548 for (const auto &argument: fdef.arguments)
549 fprintf(out, ",\n %s", stringForType(argument.type.name, false).constData());
550 }
551 }
552
553 // but constructors have no return types, so this needs comma control again
554 for (const FunctionDef &fdef : std::as_const(cdef->constructorList)) {
555 if (fdef.arguments.isEmpty())
556 continue;
557
558 fprintf(out, "%s\n // constructor '%s'", comma, fdef.name.constData());
559 comma = "";
560 for (const auto &argument: fdef.arguments) {
561 fprintf(out, "%s\n %s", comma,
562 stringForType(argument.type.name, false).constData());
563 comma = ",";
564 }
565 }
566 fprintf(out, "\n >,\n");
567
568 fprintf(out, " nullptr\n} };\n\n");
569
570//
571// Generate internal qt_static_metacall() function
572//
573 if (hasStaticMetaCall)
574 generateStaticMetacall();
575
576 if (!cdef->hasQObject)
577 return;
578
579 fprintf(out, "\nconst QMetaObject *%s::metaObject() const\n{\n return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;\n}\n",
580 cdef->qualified.constData());
581
582
583//
584// Generate smart cast function
585//
586 fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData());
587 fprintf(out, " if (!_clname) return nullptr;\n");
588 fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n"
589 " return static_cast<void*>(this);\n",
590 qualifiedClassNameIdentifier.constData());
591
592 // for all superclasses but the first one
593 if (cdef->superclassList.size() > 1) {
594 auto it = cdef->superclassList.cbegin() + 1;
595 const auto end = cdef->superclassList.cend();
596 for (; it != end; ++it) {
597 if (it->access == FunctionDef::Private)
598 continue;
599 const char *cname = it->classname.constData();
600 fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(this);\n",
601 cname, cname);
602 }
603 }
604
605 for (const QList<ClassDef::Interface> &iface : std::as_const(cdef->interfaceList)) {
606 for (qsizetype j = 0; j < iface.size(); ++j) {
607 fprintf(out, " if (!strcmp(_clname, %s))\n return ", iface.at(j).interfaceId.constData());
608 for (qsizetype k = j; k >= 0; --k)
609 fprintf(out, "static_cast< %s*>(", iface.at(k).className.constData());
610 fprintf(out, "this%s;\n", QByteArray(j + 1, ')').constData());
611 }
612 }
613 if (!purestSuperClass.isEmpty() && !isQObject) {
614 QByteArray superClass = purestSuperClass;
615 fprintf(out, " return %s::qt_metacast(_clname);\n", superClass.constData());
616 } else {
617 fprintf(out, " return nullptr;\n");
618 }
619 fprintf(out, "}\n");
620
621//
622// Generate internal qt_metacall() function
623//
624 generateMetacall();
625
626//
627// Generate internal signal functions
628//
629 for (int signalindex = 0; signalindex < int(cdef->signalList.size()); ++signalindex)
630 generateSignal(&cdef->signalList.at(signalindex), signalindex);
631
632//
633// Generate plugin meta data
634//
635 generatePluginMetaData();
636
637//
638// Generate function to make sure the non-class signals exist in the parent classes
639//
640 if (!cdef->nonClassSignalList.isEmpty()) {
641 fprintf(out, "namespace CheckNotifySignalValidity_%s {\n", qualifiedClassNameIdentifier.constData());
642 for (const QByteArray &nonClassSignal : std::as_const(cdef->nonClassSignalList)) {
643 const auto propertyIt = std::find_if(cdef->propertyList.constBegin(),
644 cdef->propertyList.constEnd(),
645 [&nonClassSignal](const PropertyDef &p) {
646 return nonClassSignal == p.notify;
647 });
648 // must find something, otherwise checkProperties wouldn't have inserted an entry into nonClassSignalList
649 Q_ASSERT(propertyIt != cdef->propertyList.constEnd());
650 fprintf(out, "template<typename T> using has_nullary_%s = decltype(std::declval<T>().%s());\n",
651 nonClassSignal.constData(),
652 nonClassSignal.constData());
653 const auto &propertyType = propertyIt->type;
654 fprintf(out, "template<typename T> using has_unary_%s = decltype(std::declval<T>().%s(std::declval<%s>()));\n",
655 nonClassSignal.constData(),
656 nonClassSignal.constData(),
657 propertyType.constData());
658 fprintf(out, "static_assert(qxp::is_detected_v<has_nullary_%s, %s> || qxp::is_detected_v<has_unary_%s, %s>,\n"
659 " \"NOTIFY signal %s does not exist in class (or is private in its parent)\");\n",
660 nonClassSignal.constData(), cdef->qualified.constData(),
661 nonClassSignal.constData(), cdef->qualified.constData(),
662 nonClassSignal.constData());
663 }
664 fprintf(out, "}\n");
665 }
666}
667
668
669void Generator::registerClassInfoStrings()
670{
671 for (const ClassInfoDef &c : std::as_const(cdef->classInfoList)) {
672 strreg(c.name);
673 strreg(c.value);
674 }
675}
676
677void Generator::generateClassInfos()
678{
679 if (cdef->classInfoList.isEmpty())
680 return;
681
682 fprintf(out, "\n // classinfo: key, value\n");
683
684 for (const ClassInfoDef &c : std::as_const(cdef->classInfoList))
685 fprintf(out, " %4d, %4d,\n", stridx(c.name), stridx(c.value));
686}
687
688void Generator::registerFunctionStrings(const QList<FunctionDef> &list)
689{
690 for (const FunctionDef &f : list) {
691 strreg(f.name);
692 if (!isBuiltinType(f.normalizedType))
693 strreg(f.normalizedType);
694 strreg(f.tag);
695
696 for (const ArgumentDef &a : f.arguments) {
697 if (!isBuiltinType(a.normalizedType))
698 strreg(a.normalizedType);
699 strreg(a.name);
700 }
701 }
702}
703
704void Generator::registerByteArrayVector(const QList<QByteArray> &list)
705{
706 for (const QByteArray &ba : list)
707 strreg(ba);
708}
709
710void Generator::generateFunctions(const QList<FunctionDef> &list, const char *functype, int type,
711 int &paramsIndex, int &initialMetatypeOffset)
712{
713 if (list.isEmpty())
714 return;
715 fprintf(out, "\n // %ss: name, argc, parameters, tag, flags, initial metatype offsets\n", functype);
716
717 for (const FunctionDef &f : list) {
718 QByteArray comment;
719 uint flags = type;
720 if (f.access == FunctionDef::Private) {
721 flags |= AccessPrivate;
722 comment.append("Private");
723 } else if (f.access == FunctionDef::Public) {
724 flags |= AccessPublic;
725 comment.append("Public");
726 } else if (f.access == FunctionDef::Protected) {
727 flags |= AccessProtected;
728 comment.append("Protected");
729 }
730 if (f.isCompat) {
731 flags |= MethodCompatibility;
732 comment.append(" | MethodCompatibility");
733 }
734 if (f.wasCloned) {
735 flags |= MethodCloned;
736 comment.append(" | MethodCloned");
737 }
738 if (f.isScriptable) {
739 flags |= MethodScriptable;
740 comment.append(" | isScriptable");
741 }
742 if (f.revision > 0) {
743 flags |= MethodRevisioned;
744 comment.append(" | MethodRevisioned");
745 }
746
747 if (f.isConst) {
748 flags |= MethodIsConst;
749 comment.append(" | MethodIsConst ");
750 }
751
752 const int argc = int(f.arguments.size());
753 fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x, %4d /* %s */,\n",
754 stridx(f.name), argc, paramsIndex, stridx(f.tag), flags, initialMetatypeOffset, comment.constData());
755
756 paramsIndex += 1 + argc * 2;
757 // constructors don't have a return type
758 initialMetatypeOffset += (f.isConstructor ? 0 : 1) + argc;
759 }
760}
761
762void Generator::generateFunctionRevisions(const QList<FunctionDef> &list, const char *functype)
763{
764 if (list.size())
765 fprintf(out, "\n // %ss: revision\n", functype);
766 for (const FunctionDef &f : list)
767 fprintf(out, " %4d,\n", f.revision);
768}
769
770void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const char *functype)
771{
772 if (list.isEmpty())
773 return;
774 fprintf(out, "\n // %ss: parameters\n", functype);
775 for (const FunctionDef &f : list) {
776 fprintf(out, " ");
777
778 // Types
779 const bool allowEmptyName = f.isConstructor;
780 generateTypeInfo(f.normalizedType, allowEmptyName);
781 fputc(',', out);
782 for (const ArgumentDef &arg : f.arguments) {
783 fputc(' ', out);
784 generateTypeInfo(arg.normalizedType, allowEmptyName);
785 fputc(',', out);
786 }
787
788 // Parameter names
789 for (const ArgumentDef &arg : f.arguments)
790 fprintf(out, " %4d,", stridx(arg.name));
791
792 fprintf(out, "\n");
793 }
794}
795
796void Generator::generateTypeInfo(const QByteArray &typeName, bool allowEmptyName)
797{
798 Q_UNUSED(allowEmptyName);
799 if (isBuiltinType(typeName)) {
800 int type;
801 const char *valueString;
802 if (typeName == "qreal") {
803 type = QMetaType::UnknownType;
804 valueString = "QReal";
805 } else {
806 type = nameToBuiltinType(typeName);
807 valueString = metaTypeEnumValueString(type);
808 }
809 if (valueString) {
810 fprintf(out, "QMetaType::%s", valueString);
811 } else {
812 Q_ASSERT(type != QMetaType::UnknownType);
813 fprintf(out, "%4d", type);
814 }
815 } else {
816 Q_ASSERT(!typeName.isEmpty() || allowEmptyName);
817 fprintf(out, "0x%.8x | %d", IsUnresolvedType, stridx(typeName));
818 }
819}
820
821void Generator::registerPropertyStrings()
822{
823 for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
824 strreg(p.name);
825 if (!isBuiltinType(p.type))
826 strreg(p.type);
827 }
828}
829
830void Generator::generateProperties()
831{
832 //
833 // Create meta data
834 //
835
836 if (cdef->propertyList.size())
837 fprintf(out, "\n // properties: name, type, flags, notifyId, revision\n");
838 for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
839 uint flags = Invalid;
840 if (!isBuiltinType(p.type))
841 flags |= EnumOrFlag;
842 if (!p.member.isEmpty() && !p.constant)
843 flags |= Writable;
844 if (!p.read.isEmpty() || !p.member.isEmpty())
845 flags |= Readable;
846 if (!p.write.isEmpty()) {
847 flags |= Writable;
848 if (p.stdCppSet())
849 flags |= StdCppSet;
850 }
851
852 if (!p.reset.isEmpty())
853 flags |= Resettable;
854
855 if (p.designable != "false")
856 flags |= Designable;
857
858 if (p.scriptable != "false")
859 flags |= Scriptable;
860
861 if (p.stored != "false")
862 flags |= Stored;
863
864 if (p.user != "false")
865 flags |= User;
866
867 if (p.constant)
868 flags |= Constant;
869 if (p.final)
870 flags |= Final;
871 if (p.required)
872 flags |= Required;
873
874 if (!p.bind.isEmpty())
875 flags |= Bindable;
876
877 fprintf(out, " %4d, ", stridx(p.name));
878 generateTypeInfo(p.type);
879 int notifyId = p.notifyId;
880 if (p.notifyId < -1) {
881 // signal is in parent class
882 const int indexInStrings = int(strings.indexOf(p.notify));
883 notifyId = indexInStrings | IsUnresolvedSignal;
884 }
885 fprintf(out, ", 0x%.8x, uint(%d), %d,\n", flags, notifyId, p.revision);
886 }
887}
888
889void Generator::registerEnumStrings()
890{
891 for (const EnumDef &e : std::as_const(cdef->enumList)) {
892 strreg(e.name);
893 if (!e.enumName.isNull())
894 strreg(e.enumName);
895 for (const QByteArray &val : e.values)
896 strreg(val);
897 }
898}
899
900void Generator::generateEnums(int index)
901{
902 if (cdef->enumDeclarations.isEmpty())
903 return;
904
905 fprintf(out, "\n // enums: name, alias, flags, count, data\n");
906 index += QMetaObjectPrivate::IntsPerEnum * cdef->enumList.size();
907 int i;
908 for (i = 0; i < cdef->enumList.size(); ++i) {
909 const EnumDef &e = cdef->enumList.at(i);
910 uint flags = e.flags | cdef->enumDeclarations.value(e.name);
911 fprintf(out, " %4d, %4d, 0x%.1x, %4d, %4d,\n",
912 stridx(e.name),
913 e.enumName.isNull() ? stridx(e.name) : stridx(e.enumName),
914 flags,
915 int(e.values.size()),
916 index);
917 index += e.values.size() * 2;
918 }
919
920 fprintf(out, "\n // enum data: key, value\n");
921 for (const EnumDef &e : std::as_const(cdef->enumList)) {
922 QByteArray prefix = cdef->qualified;
923 if (e.flags & EnumIsScoped)
924 prefix += "::" + (e.enumName.isNull() ? e.name : e.enumName);
925 for (const QByteArray &val : e.values) {
926 fprintf(out, " %4d, uint(%s::%s),\n",
927 stridx(val), prefix.constData(), val.constData());
928 }
929 }
930}
931
932void Generator::generateMetacall()
933{
934 bool isQObject = (cdef->classname == "QObject");
935
936 fprintf(out, "\nint %s::qt_metacall(QMetaObject::Call _c, int _id, void **_a)\n{\n",
937 cdef->qualified.constData());
938
939 if (!purestSuperClass.isEmpty() && !isQObject) {
940 QByteArray superClass = purestSuperClass;
941 fprintf(out, " _id = %s::qt_metacall(_c, _id, _a);\n", superClass.constData());
942 }
943
944
945 bool needElse = false;
946 QList<FunctionDef> methodList;
947 methodList += cdef->signalList;
948 methodList += cdef->slotList;
949 methodList += cdef->methodList;
950
951 // If there are no methods or properties, we will return _id anyway, so
952 // don't emit this comparison -- it is unnecessary, and it makes coverity
953 // unhappy.
954 if (methodList.size() || cdef->propertyList.size()) {
955 fprintf(out, " if (_id < 0)\n return _id;\n");
956 }
957
958 fprintf(out, " ");
959
960 if (methodList.size()) {
961 needElse = true;
962 fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n");
963 fprintf(out, " if (_id < %d)\n", int(methodList.size()));
964 fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n");
965 fprintf(out, " _id -= %d;\n }", int(methodList.size()));
966
967 fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n");
968 fprintf(out, " if (_id < %d)\n", int(methodList.size()));
969
970 if (methodsWithAutomaticTypesHelper(methodList).isEmpty())
971 fprintf(out, " *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();\n");
972 else
973 fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n");
974 fprintf(out, " _id -= %d;\n }", int(methodList.size()));
975
976 }
977
978 if (cdef->propertyList.size()) {
979 if (needElse)
980 fprintf(out, "else ");
981 fprintf(out,
982 "if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty\n"
983 " || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty\n"
984 " || _c == QMetaObject::RegisterPropertyMetaType) {\n"
985 " qt_static_metacall(this, _c, _id, _a);\n"
986 " _id -= %d;\n }", int(cdef->propertyList.size()));
987 }
988 if (methodList.size() || cdef->propertyList.size())
989 fprintf(out, "\n ");
990 fprintf(out,"return _id;\n}\n");
991}
992
993
994// ### Qt 7 (6.x?): remove
995QMultiMap<QByteArray, int> Generator::automaticPropertyMetaTypesHelper()
996{
997 QMultiMap<QByteArray, int> automaticPropertyMetaTypes;
998 for (int i = 0; i < int(cdef->propertyList.size()); ++i) {
999 const QByteArray propertyType = cdef->propertyList.at(i).type;
1000 if (registerableMetaType(propertyType) && !isBuiltinType(propertyType))
1001 automaticPropertyMetaTypes.insert(propertyType, i);
1002 }
1003 return automaticPropertyMetaTypes;
1004}
1005
1006QMap<int, QMultiMap<QByteArray, int>>
1007Generator::methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList)
1008{
1009 QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypes;
1010 for (int i = 0; i < methodList.size(); ++i) {
1011 const FunctionDef &f = methodList.at(i);
1012 for (int j = 0; j < f.arguments.size(); ++j) {
1013 const QByteArray argType = f.arguments.at(j).normalizedType;
1014 if (registerableMetaType(argType) && !isBuiltinType(argType))
1015 methodsWithAutomaticTypes[i].insert(argType, j);
1016 }
1017 }
1018 return methodsWithAutomaticTypes;
1019}
1020
1021void Generator::generateStaticMetacall()
1022{
1023 fprintf(out, "void %s::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)\n{\n",
1024 cdef->qualified.constData());
1025
1026 bool needElse = false;
1027 bool isUsed_a = false;
1028
1029 const auto generateCtorArguments = [&](int ctorindex) {
1030 const FunctionDef &f = cdef->constructorList.at(ctorindex);
1031 Q_ASSERT(!f.isPrivateSignal); // That would be a strange ctor indeed
1032 int offset = 1;
1033
1034 const auto begin = f.arguments.cbegin();
1035 const auto end = f.arguments.cend();
1036 for (auto it = begin; it != end; ++it) {
1037 const ArgumentDef &a = *it;
1038 if (it != begin)
1039 fprintf(out, ",");
1040 fprintf(out, "(*reinterpret_cast<%s>(_a[%d]))",
1041 a.typeNameForCast.constData(), offset++);
1042 }
1043 };
1044
1045 if (!cdef->constructorList.isEmpty()) {
1046 fprintf(out, " if (_c == QMetaObject::CreateInstance) {\n");
1047 fprintf(out, " switch (_id) {\n");
1048 const int ctorend = int(cdef->constructorList.size());
1049 for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) {
1050 fprintf(out, " case %d: { %s *_r = new %s(", ctorindex,
1051 cdef->classname.constData(), cdef->classname.constData());
1052 generateCtorArguments(ctorindex);
1053 fprintf(out, ");\n");
1054 fprintf(out, " if (_a[0]) *reinterpret_cast<%s**>(_a[0]) = _r; } break;\n",
1055 (cdef->hasQGadget || cdef->hasQNamespace) ? "void" : "QObject");
1056 }
1057 fprintf(out, " default: break;\n");
1058 fprintf(out, " }\n");
1059 fprintf(out, " } else if (_c == QMetaObject::ConstructInPlace) {\n");
1060 fprintf(out, " switch (_id) {\n");
1061 for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) {
1062 fprintf(out, " case %d: { new (_a[0]) %s(",
1063 ctorindex, cdef->classname.constData());
1064 generateCtorArguments(ctorindex);
1065 fprintf(out, "); } break;\n");
1066 }
1067 fprintf(out, " default: break;\n");
1068 fprintf(out, " }\n");
1069 fprintf(out, " }");
1070 needElse = true;
1071 isUsed_a = true;
1072 }
1073
1074 QList<FunctionDef> methodList;
1075 methodList += cdef->signalList;
1076 methodList += cdef->slotList;
1077 methodList += cdef->methodList;
1078
1079 if (!methodList.isEmpty()) {
1080 if (needElse)
1081 fprintf(out, " else ");
1082 else
1083 fprintf(out, " ");
1084 fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n");
1085 if (cdef->hasQObject) {
1086#ifndef QT_NO_DEBUG
1087 fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
1088#endif
1089 fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData());
1090 } else {
1091 fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData());
1092 }
1093 fprintf(out, " (void)_t;\n");
1094 fprintf(out, " switch (_id) {\n");
1095 for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) {
1096 const FunctionDef &f = methodList.at(methodindex);
1097 Q_ASSERT(!f.normalizedType.isEmpty());
1098 fprintf(out, " case %d: ", methodindex);
1099 if (f.normalizedType != "void")
1100 fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData());
1101 fprintf(out, "_t->");
1102 if (f.inPrivateClass.size())
1103 fprintf(out, "%s->", f.inPrivateClass.constData());
1104 fprintf(out, "%s(", f.name.constData());
1105 int offset = 1;
1106
1107 if (f.isRawSlot) {
1108 fprintf(out, "QMethodRawArguments{ _a }");
1109 } else {
1110 const auto begin = f.arguments.cbegin();
1111 const auto end = f.arguments.cend();
1112 for (auto it = begin; it != end; ++it) {
1113 const ArgumentDef &a = *it;
1114 if (it != begin)
1115 fprintf(out, ",");
1116 fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++);
1117 isUsed_a = true;
1118 }
1119 if (f.isPrivateSignal) {
1120 if (!f.arguments.isEmpty())
1121 fprintf(out, ", ");
1122 fprintf(out, "%s", "QPrivateSignal()");
1123 }
1124 }
1125 fprintf(out, ");");
1126 if (f.normalizedType != "void") {
1127 fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = std::move(_r); } ",
1128 noRef(f.normalizedType).constData());
1129 isUsed_a = true;
1130 }
1131 fprintf(out, " break;\n");
1132 }
1133 fprintf(out, " default: ;\n");
1134 fprintf(out, " }\n");
1135 fprintf(out, " }");
1136 needElse = true;
1137
1138 QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypes = methodsWithAutomaticTypesHelper(methodList);
1139
1140 if (!methodsWithAutomaticTypes.isEmpty()) {
1141 fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n");
1142 fprintf(out, " switch (_id) {\n");
1143 fprintf(out, " default: *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType(); break;\n");
1144 QMap<int, QMultiMap<QByteArray, int> >::const_iterator it = methodsWithAutomaticTypes.constBegin();
1145 const QMap<int, QMultiMap<QByteArray, int> >::const_iterator end = methodsWithAutomaticTypes.constEnd();
1146 for ( ; it != end; ++it) {
1147 fprintf(out, " case %d:\n", it.key());
1148 fprintf(out, " switch (*reinterpret_cast<int*>(_a[1])) {\n");
1149 fprintf(out, " default: *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType(); break;\n");
1150 auto jt = it->begin();
1151 const auto jend = it->end();
1152 while (jt != jend) {
1153 fprintf(out, " case %d:\n", jt.value());
1154 const QByteArray &lastKey = jt.key();
1155 ++jt;
1156 if (jt == jend || jt.key() != lastKey)
1157 fprintf(out, " *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType::fromType< %s >(); break;\n", lastKey.constData());
1158 }
1159 fprintf(out, " }\n");
1160 fprintf(out, " break;\n");
1161 }
1162 fprintf(out, " }\n");
1163 fprintf(out, " }");
1164 isUsed_a = true;
1165 }
1166
1167 }
1168 if (!cdef->signalList.isEmpty()) {
1169 Q_ASSERT(needElse); // if there is signal, there was method.
1170 fprintf(out, " else if (_c == QMetaObject::IndexOfMethod) {\n");
1171 fprintf(out, " int *result = reinterpret_cast<int *>(_a[0]);\n");
1172 bool anythingUsed = false;
1173 for (int methodindex = 0; methodindex < int(cdef->signalList.size()); ++methodindex) {
1174 const FunctionDef &f = cdef->signalList.at(methodindex);
1175 if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic)
1176 continue;
1177 anythingUsed = true;
1178 fprintf(out, " {\n");
1179 fprintf(out, " using _t = %s (%s::*)(",f.type.rawName.constData() , cdef->classname.constData());
1180
1181 const auto begin = f.arguments.cbegin();
1182 const auto end = f.arguments.cend();
1183 for (auto it = begin; it != end; ++it) {
1184 const ArgumentDef &a = *it;
1185 if (it != begin)
1186 fprintf(out, ", ");
1187 fprintf(out, "%s", QByteArray(a.type.name + ' ' + a.rightType).constData());
1188 }
1189 if (f.isPrivateSignal) {
1190 if (!f.arguments.isEmpty())
1191 fprintf(out, ", ");
1192 fprintf(out, "%s", "QPrivateSignal");
1193 }
1194 if (f.isConst)
1195 fprintf(out, ") const;\n");
1196 else
1197 fprintf(out, ");\n");
1198 fprintf(out, " if (_t _q_method = &%s::%s; *reinterpret_cast<_t *>(_a[1]) == _q_method) {\n",
1199 cdef->classname.constData(), f.name.constData());
1200 fprintf(out, " *result = %d;\n", methodindex);
1201 fprintf(out, " return;\n");
1202 fprintf(out, " }\n }\n");
1203 }
1204 if (!anythingUsed)
1205 fprintf(out, " (void)result;\n");
1206 fprintf(out, " }");
1207 needElse = true;
1208 }
1209
1210 const QMultiMap<QByteArray, int> automaticPropertyMetaTypes = automaticPropertyMetaTypesHelper();
1211
1212 if (!automaticPropertyMetaTypes.isEmpty()) {
1213 if (needElse)
1214 fprintf(out, " else ");
1215 else
1216 fprintf(out, " ");
1217 fprintf(out, "if (_c == QMetaObject::RegisterPropertyMetaType) {\n");
1218 fprintf(out, " switch (_id) {\n");
1219 fprintf(out, " default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n");
1220 auto it = automaticPropertyMetaTypes.begin();
1221 const auto end = automaticPropertyMetaTypes.end();
1222 while (it != end) {
1223 fprintf(out, " case %d:\n", it.value());
1224 const QByteArray &lastKey = it.key();
1225 ++it;
1226 if (it == end || it.key() != lastKey)
1227 fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", lastKey.constData());
1228 }
1229 fprintf(out, " }\n");
1230 fprintf(out, " } ");
1231 isUsed_a = true;
1232 needElse = true;
1233 }
1234
1235 if (!cdef->propertyList.empty()) {
1236 bool needGet = false;
1237 bool needTempVarForGet = false;
1238 bool needSet = false;
1239 bool needReset = false;
1240 bool hasBindableProperties = false;
1241 for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
1242 needGet |= !p.read.isEmpty() || !p.member.isEmpty();
1243 if (!p.read.isEmpty() || !p.member.isEmpty())
1244 needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec
1245 && p.gspec != PropertyDef::ReferenceSpec);
1246
1247 needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant);
1248 needReset |= !p.reset.isEmpty();
1249 hasBindableProperties |= !p.bind.isEmpty();
1250 }
1251 if (needElse)
1252 fprintf(out, " else ");
1253 fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n");
1254
1255 auto setupMemberAccess = [this]() {
1256 if (cdef->hasQObject) {
1257#ifndef QT_NO_DEBUG
1258 fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
1259#endif
1260 fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData());
1261 } else {
1262 fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData());
1263 }
1264 fprintf(out, " (void)_t;\n");
1265 };
1266
1267 if (needGet) {
1268 setupMemberAccess();
1269 if (needTempVarForGet)
1270 fprintf(out, " void *_v = _a[0];\n");
1271 fprintf(out, " switch (_id) {\n");
1272 for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
1273 const PropertyDef &p = cdef->propertyList.at(propindex);
1274 if (p.read.isEmpty() && p.member.isEmpty())
1275 continue;
1276 QByteArray prefix = "_t->";
1277 if (p.inPrivateClass.size()) {
1278 prefix += p.inPrivateClass + "->";
1279 }
1280
1282 fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(%s%s())); break;\n",
1283 propindex, prefix.constData(), p.read.constData());
1285 fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(&%s%s())); break;\n",
1286 propindex, prefix.constData(), p.read.constData());
1287 else if (auto eflags = cdef->enumDeclarations.value(p.type); eflags & EnumIsFlag)
1288 fprintf(out, " case %d: *reinterpret_cast<int*>(_v) = QFlag(%s%s()); break;\n",
1289 propindex, prefix.constData(), p.read.constData());
1290 else if (p.read == "default")
1291 fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s().value(); break;\n",
1292 propindex, p.type.constData(), prefix.constData(), p.bind.constData());
1293 else if (!p.read.isEmpty())
1294 fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n",
1295 propindex, p.type.constData(), prefix.constData(), p.read.constData());
1296 else
1297 fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n",
1298 propindex, p.type.constData(), prefix.constData(), p.member.constData());
1299 }
1300 fprintf(out, " default: break;\n");
1301 fprintf(out, " }\n");
1302 }
1303
1304 fprintf(out, " }");
1305
1306 fprintf(out, " else ");
1307 fprintf(out, "if (_c == QMetaObject::WriteProperty) {\n");
1308
1309 if (needSet) {
1310 setupMemberAccess();
1311 fprintf(out, " void *_v = _a[0];\n");
1312 fprintf(out, " switch (_id) {\n");
1313 for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
1314 const PropertyDef &p = cdef->propertyList.at(propindex);
1315 if (p.constant)
1316 continue;
1317 if (p.write.isEmpty() && p.member.isEmpty())
1318 continue;
1319 QByteArray prefix = "_t->";
1320 if (p.inPrivateClass.size()) {
1321 prefix += p.inPrivateClass + "->";
1322 }
1323 if (auto eflags = cdef->enumDeclarations.value(p.type); eflags & EnumIsFlag) {
1324 fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast<int*>(_v))); break;\n",
1325 propindex, prefix.constData(), p.write.constData());
1326 } else if (p.write == "default") {
1327 fprintf(out, " case %d: {\n", propindex);
1328 fprintf(out, " %s%s().setValue(*reinterpret_cast< %s*>(_v));\n",
1329 prefix.constData(), p.bind.constData(), p.type.constData());
1330 fprintf(out, " break;\n");
1331 fprintf(out, " }\n");
1332 } else if (!p.write.isEmpty()) {
1333 fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n",
1334 propindex, prefix.constData(), p.write.constData(), p.type.constData());
1335 } else {
1336 fprintf(out, " case %d:\n", propindex);
1337 fprintf(out, " if (%s%s != *reinterpret_cast< %s*>(_v)) {\n",
1338 prefix.constData(), p.member.constData(), p.type.constData());
1339 fprintf(out, " %s%s = *reinterpret_cast< %s*>(_v);\n",
1340 prefix.constData(), p.member.constData(), p.type.constData());
1341 if (!p.notify.isEmpty() && p.notifyId > -1) {
1342 const FunctionDef &f = cdef->signalList.at(p.notifyId);
1343 if (f.arguments.size() == 0)
1344 fprintf(out, " Q_EMIT _t->%s();\n", p.notify.constData());
1345 else if (f.arguments.size() == 1 && f.arguments.at(0).normalizedType == p.type)
1346 fprintf(out, " Q_EMIT _t->%s(%s%s);\n",
1347 p.notify.constData(), prefix.constData(), p.member.constData());
1348 } else if (!p.notify.isEmpty() && p.notifyId < -1) {
1349 fprintf(out, " Q_EMIT _t->%s();\n", p.notify.constData());
1350 }
1351 fprintf(out, " }\n");
1352 fprintf(out, " break;\n");
1353 }
1354 }
1355 fprintf(out, " default: break;\n");
1356 fprintf(out, " }\n");
1357 }
1358
1359 fprintf(out, " }");
1360
1361 fprintf(out, " else ");
1362 fprintf(out, "if (_c == QMetaObject::ResetProperty) {\n");
1363 if (needReset) {
1364 setupMemberAccess();
1365 fprintf(out, " switch (_id) {\n");
1366 for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
1367 const PropertyDef &p = cdef->propertyList.at(propindex);
1368 if (p.reset.isEmpty())
1369 continue;
1370 QByteArray prefix = "_t->";
1371 if (p.inPrivateClass.size()) {
1372 prefix += p.inPrivateClass + "->";
1373 }
1374 fprintf(out, " case %d: %s%s(); break;\n",
1375 propindex, prefix.constData(), p.reset.constData());
1376 }
1377 fprintf(out, " default: break;\n");
1378 fprintf(out, " }\n");
1379 }
1380 fprintf(out, " }");
1381
1382 fprintf(out, " else ");
1383 fprintf(out, "if (_c == QMetaObject::BindableProperty) {\n");
1384 if (hasBindableProperties) {
1385 setupMemberAccess();
1386 fprintf(out, " switch (_id) {\n");
1387 for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
1388 const PropertyDef &p = cdef->propertyList.at(propindex);
1389 if (p.bind.isEmpty())
1390 continue;
1391 QByteArray prefix = "_t->";
1392 if (p.inPrivateClass.size()) {
1393 prefix += p.inPrivateClass + "->";
1394 }
1395 fprintf(out,
1396 " case %d: *static_cast<QUntypedBindable *>(_a[0]) = %s%s(); "
1397 "break;\n",
1398 propindex, prefix.constData(), p.bind.constData());
1399 }
1400 fprintf(out, " default: break;\n");
1401 fprintf(out, " }\n");
1402 }
1403 fprintf(out, " }");
1404 needElse = true;
1405 }
1406
1407 if (needElse)
1408 fprintf(out, "\n");
1409
1410 if (methodList.isEmpty()) {
1411 fprintf(out, " (void)_o;\n");
1412 if (cdef->constructorList.isEmpty() && automaticPropertyMetaTypes.isEmpty() && methodsWithAutomaticTypesHelper(methodList).isEmpty()) {
1413 fprintf(out, " (void)_id;\n");
1414 fprintf(out, " (void)_c;\n");
1415 }
1416 }
1417 if (!isUsed_a)
1418 fprintf(out, " (void)_a;\n");
1419
1420 fprintf(out, "}\n");
1421}
1422
1423void Generator::generateSignal(const FunctionDef *def, int index)
1424{
1425 if (def->wasCloned || def->isAbstract)
1426 return;
1427 fprintf(out, "\n// SIGNAL %d\n%s %s::%s(",
1428 index, def->type.name.constData(), cdef->qualified.constData(), def->name.constData());
1429
1430 QByteArray thisPtr = "this";
1431 const char *constQualifier = "";
1432
1433 if (def->isConst) {
1434 thisPtr = "const_cast< " + cdef->qualified + " *>(this)";
1435 constQualifier = "const";
1436 }
1437
1438 Q_ASSERT(!def->normalizedType.isEmpty());
1439 if (def->arguments.isEmpty() && def->normalizedType == "void" && !def->isPrivateSignal) {
1440 fprintf(out, ")%s\n{\n"
1441 " QMetaObject::activate(%s, &staticMetaObject, %d, nullptr);\n"
1442 "}\n", constQualifier, thisPtr.constData(), index);
1443 return;
1444 }
1445
1446 int offset = 1;
1447 const auto begin = def->arguments.cbegin();
1448 const auto end = def->arguments.cend();
1449 for (auto it = begin; it != end; ++it) {
1450 const ArgumentDef &a = *it;
1451 if (it != begin)
1452 fputs(", ", out);
1453 if (a.type.name.size())
1454 fputs(a.type.name.constData(), out);
1455 fprintf(out, " _t%d", offset++);
1456 if (a.rightType.size())
1457 fputs(a.rightType.constData(), out);
1458 }
1459 if (def->isPrivateSignal) {
1460 if (!def->arguments.isEmpty())
1461 fprintf(out, ", ");
1462 fprintf(out, "QPrivateSignal _t%d", offset++);
1463 }
1464
1465 fprintf(out, ")%s\n{\n", constQualifier);
1466 if (def->type.name.size() && def->normalizedType != "void") {
1467 QByteArray returnType = noRef(def->normalizedType);
1468 fprintf(out, " %s _t0{};\n", returnType.constData());
1469 }
1470
1471 fprintf(out, " void *_a[] = { ");
1472 if (def->normalizedType == "void") {
1473 fprintf(out, "nullptr");
1474 } else {
1476 fprintf(out, "const_cast<void*>(reinterpret_cast<const volatile void*>(std::addressof(_t0)))");
1477 else
1478 fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t0)))");
1479 }
1480 int i;
1481 for (i = 1; i < offset; ++i)
1482 if (i <= def->arguments.size() && def->arguments.at(i - 1).type.isVolatile)
1483 fprintf(out, ", const_cast<void*>(reinterpret_cast<const volatile void*>(std::addressof(_t%d)))", i);
1484 else
1485 fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t%d)))", i);
1486 fprintf(out, " };\n");
1487 fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index);
1488 if (def->normalizedType != "void")
1489 fprintf(out, " return _t0;\n");
1490 fprintf(out, "}\n");
1491}
1492
1493static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v);
1494static CborError jsonObjectToCbor(CborEncoder *parent, const QJsonObject &o)
1495{
1496 auto it = o.constBegin();
1497 auto end = o.constEnd();
1498 CborEncoder map;
1499 cbor_encoder_create_map(parent, &map, o.size());
1500
1501 for ( ; it != end; ++it) {
1502 QByteArray key = it.key().toUtf8();
1503 cbor_encode_text_string(&map, key.constData(), key.size());
1504 jsonValueToCbor(&map, it.value());
1505 }
1506 return cbor_encoder_close_container(parent, &map);
1507}
1508
1509static CborError jsonArrayToCbor(CborEncoder *parent, const QJsonArray &a)
1510{
1511 CborEncoder array;
1512 cbor_encoder_create_array(parent, &array, a.size());
1513 for (const QJsonValue v : a)
1514 jsonValueToCbor(&array, v);
1515 return cbor_encoder_close_container(parent, &array);
1516}
1517
1518static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v)
1519{
1520 switch (v.type()) {
1521 case QJsonValue::Null:
1522 case QJsonValue::Undefined:
1523 return cbor_encode_null(parent);
1524 case QJsonValue::Bool:
1525 return cbor_encode_boolean(parent, v.toBool());
1526 case QJsonValue::Array:
1527 return jsonArrayToCbor(parent, v.toArray());
1528 case QJsonValue::Object:
1529 return jsonObjectToCbor(parent, v.toObject());
1530 case QJsonValue::String: {
1531 QByteArray s = v.toString().toUtf8();
1532 return cbor_encode_text_string(parent, s.constData(), s.size());
1533 }
1534 case QJsonValue::Double: {
1535 double d = v.toDouble();
1536 if (d == floor(d) && fabs(d) <= (Q_INT64_C(1) << std::numeric_limits<double>::digits))
1537 return cbor_encode_int(parent, qint64(d));
1538 return cbor_encode_double(parent, d);
1539 }
1540 }
1541 Q_UNREACHABLE_RETURN(CborUnknownError);
1542}
1543
1544void Generator::generatePluginMetaData()
1545{
1546 if (cdef->pluginData.iid.isEmpty())
1547 return;
1548
1549 auto outputCborData = [this]() {
1550 CborDevice dev(out);
1551 CborEncoder enc;
1552 cbor_encoder_init_writer(&enc, CborDevice::callback, &dev);
1553
1554 CborEncoder map;
1555 cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);
1556
1557 dev.nextItem("\"IID\"");
1558 cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID));
1559 cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size());
1560
1561 dev.nextItem("\"className\"");
1562 cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName));
1563 cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size());
1564
1565 QJsonObject o = cdef->pluginData.metaData.object();
1566 if (!o.isEmpty()) {
1567 dev.nextItem("\"MetaData\"");
1568 cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData));
1569 jsonObjectToCbor(&map, o);
1570 }
1571
1572 if (!cdef->pluginData.uri.isEmpty()) {
1573 dev.nextItem("\"URI\"");
1574 cbor_encode_int(&map, int(QtPluginMetaDataKeys::URI));
1575 cbor_encode_text_string(&map, cdef->pluginData.uri.constData(), cdef->pluginData.uri.size());
1576 }
1577
1578 // Add -M args from the command line:
1579 for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) {
1580 const QJsonArray &a = it.value();
1581 QByteArray key = it.key().toUtf8();
1582 dev.nextItem(QByteArray("command-line \"" + key + "\"").constData());
1583 cbor_encode_text_string(&map, key.constData(), key.size());
1584 jsonArrayToCbor(&map, a);
1585 }
1586
1587 // Close the CBOR map manually
1588 dev.nextItem();
1589 cbor_encoder_close_container(&enc, &map);
1590 };
1591
1592 // 'Use' all namespaces.
1593 qsizetype pos = cdef->qualified.indexOf("::");
1594 for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) )
1595 fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData());
1596
1597 fputs("\n#ifdef QT_MOC_EXPORT_PLUGIN_V2", out);
1598
1599 // Qt 6.3+ output
1600 fprintf(out, "\nstatic constexpr unsigned char qt_pluginMetaDataV2_%s[] = {",
1601 cdef->classname.constData());
1602 outputCborData();
1603 fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN_V2(%s, %s, qt_pluginMetaDataV2_%s)\n",
1604 cdef->qualified.constData(), cdef->classname.constData(), cdef->classname.constData());
1605
1606 // compatibility with Qt 6.0-6.2
1607 fprintf(out, "#else\nQT_PLUGIN_METADATA_SECTION\n"
1608 "Q_CONSTINIT static constexpr unsigned char qt_pluginMetaData_%s[] = {\n"
1609 " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n"
1610 " // metadata version, Qt version, architectural requirements\n"
1611 " 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),",
1612 cdef->classname.constData());
1613 outputCborData();
1614 fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN(%s, %s)\n"
1615 "#endif // QT_MOC_EXPORT_PLUGIN_V2\n",
1616 cdef->qualified.constData(), cdef->classname.constData());
1617
1618 fputs("\n", out);
1619}
1620
1621QT_WARNING_DISABLE_GCC("-Wunused-function")
1622QT_WARNING_DISABLE_CLANG("-Wunused-function")
1623QT_WARNING_DISABLE_CLANG("-Wundefined-internal")
1624QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
1625
1626#define CBOR_ENCODER_WRITER_CONTROL 1
1627#define CBOR_ENCODER_WRITE_FUNCTION CborDevice::callback
1628
1629QT_END_NAMESPACE
1630
1631#include "cborencoder.c"
void nextItem(const char *comment=nullptr)
Definition cbordevice.h:22
CborDevice(FILE *out)
Definition cbordevice.h:20
Generator(Moc *moc, ClassDef *classDef, const QList< QByteArray > &metaTypes, const QHash< QByteArray, QByteArray > &knownQObjectClasses, const QHash< QByteArray, QByteArray > &knownGadgets, FILE *outfile=nullptr, bool requireCompleteTypes=false)
Definition generator.cpp:61
void generateCode()
Definition moc.h:209
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
Definition qlist.h:76
static QByteArray generateQualifiedClassNameIdentifier(const QByteArray &identifier)
static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v)
static bool qualifiedNameEquals(const QByteArray &qualifiedName, const QByteArray &name)
uint nameToBuiltinType(const QByteArray &name)
Definition generator.cpp:29
static int aggregateParameterCount(const QList< FunctionDef > &list)
static CborError jsonArrayToCbor(CborEncoder *parent, const QJsonArray &a)
bool isBuiltinType(const QByteArray &type)
Definition generator.cpp:41
static const char * metaTypeEnumValueString(int type)
Definition generator.cpp:49
static void printStringWithIndentation(FILE *out, const QByteArray &s)
static qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i)
Definition generator.cpp:77
static CborError jsonObjectToCbor(CborEncoder *parent, const QJsonObject &o)
bool hasQObject
Definition moc.h:192
bool hasQGadget
Definition moc.h:193
bool requireCompleteMethodTypes
Definition moc.h:195
bool hasQNamespace
Definition moc.h:194
int revisionedMethods
Definition moc.h:190
Definition moc.h:43
bool wasCloned
Definition moc.h:83
bool isRawSlot
Definition moc.h:96
bool isConst
Definition moc.h:79
bool returnTypeIsVolatile
Definition moc.h:85
bool isAbstract
Definition moc.h:95
bool isPrivateSignal
Definition moc.h:92
@ Private
Definition moc.h:75
bool isStatic
Definition moc.h:81
int notifyId
Definition moc.h:115
bool constant
Definition moc.h:119
Specification gspec
Definition moc.h:117
@ ReferenceSpec
Definition moc.h:116
@ PointerSpec
Definition moc.h:116