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

Last change on this file since 203 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 38.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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)) {
234 type.name += ' ';
235 type.name += lexem();
236 if (lookup(0) == AND)
237 type.referenceType = Type::Reference;
238 else if (lookup(0) == STAR)
239 type.referenceType = Type::Pointer;
240 }
241 // transform stupid things like 'const void' or 'void const' into 'void'
242 if (isVoid && type.referenceType == Type::NoReference) {
243 type.name = "void";
244 }
245 return type;
246}
247
248bool Moc::parseEnum(EnumDef *def)
249{
250 bool isTypdefEnum = false; // typedef enum { ... } Foo;
251
252 if (test(IDENTIFIER)) {
253 def->name = lexem();
254 } else {
255 if (lookup(-1) != TYPEDEF)
256 return false; // anonymous enum
257 isTypdefEnum = true;
258 }
259 if (!test(LBRACE))
260 return false;
261 do {
262 if (lookup() == RBRACE) // accept trailing comma
263 break;
264 next(IDENTIFIER);
265 def->values += lexem();
266 } while (test(EQ) ? until(COMMA) : test(COMMA));
267 next(RBRACE);
268 if (isTypdefEnum) {
269 if (!test(IDENTIFIER))
270 return false;
271 def->name = lexem();
272 }
273 return true;
274}
275
276void Moc::parseFunctionArguments(FunctionDef *def)
277{
278 Q_UNUSED(def);
279 while (hasNext()) {
280 ArgumentDef arg;
281 arg.type = parseType();
282 if (arg.type.name == "void")
283 break;
284 if (test(IDENTIFIER))
285 arg.name = lexem();
286 while (test(LBRACK)) {
287 arg.rightType += lexemUntil(RBRACK);
288 }
289 if (test(CONST) || test(VOLATILE)) {
290 arg.rightType += ' ';
291 arg.rightType += lexem();
292 }
293 arg.normalizedType = normalizeType(arg.type.name + ' ' + arg.rightType);
294 arg.typeNameForCast = normalizeType(noRef(arg.type.name) + "(*)" + arg.rightType);
295 if (test(EQ))
296 arg.isDefault = true;
297 def->arguments += arg;
298 if (!until(COMMA))
299 break;
300 }
301}
302
303bool Moc::testFunctionAttribute(FunctionDef *def)
304{
305 if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) {
306 ++index;
307 return true;
308 }
309 return false;
310}
311
312bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
313{
314 switch (tok) {
315 case Q_MOC_COMPAT_TOKEN:
316 case Q_QT3_SUPPORT_TOKEN:
317 def->isCompat = true;
318 return true;
319 case Q_INVOKABLE_TOKEN:
320 def->isInvokable = true;
321 return true;
322 case Q_SIGNAL_TOKEN:
323 def->isSignal = true;
324 return true;
325 case Q_SLOT_TOKEN:
326 def->isSlot = true;
327 return true;
328 case Q_SCRIPTABLE_TOKEN:
329 def->isInvokable = def->isScriptable = true;
330 return true;
331 default: break;
332 }
333 return false;
334}
335
336// returns false if the function should be ignored
337bool Moc::parseFunction(FunctionDef *def, bool inMacro)
338{
339 def->isVirtual = false;
340 while (test(INLINE) || test(STATIC) || test(VIRTUAL)
341 || testFunctionAttribute(def)) {
342 if (lookup() == VIRTUAL)
343 def->isVirtual = true;
344 }
345 bool templateFunction = (lookup() == TEMPLATE);
346 def->type = parseType();
347 if (def->type.name.isEmpty()) {
348 if (templateFunction)
349 error("Template function as signal or slot");
350 else
351 error();
352 }
353 bool scopedFunctionName = false;
354 if (test(LPAREN)) {
355 def->name = def->type.name;
356 scopedFunctionName = def->type.isScoped;
357 def->type = Type("int");
358 } else {
359 Type tempType = parseType();;
360 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
361 if (testFunctionAttribute(def->type.firstToken, def))
362 ; // fine
363 else if (def->type.firstToken == Q_SIGNALS_TOKEN)
364 error();
365 else if (def->type.firstToken == Q_SLOTS_TOKEN)
366 error();
367 else {
368 if (!def->tag.isEmpty())
369 def->tag += ' ';
370 def->tag += def->type.name;
371 }
372 def->type = tempType;
373 tempType = parseType();
374 }
375 next(LPAREN, "Not a signal or slot declaration");
376 def->name = tempType.name;
377 scopedFunctionName = tempType.isScoped;
378 }
379
380 // we don't support references as return types, it's too dangerous
381 if (def->type.referenceType == Type::Reference)
382 def->type = Type("void");
383
384 def->normalizedType = normalizeType(def->type.name);
385
386 if (!test(RPAREN)) {
387 parseFunctionArguments(def);
388 next(RPAREN);
389 }
390
391 // support optional macros with compiler specific options
392 while (test(IDENTIFIER))
393 ;
394
395 def->isConst = test(CONST);
396
397 while (test(IDENTIFIER))
398 ;
399
400 if (inMacro) {
401 next(RPAREN);
402 prev();
403 } else {
404 if (test(THROW)) {
405 next(LPAREN);
406 until(RPAREN);
407 }
408 if (test(SEMIC))
409 ;
410 else if ((def->inlineCode = test(LBRACE)))
411 until(RBRACE);
412 else if ((def->isAbstract = test(EQ)))
413 until(SEMIC);
414 else
415 error();
416 }
417
418 if (scopedFunctionName) {
419 QByteArray msg("Function declaration ");
420 msg += def->name;
421 msg += " contains extra qualification. Ignoring as signal or slot.";
422 warning(msg.constData());
423 return false;
424 }
425 return true;
426}
427
428// like parseFunction, but never aborts with an error
429bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
430{
431 def->isVirtual = false;
432 while (test(EXPLICIT) || test(INLINE) || test(STATIC) || test(VIRTUAL)
433 || testFunctionAttribute(def)) {
434 if (lookup() == VIRTUAL)
435 def->isVirtual = true;
436 }
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 ENUM: {
664 EnumDef enumDef;
665 if (parseEnum(&enumDef))
666 def.enumList += enumDef;
667 } break;
668 default:
669 FunctionDef funcDef;
670 funcDef.access = access;
671 int rewind = index;
672 if (parseMaybeFunction(&def, &funcDef)) {
673 if (funcDef.isConstructor) {
674 if ((access == FunctionDef::Public) && funcDef.isInvokable) {
675 def.constructorList += funcDef;
676 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
677 funcDef.wasCloned = true;
678 funcDef.arguments.removeLast();
679 def.constructorList += funcDef;
680 }
681 }
682 } else if (funcDef.isDestructor) {
683 // don't care about destructors
684 } else {
685 if (access == FunctionDef::Public)
686 def.publicList += funcDef;
687 if (funcDef.isSlot) {
688 def.slotList += funcDef;
689 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
690 funcDef.wasCloned = true;
691 funcDef.arguments.removeLast();
692 def.slotList += funcDef;
693 }
694 } else if (funcDef.isSignal) {
695 def.signalList += funcDef;
696 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
697 funcDef.wasCloned = true;
698 funcDef.arguments.removeLast();
699 def.signalList += funcDef;
700 }
701 } else if (funcDef.isInvokable) {
702 def.methodList += funcDef;
703 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
704 funcDef.wasCloned = true;
705 funcDef.arguments.removeLast();
706 def.methodList += funcDef;
707 }
708 }
709 }
710 } else {
711 index = rewind;
712 }
713 }
714 }
715
716 next(RBRACE);
717
718 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
719 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
720 continue; // no meta object code required
721
722
723 if (!def.hasQObject && !def.hasQGadget)
724 error("Class declarations lacks Q_OBJECT macro.");
725
726 checkSuperClasses(&def);
727
728 classList += def;
729 knownQObjectClasses.insert(def.classname);
730 knownQObjectClasses.insert(def.qualified);
731 }
732 }
733}
734
735void Moc::generate(FILE *out)
736{
737
738 QDateTime dt = QDateTime::currentDateTime();
739 QByteArray dstr = dt.toString().toLatin1();
740 QByteArray fn = filename;
741 int i = filename.length()-1;
742 while (i>0 && filename[i-1] != '/' && filename[i-1] != '\\')
743 --i; // skip path
744 if (i >= 0)
745 fn = filename.mid(i);
746 fprintf(out, "/****************************************************************************\n"
747 "** Meta object code from reading C++ file '%s'\n**\n" , (const char*)fn);
748 fprintf(out, "** Created: %s\n"
749 "** by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , dstr.data(), mocOutputRevision, QT_VERSION_STR);
750 fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
751 "*****************************************************************************/\n\n");
752
753
754 if (!noInclude) {
755 if (includePath.size() && includePath.right(1) != "/")
756 includePath += "/";
757 for (int i = 0; i < includeFiles.size(); ++i) {
758 QByteArray inc = includeFiles.at(i);
759 if (inc[0] != '<' && inc[0] != '"') {
760 if (includePath.size() && includePath != "./")
761 inc.prepend(includePath);
762 inc = "\"" + inc + "\"";
763 }
764 fprintf(out, "#include %s\n", inc.constData());
765 }
766 }
767 if (classList.size() && classList.first().classname == "Qt")
768 fprintf(out, "#include <QtCore/qobject.h>\n");
769
770 fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
771 "#error \"The header file '%s' doesn't include <QObject>.\"\n", (const char *)fn);
772 fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
773 fprintf(out, "#error \"This file was generated using the moc from %s."
774 " It\"\n#error \"cannot be used with the include files from"
775 " this version of Qt.\"\n#error \"(The moc has changed too"
776 " much.)\"\n", QT_VERSION_STR);
777 fprintf(out, "#endif\n\n");
778
779 fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n");
780
781 for (i = 0; i < classList.size(); ++i) {
782 Generator generator(&classList[i], metaTypes, out);
783 generator.generateCode();
784 }
785
786 fprintf(out, "QT_END_MOC_NAMESPACE\n");
787}
788
789
790QList<QMetaObject*> Moc::generate(bool ignoreProperties)
791{
792 QList<QMetaObject*> result;
793 for (int i = 0; i < classList.size(); ++i) {
794 Generator generator(&classList[i], metaTypes);
795 result << generator.generateMetaObject(ignoreProperties);
796 }
797 return result;
798}
799
800void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
801{
802 next(COLON);
803 while (inClass(def) && hasNext()) {
804 switch (next()) {
805 case PUBLIC:
806 case PROTECTED:
807 case PRIVATE:
808 case Q_SIGNALS_TOKEN:
809 case Q_SLOTS_TOKEN:
810 prev();
811 return;
812 case SEMIC:
813 continue;
814 case FRIEND:
815 until(SEMIC);
816 continue;
817 case USING:
818 error("'using' directive not supported in 'slots' section");
819 default:
820 prev();
821 }
822
823 FunctionDef funcDef;
824 funcDef.access = access;
825 if (!parseFunction(&funcDef))
826 continue;
827 def->slotList += funcDef;
828 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
829 funcDef.wasCloned = true;
830 funcDef.arguments.removeLast();
831 def->slotList += funcDef;
832 }
833 }
834}
835
836void Moc::parseSignals(ClassDef *def)
837{
838 next(COLON);
839 while (inClass(def) && hasNext()) {
840 switch (next()) {
841 case PUBLIC:
842 case PROTECTED:
843 case PRIVATE:
844 case Q_SIGNALS_TOKEN:
845 case Q_SLOTS_TOKEN:
846 prev();
847 return;
848 case SEMIC:
849 continue;
850 case FRIEND:
851 until(SEMIC);
852 continue;
853 case USING:
854 error("'using' directive not supported in 'signals' section");
855 default:
856 prev();
857 }
858 FunctionDef funcDef;
859 funcDef.access = FunctionDef::Protected;
860 parseFunction(&funcDef);
861 if (funcDef.isVirtual)
862 error("Signals cannot be declared virtual");
863 if (funcDef.inlineCode)
864 error("Not a signal declaration");
865 def->signalList += funcDef;
866 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
867 funcDef.wasCloned = true;
868 funcDef.arguments.removeLast();
869 def->signalList += funcDef;
870 }
871 }
872}
873
874
875void Moc::parseProperty(ClassDef *def)
876{
877 next(LPAREN);
878 PropertyDef propDef;
879 QByteArray type = parseType().name;
880 if (type.isEmpty())
881 error();
882 propDef.designable = propDef.scriptable = propDef.stored = "true";
883 propDef.user = "false";
884 /*
885 The Q_PROPERTY construct cannot contain any commas, since
886 commas separate macro arguments. We therefore expect users
887 to type "QMap" instead of "QMap<QString, QVariant>". For
888 coherence, we also expect the same for
889 QValueList<QVariant>, the other template class supported by
890 QVariant.
891 */
892 type = normalizeType(type);
893 if (type == "QMap")
894 type = "QMap<QString,QVariant>";
895 else if (type == "QValueList")
896 type = "QValueList<QVariant>";
897 else if (type == "LongLong")
898 type = "qlonglong";
899 else if (type == "ULongLong")
900 type = "qulonglong";
901 propDef.type = type;
902
903 next();
904 propDef.name = lexem();
905 while (test(IDENTIFIER)) {
906 QByteArray l = lexem();
907 QByteArray v, v2;
908 if (test(LPAREN)) {
909 v = lexemUntil(RPAREN);
910 } else {
911 next(IDENTIFIER);
912 v = lexem();
913 if (test(LPAREN))
914 v2 = lexemUntil(RPAREN);
915 else if (v != "true" && v != "false")
916 v2 = "()";
917 }
918 switch (l[0]) {
919 case 'R':
920 if (l == "READ")
921 propDef.read = v;
922 else if (l == "RESET")
923 propDef.reset = v + v2;
924 else
925 error(2);
926 break;
927 case 'S':
928 if (l == "SCRIPTABLE")
929 propDef.scriptable = v + v2;
930 else if (l == "STORED")
931 propDef.stored = v + v2;
932 else
933 error(2);
934 break;
935 case 'W': if (l != "WRITE") error(2);
936 propDef.write = v;
937 break;
938 case 'D': if (l != "DESIGNABLE") error(2);
939 propDef.designable = v + v2;
940 break;
941 case 'E': if (l != "EDITABLE") error(2);
942 propDef.editable = v + v2;
943 break;
944 case 'N': if (l != "NOTIFY") error(2);
945 propDef.notify = v;
946 break;
947 case 'U': if (l != "USER") error(2);
948 propDef.user = v + v2;
949 break;
950 default:
951 error(2);
952 }
953 }
954 next(RPAREN);
955 if (propDef.read.isNull()) {
956 QByteArray msg;
957 msg += "Property declaration ";
958 msg += propDef.name;
959 msg += " has no READ accessor function. The property will be invalid.";
960 warning(msg.constData());
961 }
962 if(!propDef.notify.isEmpty())
963 def->notifyableProperties++;
964
965 def->propertyList += propDef;
966}
967
968void Moc::parseEnumOrFlag(ClassDef *def, bool isFlag)
969{
970 next(LPAREN);
971 QByteArray identifier;
972 while (test(IDENTIFIER)) {
973 identifier = lexem();
974 while (test(SCOPE) && test(IDENTIFIER)) {
975 identifier += "::";
976 identifier += lexem();
977 }
978 def->enumDeclarations[identifier] = isFlag;
979 }
980 next(RPAREN);
981}
982
983void Moc::parseFlag(ClassDef *def)
984{
985 next(LPAREN);
986 QByteArray flagName, enumName;
987 while (test(IDENTIFIER)) {
988 flagName = lexem();
989 while (test(SCOPE) && test(IDENTIFIER)) {
990 flagName += "::";
991 flagName += lexem();
992 }
993 }
994 next(COMMA);
995 while (test(IDENTIFIER)) {
996 enumName = lexem();
997 while (test(SCOPE) && test(IDENTIFIER)) {
998 enumName += "::";
999 enumName += lexem();
1000 }
1001 }
1002
1003 def->flagAliases.insert(enumName, flagName);
1004 next(RPAREN);
1005}
1006
1007void Moc::parseClassInfo(ClassDef *def)
1008{
1009 next(LPAREN);
1010 ClassInfoDef infoDef;
1011 next(STRING_LITERAL);
1012 infoDef.name = symbol().unquotedLexem();
1013 next(COMMA);
1014 if (test(STRING_LITERAL)) {
1015 infoDef.value = symbol().unquotedLexem();
1016 } else {
1017 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1018 next(IDENTIFIER);
1019 next(LPAREN);
1020 next(STRING_LITERAL);
1021 infoDef.value = symbol().unquotedLexem();
1022 next(RPAREN);
1023 }
1024 next(RPAREN);
1025 def->classInfoList += infoDef;
1026}
1027
1028void Moc::parseInterfaces(ClassDef *def)
1029{
1030 next(LPAREN);
1031 while (test(IDENTIFIER)) {
1032 QList<ClassDef::Interface> iface;
1033 iface += ClassDef::Interface(lexem());
1034 while (test(SCOPE)) {
1035 iface.last().className += lexem();
1036 next(IDENTIFIER);
1037 iface.last().className += lexem();
1038 }
1039 while (test(COLON)) {
1040 next(IDENTIFIER);
1041 iface += ClassDef::Interface(lexem());
1042 while (test(SCOPE)) {
1043 iface.last().className += lexem();
1044 next(IDENTIFIER);
1045 iface.last().className += lexem();
1046 }
1047 }
1048 // resolve from classnames to interface ids
1049 for (int i = 0; i < iface.count(); ++i) {
1050 const QByteArray iid = interface2IdMap.value(iface.at(i).className);
1051 if (iid.isEmpty())
1052 error("Undefined interface");
1053
1054 iface[i].interfaceId = iid;
1055 }
1056 def->interfaceList += iface;
1057 }
1058 next(RPAREN);
1059}
1060
1061void Moc::parseDeclareInterface()
1062{
1063 next(LPAREN);
1064 QByteArray interface;
1065 next(IDENTIFIER);
1066 interface += lexem();
1067 while (test(SCOPE)) {
1068 interface += lexem();
1069 next(IDENTIFIER);
1070 interface += lexem();
1071 }
1072 next(COMMA);
1073 QByteArray iid;
1074 if (test(STRING_LITERAL)) {
1075 iid = lexem();
1076 } else {
1077 next(IDENTIFIER);
1078 iid = lexem();
1079 }
1080 interface2IdMap.insert(interface, iid);
1081 next(RPAREN);
1082}
1083
1084void Moc::parseDeclareMetatype()
1085{
1086 next(LPAREN);
1087 QByteArray typeName = lexemUntil(RPAREN);
1088 typeName.remove(0, 1);
1089 typeName.chop(1);
1090 metaTypes.append(typeName);
1091}
1092
1093void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
1094{
1095 next(LPAREN);
1096 FunctionDef funcDef;
1097 next(IDENTIFIER);
1098 funcDef.inPrivateClass = lexem();
1099 // also allow void functions
1100 if (test(LPAREN)) {
1101 next(RPAREN);
1102 funcDef.inPrivateClass += "()";
1103 }
1104 next(COMMA);
1105 funcDef.access = access;
1106 parseFunction(&funcDef, true);
1107 def->slotList += funcDef;
1108 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
1109 funcDef.wasCloned = true;
1110 funcDef.arguments.removeLast();
1111 def->slotList += funcDef;
1112 }
1113}
1114
1115QByteArray Moc::lexemUntil(Token target)
1116{
1117 int from = index;
1118 until(target);
1119 QByteArray s;
1120 while (from <= index) {
1121 QByteArray n = symbols.at(from++-1).lexem();
1122 if (s.size() && n.size()
1123 && is_ident_char(s.at(s.size()-1))
1124 && is_ident_char(n.at(0)))
1125 s += ' ';
1126 s += n;
1127 }
1128 return s;
1129}
1130
1131bool Moc::until(Token target) {
1132 int braceCount = 0;
1133 int brackCount = 0;
1134 int parenCount = 0;
1135 int angleCount = 0;
1136 if (index) {
1137 switch(symbols.at(index-1).token) {
1138 case LBRACE: ++braceCount; break;
1139 case LBRACK: ++brackCount; break;
1140 case LPAREN: ++parenCount; break;
1141 case LANGLE: ++angleCount; break;
1142 default: break;
1143 }
1144 }
1145 while (index < symbols.size()) {
1146 Token t = symbols.at(index++).token;
1147 switch (t) {
1148 case LBRACE: ++braceCount; break;
1149 case RBRACE: --braceCount; break;
1150 case LBRACK: ++brackCount; break;
1151 case RBRACK: --brackCount; break;
1152 case LPAREN: ++parenCount; break;
1153 case RPAREN: --parenCount; break;
1154 case LANGLE: ++angleCount; break;
1155 case RANGLE: --angleCount; break;
1156 case GTGT: angleCount -= 2; t = RANGLE; break;
1157 default: break;
1158 }
1159 if (t == target
1160 && braceCount <= 0
1161 && brackCount <= 0
1162 && parenCount <= 0
1163 && (target != RANGLE || angleCount <= 0))
1164 return true;
1165
1166 if (braceCount < 0 || brackCount < 0 || parenCount < 0
1167 || (target == RANGLE && angleCount < 0)) {
1168 --index;
1169 break;
1170 }
1171 }
1172 return false;
1173}
1174
1175void Moc::checkSuperClasses(ClassDef *def)
1176{
1177 const QByteArray firstSuperclass = def->superclassList.value(0).first;
1178
1179 if (!knownQObjectClasses.contains(firstSuperclass)) {
1180 // enable once we /require/ include paths
1181#if 0
1182 QByteArray msg;
1183 msg += "Class ";
1184 msg += def->className;
1185 msg += " contains the Q_OBJECT macro and inherits from ";
1186 msg += def->superclassList.value(0);
1187 msg += " but that is not a known QObject subclass. You may get compilation errors.";
1188 warning(msg.constData());
1189#endif
1190 return;
1191 }
1192 for (int i = 1; i < def->superclassList.count(); ++i) {
1193 const QByteArray superClass = def->superclassList.at(i).first;
1194 if (knownQObjectClasses.contains(superClass)) {
1195 QByteArray msg;
1196 msg += "Class ";
1197 msg += def->classname;
1198 msg += " inherits from two QObject subclasses ";
1199 msg += firstSuperclass;
1200 msg += " and ";
1201 msg += superClass;
1202 msg += ". This is not supported!";
1203 warning(msg.constData());
1204 }
1205
1206 if (interface2IdMap.contains(superClass)) {
1207 bool registeredInterface = false;
1208 for (int i = 0; i < def->interfaceList.count(); ++i)
1209 if (def->interfaceList.at(i).first().className == superClass) {
1210 registeredInterface = true;
1211 break;
1212 }
1213
1214 if (!registeredInterface) {
1215 QByteArray msg;
1216 msg += "Class ";
1217 msg += def->classname;
1218 msg += " implements the interface ";
1219 msg += superClass;
1220 msg += " but does not list it in Q_INTERFACES. qobject_cast to ";
1221 msg += superClass;
1222 msg += " will not work!";
1223 warning(msg.constData());
1224 }
1225 }
1226 }
1227}
1228
1229
1230QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.