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