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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/tools/moc/generator.cpp