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