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

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

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

File size: 40.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
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 qt-info@nokia.com.
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 //skip modifiers and attributes
341 while (test(INLINE) || test(STATIC) ||
342 (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual
343 || testFunctionAttribute(def)) {}
344 bool templateFunction = (lookup() == TEMPLATE);
345 def->type = parseType();
346 if (def->type.name.isEmpty()) {
347 if (templateFunction)
348 error("Template function as signal or slot");
349 else
350 error();
351 }
352 bool scopedFunctionName = false;
353 if (test(LPAREN)) {
354 def->name = def->type.name;
355 scopedFunctionName = def->type.isScoped;
356 def->type = Type("int");
357 } else {
358 Type tempType = parseType();;
359 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
360 if (testFunctionAttribute(def->type.firstToken, def))
361 ; // fine
362 else if (def->type.firstToken == Q_SIGNALS_TOKEN)
363 error();
364 else if (def->type.firstToken == Q_SLOTS_TOKEN)
365 error();
366 else {
367 if (!def->tag.isEmpty())
368 def->tag += ' ';
369 def->tag += def->type.name;
370 }
371 def->type = tempType;
372 tempType = parseType();
373 }
374 next(LPAREN, "Not a signal or slot declaration");
375 def->name = tempType.name;
376 scopedFunctionName = tempType.isScoped;
377 }
378
379 // we don't support references as return types, it's too dangerous
380 if (def->type.referenceType == Type::Reference)
381 def->type = Type("void");
382
383 def->normalizedType = normalizeType(def->type.name);
384
385 if (!test(RPAREN)) {
386 parseFunctionArguments(def);
387 next(RPAREN);
388 }
389
390 // support optional macros with compiler specific options
391 while (test(IDENTIFIER))
392 ;
393
394 def->isConst = test(CONST);
395
396 while (test(IDENTIFIER))
397 ;
398
399 if (inMacro) {
400 next(RPAREN);
401 prev();
402 } else {
403 if (test(THROW)) {
404 next(LPAREN);
405 until(RPAREN);
406 }
407 if (test(SEMIC))
408 ;
409 else if ((def->inlineCode = test(LBRACE)))
410 until(RBRACE);
411 else if ((def->isAbstract = test(EQ)))
412 until(SEMIC);
413 else
414 error();
415 }
416
417 if (scopedFunctionName) {
418 QByteArray msg("Function declaration ");
419 msg += def->name;
420 msg += " contains extra qualification. Ignoring as signal or slot.";
421 warning(msg.constData());
422 return false;
423 }
424 return true;
425}
426
427// like parseFunction, but never aborts with an error
428bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
429{
430 def->isVirtual = false;
431 //skip modifiers and attributes
432 while (test(EXPLICIT) || test(INLINE) || test(STATIC) ||
433 (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual
434 || testFunctionAttribute(def)) {}
435 bool tilde = test(TILDE);
436 def->type = parseType();
437 if (def->type.name.isEmpty())
438 return false;
439 bool scopedFunctionName = false;
440 if (test(LPAREN)) {
441 def->name = def->type.name;
442 scopedFunctionName = def->type.isScoped;
443 if (def->name == cdef->classname) {
444 def->isDestructor = tilde;
445 def->isConstructor = !tilde;
446 def->type = Type();
447 } else {
448 def->type = Type("int");
449 }
450 } else {
451 Type tempType = parseType();;
452 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
453 if (testFunctionAttribute(def->type.firstToken, def))
454 ; // fine
455 else if (def->type.name == "Q_SIGNAL")
456 def->isSignal = true;
457 else if (def->type.name == "Q_SLOT")
458 def->isSlot = true;
459 else {
460 if (!def->tag.isEmpty())
461 def->tag += ' ';
462 def->tag += def->type.name;
463 }
464 def->type = tempType;
465 tempType = parseType();
466 }
467 if (!test(LPAREN))
468 return false;
469 def->name = tempType.name;
470 scopedFunctionName = tempType.isScoped;
471 }
472
473 // we don't support references as return types, it's too dangerous
474 if (def->type.referenceType == Type::Reference)
475 def->type = Type("void");
476
477 def->normalizedType = normalizeType(def->type.name);
478
479 if (!test(RPAREN)) {
480 parseFunctionArguments(def);
481 if (!test(RPAREN))
482 return false;
483 }
484 def->isConst = test(CONST);
485 if (scopedFunctionName
486 && (def->isSignal || def->isSlot || def->isInvokable)) {
487 QByteArray msg("parsemaybe: Function declaration ");
488 msg += def->name;
489 msg += " contains extra qualification. Ignoring as signal or slot.";
490 warning(msg.constData());
491 return false;
492 }
493 return true;
494}
495
496
497void Moc::parse()
498{
499 QList<NamespaceDef> namespaceList;
500 bool templateClass = false;
501 while (hasNext()) {
502 Token t = next();
503 switch (t) {
504 case NAMESPACE: {
505 int rewind = index;
506 if (test(IDENTIFIER)) {
507 if (test(EQ)) {
508 // namespace Foo = Bar::Baz;
509 until(SEMIC);
510 } else if (!test(SEMIC)) {
511 NamespaceDef def;
512 def.name = lexem();
513 next(LBRACE);
514 def.begin = index - 1;
515 until(RBRACE);
516 def.end = index;
517 index = def.begin + 1;
518 namespaceList += def;
519 index = rewind;
520 }
521 }
522 break;
523 }
524 case SEMIC:
525 case RBRACE:
526 templateClass = false;
527 break;
528 case TEMPLATE:
529 templateClass = true;
530 break;
531 case MOC_INCLUDE_BEGIN:
532 currentFilenames.push(symbol().unquotedLexem());
533 break;
534 case MOC_INCLUDE_END:
535 currentFilenames.pop();
536 break;
537 case Q_DECLARE_INTERFACE_TOKEN:
538 parseDeclareInterface();
539 break;
540 case Q_DECLARE_METATYPE_TOKEN:
541 parseDeclareMetatype();
542 break;
543 case USING:
544 if (test(NAMESPACE)) {
545 while (test(SCOPE) || test(IDENTIFIER))
546 ;
547 next(SEMIC);
548 }
549 break;
550 case CLASS:
551 case STRUCT: {
552 if (currentFilenames.size() <= 1)
553 break;
554
555 ClassDef def;
556 if (!parseClassHead(&def))
557 continue;
558
559 while (inClass(&def) && hasNext()) {
560 if (next() == Q_OBJECT_TOKEN) {
561 def.hasQObject = true;
562 break;
563 }
564 }
565
566 if (!def.hasQObject)
567 continue;
568
569 for (int i = namespaceList.size() - 1; i >= 0; --i)
570 if (inNamespace(&namespaceList.at(i)))
571 def.qualified.prepend(namespaceList.at(i).name + "::");
572
573 knownQObjectClasses.insert(def.classname);
574 knownQObjectClasses.insert(def.qualified);
575
576 continue; }
577 default: break;
578 }
579 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
580 continue;
581 ClassDef def;
582 if (parseClassHead(&def)) {
583 FunctionDef::Access access = FunctionDef::Private;
584 for (int i = namespaceList.size() - 1; i >= 0; --i)
585 if (inNamespace(&namespaceList.at(i)))
586 def.qualified.prepend(namespaceList.at(i).name + "::");
587 while (inClass(&def) && hasNext()) {
588 switch ((t = next())) {
589 case PRIVATE:
590 access = FunctionDef::Private;
591 if (test(Q_SIGNALS_TOKEN))
592 error("Signals cannot have access specifier");
593 break;
594 case PROTECTED:
595 access = FunctionDef::Protected;
596 if (test(Q_SIGNALS_TOKEN))
597 error("Signals cannot have access specifier");
598 break;
599 case PUBLIC:
600 access = FunctionDef::Public;
601 if (test(Q_SIGNALS_TOKEN))
602 error("Signals cannot have access specifier");
603 break;
604 case CLASS: {
605 ClassDef nestedDef;
606 if (parseClassHead(&nestedDef)) {
607 while (inClass(&nestedDef) && inClass(&def)) {
608 t = next();
609 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
610 error("Meta object features not supported for nested classes");
611 }
612 }
613 } break;
614 case Q_SIGNALS_TOKEN:
615 parseSignals(&def);
616 break;
617 case Q_SLOTS_TOKEN:
618 switch (lookup(-1)) {
619 case PUBLIC:
620 case PROTECTED:
621 case PRIVATE:
622 parseSlots(&def, access);
623 break;
624 default:
625 error("Missing access specifier for slots");
626 }
627 break;
628 case Q_OBJECT_TOKEN:
629 def.hasQObject = true;
630 if (templateClass)
631 error("Template classes not supported by Q_OBJECT");
632 if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
633 error("Class contains Q_OBJECT macro but does not inherit from QObject");
634 break;
635 case Q_GADGET_TOKEN:
636 def.hasQGadget = true;
637 if (templateClass)
638 error("Template classes not supported by Q_GADGET");
639 break;
640 case Q_PROPERTY_TOKEN:
641 parseProperty(&def);
642 break;
643 case Q_ENUMS_TOKEN:
644 parseEnumOrFlag(&def, false);
645 break;
646 case Q_FLAGS_TOKEN:
647 parseEnumOrFlag(&def, true);
648 break;
649 case Q_DECLARE_FLAGS_TOKEN:
650 parseFlag(&def);
651 break;
652 case Q_CLASSINFO_TOKEN:
653 parseClassInfo(&def);
654 break;
655 case Q_INTERFACES_TOKEN:
656 parseInterfaces(&def);
657 break;
658 case Q_PRIVATE_SLOT_TOKEN:
659 parseSlotInPrivate(&def, access);
660 break;
661 case Q_PRIVATE_PROPERTY_TOKEN:
662 parsePrivateProperty(&def);
663 break;
664 case ENUM: {
665 EnumDef enumDef;
666 if (parseEnum(&enumDef))
667 def.enumList += enumDef;
668 } break;
669 default:
670 FunctionDef funcDef;
671 funcDef.access = access;
672 int rewind = index;
673 if (parseMaybeFunction(&def, &funcDef)) {
674 if (funcDef.isConstructor) {
675 if ((access == FunctionDef::Public) && funcDef.isInvokable) {
676 def.constructorList += funcDef;
677 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
678 funcDef.wasCloned = true;
679 funcDef.arguments.removeLast();
680 def.constructorList += funcDef;
681 }
682 }
683 } else if (funcDef.isDestructor) {
684 // don't care about destructors
685 } else {
686 if (access == FunctionDef::Public)
687 def.publicList += funcDef;
688 if (funcDef.isSlot) {
689 def.slotList += funcDef;
690 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
691 funcDef.wasCloned = true;
692 funcDef.arguments.removeLast();
693 def.slotList += funcDef;
694 }
695 } else if (funcDef.isSignal) {
696 def.signalList += funcDef;
697 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
698 funcDef.wasCloned = true;
699 funcDef.arguments.removeLast();
700 def.signalList += funcDef;
701 }
702 } else if (funcDef.isInvokable) {
703 def.methodList += funcDef;
704 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
705 funcDef.wasCloned = true;
706 funcDef.arguments.removeLast();
707 def.methodList += funcDef;
708 }
709 }
710 }
711 } else {
712 index = rewind;
713 }
714 }
715 }
716
717 next(RBRACE);
718
719 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
720 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
721 continue; // no meta object code required
722
723
724 if (!def.hasQObject && !def.hasQGadget)
725 error("Class declarations lacks Q_OBJECT macro.");
726
727 checkSuperClasses(&def);
728
729 classList += def;
730 knownQObjectClasses.insert(def.classname);
731 knownQObjectClasses.insert(def.qualified);
732 }
733 }
734}
735
736void Moc::generate(FILE *out)
737{
738
739 QDateTime dt = QDateTime::currentDateTime();
740 QByteArray dstr = dt.toString().toLatin1();
741 QByteArray fn = filename;
742 int i = filename.length()-1;
743 while (i>0 && filename[i-1] != '/' && filename[i-1] != '\\')
744 --i; // skip path
745 if (i >= 0)
746 fn = filename.mid(i);
747 fprintf(out, "/****************************************************************************\n"
748 "** Meta object code from reading C++ file '%s'\n**\n" , (const char*)fn);
749 fprintf(out, "** Created: %s\n"
750 "** by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , dstr.data(), mocOutputRevision, QT_VERSION_STR);
751 fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
752 "*****************************************************************************/\n\n");
753
754
755 if (!noInclude) {
756 if (includePath.size() && !includePath.endsWith('/'))
757 includePath += '/';
758 for (int i = 0; i < includeFiles.size(); ++i) {
759 QByteArray inc = includeFiles.at(i);
760 if (inc[0] != '<' && inc[0] != '"') {
761 if (includePath.size() && includePath != "./")
762 inc.prepend(includePath);
763 inc = '\"' + inc + '\"';
764 }
765 fprintf(out, "#include %s\n", inc.constData());
766 }
767 }
768 if (classList.size() && classList.first().classname == "Qt")
769 fprintf(out, "#include <QtCore/qobject.h>\n");
770
771 if (mustIncludeQMetaTypeH)
772 fprintf(out, "#include <QtCore/qmetatype.h>\n");
773
774 fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
775 "#error \"The header file '%s' doesn't include <QObject>.\"\n", (const char *)fn);
776 fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
777 fprintf(out, "#error \"This file was generated using the moc from %s."
778 " It\"\n#error \"cannot be used with the include files from"
779 " this version of Qt.\"\n#error \"(The moc has changed too"
780 " much.)\"\n", QT_VERSION_STR);
781 fprintf(out, "#endif\n\n");
782
783 fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n");
784
785 for (i = 0; i < classList.size(); ++i) {
786 Generator generator(&classList[i], metaTypes, out);
787 generator.generateCode();
788 }
789
790 fprintf(out, "QT_END_MOC_NAMESPACE\n");
791}
792
793
794QList<QMetaObject*> Moc::generate(bool ignoreProperties)
795{
796 QList<QMetaObject*> result;
797 for (int i = 0; i < classList.size(); ++i) {
798 Generator generator(&classList[i], metaTypes);
799 result << generator.generateMetaObject(ignoreProperties);
800 }
801 return result;
802}
803
804void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
805{
806 next(COLON);
807 while (inClass(def) && hasNext()) {
808 switch (next()) {
809 case PUBLIC:
810 case PROTECTED:
811 case PRIVATE:
812 case Q_SIGNALS_TOKEN:
813 case Q_SLOTS_TOKEN:
814 prev();
815 return;
816 case SEMIC:
817 continue;
818 case FRIEND:
819 until(SEMIC);
820 continue;
821 case USING:
822 error("'using' directive not supported in 'slots' section");
823 default:
824 prev();
825 }
826
827 FunctionDef funcDef;
828 funcDef.access = access;
829 if (!parseFunction(&funcDef))
830 continue;
831 def->slotList += funcDef;
832 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
833 funcDef.wasCloned = true;
834 funcDef.arguments.removeLast();
835 def->slotList += funcDef;
836 }
837 }
838}
839
840void Moc::parseSignals(ClassDef *def)
841{
842 next(COLON);
843 while (inClass(def) && hasNext()) {
844 switch (next()) {
845 case PUBLIC:
846 case PROTECTED:
847 case PRIVATE:
848 case Q_SIGNALS_TOKEN:
849 case Q_SLOTS_TOKEN:
850 prev();
851 return;
852 case SEMIC:
853 continue;
854 case FRIEND:
855 until(SEMIC);
856 continue;
857 case USING:
858 error("'using' directive not supported in 'signals' section");
859 default:
860 prev();
861 }
862 FunctionDef funcDef;
863 funcDef.access = FunctionDef::Protected;
864 parseFunction(&funcDef);
865 if (funcDef.isVirtual)
866 warning("Signals cannot be declared virtual");
867 if (funcDef.inlineCode)
868 error("Not a signal declaration");
869 def->signalList += funcDef;
870 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
871 funcDef.wasCloned = true;
872 funcDef.arguments.removeLast();
873 def->signalList += funcDef;
874 }
875 }
876}
877
878void Moc::createPropertyDef(PropertyDef &propDef)
879{
880 QByteArray type = parseType().name;
881 if (type.isEmpty())
882 error();
883 propDef.designable = propDef.scriptable = propDef.stored = "true";
884 propDef.user = "false";
885 /*
886 The Q_PROPERTY construct cannot contain any commas, since
887 commas separate macro arguments. We therefore expect users
888 to type "QMap" instead of "QMap<QString, QVariant>". For
889 coherence, we also expect the same for
890 QValueList<QVariant>, the other template class supported by
891 QVariant.
892 */
893 type = normalizeType(type);
894 if (type == "QMap")
895 type = "QMap<QString,QVariant>";
896 else if (type == "QValueList")
897 type = "QValueList<QVariant>";
898 else if (type == "LongLong")
899 type = "qlonglong";
900 else if (type == "ULongLong")
901 type = "qulonglong";
902 else if (type == "qreal")
903 mustIncludeQMetaTypeH = true;
904
905 propDef.type = type;
906
907 next();
908 propDef.name = lexem();
909 while (test(IDENTIFIER)) {
910 QByteArray l = lexem();
911
912 if (l[0] == 'C' && l == "CONSTANT") {
913 propDef.constant = true;
914 continue;
915 } else if(l[0] == 'F' && l == "FINAL") {
916 propDef.final = true;
917 continue;
918 }
919
920 QByteArray v, v2;
921 if (test(LPAREN)) {
922 v = lexemUntil(RPAREN);
923 } else {
924 next(IDENTIFIER);
925 v = lexem();
926 if (test(LPAREN))
927 v2 = lexemUntil(RPAREN);
928 else if (v != "true" && v != "false")
929 v2 = "()";
930 }
931 switch (l[0]) {
932 case 'R':
933 if (l == "READ")
934 propDef.read = v;
935 else if (l == "RESET")
936 propDef.reset = v + v2;
937 else
938 error(2);
939 break;
940 case 'S':
941 if (l == "SCRIPTABLE")
942 propDef.scriptable = v + v2;
943 else if (l == "STORED")
944 propDef.stored = v + v2;
945 else
946 error(2);
947 break;
948 case 'W': if (l != "WRITE") error(2);
949 propDef.write = v;
950 break;
951 case 'D': if (l != "DESIGNABLE") error(2);
952 propDef.designable = v + v2;
953 break;
954 case 'E': if (l != "EDITABLE") error(2);
955 propDef.editable = v + v2;
956 break;
957 case 'N': if (l != "NOTIFY") error(2);
958 propDef.notify = v;
959 break;
960 case 'U': if (l != "USER") error(2);
961 propDef.user = v + v2;
962 break;
963 default:
964 error(2);
965 }
966 }
967 if (propDef.read.isNull()) {
968 QByteArray msg;
969 msg += "Property declaration ";
970 msg += propDef.name;
971 msg += " has no READ accessor function. The property will be invalid.";
972 warning(msg.constData());
973 }
974 if (propDef.constant && !propDef.write.isNull()) {
975 QByteArray msg;
976 msg += "Property declaration ";
977 msg += propDef.name;
978 msg += " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
979 propDef.constant = false;
980 warning(msg.constData());
981 }
982 if (propDef.constant && !propDef.notify.isNull()) {
983 QByteArray msg;
984 msg += "Property declaration ";
985 msg += propDef.name;
986 msg += " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
987 propDef.constant = false;
988 warning(msg.constData());
989 }
990}
991
992void Moc::parseProperty(ClassDef *def)
993{
994 next(LPAREN);
995 PropertyDef propDef;
996 createPropertyDef(propDef);
997 next(RPAREN);
998
999
1000 if(!propDef.notify.isEmpty())
1001 def->notifyableProperties++;
1002 def->propertyList += propDef;
1003}
1004
1005void Moc::parsePrivateProperty(ClassDef *def)
1006{
1007 next(LPAREN);
1008 PropertyDef propDef;
1009 next(IDENTIFIER);
1010 propDef.inPrivateClass = lexem();
1011 while (test(SCOPE)) {
1012 propDef.inPrivateClass += lexem();
1013 next(IDENTIFIER);
1014 propDef.inPrivateClass += lexem();
1015 }
1016 // also allow void functions
1017 if (test(LPAREN)) {
1018 next(RPAREN);
1019 propDef.inPrivateClass += "()";
1020 }
1021
1022 next(COMMA);
1023
1024 createPropertyDef(propDef);
1025
1026 if(!propDef.notify.isEmpty())
1027 def->notifyableProperties++;
1028
1029 def->propertyList += propDef;
1030}
1031
1032void Moc::parseEnumOrFlag(ClassDef *def, bool isFlag)
1033{
1034 next(LPAREN);
1035 QByteArray identifier;
1036 while (test(IDENTIFIER)) {
1037 identifier = lexem();
1038 while (test(SCOPE) && test(IDENTIFIER)) {
1039 identifier += "::";
1040 identifier += lexem();
1041 }
1042 def->enumDeclarations[identifier] = isFlag;
1043 }
1044 next(RPAREN);
1045}
1046
1047void Moc::parseFlag(ClassDef *def)
1048{
1049 next(LPAREN);
1050 QByteArray flagName, enumName;
1051 while (test(IDENTIFIER)) {
1052 flagName = lexem();
1053 while (test(SCOPE) && test(IDENTIFIER)) {
1054 flagName += "::";
1055 flagName += lexem();
1056 }
1057 }
1058 next(COMMA);
1059 while (test(IDENTIFIER)) {
1060 enumName = lexem();
1061 while (test(SCOPE) && test(IDENTIFIER)) {
1062 enumName += "::";
1063 enumName += lexem();
1064 }
1065 }
1066
1067 def->flagAliases.insert(enumName, flagName);
1068 next(RPAREN);
1069}
1070
1071void Moc::parseClassInfo(ClassDef *def)
1072{
1073 next(LPAREN);
1074 ClassInfoDef infoDef;
1075 next(STRING_LITERAL);
1076 infoDef.name = symbol().unquotedLexem();
1077 next(COMMA);
1078 if (test(STRING_LITERAL)) {
1079 infoDef.value = symbol().unquotedLexem();
1080 } else {
1081 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1082 next(IDENTIFIER);
1083 next(LPAREN);
1084 next(STRING_LITERAL);
1085 infoDef.value = symbol().unquotedLexem();
1086 next(RPAREN);
1087 }
1088 next(RPAREN);
1089 def->classInfoList += infoDef;
1090}
1091
1092void Moc::parseInterfaces(ClassDef *def)
1093{
1094 next(LPAREN);
1095 while (test(IDENTIFIER)) {
1096 QList<ClassDef::Interface> iface;
1097 iface += ClassDef::Interface(lexem());
1098 while (test(SCOPE)) {
1099 iface.last().className += lexem();
1100 next(IDENTIFIER);
1101 iface.last().className += lexem();
1102 }
1103 while (test(COLON)) {
1104 next(IDENTIFIER);
1105 iface += ClassDef::Interface(lexem());
1106 while (test(SCOPE)) {
1107 iface.last().className += lexem();
1108 next(IDENTIFIER);
1109 iface.last().className += lexem();
1110 }
1111 }
1112 // resolve from classnames to interface ids
1113 for (int i = 0; i < iface.count(); ++i) {
1114 const QByteArray iid = interface2IdMap.value(iface.at(i).className);
1115 if (iid.isEmpty())
1116 error("Undefined interface");
1117
1118 iface[i].interfaceId = iid;
1119 }
1120 def->interfaceList += iface;
1121 }
1122 next(RPAREN);
1123}
1124
1125void Moc::parseDeclareInterface()
1126{
1127 next(LPAREN);
1128 QByteArray interface;
1129 next(IDENTIFIER);
1130 interface += lexem();
1131 while (test(SCOPE)) {
1132 interface += lexem();
1133 next(IDENTIFIER);
1134 interface += lexem();
1135 }
1136 next(COMMA);
1137 QByteArray iid;
1138 if (test(STRING_LITERAL)) {
1139 iid = lexem();
1140 } else {
1141 next(IDENTIFIER);
1142 iid = lexem();
1143 }
1144 interface2IdMap.insert(interface, iid);
1145 next(RPAREN);
1146}
1147
1148void Moc::parseDeclareMetatype()
1149{
1150 next(LPAREN);
1151 QByteArray typeName = lexemUntil(RPAREN);
1152 typeName.remove(0, 1);
1153 typeName.chop(1);
1154 metaTypes.append(typeName);
1155}
1156
1157void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
1158{
1159 next(LPAREN);
1160 FunctionDef funcDef;
1161 next(IDENTIFIER);
1162 funcDef.inPrivateClass = lexem();
1163 // also allow void functions
1164 if (test(LPAREN)) {
1165 next(RPAREN);
1166 funcDef.inPrivateClass += "()";
1167 }
1168 next(COMMA);
1169 funcDef.access = access;
1170 parseFunction(&funcDef, true);
1171 def->slotList += funcDef;
1172 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
1173 funcDef.wasCloned = true;
1174 funcDef.arguments.removeLast();
1175 def->slotList += funcDef;
1176 }
1177}
1178
1179QByteArray Moc::lexemUntil(Token target)
1180{
1181 int from = index;
1182 until(target);
1183 QByteArray s;
1184 while (from <= index) {
1185 QByteArray n = symbols.at(from++-1).lexem();
1186 if (s.size() && n.size()
1187 && is_ident_char(s.at(s.size()-1))
1188 && is_ident_char(n.at(0)))
1189 s += ' ';
1190 s += n;
1191 }
1192 return s;
1193}
1194
1195bool Moc::until(Token target) {
1196 int braceCount = 0;
1197 int brackCount = 0;
1198 int parenCount = 0;
1199 int angleCount = 0;
1200 if (index) {
1201 switch(symbols.at(index-1).token) {
1202 case LBRACE: ++braceCount; break;
1203 case LBRACK: ++brackCount; break;
1204 case LPAREN: ++parenCount; break;
1205 case LANGLE: ++angleCount; break;
1206 default: break;
1207 }
1208 }
1209 while (index < symbols.size()) {
1210 Token t = symbols.at(index++).token;
1211 switch (t) {
1212 case LBRACE: ++braceCount; break;
1213 case RBRACE: --braceCount; break;
1214 case LBRACK: ++brackCount; break;
1215 case RBRACK: --brackCount; break;
1216 case LPAREN: ++parenCount; break;
1217 case RPAREN: --parenCount; break;
1218 case LANGLE: ++angleCount; break;
1219 case RANGLE: --angleCount; break;
1220 case GTGT: angleCount -= 2; t = RANGLE; break;
1221 default: break;
1222 }
1223 if (t == target
1224 && braceCount <= 0
1225 && brackCount <= 0
1226 && parenCount <= 0
1227 && (target != RANGLE || angleCount <= 0))
1228 return true;
1229
1230 if (braceCount < 0 || brackCount < 0 || parenCount < 0
1231 || (target == RANGLE && angleCount < 0)) {
1232 --index;
1233 break;
1234 }
1235 }
1236 return false;
1237}
1238
1239void Moc::checkSuperClasses(ClassDef *def)
1240{
1241 const QByteArray firstSuperclass = def->superclassList.value(0).first;
1242
1243 if (!knownQObjectClasses.contains(firstSuperclass)) {
1244 // enable once we /require/ include paths
1245#if 0
1246 QByteArray msg;
1247 msg += "Class ";
1248 msg += def->className;
1249 msg += " contains the Q_OBJECT macro and inherits from ";
1250 msg += def->superclassList.value(0);
1251 msg += " but that is not a known QObject subclass. You may get compilation errors.";
1252 warning(msg.constData());
1253#endif
1254 return;
1255 }
1256 for (int i = 1; i < def->superclassList.count(); ++i) {
1257 const QByteArray superClass = def->superclassList.at(i).first;
1258 if (knownQObjectClasses.contains(superClass)) {
1259 QByteArray msg;
1260 msg += "Class ";
1261 msg += def->classname;
1262 msg += " inherits from two QObject subclasses ";
1263 msg += firstSuperclass;
1264 msg += " and ";
1265 msg += superClass;
1266 msg += ". This is not supported!";
1267 warning(msg.constData());
1268 }
1269
1270 if (interface2IdMap.contains(superClass)) {
1271 bool registeredInterface = false;
1272 for (int i = 0; i < def->interfaceList.count(); ++i)
1273 if (def->interfaceList.at(i).first().className == superClass) {
1274 registeredInterface = true;
1275 break;
1276 }
1277
1278 if (!registeredInterface) {
1279 QByteArray msg;
1280 msg += "Class ";
1281 msg += def->classname;
1282 msg += " implements the interface ";
1283 msg += superClass;
1284 msg += " but does not list it in Q_INTERFACES. qobject_cast to ";
1285 msg += superClass;
1286 msg += " will not work!";
1287 warning(msg.constData());
1288 }
1289 }
1290 }
1291}
1292
1293
1294QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.