1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the tools applications of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29
30 #include "moc.h"
31 #include "generator.h"
32 #include "qdatetime.h"
33 #include "utils.h"
34 #include "outputrevision.h"
35 #include <QtCore/qfile.h>
36 #include <QtCore/qfileinfo.h>
37 #include <QtCore/qdir.h>
38 #include <QtCore/qjsondocument.h>
39
40 // for normalizeTypeInternal
41 #include <private/qmetaobject_moc_p.h>
42
43 QT_BEGIN_NAMESPACE
44
45 // only moc needs this function
normalizeType(const QByteArray & ba,bool fixScope=false)46 static QByteArray normalizeType(const QByteArray &ba, bool fixScope = false)
47 {
48 const char *s = ba.constData();
49 int len = ba.size();
50 char stackbuf[64];
51 char *buf = (len >= 64 ? new char[len + 1] : stackbuf);
52 char *d = buf;
53 char last = 0;
54 while(*s && is_space(*s))
55 s++;
56 while (*s) {
57 while (*s && !is_space(*s))
58 last = *d++ = *s++;
59 while (*s && is_space(*s))
60 s++;
61 if (*s && ((is_ident_char(*s) && is_ident_char(last))
62 || ((*s == ':') && (last == '<')))) {
63 last = *d++ = ' ';
64 }
65 }
66 *d = '\0';
67 QByteArray result = normalizeTypeInternal(buf, d, fixScope);
68 if (buf != stackbuf)
69 delete [] buf;
70 return result;
71 }
72
parseClassHead(ClassDef * def)73 bool Moc::parseClassHead(ClassDef *def)
74 {
75 // figure out whether this is a class declaration, or only a
76 // forward or variable declaration.
77 int i = 0;
78 Token token;
79 do {
80 token = lookup(i++);
81 if (token == COLON || token == LBRACE)
82 break;
83 if (token == SEMIC || token == RANGLE)
84 return false;
85 } while (token);
86
87 if (!test(IDENTIFIER)) // typedef struct { ... }
88 return false;
89 QByteArray name = lexem();
90
91 // support "class IDENT name" and "class IDENT(IDENT) name"
92 // also support "class IDENT name (final|sealed|Q_DECL_FINAL)"
93 if (test(LPAREN)) {
94 until(RPAREN);
95 if (!test(IDENTIFIER))
96 return false;
97 name = lexem();
98 } else if (test(IDENTIFIER)) {
99 const QByteArray lex = lexem();
100 if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL")
101 name = lex;
102 }
103
104 def->qualified += name;
105 while (test(SCOPE)) {
106 def->qualified += lexem();
107 if (test(IDENTIFIER)) {
108 name = lexem();
109 def->qualified += name;
110 }
111 }
112 def->classname = name;
113
114 if (test(IDENTIFIER)) {
115 const QByteArray lex = lexem();
116 if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL")
117 return false;
118 }
119
120 if (test(COLON)) {
121 do {
122 test(VIRTUAL);
123 FunctionDef::Access access = FunctionDef::Public;
124 if (test(PRIVATE))
125 access = FunctionDef::Private;
126 else if (test(PROTECTED))
127 access = FunctionDef::Protected;
128 else
129 test(PUBLIC);
130 test(VIRTUAL);
131 const QByteArray type = parseType().name;
132 // ignore the 'class Foo : BAR(Baz)' case
133 if (test(LPAREN)) {
134 until(RPAREN);
135 } else {
136 def->superclassList += qMakePair(type, access);
137 }
138 } while (test(COMMA));
139
140 if (!def->superclassList.isEmpty()
141 && knownGadgets.contains(def->superclassList.constFirst().first)) {
142 // Q_GADGET subclasses are treated as Q_GADGETs
143 knownGadgets.insert(def->classname, def->qualified);
144 knownGadgets.insert(def->qualified, def->qualified);
145 }
146 }
147 if (!test(LBRACE))
148 return false;
149 def->begin = index - 1;
150 bool foundRBrace = until(RBRACE);
151 def->end = index;
152 index = def->begin + 1;
153 return foundRBrace;
154 }
155
parseType()156 Type Moc::parseType()
157 {
158 Type type;
159 bool hasSignedOrUnsigned = false;
160 bool isVoid = false;
161 type.firstToken = lookup();
162 for (;;) {
163 skipCxxAttributes();
164 switch (next()) {
165 case SIGNED:
166 case UNSIGNED:
167 hasSignedOrUnsigned = true;
168 Q_FALLTHROUGH();
169 case CONST:
170 case VOLATILE:
171 type.name += lexem();
172 type.name += ' ';
173 if (lookup(0) == VOLATILE)
174 type.isVolatile = true;
175 continue;
176 case Q_MOC_COMPAT_TOKEN:
177 case Q_INVOKABLE_TOKEN:
178 case Q_SCRIPTABLE_TOKEN:
179 case Q_SIGNALS_TOKEN:
180 case Q_SLOTS_TOKEN:
181 case Q_SIGNAL_TOKEN:
182 case Q_SLOT_TOKEN:
183 type.name += lexem();
184 return type;
185 case NOTOKEN:
186 return type;
187 default:
188 prev();
189 break;
190 }
191 break;
192 }
193
194 skipCxxAttributes();
195 test(ENUM) || test(CLASS) || test(STRUCT);
196 for(;;) {
197 skipCxxAttributes();
198 switch (next()) {
199 case IDENTIFIER:
200 // void mySlot(unsigned myArg)
201 if (hasSignedOrUnsigned) {
202 prev();
203 break;
204 }
205 Q_FALLTHROUGH();
206 case CHAR:
207 case SHORT:
208 case INT:
209 case LONG:
210 type.name += lexem();
211 // preserve '[unsigned] long long', 'short int', 'long int', 'long double'
212 if (test(LONG) || test(INT) || test(DOUBLE)) {
213 type.name += ' ';
214 prev();
215 continue;
216 }
217 break;
218 case FLOAT:
219 case DOUBLE:
220 case VOID:
221 case BOOL:
222 type.name += lexem();
223 isVoid |= (lookup(0) == VOID);
224 break;
225 case NOTOKEN:
226 return type;
227 default:
228 prev();
229 ;
230 }
231 if (test(LANGLE)) {
232 if (type.name.isEmpty()) {
233 // '<' cannot start a type
234 return type;
235 }
236 type.name += lexemUntil(RANGLE);
237 }
238 if (test(SCOPE)) {
239 type.name += lexem();
240 type.isScoped = true;
241 } else {
242 break;
243 }
244 }
245 while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED)
246 || test(STAR) || test(AND) || test(ANDAND)) {
247 type.name += ' ';
248 type.name += lexem();
249 if (lookup(0) == AND)
250 type.referenceType = Type::Reference;
251 else if (lookup(0) == ANDAND)
252 type.referenceType = Type::RValueReference;
253 else if (lookup(0) == STAR)
254 type.referenceType = Type::Pointer;
255 }
256 type.rawName = type.name;
257 // transform stupid things like 'const void' or 'void const' into 'void'
258 if (isVoid && type.referenceType == Type::NoReference) {
259 type.name = "void";
260 }
261 return type;
262 }
263
264 enum class IncludeState {
265 IncludeBegin,
266 IncludeEnd,
267 NoInclude,
268 };
269
parseEnum(EnumDef * def)270 bool Moc::parseEnum(EnumDef *def)
271 {
272 bool isTypdefEnum = false; // typedef enum { ... } Foo;
273
274 if (test(CLASS) || test(STRUCT))
275 def->isEnumClass = true;
276
277 if (test(IDENTIFIER)) {
278 def->name = lexem();
279 } else {
280 if (lookup(-1) != TYPEDEF)
281 return false; // anonymous enum
282 isTypdefEnum = true;
283 }
284 if (test(COLON)) { // C++11 strongly typed enum
285 // enum Foo : unsigned long { ... };
286 parseType(); //ignore the result
287 }
288 if (!test(LBRACE))
289 return false;
290 auto handleInclude = [this]() -> IncludeState {
291 bool hadIncludeBegin = false;
292 if (test(MOC_INCLUDE_BEGIN)) {
293 currentFilenames.push(symbol().unquotedLexem());
294 // we do not return early to handle empty headers in one go
295 hadIncludeBegin = true;
296 }
297 if (test(NOTOKEN)) {
298 next(MOC_INCLUDE_END);
299 currentFilenames.pop();
300 return IncludeState::IncludeEnd;
301 }
302 if (hadIncludeBegin)
303 return IncludeState::IncludeBegin;
304 else
305 return IncludeState::NoInclude;
306 };
307 do {
308 if (lookup() == RBRACE) // accept trailing comma
309 break;
310 if ( handleInclude() == IncludeState::IncludeEnd)
311 continue;
312 next(IDENTIFIER);
313 def->values += lexem();
314 handleInclude();
315 skipCxxAttributes();
316 } while (test(EQ) ? until(COMMA) : test(COMMA));
317 next(RBRACE);
318 if (isTypdefEnum) {
319 if (!test(IDENTIFIER))
320 return false;
321 def->name = lexem();
322 }
323 return true;
324 }
325
parseFunctionArguments(FunctionDef * def)326 void Moc::parseFunctionArguments(FunctionDef *def)
327 {
328 Q_UNUSED(def);
329 while (hasNext()) {
330 ArgumentDef arg;
331 arg.type = parseType();
332 if (arg.type.name == "void")
333 break;
334 if (test(IDENTIFIER))
335 arg.name = lexem();
336 while (test(LBRACK)) {
337 arg.rightType += lexemUntil(RBRACK);
338 }
339 if (test(CONST) || test(VOLATILE)) {
340 arg.rightType += ' ';
341 arg.rightType += lexem();
342 }
343 arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType));
344 arg.typeNameForCast = normalizeType(QByteArray(noRef(arg.type.name) + "(*)" + arg.rightType));
345 if (test(EQ))
346 arg.isDefault = true;
347 def->arguments += arg;
348 if (!until(COMMA))
349 break;
350 }
351
352 if (!def->arguments.isEmpty()
353 && def->arguments.constLast().normalizedType == "QPrivateSignal") {
354 def->arguments.removeLast();
355 def->isPrivateSignal = true;
356 }
357 }
358
testFunctionAttribute(FunctionDef * def)359 bool Moc::testFunctionAttribute(FunctionDef *def)
360 {
361 if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) {
362 ++index;
363 return true;
364 }
365 return false;
366 }
367
testFunctionAttribute(Token tok,FunctionDef * def)368 bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
369 {
370 switch (tok) {
371 case Q_MOC_COMPAT_TOKEN:
372 def->isCompat = true;
373 return true;
374 case Q_INVOKABLE_TOKEN:
375 def->isInvokable = true;
376 return true;
377 case Q_SIGNAL_TOKEN:
378 def->isSignal = true;
379 return true;
380 case Q_SLOT_TOKEN:
381 def->isSlot = true;
382 return true;
383 case Q_SCRIPTABLE_TOKEN:
384 def->isInvokable = def->isScriptable = true;
385 return true;
386 default: break;
387 }
388 return false;
389 }
390
skipCxxAttributes()391 bool Moc::skipCxxAttributes()
392 {
393 auto rewind = index;
394 if (test(LBRACK) && test(LBRACK) && until(RBRACK) && test(RBRACK))
395 return true;
396 index = rewind;
397 return false;
398 }
399
testFunctionRevision(FunctionDef * def)400 bool Moc::testFunctionRevision(FunctionDef *def)
401 {
402 if (test(Q_REVISION_TOKEN)) {
403 next(LPAREN);
404 QByteArray revision = lexemUntil(RPAREN);
405 revision.remove(0, 1);
406 revision.chop(1);
407 bool ok = false;
408 def->revision = revision.toInt(&ok);
409 if (!ok || def->revision < 0)
410 error("Invalid revision");
411 return true;
412 }
413
414 return false;
415 }
416
417 // returns false if the function should be ignored
parseFunction(FunctionDef * def,bool inMacro)418 bool Moc::parseFunction(FunctionDef *def, bool inMacro)
419 {
420 def->isVirtual = false;
421 def->isStatic = false;
422 //skip modifiers and attributes
423 while (test(INLINE) || (test(STATIC) && (def->isStatic = true) == true) ||
424 (test(VIRTUAL) && (def->isVirtual = true) == true) //mark as virtual
425 || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {}
426 bool templateFunction = (lookup() == TEMPLATE);
427 def->type = parseType();
428 if (def->type.name.isEmpty()) {
429 if (templateFunction)
430 error("Template function as signal or slot");
431 else
432 error();
433 }
434 bool scopedFunctionName = false;
435 if (test(LPAREN)) {
436 def->name = def->type.name;
437 scopedFunctionName = def->type.isScoped;
438 def->type = Type("int");
439 } else {
440 Type tempType = parseType();;
441 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
442 if (testFunctionAttribute(def->type.firstToken, def))
443 ; // fine
444 else if (def->type.firstToken == Q_SIGNALS_TOKEN)
445 error();
446 else if (def->type.firstToken == Q_SLOTS_TOKEN)
447 error();
448 else {
449 if (!def->tag.isEmpty())
450 def->tag += ' ';
451 def->tag += def->type.name;
452 }
453 def->type = tempType;
454 tempType = parseType();
455 }
456 next(LPAREN, "Not a signal or slot declaration");
457 def->name = tempType.name;
458 scopedFunctionName = tempType.isScoped;
459 }
460
461 // we don't support references as return types, it's too dangerous
462 if (def->type.referenceType == Type::Reference) {
463 QByteArray rawName = def->type.rawName;
464 def->type = Type("void");
465 def->type.rawName = rawName;
466 }
467
468 def->normalizedType = normalizeType(def->type.name);
469
470 if (!test(RPAREN)) {
471 parseFunctionArguments(def);
472 next(RPAREN);
473 }
474
475 // support optional macros with compiler specific options
476 while (test(IDENTIFIER))
477 ;
478
479 def->isConst = test(CONST);
480
481 while (test(IDENTIFIER))
482 ;
483
484 if (inMacro) {
485 next(RPAREN);
486 prev();
487 } else {
488 if (test(THROW)) {
489 next(LPAREN);
490 until(RPAREN);
491 }
492 if (test(SEMIC))
493 ;
494 else if ((def->inlineCode = test(LBRACE)))
495 until(RBRACE);
496 else if ((def->isAbstract = test(EQ)))
497 until(SEMIC);
498 else if (skipCxxAttributes())
499 until(SEMIC);
500 else
501 error();
502 }
503 if (scopedFunctionName) {
504 const QByteArray msg = "Function declaration " + def->name
505 + " contains extra qualification. Ignoring as signal or slot.";
506 warning(msg.constData());
507 return false;
508 }
509 return true;
510 }
511
512 // like parseFunction, but never aborts with an error
parseMaybeFunction(const ClassDef * cdef,FunctionDef * def)513 bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
514 {
515 def->isVirtual = false;
516 def->isStatic = false;
517 //skip modifiers and attributes
518 while (test(EXPLICIT) || test(INLINE) || (test(STATIC) && (def->isStatic = true) == true) ||
519 (test(VIRTUAL) && (def->isVirtual = true) == true) //mark as virtual
520 || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {}
521 bool tilde = test(TILDE);
522 def->type = parseType();
523 if (def->type.name.isEmpty())
524 return false;
525 bool scopedFunctionName = false;
526 if (test(LPAREN)) {
527 def->name = def->type.name;
528 scopedFunctionName = def->type.isScoped;
529 if (def->name == cdef->classname) {
530 def->isDestructor = tilde;
531 def->isConstructor = !tilde;
532 def->type = Type();
533 } else {
534 def->type = Type("int");
535 }
536 } else {
537 Type tempType = parseType();;
538 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
539 if (testFunctionAttribute(def->type.firstToken, def))
540 ; // fine
541 else if (def->type.name == "Q_SIGNAL")
542 def->isSignal = true;
543 else if (def->type.name == "Q_SLOT")
544 def->isSlot = true;
545 else {
546 if (!def->tag.isEmpty())
547 def->tag += ' ';
548 def->tag += def->type.name;
549 }
550 def->type = tempType;
551 tempType = parseType();
552 }
553 if (!test(LPAREN))
554 return false;
555 def->name = tempType.name;
556 scopedFunctionName = tempType.isScoped;
557 }
558
559 // we don't support references as return types, it's too dangerous
560 if (def->type.referenceType == Type::Reference) {
561 QByteArray rawName = def->type.rawName;
562 def->type = Type("void");
563 def->type.rawName = rawName;
564 }
565
566 def->normalizedType = normalizeType(def->type.name);
567
568 if (!test(RPAREN)) {
569 parseFunctionArguments(def);
570 if (!test(RPAREN))
571 return false;
572 }
573 def->isConst = test(CONST);
574 if (scopedFunctionName
575 && (def->isSignal || def->isSlot || def->isInvokable)) {
576 const QByteArray msg = "parsemaybe: Function declaration " + def->name
577 + " contains extra qualification. Ignoring as signal or slot.";
578 warning(msg.constData());
579 return false;
580 }
581 return true;
582 }
583
parse()584 void Moc::parse()
585 {
586 QVector<NamespaceDef> namespaceList;
587 bool templateClass = false;
588 while (hasNext()) {
589 Token t = next();
590 switch (t) {
591 case NAMESPACE: {
592 int rewind = index;
593 if (test(IDENTIFIER)) {
594 QByteArray nsName = lexem();
595 QByteArrayList nested;
596 while (test(SCOPE)) {
597 next(IDENTIFIER);
598 nested.append(nsName);
599 nsName = lexem();
600 }
601 if (test(EQ)) {
602 // namespace Foo = Bar::Baz;
603 until(SEMIC);
604 } else if (test(LPAREN)) {
605 // Ignore invalid code such as: 'namespace __identifier("x")' (QTBUG-56634)
606 until(RPAREN);
607 } else if (!test(SEMIC)) {
608 NamespaceDef def;
609 def.classname = nsName;
610 def.doGenerate = currentFilenames.size() <= 1;
611
612 next(LBRACE);
613 def.begin = index - 1;
614 until(RBRACE);
615 def.end = index;
616 index = def.begin + 1;
617
618 for (int i = namespaceList.size() - 1; i >= 0; --i) {
619 if (inNamespace(&namespaceList.at(i))) {
620 def.qualified.prepend(namespaceList.at(i).classname + "::");
621 }
622 }
623 for (const QByteArray &ns : nested) {
624 NamespaceDef parentNs;
625 parentNs.classname = ns;
626 parentNs.qualified = def.qualified;
627 def.qualified += ns + "::";
628 parentNs.begin = def.begin;
629 parentNs.end = def.end;
630 namespaceList += parentNs;
631 }
632
633 while (inNamespace(&def) && hasNext()) {
634 switch (next()) {
635 case NAMESPACE:
636 if (test(IDENTIFIER)) {
637 while (test(SCOPE))
638 next(IDENTIFIER);
639 if (test(EQ)) {
640 // namespace Foo = Bar::Baz;
641 until(SEMIC);
642 } else if (!test(SEMIC)) {
643 until(RBRACE);
644 }
645 }
646 break;
647 case Q_NAMESPACE_TOKEN:
648 def.hasQNamespace = true;
649 break;
650 case Q_NAMESPACE_EXPORT_TOKEN:
651 next(LPAREN);
652 while (test(IDENTIFIER))
653 {}
654 next(RPAREN);
655 def.hasQNamespace = true;
656 break;
657 case Q_ENUMS_TOKEN:
658 case Q_ENUM_NS_TOKEN:
659 parseEnumOrFlag(&def, false);
660 break;
661 case Q_ENUM_TOKEN:
662 error("Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead");
663 break;
664 case Q_FLAGS_TOKEN:
665 case Q_FLAG_NS_TOKEN:
666 parseEnumOrFlag(&def, true);
667 break;
668 case Q_FLAG_TOKEN:
669 error("Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead");
670 break;
671 case Q_DECLARE_FLAGS_TOKEN:
672 parseFlag(&def);
673 break;
674 case Q_CLASSINFO_TOKEN:
675 parseClassInfo(&def);
676 break;
677 case ENUM: {
678 EnumDef enumDef;
679 if (parseEnum(&enumDef))
680 def.enumList += enumDef;
681 } break;
682 case CLASS:
683 case STRUCT: {
684 ClassDef classdef;
685 if (!parseClassHead(&classdef))
686 continue;
687 while (inClass(&classdef) && hasNext())
688 next(); // consume all Q_XXXX macros from this class
689 } break;
690 default: break;
691 }
692 }
693 namespaceList += def;
694 index = rewind;
695 if (!def.hasQNamespace && (!def.classInfoList.isEmpty() || !def.enumDeclarations.isEmpty()))
696 error("Namespace declaration lacks Q_NAMESPACE macro.");
697 }
698 }
699 break;
700 }
701 case SEMIC:
702 case RBRACE:
703 templateClass = false;
704 break;
705 case TEMPLATE:
706 templateClass = true;
707 break;
708 case MOC_INCLUDE_BEGIN:
709 currentFilenames.push(symbol().unquotedLexem());
710 break;
711 case MOC_INCLUDE_END:
712 currentFilenames.pop();
713 break;
714 case Q_DECLARE_INTERFACE_TOKEN:
715 parseDeclareInterface();
716 break;
717 case Q_DECLARE_METATYPE_TOKEN:
718 parseDeclareMetatype();
719 break;
720 case USING:
721 if (test(NAMESPACE)) {
722 while (test(SCOPE) || test(IDENTIFIER))
723 ;
724 // Ignore invalid code such as: 'using namespace __identifier("x")' (QTBUG-63772)
725 if (test(LPAREN))
726 until(RPAREN);
727 next(SEMIC);
728 }
729 break;
730 case CLASS:
731 case STRUCT: {
732 if (currentFilenames.size() <= 1)
733 break;
734
735 ClassDef def;
736 if (!parseClassHead(&def))
737 continue;
738
739 while (inClass(&def) && hasNext()) {
740 switch (next()) {
741 case Q_OBJECT_TOKEN:
742 def.hasQObject = true;
743 break;
744 case Q_GADGET_TOKEN:
745 def.hasQGadget = true;
746 break;
747 default: break;
748 }
749 }
750
751 if (!def.hasQObject && !def.hasQGadget)
752 continue;
753
754 for (int i = namespaceList.size() - 1; i >= 0; --i)
755 if (inNamespace(&namespaceList.at(i)))
756 def.qualified.prepend(namespaceList.at(i).classname + "::");
757
758 QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
759 classHash.insert(def.classname, def.qualified);
760 classHash.insert(def.qualified, def.qualified);
761
762 continue; }
763 default: break;
764 }
765 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
766 continue;
767 ClassDef def;
768 if (parseClassHead(&def)) {
769 FunctionDef::Access access = FunctionDef::Private;
770 for (int i = namespaceList.size() - 1; i >= 0; --i)
771 if (inNamespace(&namespaceList.at(i)))
772 def.qualified.prepend(namespaceList.at(i).classname + "::");
773 while (inClass(&def) && hasNext()) {
774 switch ((t = next())) {
775 case PRIVATE:
776 access = FunctionDef::Private;
777 if (test(Q_SIGNALS_TOKEN))
778 error("Signals cannot have access specifier");
779 break;
780 case PROTECTED:
781 access = FunctionDef::Protected;
782 if (test(Q_SIGNALS_TOKEN))
783 error("Signals cannot have access specifier");
784 break;
785 case PUBLIC:
786 access = FunctionDef::Public;
787 if (test(Q_SIGNALS_TOKEN))
788 error("Signals cannot have access specifier");
789 break;
790 case CLASS: {
791 ClassDef nestedDef;
792 if (parseClassHead(&nestedDef)) {
793 while (inClass(&nestedDef) && inClass(&def)) {
794 t = next();
795 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
796 error("Meta object features not supported for nested classes");
797 }
798 }
799 } break;
800 case Q_SIGNALS_TOKEN:
801 parseSignals(&def);
802 break;
803 case Q_SLOTS_TOKEN:
804 switch (lookup(-1)) {
805 case PUBLIC:
806 case PROTECTED:
807 case PRIVATE:
808 parseSlots(&def, access);
809 break;
810 default:
811 error("Missing access specifier for slots");
812 }
813 break;
814 case Q_OBJECT_TOKEN:
815 def.hasQObject = true;
816 if (templateClass)
817 error("Template classes not supported by Q_OBJECT");
818 if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
819 error("Class contains Q_OBJECT macro but does not inherit from QObject");
820 break;
821 case Q_GADGET_TOKEN:
822 def.hasQGadget = true;
823 if (templateClass)
824 error("Template classes not supported by Q_GADGET");
825 break;
826 case Q_PROPERTY_TOKEN:
827 parseProperty(&def);
828 break;
829 case Q_PLUGIN_METADATA_TOKEN:
830 parsePluginData(&def);
831 break;
832 case Q_ENUMS_TOKEN:
833 case Q_ENUM_TOKEN:
834 parseEnumOrFlag(&def, false);
835 break;
836 case Q_ENUM_NS_TOKEN:
837 error("Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead");
838 break;
839 case Q_FLAGS_TOKEN:
840 case Q_FLAG_TOKEN:
841 parseEnumOrFlag(&def, true);
842 break;
843 case Q_FLAG_NS_TOKEN:
844 error("Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead");
845 break;
846 case Q_DECLARE_FLAGS_TOKEN:
847 parseFlag(&def);
848 break;
849 case Q_CLASSINFO_TOKEN:
850 parseClassInfo(&def);
851 break;
852 case Q_INTERFACES_TOKEN:
853 parseInterfaces(&def);
854 break;
855 case Q_PRIVATE_SLOT_TOKEN:
856 parseSlotInPrivate(&def, access);
857 break;
858 case Q_PRIVATE_PROPERTY_TOKEN:
859 parsePrivateProperty(&def);
860 break;
861 case ENUM: {
862 EnumDef enumDef;
863 if (parseEnum(&enumDef))
864 def.enumList += enumDef;
865 } break;
866 case SEMIC:
867 case COLON:
868 break;
869 default:
870 FunctionDef funcDef;
871 funcDef.access = access;
872 int rewind = index--;
873 if (parseMaybeFunction(&def, &funcDef)) {
874 if (funcDef.isConstructor) {
875 if ((access == FunctionDef::Public) && funcDef.isInvokable) {
876 def.constructorList += funcDef;
877 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
878 funcDef.wasCloned = true;
879 funcDef.arguments.removeLast();
880 def.constructorList += funcDef;
881 }
882 }
883 } else if (funcDef.isDestructor) {
884 // don't care about destructors
885 } else {
886 if (access == FunctionDef::Public)
887 def.publicList += funcDef;
888 if (funcDef.isSlot) {
889 def.slotList += funcDef;
890 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
891 funcDef.wasCloned = true;
892 funcDef.arguments.removeLast();
893 def.slotList += funcDef;
894 }
895 if (funcDef.revision > 0)
896 ++def.revisionedMethods;
897 } else if (funcDef.isSignal) {
898 def.signalList += funcDef;
899 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
900 funcDef.wasCloned = true;
901 funcDef.arguments.removeLast();
902 def.signalList += funcDef;
903 }
904 if (funcDef.revision > 0)
905 ++def.revisionedMethods;
906 } else if (funcDef.isInvokable) {
907 def.methodList += funcDef;
908 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
909 funcDef.wasCloned = true;
910 funcDef.arguments.removeLast();
911 def.methodList += funcDef;
912 }
913 if (funcDef.revision > 0)
914 ++def.revisionedMethods;
915 }
916 }
917 } else {
918 index = rewind;
919 }
920 }
921 }
922
923 next(RBRACE);
924
925 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
926 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
927 continue; // no meta object code required
928
929
930 if (!def.hasQObject && !def.hasQGadget)
931 error("Class declaration lacks Q_OBJECT macro.");
932
933 // Add meta tags to the plugin meta data:
934 if (!def.pluginData.iid.isEmpty())
935 def.pluginData.metaArgs = metaArgs;
936
937 checkSuperClasses(&def);
938 checkProperties(&def);
939
940 classList += def;
941 QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
942 classHash.insert(def.classname, def.qualified);
943 classHash.insert(def.qualified, def.qualified);
944 }
945 }
946 for (const auto &n : qAsConst(namespaceList)) {
947 if (!n.hasQNamespace)
948 continue;
949 ClassDef def;
950 static_cast<BaseDef &>(def) = static_cast<BaseDef>(n);
951 def.qualified += def.classname;
952 def.hasQNamespace = true;
953 auto it = std::find_if(classList.begin(), classList.end(), [&def](const ClassDef &val) {
954 return def.classname == val.classname && def.qualified == val.qualified;
955 });
956
957 if (it != classList.end()) {
958 it->classInfoList += def.classInfoList;
959 it->enumDeclarations.insert(def.enumDeclarations);
960 it->enumList += def.enumList;
961 it->flagAliases.insert(def.flagAliases);
962 } else {
963 knownGadgets.insert(def.classname, def.qualified);
964 knownGadgets.insert(def.qualified, def.qualified);
965 if (n.doGenerate)
966 classList += def;
967 }
968 }
969 }
970
any_type_contains(const QVector<PropertyDef> & properties,const QByteArray & pattern)971 static bool any_type_contains(const QVector<PropertyDef> &properties, const QByteArray &pattern)
972 {
973 for (const auto &p : properties) {
974 if (p.type.contains(pattern))
975 return true;
976 }
977 return false;
978 }
979
any_arg_contains(const QVector<FunctionDef> & functions,const QByteArray & pattern)980 static bool any_arg_contains(const QVector<FunctionDef> &functions, const QByteArray &pattern)
981 {
982 for (const auto &f : functions) {
983 for (const auto &arg : f.arguments) {
984 if (arg.normalizedType.contains(pattern))
985 return true;
986 }
987 }
988 return false;
989 }
990
make_candidates()991 static QByteArrayList make_candidates()
992 {
993 QByteArrayList result;
994 result
995 #define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER
996 QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER)
997 #undef STREAM_SMART_POINTER
998 #define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME
999 QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE)
1000 #undef STREAM_1ARG_TEMPLATE
1001 ;
1002 return result;
1003 }
1004
requiredQtContainers(const QVector<ClassDef> & classes)1005 static QByteArrayList requiredQtContainers(const QVector<ClassDef> &classes)
1006 {
1007 static const QByteArrayList candidates = make_candidates();
1008
1009 QByteArrayList required;
1010 required.reserve(candidates.size());
1011
1012 for (const auto &candidate : candidates) {
1013 const QByteArray pattern = candidate + '<';
1014
1015 for (const auto &c : classes) {
1016 if (any_type_contains(c.propertyList, pattern) ||
1017 any_arg_contains(c.slotList, pattern) ||
1018 any_arg_contains(c.signalList, pattern) ||
1019 any_arg_contains(c.methodList, pattern)) {
1020 required.push_back(candidate);
1021 break;
1022 }
1023 }
1024 }
1025
1026 return required;
1027 }
1028
generate(FILE * out,FILE * jsonOutput)1029 void Moc::generate(FILE *out, FILE *jsonOutput)
1030 {
1031 QByteArray fn = filename;
1032 int i = filename.length()-1;
1033 while (i > 0 && filename.at(i - 1) != '/' && filename.at(i - 1) != '\\')
1034 --i; // skip path
1035 if (i >= 0)
1036 fn = filename.mid(i);
1037 fprintf(out, "/****************************************************************************\n"
1038 "** Meta object code from reading C++ file '%s'\n**\n" , fn.constData());
1039 fprintf(out, "** Created by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , mocOutputRevision, QT_VERSION_STR);
1040 fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
1041 "*****************************************************************************/\n\n");
1042
1043 fprintf(out, "#include <memory>\n"); // For std::addressof
1044 if (!noInclude) {
1045 if (includePath.size() && !includePath.endsWith('/'))
1046 includePath += '/';
1047 for (int i = 0; i < includeFiles.size(); ++i) {
1048 QByteArray inc = includeFiles.at(i);
1049 if (inc.at(0) != '<' && inc.at(0) != '"') {
1050 if (includePath.size() && includePath != "./")
1051 inc.prepend(includePath);
1052 inc = '\"' + inc + '\"';
1053 }
1054 fprintf(out, "#include %s\n", inc.constData());
1055 }
1056 }
1057 if (classList.size() && classList.constFirst().classname == "Qt")
1058 fprintf(out, "#include <QtCore/qobject.h>\n");
1059
1060 fprintf(out, "#include <QtCore/qbytearray.h>\n"); // For QByteArrayData
1061 fprintf(out, "#include <QtCore/qmetatype.h>\n"); // For QMetaType::Type
1062 if (mustIncludeQPluginH)
1063 fprintf(out, "#include <QtCore/qplugin.h>\n");
1064
1065 const auto qtContainers = requiredQtContainers(classList);
1066 for (const QByteArray &qtContainer : qtContainers)
1067 fprintf(out, "#include <QtCore/%s>\n", qtContainer.constData());
1068
1069
1070 fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
1071 "#error \"The header file '%s' doesn't include <QObject>.\"\n", fn.constData());
1072 fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
1073 fprintf(out, "#error \"This file was generated using the moc from %s."
1074 " It\"\n#error \"cannot be used with the include files from"
1075 " this version of Qt.\"\n#error \"(The moc has changed too"
1076 " much.)\"\n", QT_VERSION_STR);
1077 fprintf(out, "#endif\n\n");
1078
1079 fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n");
1080 fprintf(out, "QT_WARNING_PUSH\n");
1081 fprintf(out, "QT_WARNING_DISABLE_DEPRECATED\n");
1082
1083 fputs("", out);
1084 for (i = 0; i < classList.size(); ++i) {
1085 Generator generator(&classList[i], metaTypes, knownQObjectClasses, knownGadgets, out);
1086 generator.generateCode();
1087 }
1088 fputs("", out);
1089
1090 fprintf(out, "QT_WARNING_POP\n");
1091 fprintf(out, "QT_END_MOC_NAMESPACE\n");
1092
1093 if (jsonOutput) {
1094 QJsonObject mocData;
1095 mocData[QLatin1String("outputRevision")] = mocOutputRevision;
1096 mocData[QLatin1String("inputFile")] = QLatin1String(fn.constData());
1097
1098 QJsonArray classesJsonFormatted;
1099
1100 for (const ClassDef &cdef: qAsConst(classList))
1101 classesJsonFormatted.append(cdef.toJson());
1102
1103 if (!classesJsonFormatted.isEmpty())
1104 mocData[QLatin1String("classes")] = classesJsonFormatted;
1105
1106 QJsonDocument jsonDoc(mocData);
1107 fputs(jsonDoc.toJson().constData(), jsonOutput);
1108 }
1109 }
1110
parseSlots(ClassDef * def,FunctionDef::Access access)1111 void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
1112 {
1113 int defaultRevision = -1;
1114 if (test(Q_REVISION_TOKEN)) {
1115 next(LPAREN);
1116 QByteArray revision = lexemUntil(RPAREN);
1117 revision.remove(0, 1);
1118 revision.chop(1);
1119 bool ok = false;
1120 defaultRevision = revision.toInt(&ok);
1121 if (!ok || defaultRevision < 0)
1122 error("Invalid revision");
1123 }
1124
1125 next(COLON);
1126 while (inClass(def) && hasNext()) {
1127 switch (next()) {
1128 case PUBLIC:
1129 case PROTECTED:
1130 case PRIVATE:
1131 case Q_SIGNALS_TOKEN:
1132 case Q_SLOTS_TOKEN:
1133 prev();
1134 return;
1135 case SEMIC:
1136 continue;
1137 case FRIEND:
1138 until(SEMIC);
1139 continue;
1140 case USING:
1141 error("'using' directive not supported in 'slots' section");
1142 default:
1143 prev();
1144 }
1145
1146 FunctionDef funcDef;
1147 funcDef.access = access;
1148 if (!parseFunction(&funcDef))
1149 continue;
1150 if (funcDef.revision > 0) {
1151 ++def->revisionedMethods;
1152 } else if (defaultRevision != -1) {
1153 funcDef.revision = defaultRevision;
1154 ++def->revisionedMethods;
1155 }
1156 def->slotList += funcDef;
1157 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
1158 funcDef.wasCloned = true;
1159 funcDef.arguments.removeLast();
1160 def->slotList += funcDef;
1161 }
1162 }
1163 }
1164
parseSignals(ClassDef * def)1165 void Moc::parseSignals(ClassDef *def)
1166 {
1167 int defaultRevision = -1;
1168 if (test(Q_REVISION_TOKEN)) {
1169 next(LPAREN);
1170 QByteArray revision = lexemUntil(RPAREN);
1171 revision.remove(0, 1);
1172 revision.chop(1);
1173 bool ok = false;
1174 defaultRevision = revision.toInt(&ok);
1175 if (!ok || defaultRevision < 0)
1176 error("Invalid revision");
1177 }
1178
1179 next(COLON);
1180 while (inClass(def) && hasNext()) {
1181 switch (next()) {
1182 case PUBLIC:
1183 case PROTECTED:
1184 case PRIVATE:
1185 case Q_SIGNALS_TOKEN:
1186 case Q_SLOTS_TOKEN:
1187 prev();
1188 return;
1189 case SEMIC:
1190 continue;
1191 case FRIEND:
1192 until(SEMIC);
1193 continue;
1194 case USING:
1195 error("'using' directive not supported in 'signals' section");
1196 default:
1197 prev();
1198 }
1199 FunctionDef funcDef;
1200 funcDef.access = FunctionDef::Public;
1201 parseFunction(&funcDef);
1202 if (funcDef.isVirtual)
1203 warning("Signals cannot be declared virtual");
1204 if (funcDef.inlineCode)
1205 error("Not a signal declaration");
1206 if (funcDef.revision > 0) {
1207 ++def->revisionedMethods;
1208 } else if (defaultRevision != -1) {
1209 funcDef.revision = defaultRevision;
1210 ++def->revisionedMethods;
1211 }
1212 def->signalList += funcDef;
1213 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
1214 funcDef.wasCloned = true;
1215 funcDef.arguments.removeLast();
1216 def->signalList += funcDef;
1217 }
1218 }
1219 }
1220
createPropertyDef(PropertyDef & propDef)1221 void Moc::createPropertyDef(PropertyDef &propDef)
1222 {
1223 QByteArray type = parseType().name;
1224 if (type.isEmpty())
1225 error();
1226 propDef.designable = propDef.scriptable = propDef.stored = "true";
1227 propDef.user = "false";
1228 /*
1229 The Q_PROPERTY construct cannot contain any commas, since
1230 commas separate macro arguments. We therefore expect users
1231 to type "QMap" instead of "QMap<QString, QVariant>". For
1232 coherence, we also expect the same for
1233 QValueList<QVariant>, the other template class supported by
1234 QVariant.
1235 */
1236 type = normalizeType(type);
1237 if (type == "QMap")
1238 type = "QMap<QString,QVariant>";
1239 else if (type == "QValueList")
1240 type = "QValueList<QVariant>";
1241 else if (type == "LongLong")
1242 type = "qlonglong";
1243 else if (type == "ULongLong")
1244 type = "qulonglong";
1245
1246 propDef.type = type;
1247
1248 auto checkIsFunction = [&](const QByteArray &def, const char *name) {
1249 if (def.endsWith(')')) {
1250 QByteArray msg = "Providing a function for ";
1251 msg += name;
1252 msg += " in a property declaration is deprecated and will not be supported in Qt 6 anymore.";
1253 warning(msg.constData());
1254 }
1255 };
1256
1257 next();
1258 propDef.name = lexem();
1259 while (test(IDENTIFIER)) {
1260 const QByteArray l = lexem();
1261 if (l[0] == 'C' && l == "CONSTANT") {
1262 propDef.constant = true;
1263 continue;
1264 } else if(l[0] == 'F' && l == "FINAL") {
1265 propDef.final = true;
1266 continue;
1267 } else if (l[0] == 'R' && l == "REQUIRED") {
1268 propDef.required = true;
1269 continue;
1270 }
1271
1272 QByteArray v, v2;
1273 if (test(LPAREN)) {
1274 v = lexemUntil(RPAREN);
1275 v = v.mid(1, v.length() - 2); // removes the '(' and ')'
1276 } else if (test(INTEGER_LITERAL)) {
1277 v = lexem();
1278 if (l != "REVISION")
1279 error(1);
1280 } else {
1281 next(IDENTIFIER);
1282 v = lexem();
1283 if (test(LPAREN))
1284 v2 = lexemUntil(RPAREN);
1285 else if (v != "true" && v != "false")
1286 v2 = "()";
1287 }
1288 switch (l[0]) {
1289 case 'M':
1290 if (l == "MEMBER")
1291 propDef.member = v;
1292 else
1293 error(2);
1294 break;
1295 case 'R':
1296 if (l == "READ")
1297 propDef.read = v;
1298 else if (l == "RESET")
1299 propDef.reset = v + v2;
1300 else if (l == "REVISION") {
1301 bool ok = false;
1302 propDef.revision = v.toInt(&ok);
1303 if (!ok || propDef.revision < 0)
1304 error(1);
1305 } else
1306 error(2);
1307 break;
1308 case 'S':
1309 if (l == "SCRIPTABLE") {
1310 propDef.scriptable = v + v2;
1311 checkIsFunction(propDef.scriptable, "SCRIPTABLE");
1312 } else if (l == "STORED") {
1313 propDef.stored = v + v2;
1314 checkIsFunction(propDef.stored, "STORED");
1315 } else
1316 error(2);
1317 break;
1318 case 'W': if (l != "WRITE") error(2);
1319 propDef.write = v;
1320 break;
1321 case 'D': if (l != "DESIGNABLE") error(2);
1322 propDef.designable = v + v2;
1323 checkIsFunction(propDef.designable, "DESIGNABLE");
1324 break;
1325 case 'E': if (l != "EDITABLE") error(2); {
1326 const QByteArray msg = "EDITABLE flag for property declaration is deprecated.";
1327 warning(msg.constData());
1328 propDef.editable = v + v2;
1329 checkIsFunction(propDef.editable, "EDITABLE");
1330 }
1331 break;
1332 case 'N': if (l != "NOTIFY") error(2);
1333 propDef.notify = v;
1334 break;
1335 case 'U': if (l != "USER") error(2);
1336 propDef.user = v + v2;
1337 checkIsFunction(propDef.user, "USER");
1338 break;
1339 default:
1340 error(2);
1341 }
1342 }
1343 if (propDef.read.isNull() && propDef.member.isNull()) {
1344 const QByteArray msg = "Property declaration " + propDef.name
1345 + " has no READ accessor function or associated MEMBER variable. The property will be invalid.";
1346 warning(msg.constData());
1347 }
1348 if (propDef.constant && !propDef.write.isNull()) {
1349 const QByteArray msg = "Property declaration " + propDef.name
1350 + " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
1351 propDef.constant = false;
1352 warning(msg.constData());
1353 }
1354 if (propDef.constant && !propDef.notify.isNull()) {
1355 const QByteArray msg = "Property declaration " + propDef.name
1356 + " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
1357 propDef.constant = false;
1358 warning(msg.constData());
1359 }
1360 }
1361
parseProperty(ClassDef * def)1362 void Moc::parseProperty(ClassDef *def)
1363 {
1364 next(LPAREN);
1365 PropertyDef propDef;
1366 createPropertyDef(propDef);
1367 next(RPAREN);
1368
1369 if(!propDef.notify.isEmpty())
1370 def->notifyableProperties++;
1371 if (propDef.revision > 0)
1372 ++def->revisionedProperties;
1373 def->propertyList += propDef;
1374 }
1375
parsePluginData(ClassDef * def)1376 void Moc::parsePluginData(ClassDef *def)
1377 {
1378 next(LPAREN);
1379 QByteArray metaData;
1380 while (test(IDENTIFIER)) {
1381 QByteArray l = lexem();
1382 if (l == "IID") {
1383 next(STRING_LITERAL);
1384 def->pluginData.iid = unquotedLexem();
1385 } else if (l == "URI") {
1386 next(STRING_LITERAL);
1387 def->pluginData.uri = unquotedLexem();
1388 } else if (l == "FILE") {
1389 next(STRING_LITERAL);
1390 QByteArray metaDataFile = unquotedLexem();
1391 QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top().constData())).dir(), QString::fromLocal8Bit(metaDataFile.constData()));
1392 for (int j = 0; j < includes.size() && !fi.exists(); ++j) {
1393 const IncludePath &p = includes.at(j);
1394 if (p.isFrameworkPath)
1395 continue;
1396
1397 fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(metaDataFile.constData()));
1398 // try again, maybe there's a file later in the include paths with the same name
1399 if (fi.isDir()) {
1400 fi = QFileInfo();
1401 continue;
1402 }
1403 }
1404 if (!fi.exists()) {
1405 const QByteArray msg = "Plugin Metadata file " + lexem()
1406 + " does not exist. Declaration will be ignored";
1407 error(msg.constData());
1408 return;
1409 }
1410 QFile file(fi.canonicalFilePath());
1411 if (!file.open(QFile::ReadOnly)) {
1412 QByteArray msg = "Plugin Metadata file " + lexem() + " could not be opened: "
1413 + file.errorString().toUtf8();
1414 error(msg.constData());
1415 return;
1416 }
1417 parsedPluginMetadataFiles.append(fi.canonicalFilePath());
1418 metaData = file.readAll();
1419 }
1420 }
1421
1422 if (!metaData.isEmpty()) {
1423 def->pluginData.metaData = QJsonDocument::fromJson(metaData);
1424 if (!def->pluginData.metaData.isObject()) {
1425 const QByteArray msg = "Plugin Metadata file " + lexem()
1426 + " does not contain a valid JSON object. Declaration will be ignored";
1427 warning(msg.constData());
1428 def->pluginData.iid = QByteArray();
1429 def->pluginData.uri = QByteArray();
1430 return;
1431 }
1432 }
1433
1434 mustIncludeQPluginH = true;
1435 next(RPAREN);
1436 }
1437
parsePrivateProperty(ClassDef * def)1438 void Moc::parsePrivateProperty(ClassDef *def)
1439 {
1440 next(LPAREN);
1441 PropertyDef propDef;
1442 next(IDENTIFIER);
1443 propDef.inPrivateClass = lexem();
1444 while (test(SCOPE)) {
1445 propDef.inPrivateClass += lexem();
1446 next(IDENTIFIER);
1447 propDef.inPrivateClass += lexem();
1448 }
1449 // also allow void functions
1450 if (test(LPAREN)) {
1451 next(RPAREN);
1452 propDef.inPrivateClass += "()";
1453 }
1454
1455 next(COMMA);
1456
1457 createPropertyDef(propDef);
1458
1459 if(!propDef.notify.isEmpty())
1460 def->notifyableProperties++;
1461 if (propDef.revision > 0)
1462 ++def->revisionedProperties;
1463
1464 def->propertyList += propDef;
1465 }
1466
parseEnumOrFlag(BaseDef * def,bool isFlag)1467 void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag)
1468 {
1469 next(LPAREN);
1470 QByteArray identifier;
1471 while (test(IDENTIFIER)) {
1472 identifier = lexem();
1473 while (test(SCOPE) && test(IDENTIFIER)) {
1474 identifier += "::";
1475 identifier += lexem();
1476 }
1477 def->enumDeclarations[identifier] = isFlag;
1478 }
1479 next(RPAREN);
1480 }
1481
parseFlag(BaseDef * def)1482 void Moc::parseFlag(BaseDef *def)
1483 {
1484 next(LPAREN);
1485 QByteArray flagName, enumName;
1486 while (test(IDENTIFIER)) {
1487 flagName = lexem();
1488 while (test(SCOPE) && test(IDENTIFIER)) {
1489 flagName += "::";
1490 flagName += lexem();
1491 }
1492 }
1493 next(COMMA);
1494 while (test(IDENTIFIER)) {
1495 enumName = lexem();
1496 while (test(SCOPE) && test(IDENTIFIER)) {
1497 enumName += "::";
1498 enumName += lexem();
1499 }
1500 }
1501
1502 def->flagAliases.insert(enumName, flagName);
1503 next(RPAREN);
1504 }
1505
parseClassInfo(BaseDef * def)1506 void Moc::parseClassInfo(BaseDef *def)
1507 {
1508 next(LPAREN);
1509 ClassInfoDef infoDef;
1510 next(STRING_LITERAL);
1511 infoDef.name = symbol().unquotedLexem();
1512 next(COMMA);
1513 if (test(STRING_LITERAL)) {
1514 infoDef.value = symbol().unquotedLexem();
1515 } else {
1516 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1517 next(IDENTIFIER);
1518 next(LPAREN);
1519 next(STRING_LITERAL);
1520 infoDef.value = symbol().unquotedLexem();
1521 next(RPAREN);
1522 }
1523 next(RPAREN);
1524 def->classInfoList += infoDef;
1525 }
1526
parseInterfaces(ClassDef * def)1527 void Moc::parseInterfaces(ClassDef *def)
1528 {
1529 next(LPAREN);
1530 while (test(IDENTIFIER)) {
1531 QVector<ClassDef::Interface> iface;
1532 iface += ClassDef::Interface(lexem());
1533 while (test(SCOPE)) {
1534 iface.last().className += lexem();
1535 next(IDENTIFIER);
1536 iface.last().className += lexem();
1537 }
1538 while (test(COLON)) {
1539 next(IDENTIFIER);
1540 iface += ClassDef::Interface(lexem());
1541 while (test(SCOPE)) {
1542 iface.last().className += lexem();
1543 next(IDENTIFIER);
1544 iface.last().className += lexem();
1545 }
1546 }
1547 // resolve from classnames to interface ids
1548 for (int i = 0; i < iface.count(); ++i) {
1549 const QByteArray iid = interface2IdMap.value(iface.at(i).className);
1550 if (iid.isEmpty())
1551 error("Undefined interface");
1552
1553 iface[i].interfaceId = iid;
1554 }
1555 def->interfaceList += iface;
1556 }
1557 next(RPAREN);
1558 }
1559
parseDeclareInterface()1560 void Moc::parseDeclareInterface()
1561 {
1562 next(LPAREN);
1563 QByteArray interface;
1564 next(IDENTIFIER);
1565 interface += lexem();
1566 while (test(SCOPE)) {
1567 interface += lexem();
1568 next(IDENTIFIER);
1569 interface += lexem();
1570 }
1571 next(COMMA);
1572 QByteArray iid;
1573 if (test(STRING_LITERAL)) {
1574 iid = lexem();
1575 } else {
1576 next(IDENTIFIER);
1577 iid = lexem();
1578 }
1579 interface2IdMap.insert(interface, iid);
1580 next(RPAREN);
1581 }
1582
parseDeclareMetatype()1583 void Moc::parseDeclareMetatype()
1584 {
1585 next(LPAREN);
1586 QByteArray typeName = lexemUntil(RPAREN);
1587 typeName.remove(0, 1);
1588 typeName.chop(1);
1589 metaTypes.append(typeName);
1590 }
1591
parseSlotInPrivate(ClassDef * def,FunctionDef::Access access)1592 void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
1593 {
1594 next(LPAREN);
1595 FunctionDef funcDef;
1596 next(IDENTIFIER);
1597 funcDef.inPrivateClass = lexem();
1598 // also allow void functions
1599 if (test(LPAREN)) {
1600 next(RPAREN);
1601 funcDef.inPrivateClass += "()";
1602 }
1603 next(COMMA);
1604 funcDef.access = access;
1605 parseFunction(&funcDef, true);
1606 def->slotList += funcDef;
1607 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
1608 funcDef.wasCloned = true;
1609 funcDef.arguments.removeLast();
1610 def->slotList += funcDef;
1611 }
1612 if (funcDef.revision > 0)
1613 ++def->revisionedMethods;
1614
1615 }
1616
lexemUntil(Token target)1617 QByteArray Moc::lexemUntil(Token target)
1618 {
1619 int from = index;
1620 until(target);
1621 QByteArray s;
1622 while (from <= index) {
1623 QByteArray n = symbols.at(from++-1).lexem();
1624 if (s.size() && n.size()) {
1625 char prev = s.at(s.size()-1);
1626 char next = n.at(0);
1627 if ((is_ident_char(prev) && is_ident_char(next))
1628 || (prev == '<' && next == ':')
1629 || (prev == '>' && next == '>'))
1630 s += ' ';
1631 }
1632 s += n;
1633 }
1634 return s;
1635 }
1636
until(Token target)1637 bool Moc::until(Token target) {
1638 int braceCount = 0;
1639 int brackCount = 0;
1640 int parenCount = 0;
1641 int angleCount = 0;
1642 if (index) {
1643 switch(symbols.at(index-1).token) {
1644 case LBRACE: ++braceCount; break;
1645 case LBRACK: ++brackCount; break;
1646 case LPAREN: ++parenCount; break;
1647 case LANGLE: ++angleCount; break;
1648 default: break;
1649 }
1650 }
1651
1652 //when searching commas within the default argument, we should take care of template depth (anglecount)
1653 // unfortunatelly, we do not have enough semantic information to know if '<' is the operator< or
1654 // the beginning of a template type. so we just use heuristics.
1655 int possible = -1;
1656
1657 while (index < symbols.size()) {
1658 Token t = symbols.at(index++).token;
1659 switch (t) {
1660 case LBRACE: ++braceCount; break;
1661 case RBRACE: --braceCount; break;
1662 case LBRACK: ++brackCount; break;
1663 case RBRACK: --brackCount; break;
1664 case LPAREN: ++parenCount; break;
1665 case RPAREN: --parenCount; break;
1666 case LANGLE:
1667 if (parenCount == 0 && braceCount == 0)
1668 ++angleCount;
1669 break;
1670 case RANGLE:
1671 if (parenCount == 0 && braceCount == 0)
1672 --angleCount;
1673 break;
1674 case GTGT:
1675 if (parenCount == 0 && braceCount == 0) {
1676 angleCount -= 2;
1677 t = RANGLE;
1678 }
1679 break;
1680 default: break;
1681 }
1682 if (t == target
1683 && braceCount <= 0
1684 && brackCount <= 0
1685 && parenCount <= 0
1686 && (target != RANGLE || angleCount <= 0)) {
1687 if (target != COMMA || angleCount <= 0)
1688 return true;
1689 possible = index;
1690 }
1691
1692 if (target == COMMA && t == EQ && possible != -1) {
1693 index = possible;
1694 return true;
1695 }
1696
1697 if (braceCount < 0 || brackCount < 0 || parenCount < 0
1698 || (target == RANGLE && angleCount < 0)) {
1699 --index;
1700 break;
1701 }
1702
1703 if (braceCount <= 0 && t == SEMIC) {
1704 // Abort on semicolon. Allow recovering bad template parsing (QTBUG-31218)
1705 break;
1706 }
1707 }
1708
1709 if(target == COMMA && angleCount != 0 && possible != -1) {
1710 index = possible;
1711 return true;
1712 }
1713
1714 return false;
1715 }
1716
checkSuperClasses(ClassDef * def)1717 void Moc::checkSuperClasses(ClassDef *def)
1718 {
1719 const QByteArray firstSuperclass = def->superclassList.value(0).first;
1720
1721 if (!knownQObjectClasses.contains(firstSuperclass)) {
1722 // enable once we /require/ include paths
1723 #if 0
1724 const QByteArray msg
1725 = "Class "
1726 + def->className
1727 + " contains the Q_OBJECT macro and inherits from "
1728 + def->superclassList.value(0)
1729 + " but that is not a known QObject subclass. You may get compilation errors.";
1730 warning(msg.constData());
1731 #endif
1732 return;
1733 }
1734 for (int i = 1; i < def->superclassList.count(); ++i) {
1735 const QByteArray superClass = def->superclassList.at(i).first;
1736 if (knownQObjectClasses.contains(superClass)) {
1737 const QByteArray msg
1738 = "Class "
1739 + def->classname
1740 + " inherits from two QObject subclasses "
1741 + firstSuperclass
1742 + " and "
1743 + superClass
1744 + ". This is not supported!";
1745 warning(msg.constData());
1746 }
1747
1748 if (interface2IdMap.contains(superClass)) {
1749 bool registeredInterface = false;
1750 for (int i = 0; i < def->interfaceList.count(); ++i)
1751 if (def->interfaceList.at(i).constFirst().className == superClass) {
1752 registeredInterface = true;
1753 break;
1754 }
1755
1756 if (!registeredInterface) {
1757 const QByteArray msg
1758 = "Class "
1759 + def->classname
1760 + " implements the interface "
1761 + superClass
1762 + " but does not list it in Q_INTERFACES. qobject_cast to "
1763 + superClass
1764 + " will not work!";
1765 warning(msg.constData());
1766 }
1767 }
1768 }
1769 }
1770
checkProperties(ClassDef * cdef)1771 void Moc::checkProperties(ClassDef *cdef)
1772 {
1773 //
1774 // specify get function, for compatibiliy we accept functions
1775 // returning pointers, or const char * for QByteArray.
1776 //
1777 QSet<QByteArray> definedProperties;
1778 for (int i = 0; i < cdef->propertyList.count(); ++i) {
1779 PropertyDef &p = cdef->propertyList[i];
1780 if (p.read.isEmpty() && p.member.isEmpty())
1781 continue;
1782 if (definedProperties.contains(p.name)) {
1783 QByteArray msg = "The property '" + p.name + "' is defined multiple times in class " + cdef->classname + ".";
1784 warning(msg.constData());
1785 }
1786 definedProperties.insert(p.name);
1787
1788 for (int j = 0; j < cdef->publicList.count(); ++j) {
1789 const FunctionDef &f = cdef->publicList.at(j);
1790 if (f.name != p.read)
1791 continue;
1792 if (!f.isConst) // get functions must be const
1793 continue;
1794 if (f.arguments.size()) // and must not take any arguments
1795 continue;
1796 PropertyDef::Specification spec = PropertyDef::ValueSpec;
1797 QByteArray tmp = f.normalizedType;
1798 if (p.type == "QByteArray" && tmp == "const char *")
1799 tmp = "QByteArray";
1800 if (tmp.left(6) == "const ")
1801 tmp = tmp.mid(6);
1802 if (p.type != tmp && tmp.endsWith('*')) {
1803 tmp.chop(1);
1804 spec = PropertyDef::PointerSpec;
1805 } else if (f.type.name.endsWith('&')) { // raw type, not normalized type
1806 spec = PropertyDef::ReferenceSpec;
1807 }
1808 if (p.type != tmp)
1809 continue;
1810 p.gspec = spec;
1811 break;
1812 }
1813 if(!p.notify.isEmpty()) {
1814 int notifyId = -1;
1815 for (int j = 0; j < cdef->signalList.count(); ++j) {
1816 const FunctionDef &f = cdef->signalList.at(j);
1817 if(f.name != p.notify) {
1818 continue;
1819 } else {
1820 notifyId = j /* Signal indexes start from 0 */;
1821 break;
1822 }
1823 }
1824 p.notifyId = notifyId;
1825 if (notifyId == -1) {
1826 int index = cdef->nonClassSignalList.indexOf(p.notify);
1827 if (index == -1) {
1828 cdef->nonClassSignalList << p.notify;
1829 p.notifyId = -1 - cdef->nonClassSignalList.count();
1830 } else {
1831 p.notifyId = -2 - index;
1832 }
1833 }
1834 }
1835 }
1836 }
1837
toJson() const1838 QJsonObject ClassDef::toJson() const
1839 {
1840 QJsonObject cls;
1841 cls[QLatin1String("className")] = QString::fromUtf8(classname.constData());
1842 cls[QLatin1String("qualifiedClassName")] = QString::fromUtf8(qualified.constData());
1843
1844 QJsonArray classInfos;
1845 for (const auto &info: qAsConst(classInfoList)) {
1846 QJsonObject infoJson;
1847 infoJson[QLatin1String("name")] = QString::fromUtf8(info.name);
1848 infoJson[QLatin1String("value")] = QString::fromUtf8(info.value);
1849 classInfos.append(infoJson);
1850 }
1851
1852 if (classInfos.size())
1853 cls[QLatin1String("classInfos")] = classInfos;
1854
1855 const auto appendFunctions = [&cls](const QString &type, const QVector<FunctionDef> &funcs) {
1856 QJsonArray jsonFuncs;
1857
1858 for (const FunctionDef &fdef: funcs)
1859 jsonFuncs.append(fdef.toJson());
1860
1861 if (!jsonFuncs.isEmpty())
1862 cls[type] = jsonFuncs;
1863 };
1864
1865 appendFunctions(QLatin1String("signals"), signalList);
1866 appendFunctions(QLatin1String("slots"), slotList);
1867 appendFunctions(QLatin1String("constructors"), constructorList);
1868 appendFunctions(QLatin1String("methods"), methodList);
1869
1870 QJsonArray props;
1871
1872 for (const PropertyDef &propDef: qAsConst(propertyList))
1873 props.append(propDef.toJson());
1874
1875 if (!props.isEmpty())
1876 cls[QLatin1String("properties")] = props;
1877
1878 if (hasQObject)
1879 cls[QLatin1String("object")] = true;
1880 if (hasQGadget)
1881 cls[QLatin1String("gadget")] = true;
1882 if (hasQNamespace)
1883 cls[QLatin1String("namespace")] = true;
1884
1885 QJsonArray superClasses;
1886
1887 for (const auto &super: qAsConst(superclassList)) {
1888 const auto name = super.first;
1889 const auto access = super.second;
1890 QJsonObject superCls;
1891 superCls[QLatin1String("name")] = QString::fromUtf8(name);
1892 FunctionDef::accessToJson(&superCls, access);
1893 superClasses.append(superCls);
1894 }
1895
1896 if (!superClasses.isEmpty())
1897 cls[QLatin1String("superClasses")] = superClasses;
1898
1899 QJsonArray enums;
1900 for (const EnumDef &enumDef: qAsConst(enumList))
1901 enums.append(enumDef.toJson(*this));
1902 if (!enums.isEmpty())
1903 cls[QLatin1String("enums")] = enums;
1904
1905 QJsonArray ifaces;
1906 for (const QVector<Interface> &ifaceList: interfaceList) {
1907 QJsonArray jsonList;
1908 for (const Interface &iface: ifaceList) {
1909 QJsonObject ifaceJson;
1910 ifaceJson[QLatin1String("id")] = QString::fromUtf8(iface.interfaceId);
1911 ifaceJson[QLatin1String("className")] = QString::fromUtf8(iface.className);
1912 jsonList.append(ifaceJson);
1913 }
1914 ifaces.append(jsonList);
1915 }
1916 if (!ifaces.isEmpty())
1917 cls[QLatin1String("interfaces")] = ifaces;
1918
1919 return cls;
1920 }
1921
toJson() const1922 QJsonObject FunctionDef::toJson() const
1923 {
1924 QJsonObject fdef;
1925 fdef[QLatin1String("name")] = QString::fromUtf8(name);
1926 if (!tag.isEmpty())
1927 fdef[QLatin1String("tag")] = QString::fromUtf8(tag);
1928 fdef[QLatin1String("returnType")] = QString::fromUtf8(normalizedType);
1929
1930 QJsonArray args;
1931 for (const ArgumentDef &arg: arguments)
1932 args.append(arg.toJson());
1933
1934 if (!args.isEmpty())
1935 fdef[QLatin1String("arguments")] = args;
1936
1937 accessToJson(&fdef, access);
1938
1939 if (revision > 0)
1940 fdef[QLatin1String("revision")] = revision;
1941
1942 return fdef;
1943 }
1944
accessToJson(QJsonObject * obj,FunctionDef::Access acs)1945 void FunctionDef::accessToJson(QJsonObject *obj, FunctionDef::Access acs)
1946 {
1947 switch (acs) {
1948 case Private: (*obj)[QLatin1String("access")] = QLatin1String("private"); break;
1949 case Public: (*obj)[QLatin1String("access")] = QLatin1String("public"); break;
1950 case Protected: (*obj)[QLatin1String("access")] = QLatin1String("protected"); break;
1951 }
1952 }
1953
toJson() const1954 QJsonObject ArgumentDef::toJson() const
1955 {
1956 QJsonObject arg;
1957 arg[QLatin1String("type")] = QString::fromUtf8(normalizedType);
1958 if (!name.isEmpty())
1959 arg[QLatin1String("name")] = QString::fromUtf8(name);
1960 return arg;
1961 }
1962
toJson() const1963 QJsonObject PropertyDef::toJson() const
1964 {
1965 QJsonObject prop;
1966 prop[QLatin1String("name")] = QString::fromUtf8(name);
1967 prop[QLatin1String("type")] = QString::fromUtf8(type);
1968
1969 const auto jsonify = [&prop](const char *str, const QByteArray &member) {
1970 if (!member.isEmpty())
1971 prop[QLatin1String(str)] = QString::fromUtf8(member);
1972 };
1973
1974 jsonify("member", member);
1975 jsonify("read", read);
1976 jsonify("write", write);
1977 jsonify("reset", reset);
1978 jsonify("notify", notify);
1979 jsonify("privateClass", inPrivateClass);
1980
1981 const auto jsonifyBoolOrString = [&prop](const char *str, const QByteArray &boolOrString) {
1982 QJsonValue value;
1983 if (boolOrString == "true")
1984 value = true;
1985 else if (boolOrString == "false")
1986 value = false;
1987 else
1988 value = QString::fromUtf8(boolOrString); // function name to query at run-time
1989 prop[QLatin1String(str)] = value;
1990 };
1991
1992 jsonifyBoolOrString("designable", designable);
1993 jsonifyBoolOrString("scriptable", scriptable);
1994 jsonifyBoolOrString("stored", stored);
1995 jsonifyBoolOrString("user", user);
1996
1997 prop[QLatin1String("constant")] = constant;
1998 prop[QLatin1String("final")] = final;
1999 prop[QLatin1String("required")] = required;
2000
2001 if (revision > 0)
2002 prop[QLatin1String("revision")] = revision;
2003
2004 return prop;
2005 }
2006
toJson(const ClassDef & cdef) const2007 QJsonObject EnumDef::toJson(const ClassDef &cdef) const
2008 {
2009 QJsonObject def;
2010 def[QLatin1String("name")] = QString::fromUtf8(name);
2011 if (!enumName.isEmpty())
2012 def[QLatin1String("alias")] = QString::fromUtf8(enumName);
2013 def[QLatin1String("isFlag")] = cdef.enumDeclarations.value(name);
2014 def[QLatin1String("isClass")] = isEnumClass;
2015
2016 QJsonArray valueArr;
2017 for (const QByteArray &value: values)
2018 valueArr.append(QString::fromUtf8(value));
2019 if (!valueArr.isEmpty())
2020 def[QLatin1String("values")] = valueArr;
2021
2022 return def;
2023 }
2024
2025 QT_END_NAMESPACE
2026