source: trunk/src/tools/moc/moc.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 43.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "moc.h"
43#include "generator.h"
44#include "qdatetime.h"
45#include "utils.h"
46#include "outputrevision.h"
47
48// for normalizeTypeInternal
49#include <private/qmetaobject_p.h>
50
51QT_BEGIN_NAMESPACE
52
53// only moc needs this function
54static QByteArray normalizeType(const char *s, bool fixScope = false)
55{
56 int len = qstrlen(s);
57 char stackbuf[64];
58 char *buf = (len >= 64 ? new char[len + 1] : stackbuf);
59 char *d = buf;
60 char last = 0;
61 while(*s && is_space(*s))
62 s++;
63 while (*s) {
64 while (*s && !is_space(*s))
65 last = *d++ = *s++;
66 while (*s && is_space(*s))
67 s++;
68 if (*s && ((is_ident_char(*s) && is_ident_char(last))
69 || ((*s == ':') && (last == '<')))) {
70 last = *d++ = ' ';
71 }
72 }
73 *d = '\0';
74 QByteArray result;
75 if (strncmp("void", buf, d - buf) != 0)
76 result = normalizeTypeInternal(buf, d, fixScope);
77 if (buf != stackbuf)
78 delete [] buf;
79 return result;
80}
81
82bool Moc::parseClassHead(ClassDef *def)
83{
84 // figure out whether this is a class declaration, or only a
85 // forward or variable declaration.
86 int i = 0;
87 Token token;
88 do {
89 token = lookup(i++);
90 if (token == COLON || token == LBRACE)
91 break;
92 if (token == SEMIC || token == RANGLE)
93 return false;
94 } while (token);
95
96 if (!test(IDENTIFIER)) // typedef struct { ... }
97 return false;
98 QByteArray name = lexem();
99
100 // support "class IDENT name" and "class IDENT(IDENT) name"
101 if (test(LPAREN)) {
102 until(RPAREN);
103 if (!test(IDENTIFIER))
104 return false;
105 name = lexem();
106 } else if (test(IDENTIFIER)) {
107 name = lexem();
108 }
109
110 def->qualified += name;
111 while (test(SCOPE)) {
112 def->qualified += lexem();
113 if (test(IDENTIFIER)) {
114 name = lexem();
115 def->qualified += name;
116 }
117 }
118 def->classname = name;
119 if (test(COLON)) {
120 do {
121 test(VIRTUAL);
122 FunctionDef::Access access = FunctionDef::Public;
123 if (test(PRIVATE))
124 access = FunctionDef::Private;
125 else if (test(PROTECTED))
126 access = FunctionDef::Protected;
127 else
128 test(PUBLIC);
129 test(VIRTUAL);
130 const QByteArray type = parseType().name;
131 // ignore the 'class Foo : BAR(Baz)' case
132 if (test(LPAREN)) {
133 until(RPAREN);
134 } else {
135 def->superclassList += qMakePair(type, access);
136 }
137 } while (test(COMMA));
138 }
139 if (!test(LBRACE))
140 return false;
141 def->begin = index - 1;
142 bool foundRBrace = until(RBRACE);
143 def->end = index;
144 index = def->begin + 1;
145 return foundRBrace;
146}
147
148Type Moc::parseType()
149{
150 Type type;
151 bool hasSignedOrUnsigned = false;
152 bool isVoid = false;
153 type.firstToken = lookup();
154 for (;;) {
155 switch (next()) {
156 case SIGNED:
157 case UNSIGNED:
158 hasSignedOrUnsigned = true;
159 // fall through
160 case CONST:
161 case VOLATILE:
162 type.name += lexem();
163 type.name += ' ';
164 if (lookup(0) == VOLATILE)
165 type.isVolatile = true;
166 continue;
167 case Q_MOC_COMPAT_TOKEN:
168 case Q_QT3_SUPPORT_TOKEN:
169 case Q_INVOKABLE_TOKEN:
170 case Q_SCRIPTABLE_TOKEN:
171 case Q_SIGNALS_TOKEN:
172 case Q_SLOTS_TOKEN:
173 case Q_SIGNAL_TOKEN:
174 case Q_SLOT_TOKEN:
175 type.name += lexem();
176 return type;
177 default:
178 prev();
179 break;
180 }
181 break;
182 }
183 test(ENUM) || test(CLASS) || test(STRUCT);
184 for(;;) {
185 switch (next()) {
186 case IDENTIFIER:
187 // void mySlot(unsigned myArg)
188 if (hasSignedOrUnsigned) {
189 prev();
190 break;
191 }
192 case CHAR:
193 case SHORT:
194 case INT:
195 case LONG:
196 type.name += lexem();
197 // preserve '[unsigned] long long', 'short int', 'long int', 'long double'
198 if (test(LONG) || test(INT) || test(DOUBLE)) {
199 type.name += ' ';
200 prev();
201 continue;
202 }
203 break;
204 case FLOAT:
205 case DOUBLE:
206 case VOID:
207 case BOOL:
208 type.name += lexem();
209 isVoid |= (lookup(0) == VOID);
210 break;
211 default:
212 prev();
213 ;
214 }
215 if (test(LANGLE)) {
216 QByteArray templ = lexemUntil(RANGLE);
217 for (int i = 0; i < templ.size(); ++i) {
218 type.name += templ.at(i);
219 if ((templ.at(i) == '<' && i < templ.size()-1 && templ.at(i+1) == ':')
220 || (templ.at(i) == '>' && i < templ.size()-1 && templ.at(i+1) == '>')) {
221 type.name += ' ';
222 }
223 }
224 }
225 if (test(SCOPE)) {
226 type.name += lexem();
227 type.isScoped = true;
228 } else {
229 break;
230 }
231 }
232 while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED)
233 || test(STAR) || test(AND) || test(ANDAND)) {
234 type.name += ' ';
235 type.name += lexem();
236 if (lookup(0) == AND)
237 type.referenceType = Type::Reference;
238 else if (lookup(0) == ANDAND)
239 type.referenceType = Type::RValueReference;
240 else if (lookup(0) == STAR)
241 type.referenceType = Type::Pointer;
242 }
243 // transform stupid things like 'const void' or 'void const' into 'void'
244 if (isVoid && type.referenceType == Type::NoReference) {
245 type.name = "void";
246 }
247 return type;
248}
249
250bool Moc::parseEnum(EnumDef *def)
251{
252 bool isTypdefEnum = false; // typedef enum { ... } Foo;
253
254 if (test(IDENTIFIER)) {
255 def->name = lexem();
256 } else {
257 if (lookup(-1) != TYPEDEF)
258 return false; // anonymous enum
259 isTypdefEnum = true;
260 }
261 if (!test(LBRACE))
262 return false;
263 do {
264 if (lookup() == RBRACE) // accept trailing comma
265 break;
266 next(IDENTIFIER);
267 def->values += lexem();
268 } while (test(EQ) ? until(COMMA) : test(COMMA));
269 next(RBRACE);
270 if (isTypdefEnum) {
271 if (!test(IDENTIFIER))
272 return false;
273 def->name = lexem();
274 }
275 return true;
276}
277
278void Moc::parseFunctionArguments(FunctionDef *def)
279{
280 Q_UNUSED(def);
281 while (hasNext()) {
282 ArgumentDef arg;
283 arg.type = parseType();
284 if (arg.type.name == "void")
285 break;
286 if (test(IDENTIFIER))
287 arg.name = lexem();
288 while (test(LBRACK)) {
289 arg.rightType += lexemUntil(RBRACK);
290 }
291 if (test(CONST) || test(VOLATILE)) {
292 arg.rightType += ' ';
293 arg.rightType += lexem();
294 }
295 arg.normalizedType = normalizeType(arg.type.name + ' ' + arg.rightType);
296 arg.typeNameForCast = normalizeType(noRef(arg.type.name) + "(*)" + arg.rightType);
297 if (test(EQ))
298 arg.isDefault = true;
299 def->arguments += arg;
300 if (!until(COMMA))
301 break;
302 }
303}
304
305bool Moc::testFunctionAttribute(FunctionDef *def)
306{
307 if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) {
308 ++index;
309 return true;
310 }
311 return false;
312}
313
314bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
315{
316 switch (tok) {
317 case Q_MOC_COMPAT_TOKEN:
318 case Q_QT3_SUPPORT_TOKEN:
319 def->isCompat = true;
320 return true;
321 case Q_INVOKABLE_TOKEN:
322 def->isInvokable = true;
323 return true;
324 case Q_SIGNAL_TOKEN:
325 def->isSignal = true;
326 return true;
327 case Q_SLOT_TOKEN:
328 def->isSlot = true;
329 return true;
330 case Q_SCRIPTABLE_TOKEN:
331 def->isInvokable = def->isScriptable = true;
332 return true;
333 default: break;
334 }
335 return false;
336}
337
338// returns false if the function should be ignored
339bool Moc::parseFunction(FunctionDef *def, bool inMacro)
340{
341 def->isVirtual = false;
342 //skip modifiers and attributes
343 while (test(INLINE) || test(STATIC) ||
344 (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual
345 || testFunctionAttribute(def)) {}
346 bool templateFunction = (lookup() == TEMPLATE);
347 def->type = parseType();
348 if (def->type.name.isEmpty()) {
349 if (templateFunction)
350 error("Template function as signal or slot");
351 else
352 error();
353 }
354 bool scopedFunctionName = false;
355 if (test(LPAREN)) {
356 def->name = def->type.name;
357 scopedFunctionName = def->type.isScoped;
358 def->type = Type("int");
359 } else {
360 Type tempType = parseType();;
361 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
362 if (testFunctionAttribute(def->type.firstToken, def))
363 ; // fine
364 else if (def->type.firstToken == Q_SIGNALS_TOKEN)
365 error();
366 else if (def->type.firstToken == Q_SLOTS_TOKEN)
367 error();
368 else {
369 if (!def->tag.isEmpty())
370 def->tag += ' ';
371 def->tag += def->type.name;
372 }
373 def->type = tempType;
374 tempType = parseType();
375 }
376 next(LPAREN, "Not a signal or slot declaration");
377 def->name = tempType.name;
378 scopedFunctionName = tempType.isScoped;
379 }
380
381 // we don't support references as return types, it's too dangerous
382 if (def->type.referenceType == Type::Reference)
383 def->type = Type("void");
384
385 def->normalizedType = normalizeType(def->type.name);
386
387 if (!test(RPAREN)) {
388 parseFunctionArguments(def);
389 next(RPAREN);
390 }
391
392 // support optional macros with compiler specific options
393 while (test(IDENTIFIER))
394 ;
395
396 def->isConst = test(CONST);
397
398 while (test(IDENTIFIER))
399 ;
400
401 if (inMacro) {
402 next(RPAREN);
403 prev();
404 } else {
405 if (test(THROW)) {
406 next(LPAREN);
407 until(RPAREN);
408 }
409 if (test(SEMIC))
410 ;
411 else if ((def->inlineCode = test(LBRACE)))
412 until(RBRACE);
413 else if ((def->isAbstract = test(EQ)))
414 until(SEMIC);
415 else
416 error();
417 }
418
419 if (scopedFunctionName) {
420 QByteArray msg("Function declaration ");
421 msg += def->name;
422 msg += " contains extra qualification. Ignoring as signal or slot.";
423 warning(msg.constData());
424 return false;
425 }
426 return true;
427}
428
429// like parseFunction, but never aborts with an error
430bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
431{
432 def->isVirtual = false;
433 //skip modifiers and attributes
434 while (test(EXPLICIT) || test(INLINE) || test(STATIC) ||
435 (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual
436 || testFunctionAttribute(def)) {}
437 bool tilde = test(TILDE);
438 def->type = parseType();
439 if (def->type.name.isEmpty())
440 return false;
441 bool scopedFunctionName = false;
442 if (test(LPAREN)) {
443 def->name = def->type.name;
444 scopedFunctionName = def->type.isScoped;
445 if (def->name == cdef->classname) {
446 def->isDestructor = tilde;
447 def->isConstructor = !tilde;
448 def->type = Type();
449 } else {
450 def->type = Type("int");
451 }
452 } else {
453 Type tempType = parseType();;
454 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
455 if (testFunctionAttribute(def->type.firstToken, def))
456 ; // fine
457 else if (def->type.name == "Q_SIGNAL")
458 def->isSignal = true;
459 else if (def->type.name == "Q_SLOT")
460 def->isSlot = true;
461 else {
462 if (!def->tag.isEmpty())
463 def->tag += ' ';
464 def->tag += def->type.name;
465 }
466 def->type = tempType;
467 tempType = parseType();
468 }
469 if (!test(LPAREN))
470 return false;
471 def->name = tempType.name;
472 scopedFunctionName = tempType.isScoped;
473 }
474
475 // we don't support references as return types, it's too dangerous
476 if (def->type.referenceType == Type::Reference)
477 def->type = Type("void");
478
479 def->normalizedType = normalizeType(def->type.name);
480
481 if (!test(RPAREN)) {
482 parseFunctionArguments(def);
483 if (!test(RPAREN))
484 return false;
485 }
486 def->isConst = test(CONST);
487 if (scopedFunctionName
488 && (def->isSignal || def->isSlot || def->isInvokable)) {
489 QByteArray msg("parsemaybe: Function declaration ");
490 msg += def->name;
491 msg += " contains extra qualification. Ignoring as signal or slot.";
492 warning(msg.constData());
493 return false;
494 }
495 return true;
496}
497
498
499void Moc::parse()
500{
501 QList<NamespaceDef> namespaceList;
502 bool templateClass = false;
503 while (hasNext()) {
504 Token t = next();
505 switch (t) {
506 case NAMESPACE: {
507 int rewind = index;
508 if (test(IDENTIFIER)) {
509 if (test(EQ)) {
510 // namespace Foo = Bar::Baz;
511 until(SEMIC);
512 } else if (!test(SEMIC)) {
513 NamespaceDef def;
514 def.name = lexem();
515 next(LBRACE);
516 def.begin = index - 1;
517 until(RBRACE);
518 def.end = index;
519 index = def.begin + 1;
520 namespaceList += def;
521 index = rewind;
522 }
523 }
524 break;
525 }
526 case SEMIC:
527 case RBRACE:
528 templateClass = false;
529 break;
530 case TEMPLATE:
531 templateClass = true;
532 break;
533 case MOC_INCLUDE_BEGIN:
534 currentFilenames.push(symbol().unquotedLexem());
535 break;
536 case MOC_INCLUDE_END:
537 currentFilenames.pop();
538 break;
539 case Q_DECLARE_INTERFACE_TOKEN:
540 parseDeclareInterface();
541 break;
542 case Q_DECLARE_METATYPE_TOKEN:
543 parseDeclareMetatype();
544 break;
545 case USING:
546 if (test(NAMESPACE)) {
547 while (test(SCOPE) || test(IDENTIFIER))
548 ;
549 next(SEMIC);
550 }
551 break;
552 case CLASS:
553 case STRUCT: {
554 if (currentFilenames.size() <= 1)
555 break;
556
557 ClassDef def;
558 if (!parseClassHead(&def))
559 continue;
560
561 while (inClass(&def) && hasNext()) {
562 if (next() == Q_OBJECT_TOKEN) {
563 def.hasQObject = true;
564 break;
565 }
566 }
567
568 if (!def.hasQObject)
569 continue;
570
571 for (int i = namespaceList.size() - 1; i >= 0; --i)
572 if (inNamespace(&namespaceList.at(i)))
573 def.qualified.prepend(namespaceList.at(i).name + "::");
574
575 knownQObjectClasses.insert(def.classname);
576 knownQObjectClasses.insert(def.qualified);
577
578 continue; }
579 default: break;
580 }
581 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
582 continue;
583 ClassDef def;
584 if (parseClassHead(&def)) {
585 FunctionDef::Access access = FunctionDef::Private;
586 for (int i = namespaceList.size() - 1; i >= 0; --i)
587 if (inNamespace(&namespaceList.at(i)))
588 def.qualified.prepend(namespaceList.at(i).name + "::");
589 while (inClass(&def) && hasNext()) {
590 switch ((t = next())) {
591 case PRIVATE:
592 access = FunctionDef::Private;
593 if (test(Q_SIGNALS_TOKEN))
594 error("Signals cannot have access specifier");
595 break;
596 case PROTECTED:
597 access = FunctionDef::Protected;
598 if (test(Q_SIGNALS_TOKEN))
599 error("Signals cannot have access specifier");
600 break;
601 case PUBLIC:
602 access = FunctionDef::Public;
603 if (test(Q_SIGNALS_TOKEN))
604 error("Signals cannot have access specifier");
605 break;
606 case CLASS: {
607 ClassDef nestedDef;
608 if (parseClassHead(&nestedDef)) {
609 while (inClass(&nestedDef) && inClass(&def)) {
610 t = next();
611 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
612 error("Meta object features not supported for nested classes");
613 }
614 }
615 } break;
616 case Q_SIGNALS_TOKEN:
617 parseSignals(&def);
618 break;
619 case Q_SLOTS_TOKEN:
620 switch (lookup(-1)) {
621 case PUBLIC:
622 case PROTECTED:
623 case PRIVATE:
624 parseSlots(&def, access);
625 break;
626 default:
627 error("Missing access specifier for slots");
628 }
629 break;
630 case Q_OBJECT_TOKEN:
631 def.hasQObject = true;
632 if (templateClass)
633 error("Template classes not supported by Q_OBJECT");
634 if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
635 error("Class contains Q_OBJECT macro but does not inherit from QObject");
636 break;
637 case Q_GADGET_TOKEN:
638 def.hasQGadget = true;
639 if (templateClass)
640 error("Template classes not supported by Q_GADGET");
641 break;
642 case Q_PROPERTY_TOKEN:
643 parseProperty(&def);
644 break;
645 case Q_ENUMS_TOKEN:
646 parseEnumOrFlag(&def, false);
647 break;
648 case Q_FLAGS_TOKEN:
649 parseEnumOrFlag(&def, true);
650 break;
651 case Q_DECLARE_FLAGS_TOKEN:
652 parseFlag(&def);
653 break;
654 case Q_CLASSINFO_TOKEN:
655 parseClassInfo(&def);
656 break;
657 case Q_INTERFACES_TOKEN:
658 parseInterfaces(&def);
659 break;
660 case Q_PRIVATE_SLOT_TOKEN:
661 parseSlotInPrivate(&def, access);
662 break;
663 case Q_PRIVATE_PROPERTY_TOKEN:
664 parsePrivateProperty(&def);
665 break;
666 case ENUM: {
667 EnumDef enumDef;
668 if (parseEnum(&enumDef))
669 def.enumList += enumDef;
670 } break;
671 default:
672 FunctionDef funcDef;
673 funcDef.access = access;
674 int rewind = index;
675 if (parseMaybeFunction(&def, &funcDef)) {
676 if (funcDef.isConstructor) {
677 if ((access == FunctionDef::Public) && funcDef.isInvokable) {
678 def.constructorList += funcDef;
679 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
680 funcDef.wasCloned = true;
681 funcDef.arguments.removeLast();
682 def.constructorList += funcDef;
683 }
684 }
685 } else if (funcDef.isDestructor) {
686 // don't care about destructors
687 } else {
688 if (access == FunctionDef::Public)
689 def.publicList += funcDef;
690 if (funcDef.isSlot) {
691 def.slotList += funcDef;
692 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
693 funcDef.wasCloned = true;
694 funcDef.arguments.removeLast();
695 def.slotList += funcDef;
696 }
697 } else if (funcDef.isSignal) {
698 def.signalList += funcDef;
699 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
700 funcDef.wasCloned = true;
701 funcDef.arguments.removeLast();
702 def.signalList += funcDef;
703 }
704 } else if (funcDef.isInvokable) {
705 def.methodList += funcDef;
706 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
707 funcDef.wasCloned = true;
708 funcDef.arguments.removeLast();
709 def.methodList += funcDef;
710 }
711 }
712 }
713 } else {
714 index = rewind;
715 }
716 }
717 }
718
719 next(RBRACE);
720
721 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
722 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
723 continue; // no meta object code required
724
725
726 if (!def.hasQObject && !def.hasQGadget)
727 error("Class declarations lacks Q_OBJECT macro.");
728
729 checkSuperClasses(&def);
730 checkProperties(&def);
731
732 classList += def;
733 knownQObjectClasses.insert(def.classname);
734 knownQObjectClasses.insert(def.qualified);
735 }
736 }
737}
738
739void Moc::generate(FILE *out)
740{
741
742 QDateTime dt = QDateTime::currentDateTime();
743 QByteArray dstr = dt.toString().toLatin1();
744 QByteArray fn = filename;
745 int i = filename.length()-1;
746 while (i>0 && filename[i-1] != '/' && filename[i-1] != '\\')
747 --i; // skip path
748 if (i >= 0)
749 fn = filename.mid(i);
750 fprintf(out, "/****************************************************************************\n"
751 "** Meta object code from reading C++ file '%s'\n**\n" , (const char*)fn);
752 fprintf(out, "** Created: %s\n"
753 "** by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , dstr.data(), mocOutputRevision, QT_VERSION_STR);
754 fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
755 "*****************************************************************************/\n\n");
756
757
758 if (!noInclude) {
759 if (includePath.size() && !includePath.endsWith('/'))
760 includePath += '/';
761 for (int i = 0; i < includeFiles.size(); ++i) {
762 QByteArray inc = includeFiles.at(i);
763 if (inc[0] != '<' && inc[0] != '"') {
764 if (includePath.size() && includePath != "./")
765 inc.prepend(includePath);
766 inc = '\"' + inc + '\"';
767 }
768 fprintf(out, "#include %s\n", inc.constData());
769 }
770 }
771 if (classList.size() && classList.first().classname == "Qt")
772 fprintf(out, "#include <QtCore/qobject.h>\n");
773
774 if (mustIncludeQMetaTypeH)
775 fprintf(out, "#include <QtCore/qmetatype.h>\n");
776
777 fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
778 "#error \"The header file '%s' doesn't include <QObject>.\"\n", (const char *)fn);
779 fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
780 fprintf(out, "#error \"This file was generated using the moc from %s."
781 " It\"\n#error \"cannot be used with the include files from"
782 " this version of Qt.\"\n#error \"(The moc has changed too"
783 " much.)\"\n", QT_VERSION_STR);
784 fprintf(out, "#endif\n\n");
785
786 fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n");
787
788 for (i = 0; i < classList.size(); ++i) {
789 Generator generator(&classList[i], metaTypes, out);
790 generator.generateCode();
791 }
792
793 fprintf(out, "QT_END_MOC_NAMESPACE\n");
794}
795
796
797QList<QMetaObject*> Moc::generate(bool ignoreProperties)
798{
799 QList<QMetaObject*> result;
800 for (int i = 0; i < classList.size(); ++i) {
801 Generator generator(&classList[i], metaTypes);
802 result << generator.generateMetaObject(ignoreProperties);
803 }
804 return result;
805}
806
807void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
808{
809 next(COLON);
810 while (inClass(def) && hasNext()) {
811 switch (next()) {
812 case PUBLIC:
813 case PROTECTED:
814 case PRIVATE:
815 case Q_SIGNALS_TOKEN:
816 case Q_SLOTS_TOKEN:
817 prev();
818 return;
819 case SEMIC:
820 continue;
821 case FRIEND:
822 until(SEMIC);
823 continue;
824 case USING:
825 error("'using' directive not supported in 'slots' section");
826 default:
827 prev();
828 }
829
830 FunctionDef funcDef;
831 funcDef.access = access;
832 if (!parseFunction(&funcDef))
833 continue;
834 def->slotList += funcDef;
835 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
836 funcDef.wasCloned = true;
837 funcDef.arguments.removeLast();
838 def->slotList += funcDef;
839 }
840 }
841}
842
843void Moc::parseSignals(ClassDef *def)
844{
845 next(COLON);
846 while (inClass(def) && hasNext()) {
847 switch (next()) {
848 case PUBLIC:
849 case PROTECTED:
850 case PRIVATE:
851 case Q_SIGNALS_TOKEN:
852 case Q_SLOTS_TOKEN:
853 prev();
854 return;
855 case SEMIC:
856 continue;
857 case FRIEND:
858 until(SEMIC);
859 continue;
860 case USING:
861 error("'using' directive not supported in 'signals' section");
862 default:
863 prev();
864 }
865 FunctionDef funcDef;
866 funcDef.access = FunctionDef::Protected;
867 parseFunction(&funcDef);
868 if (funcDef.isVirtual)
869 warning("Signals cannot be declared virtual");
870 if (funcDef.inlineCode)
871 error("Not a signal declaration");
872 def->signalList += funcDef;
873 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
874 funcDef.wasCloned = true;
875 funcDef.arguments.removeLast();
876 def->signalList += funcDef;
877 }
878 }
879}
880
881void Moc::createPropertyDef(PropertyDef &propDef)
882{
883 QByteArray type = parseType().name;
884 if (type.isEmpty())
885 error();
886 propDef.designable = propDef.scriptable = propDef.stored = "true";
887 propDef.user = "false";
888 /*
889 The Q_PROPERTY construct cannot contain any commas, since
890 commas separate macro arguments. We therefore expect users
891 to type "QMap" instead of "QMap<QString, QVariant>". For
892 coherence, we also expect the same for
893 QValueList<QVariant>, the other template class supported by
894 QVariant.
895 */
896 type = normalizeType(type);
897 if (type == "QMap")
898 type = "QMap<QString,QVariant>";
899 else if (type == "QValueList")
900 type = "QValueList<QVariant>";
901 else if (type == "LongLong")
902 type = "qlonglong";
903 else if (type == "ULongLong")
904 type = "qulonglong";
905 else if (type == "qreal")
906 mustIncludeQMetaTypeH = true;
907
908 propDef.type = type;
909
910 next();
911 propDef.name = lexem();
912 while (test(IDENTIFIER)) {
913 QByteArray l = lexem();
914
915 if (l[0] == 'C' && l == "CONSTANT") {
916 propDef.constant = true;
917 continue;
918 } else if(l[0] == 'F' && l == "FINAL") {
919 propDef.final = true;
920 continue;
921 }
922
923 QByteArray v, v2;
924 if (test(LPAREN)) {
925 v = lexemUntil(RPAREN);
926 } else {
927 next(IDENTIFIER);
928 v = lexem();
929 if (test(LPAREN))
930 v2 = lexemUntil(RPAREN);
931 else if (v != "true" && v != "false")
932 v2 = "()";
933 }
934 switch (l[0]) {
935 case 'R':
936 if (l == "READ")
937 propDef.read = v;
938 else if (l == "RESET")
939 propDef.reset = v + v2;
940 else
941 error(2);
942 break;
943 case 'S':
944 if (l == "SCRIPTABLE")
945 propDef.scriptable = v + v2;
946 else if (l == "STORED")
947 propDef.stored = v + v2;
948 else
949 error(2);
950 break;
951 case 'W': if (l != "WRITE") error(2);
952 propDef.write = v;
953 break;
954 case 'D': if (l != "DESIGNABLE") error(2);
955 propDef.designable = v + v2;
956 break;
957 case 'E': if (l != "EDITABLE") error(2);
958 propDef.editable = v + v2;
959 break;
960 case 'N': if (l != "NOTIFY") error(2);
961 propDef.notify = v;
962 break;
963 case 'U': if (l != "USER") error(2);
964 propDef.user = v + v2;
965 break;
966 default:
967 error(2);
968 }
969 }
970 if (propDef.read.isNull()) {
971 QByteArray msg;
972 msg += "Property declaration ";
973 msg += propDef.name;
974 msg += " has no READ accessor function. The property will be invalid.";
975 warning(msg.constData());
976 }
977 if (propDef.constant && !propDef.write.isNull()) {
978 QByteArray msg;
979 msg += "Property declaration ";
980 msg += propDef.name;
981 msg += " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
982 propDef.constant = false;
983 warning(msg.constData());
984 }
985 if (propDef.constant && !propDef.notify.isNull()) {
986 QByteArray msg;
987 msg += "Property declaration ";
988 msg += propDef.name;
989 msg += " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
990 propDef.constant = false;
991 warning(msg.constData());
992 }
993}
994
995void Moc::parseProperty(ClassDef *def)
996{
997 next(LPAREN);
998 PropertyDef propDef;
999 createPropertyDef(propDef);
1000 next(RPAREN);
1001
1002
1003 if(!propDef.notify.isEmpty())
1004 def->notifyableProperties++;
1005 def->propertyList += propDef;
1006}
1007
1008void Moc::parsePrivateProperty(ClassDef *def)
1009{
1010 next(LPAREN);
1011 PropertyDef propDef;
1012 next(IDENTIFIER);
1013 propDef.inPrivateClass = lexem();
1014 while (test(SCOPE)) {
1015 propDef.inPrivateClass += lexem();
1016 next(IDENTIFIER);
1017 propDef.inPrivateClass += lexem();
1018 }
1019 // also allow void functions
1020 if (test(LPAREN)) {
1021 next(RPAREN);
1022 propDef.inPrivateClass += "()";
1023 }
1024
1025 next(COMMA);
1026
1027 createPropertyDef(propDef);
1028
1029 if(!propDef.notify.isEmpty())
1030 def->notifyableProperties++;
1031
1032 def->propertyList += propDef;
1033}
1034
1035void Moc::parseEnumOrFlag(ClassDef *def, bool isFlag)
1036{
1037 next(LPAREN);
1038 QByteArray identifier;
1039 while (test(IDENTIFIER)) {
1040 identifier = lexem();
1041 while (test(SCOPE) && test(IDENTIFIER)) {
1042 identifier += "::";
1043 identifier += lexem();
1044 }
1045 def->enumDeclarations[identifier] = isFlag;
1046 }
1047 next(RPAREN);
1048}
1049
1050void Moc::parseFlag(ClassDef *def)
1051{
1052 next(LPAREN);
1053 QByteArray flagName, enumName;
1054 while (test(IDENTIFIER)) {
1055 flagName = lexem();
1056 while (test(SCOPE) && test(IDENTIFIER)) {
1057 flagName += "::";
1058 flagName += lexem();
1059 }
1060 }
1061 next(COMMA);
1062 while (test(IDENTIFIER)) {
1063 enumName = lexem();
1064 while (test(SCOPE) && test(IDENTIFIER)) {
1065 enumName += "::";
1066 enumName += lexem();
1067 }
1068 }
1069
1070 def->flagAliases.insert(enumName, flagName);
1071 next(RPAREN);
1072}
1073
1074void Moc::parseClassInfo(ClassDef *def)
1075{
1076 next(LPAREN);
1077 ClassInfoDef infoDef;
1078 next(STRING_LITERAL);
1079 infoDef.name = symbol().unquotedLexem();
1080 next(COMMA);
1081 if (test(STRING_LITERAL)) {
1082 infoDef.value = symbol().unquotedLexem();
1083 } else {
1084 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1085 next(IDENTIFIER);
1086 next(LPAREN);
1087 next(STRING_LITERAL);
1088 infoDef.value = symbol().unquotedLexem();
1089 next(RPAREN);
1090 }
1091 next(RPAREN);
1092 def->classInfoList += infoDef;
1093}
1094
1095void Moc::parseInterfaces(ClassDef *def)
1096{
1097 next(LPAREN);
1098 while (test(IDENTIFIER)) {
1099 QList<ClassDef::Interface> iface;
1100 iface += ClassDef::Interface(lexem());
1101 while (test(SCOPE)) {
1102 iface.last().className += lexem();
1103 next(IDENTIFIER);
1104 iface.last().className += lexem();
1105 }
1106 while (test(COLON)) {
1107 next(IDENTIFIER);
1108 iface += ClassDef::Interface(lexem());
1109 while (test(SCOPE)) {
1110 iface.last().className += lexem();
1111 next(IDENTIFIER);
1112 iface.last().className += lexem();
1113 }
1114 }
1115 // resolve from classnames to interface ids
1116 for (int i = 0; i < iface.count(); ++i) {
1117 const QByteArray iid = interface2IdMap.value(iface.at(i).className);
1118 if (iid.isEmpty())
1119 error("Undefined interface");
1120
1121 iface[i].interfaceId = iid;
1122 }
1123 def->interfaceList += iface;
1124 }
1125 next(RPAREN);
1126}
1127
1128void Moc::parseDeclareInterface()
1129{
1130 next(LPAREN);
1131 QByteArray interface;
1132 next(IDENTIFIER);
1133 interface += lexem();
1134 while (test(SCOPE)) {
1135 interface += lexem();
1136 next(IDENTIFIER);
1137 interface += lexem();
1138 }
1139 next(COMMA);
1140 QByteArray iid;
1141 if (test(STRING_LITERAL)) {
1142 iid = lexem();
1143 } else {
1144 next(IDENTIFIER);
1145 iid = lexem();
1146 }
1147 interface2IdMap.insert(interface, iid);
1148 next(RPAREN);
1149}
1150
1151void Moc::parseDeclareMetatype()
1152{
1153 next(LPAREN);
1154 QByteArray typeName = lexemUntil(RPAREN);
1155 typeName.remove(0, 1);
1156 typeName.chop(1);
1157 metaTypes.append(typeName);
1158}
1159
1160void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
1161{
1162 next(LPAREN);
1163 FunctionDef funcDef;
1164 next(IDENTIFIER);
1165 funcDef.inPrivateClass = lexem();
1166 // also allow void functions
1167 if (test(LPAREN)) {
1168 next(RPAREN);
1169 funcDef.inPrivateClass += "()";
1170 }
1171 next(COMMA);
1172 funcDef.access = access;
1173 parseFunction(&funcDef, true);
1174 def->slotList += funcDef;
1175 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
1176 funcDef.wasCloned = true;
1177 funcDef.arguments.removeLast();
1178 def->slotList += funcDef;
1179 }
1180}
1181
1182QByteArray Moc::lexemUntil(Token target)
1183{
1184 int from = index;
1185 until(target);
1186 QByteArray s;
1187 while (from <= index) {
1188 QByteArray n = symbols.at(from++-1).lexem();
1189 if (s.size() && n.size()
1190 && is_ident_char(s.at(s.size()-1))
1191 && is_ident_char(n.at(0)))
1192 s += ' ';
1193 s += n;
1194 }
1195 return s;
1196}
1197
1198bool Moc::until(Token target) {
1199 int braceCount = 0;
1200 int brackCount = 0;
1201 int parenCount = 0;
1202 int angleCount = 0;
1203 if (index) {
1204 switch(symbols.at(index-1).token) {
1205 case LBRACE: ++braceCount; break;
1206 case LBRACK: ++brackCount; break;
1207 case LPAREN: ++parenCount; break;
1208 case LANGLE: ++angleCount; break;
1209 default: break;
1210 }
1211 }
1212
1213 //when searching commas within the default argument, we should take care of template depth (anglecount)
1214 // unfortunatelly, we do not have enough semantic information to know if '<' is the operator< or
1215 // the beginning of a template type. so we just use heuristics.
1216 int possible = -1;
1217
1218 while (index < symbols.size()) {
1219 Token t = symbols.at(index++).token;
1220 switch (t) {
1221 case LBRACE: ++braceCount; break;
1222 case RBRACE: --braceCount; break;
1223 case LBRACK: ++brackCount; break;
1224 case RBRACK: --brackCount; break;
1225 case LPAREN: ++parenCount; break;
1226 case RPAREN: --parenCount; break;
1227 case LANGLE: ++angleCount; break;
1228 case RANGLE: --angleCount; break;
1229 case GTGT: angleCount -= 2; t = RANGLE; break;
1230 default: break;
1231 }
1232 if (t == target
1233 && braceCount <= 0
1234 && brackCount <= 0
1235 && parenCount <= 0
1236 && (target != RANGLE || angleCount <= 0)) {
1237 if (target != COMMA || angleCount <= 0)
1238 return true;
1239 possible = index;
1240 }
1241
1242 if (target == COMMA && t == EQ && possible != -1) {
1243 index = possible;
1244 return true;
1245 }
1246
1247 if (braceCount < 0 || brackCount < 0 || parenCount < 0
1248 || (target == RANGLE && angleCount < 0)) {
1249 --index;
1250 break;
1251 }
1252 }
1253
1254 if(target == COMMA && angleCount != 0 && possible != -1) {
1255 index = possible;
1256 return true;
1257 }
1258
1259 return false;
1260}
1261
1262void Moc::checkSuperClasses(ClassDef *def)
1263{
1264 const QByteArray firstSuperclass = def->superclassList.value(0).first;
1265
1266 if (!knownQObjectClasses.contains(firstSuperclass)) {
1267 // enable once we /require/ include paths
1268#if 0
1269 QByteArray msg;
1270 msg += "Class ";
1271 msg += def->className;
1272 msg += " contains the Q_OBJECT macro and inherits from ";
1273 msg += def->superclassList.value(0);
1274 msg += " but that is not a known QObject subclass. You may get compilation errors.";
1275 warning(msg.constData());
1276#endif
1277 return;
1278 }
1279 for (int i = 1; i < def->superclassList.count(); ++i) {
1280 const QByteArray superClass = def->superclassList.at(i).first;
1281 if (knownQObjectClasses.contains(superClass)) {
1282 QByteArray msg;
1283 msg += "Class ";
1284 msg += def->classname;
1285 msg += " inherits from two QObject subclasses ";
1286 msg += firstSuperclass;
1287 msg += " and ";
1288 msg += superClass;
1289 msg += ". This is not supported!";
1290 warning(msg.constData());
1291 }
1292
1293 if (interface2IdMap.contains(superClass)) {
1294 bool registeredInterface = false;
1295 for (int i = 0; i < def->interfaceList.count(); ++i)
1296 if (def->interfaceList.at(i).first().className == superClass) {
1297 registeredInterface = true;
1298 break;
1299 }
1300
1301 if (!registeredInterface) {
1302 QByteArray msg;
1303 msg += "Class ";
1304 msg += def->classname;
1305 msg += " implements the interface ";
1306 msg += superClass;
1307 msg += " but does not list it in Q_INTERFACES. qobject_cast to ";
1308 msg += superClass;
1309 msg += " will not work!";
1310 warning(msg.constData());
1311 }
1312 }
1313 }
1314}
1315
1316void Moc::checkProperties(ClassDef *cdef)
1317{
1318 //
1319 // specify get function, for compatibiliy we accept functions
1320 // returning pointers, or const char * for QByteArray.
1321 //
1322 for (int i = 0; i < cdef->propertyList.count(); ++i) {
1323 PropertyDef &p = cdef->propertyList[i];
1324 if (p.read.isEmpty())
1325 continue;
1326 for (int j = 0; j < cdef->publicList.count(); ++j) {
1327 const FunctionDef &f = cdef->publicList.at(j);
1328 if (f.name != p.read)
1329 continue;
1330 if (!f.isConst) // get functions must be const
1331 continue;
1332 if (f.arguments.size()) // and must not take any arguments
1333 continue;
1334 PropertyDef::Specification spec = PropertyDef::ValueSpec;
1335 QByteArray tmp = f.normalizedType;
1336 if (p.type == "QByteArray" && tmp == "const char *")
1337 tmp = "QByteArray";
1338 if (tmp.left(6) == "const ")
1339 tmp = tmp.mid(6);
1340 if (p.type != tmp && tmp.endsWith('*')) {
1341 tmp.chop(1);
1342 spec = PropertyDef::PointerSpec;
1343 } else if (f.type.name.endsWith('&')) { // raw type, not normalized type
1344 spec = PropertyDef::ReferenceSpec;
1345 }
1346 if (p.type != tmp)
1347 continue;
1348 p.gspec = spec;
1349 break;
1350 }
1351 if(!p.notify.isEmpty()) {
1352 int notifyId = -1;
1353 for (int j = 0; j < cdef->signalList.count(); ++j) {
1354 const FunctionDef &f = cdef->signalList.at(j);
1355 if(f.name != p.notify) {
1356 continue;
1357 } else {
1358 notifyId = j /* Signal indexes start from 0 */;
1359 break;
1360 }
1361 }
1362 p.notifyId = notifyId;
1363 if (notifyId == -1) {
1364 QByteArray msg = "NOTIFY signal '" + p.notify + "' of property '" + p.name
1365 + "' does not exist in class " + cdef->classname + ".";
1366 error(msg.constData());
1367 }
1368 }
1369 }
1370}
1371
1372
1373
1374QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.