PageRenderTime 63ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/tools/moc/moc.cpp

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