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