| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2019 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the tools applications of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | 
| 21 | ** included in the packaging of this file. Please review the following | 
| 22 | ** information to ensure the GNU General Public License requirements will | 
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
| 24 | ** | 
| 25 | ** $QT_END_LICENSE$ | 
| 26 | ** | 
| 27 | ****************************************************************************/ | 
| 28 |  | 
| 29 | #include "typedescriptionreader.h" | 
| 30 |  | 
| 31 | #include <QtQml/private/qqmljsparser_p.h> | 
| 32 | #include <QtQml/private/qqmljslexer_p.h> | 
| 33 | #include <QtQml/private/qqmljsengine_p.h> | 
| 34 |  | 
| 35 | #include <QtCore/qdir.h> | 
| 36 |  | 
| 37 | using namespace QQmlJS; | 
| 38 | using namespace QQmlJS::AST; | 
| 39 |  | 
| 40 | QString toString(const UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.')) | 
| 41 | { | 
| 42 |     QString result; | 
| 43 |  | 
| 44 |     for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) { | 
| 45 |         if (iter != qualifiedId) | 
| 46 |             result += delimiter; | 
| 47 |  | 
| 48 |         result += iter->name; | 
| 49 |     } | 
| 50 |  | 
| 51 |     return result; | 
| 52 | } | 
| 53 |  | 
| 54 | bool TypeDescriptionReader::operator()( | 
| 55 |         QHash<QString, ScopeTree::ConstPtr> *objects, | 
| 56 |         QList<ModuleApiInfo> *moduleApis, | 
| 57 |         QStringList *dependencies) | 
| 58 | { | 
| 59 |     Engine engine; | 
| 60 |  | 
| 61 |     Lexer lexer(&engine); | 
| 62 |     Parser parser(&engine); | 
| 63 |  | 
| 64 |     lexer.setCode(code: m_source, /*lineno = */ 1, /*qmlMode = */true); | 
| 65 |  | 
| 66 |     if (!parser.parse()) { | 
| 67 |         m_errorMessage = QString::fromLatin1(str: "%1:%2: %3" ).arg( | 
| 68 |                     QString::number(parser.errorLineNumber()), | 
| 69 |                     QString::number(parser.errorColumnNumber()), | 
| 70 |                     parser.errorMessage()); | 
| 71 |         return false; | 
| 72 |     } | 
| 73 |  | 
| 74 |     m_objects = objects; | 
| 75 |     m_moduleApis = moduleApis; | 
| 76 |     m_dependencies = dependencies; | 
| 77 |     readDocument(ast: parser.ast()); | 
| 78 |  | 
| 79 |     return m_errorMessage.isEmpty(); | 
| 80 | } | 
| 81 |  | 
| 82 | void TypeDescriptionReader::readDocument(UiProgram *ast) | 
| 83 | { | 
| 84 |     if (!ast) { | 
| 85 |         addError(loc: SourceLocation(), message: tr(sourceText: "Could not parse document." )); | 
| 86 |         return; | 
| 87 |     } | 
| 88 |  | 
| 89 |     if (!ast->headers || ast->headers->next || !cast<UiImport *>(ast: ast->headers->headerItem)) { | 
| 90 |         addError(loc: SourceLocation(), message: tr(sourceText: "Expected a single import." )); | 
| 91 |         return; | 
| 92 |     } | 
| 93 |  | 
| 94 |     auto *import = cast<UiImport *>(ast: ast->headers->headerItem); | 
| 95 |     if (toString(qualifiedId: import->importUri) != QLatin1String("QtQuick.tooling" )) { | 
| 96 |         addError(loc: import->importToken, message: tr(sourceText: "Expected import of QtQuick.tooling." )); | 
| 97 |         return; | 
| 98 |     } | 
| 99 |  | 
| 100 |     if (!import->version) { | 
| 101 |         addError(loc: import->firstSourceLocation(), message: tr(sourceText: "Import statement without version." )); | 
| 102 |         return; | 
| 103 |     } | 
| 104 |  | 
| 105 |     if (import->version->majorVersion != 1) { | 
| 106 |         addError(loc: import->version->firstSourceLocation(), | 
| 107 |                  message: tr(sourceText: "Major version different from 1 not supported." )); | 
| 108 |         return; | 
| 109 |     } | 
| 110 |  | 
| 111 |     if (!ast->members || !ast->members->member || ast->members->next) { | 
| 112 |         addError(loc: SourceLocation(), message: tr(sourceText: "Expected document to contain a single object definition." )); | 
| 113 |         return; | 
| 114 |     } | 
| 115 |  | 
| 116 |     auto *module = cast<UiObjectDefinition *>(ast: ast->members->member); | 
| 117 |     if (!module) { | 
| 118 |         addError(loc: SourceLocation(), message: tr(sourceText: "Expected document to contain a single object definition." )); | 
| 119 |         return; | 
| 120 |     } | 
| 121 |  | 
| 122 |     if (toString(qualifiedId: module->qualifiedTypeNameId) != QLatin1String("Module" )) { | 
| 123 |         addError(loc: SourceLocation(), message: tr(sourceText: "Expected document to contain a Module {} member." )); | 
| 124 |         return; | 
| 125 |     } | 
| 126 |  | 
| 127 |     readModule(ast: module); | 
| 128 | } | 
| 129 |  | 
| 130 | void TypeDescriptionReader::readModule(UiObjectDefinition *ast) | 
| 131 | { | 
| 132 |     for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { | 
| 133 |         UiObjectMember *member = it->member; | 
| 134 |         auto *component = cast<UiObjectDefinition *>(ast: member); | 
| 135 |  | 
| 136 |         auto *script = cast<UiScriptBinding *>(ast: member); | 
| 137 |         if (script && (toString(qualifiedId: script->qualifiedId) == QStringLiteral("dependencies" ))) { | 
| 138 |             readDependencies(ast: script); | 
| 139 |             continue; | 
| 140 |         } | 
| 141 |  | 
| 142 |         QString typeName; | 
| 143 |         if (component) | 
| 144 |             typeName = toString(qualifiedId: component->qualifiedTypeNameId); | 
| 145 |  | 
| 146 |         if (!component || (typeName != QLatin1String("Component" ) | 
| 147 |                            && typeName != QLatin1String("ModuleApi" ))) { | 
| 148 |             continue; | 
| 149 |         } | 
| 150 |  | 
| 151 |         if (typeName == QLatin1String("Component" )) | 
| 152 |             readComponent(ast: component); | 
| 153 |         else if (typeName == QLatin1String("ModuleApi" )) | 
| 154 |             readModuleApi(ast: component); | 
| 155 |     } | 
| 156 | } | 
| 157 |  | 
| 158 | void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message) | 
| 159 | { | 
| 160 |     m_errorMessage += QString::fromLatin1(str: "%1:%2:%3: %4\n" ).arg( | 
| 161 |                 args: QDir::toNativeSeparators(pathName: m_fileName), | 
| 162 |                 args: QString::number(loc.startLine), | 
| 163 |                 args: QString::number(loc.startColumn), | 
| 164 |                 args: message); | 
| 165 | } | 
| 166 |  | 
| 167 | void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message) | 
| 168 | { | 
| 169 |     m_warningMessage += QString::fromLatin1(str: "%1:%2:%3: %4\n" ).arg( | 
| 170 |                 args: QDir::toNativeSeparators(pathName: m_fileName), | 
| 171 |                 args: QString::number(loc.startLine), | 
| 172 |                 args: QString::number(loc.startColumn), | 
| 173 |                 args: message); | 
| 174 | } | 
| 175 |  | 
| 176 | void TypeDescriptionReader::readDependencies(UiScriptBinding *ast) | 
| 177 | { | 
| 178 |     auto *stmt = cast<ExpressionStatement*>(ast: ast->statement); | 
| 179 |     if (!stmt) { | 
| 180 |         addError(loc: ast->statement->firstSourceLocation(), message: tr(sourceText: "Expected dependency definitions" )); | 
| 181 |         return; | 
| 182 |     } | 
| 183 |     auto *exp = cast<ArrayPattern *>(ast: stmt->expression); | 
| 184 |     if (!exp) { | 
| 185 |         addError(loc: stmt->expression->firstSourceLocation(), message: tr(sourceText: "Expected dependency definitions" )); | 
| 186 |         return; | 
| 187 |     } | 
| 188 |     for (PatternElementList *l = exp->elements; l; l = l->next) { | 
| 189 |         auto *str = cast<StringLiteral *>(ast: l->element->initializer); | 
| 190 |         *m_dependencies << str->value.toString(); | 
| 191 |     } | 
| 192 | } | 
| 193 |  | 
| 194 | void TypeDescriptionReader::readComponent(UiObjectDefinition *ast) | 
| 195 | { | 
| 196 |     ScopeTree::Ptr scope(new ScopeTree(ScopeType::QMLScope)); | 
| 197 |  | 
| 198 |     for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { | 
| 199 |         UiObjectMember *member = it->member; | 
| 200 |         auto *component = cast<UiObjectDefinition *>(ast: member); | 
| 201 |         auto *script = cast<UiScriptBinding *>(ast: member); | 
| 202 |         if (component) { | 
| 203 |             QString name = toString(qualifiedId: component->qualifiedTypeNameId); | 
| 204 |             if (name == QLatin1String("Property" )) | 
| 205 |                 readProperty(ast: component, scope); | 
| 206 |             else if (name == QLatin1String("Method" ) || name == QLatin1String("Signal" )) | 
| 207 |                 readSignalOrMethod(ast: component, isMethod: name == QLatin1String("Method" ), scope); | 
| 208 |             else if (name == QLatin1String("Enum" )) | 
| 209 |                 readEnum(ast: component, scope); | 
| 210 |             else | 
| 211 |                 addWarning(loc: component->firstSourceLocation(), | 
| 212 |                            message: tr(sourceText: "Expected only Property, Method, Signal and Enum object definitions, "  | 
| 213 |                               "not \"%1\"." ).arg(a: name)); | 
| 214 |         } else if (script) { | 
| 215 |             QString name = toString(qualifiedId: script->qualifiedId); | 
| 216 |             if (name == QLatin1String("name" )) { | 
| 217 |                 scope->setClassName(readStringBinding(ast: script)); | 
| 218 |             } else if (name == QLatin1String("prototype" )) { | 
| 219 |                 scope->setSuperclassName(readStringBinding(ast: script)); | 
| 220 |             } else if (name == QLatin1String("defaultProperty" )) { | 
| 221 |                 scope->setDefaultPropertyName(readStringBinding(ast: script)); | 
| 222 |             } else if (name == QLatin1String("exports" )) { | 
| 223 |                 readExports(ast: script, scope); | 
| 224 |             } else if (name == QLatin1String("exportMetaObjectRevisions" )) { | 
| 225 |                 readMetaObjectRevisions(ast: script, scope); | 
| 226 |             } else if (name == QLatin1String("attachedType" )) { | 
| 227 |                 scope->setAttachedTypeName(readStringBinding(ast: script)); | 
| 228 |             } else if (name == QLatin1String("isSingleton" )) { | 
| 229 |                 scope->setIsSingleton(readBoolBinding(ast: script)); | 
| 230 |             } else if (name == QLatin1String("isCreatable" )) { | 
| 231 |                 scope->setIsCreatable(readBoolBinding(ast: script)); | 
| 232 |             } else if (name == QLatin1String("isComposite" )) { | 
| 233 |                 scope->setIsComposite(readBoolBinding(ast: script)); | 
| 234 |             } else { | 
| 235 |                 addWarning(loc: script->firstSourceLocation(), | 
| 236 |                            message: tr(sourceText: "Expected only name, prototype, defaultProperty, attachedType, "  | 
| 237 |                               "exports, isSingleton, isCreatable, isComposite and "  | 
| 238 |                               "exportMetaObjectRevisions script bindings, not \"%1\"." ).arg(a: name)); | 
| 239 |             } | 
| 240 |         } else { | 
| 241 |             addWarning(loc: member->firstSourceLocation(), | 
| 242 |                        message: tr(sourceText: "Expected only script bindings and object definitions." )); | 
| 243 |         } | 
| 244 |     } | 
| 245 |  | 
| 246 |     if (scope->className().isEmpty()) { | 
| 247 |         addError(loc: ast->firstSourceLocation(), message: tr(sourceText: "Component definition is missing a name binding." )); | 
| 248 |         return; | 
| 249 |     } | 
| 250 |  | 
| 251 |     // ### add implicit export into the package of c++ types | 
| 252 |     scope->addExport(name: scope->className(), QStringLiteral("<cpp>" ), version: ComponentVersion()); | 
| 253 |     m_objects->insert(akey: scope->className(), avalue: scope); | 
| 254 | } | 
| 255 |  | 
| 256 | void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast) | 
| 257 | { | 
| 258 |     ModuleApiInfo apiInfo; | 
| 259 |  | 
| 260 |     for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { | 
| 261 |         UiObjectMember *member = it->member; | 
| 262 |         auto *script = cast<UiScriptBinding *>(ast: member); | 
| 263 |  | 
| 264 |         if (script) { | 
| 265 |             const QString name = toString(qualifiedId: script->qualifiedId); | 
| 266 |             if (name == QLatin1String("uri" )) { | 
| 267 |                 apiInfo.uri = readStringBinding(ast: script); | 
| 268 |             } else if (name == QLatin1String("version" )) { | 
| 269 |                 apiInfo.version = readNumericVersionBinding(ast: script); | 
| 270 |             } else if (name == QLatin1String("name" )) { | 
| 271 |                 apiInfo.cppName = readStringBinding(ast: script); | 
| 272 |             } else { | 
| 273 |                 addWarning(loc: script->firstSourceLocation(), | 
| 274 |                            message: tr(sourceText: "Expected only uri, version and name script bindings." )); | 
| 275 |             } | 
| 276 |         } else { | 
| 277 |             addWarning(loc: member->firstSourceLocation(), message: tr(sourceText: "Expected only script bindings." )); | 
| 278 |         } | 
| 279 |     } | 
| 280 |  | 
| 281 |     if (!apiInfo.version.isValid()) { | 
| 282 |         addError(loc: ast->firstSourceLocation(), | 
| 283 |                  message: tr(sourceText: "ModuleApi definition has no or invalid version binding." )); | 
| 284 |         return; | 
| 285 |     } | 
| 286 |  | 
| 287 |     if (m_moduleApis) | 
| 288 |         m_moduleApis->append(t: apiInfo); | 
| 289 | } | 
| 290 |  | 
| 291 | void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, | 
| 292 |                                                const ScopeTree::Ptr &scope) | 
| 293 | { | 
| 294 |     MetaMethod metaMethod; | 
| 295 |     // ### confusion between Method and Slot. Method should be removed. | 
| 296 |     if (isMethod) | 
| 297 |         metaMethod.setMethodType(MetaMethod::Slot); | 
| 298 |     else | 
| 299 |         metaMethod.setMethodType(MetaMethod::Signal); | 
| 300 |  | 
| 301 |     for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { | 
| 302 |         UiObjectMember *member = it->member; | 
| 303 |         auto *component = cast<UiObjectDefinition *>(ast: member); | 
| 304 |         auto *script = cast<UiScriptBinding *>(ast: member); | 
| 305 |         if (component) { | 
| 306 |             QString name = toString(qualifiedId: component->qualifiedTypeNameId); | 
| 307 |             if (name == QLatin1String("Parameter" )) { | 
| 308 |                 readParameter(ast: component, metaMethod: &metaMethod); | 
| 309 |             } else { | 
| 310 |                 addWarning(loc: component->firstSourceLocation(), | 
| 311 |                            message: tr(sourceText: "Expected only Parameter object definitions." )); | 
| 312 |             } | 
| 313 |         } else if (script) { | 
| 314 |             QString name = toString(qualifiedId: script->qualifiedId); | 
| 315 |             if (name == QLatin1String("name" )) { | 
| 316 |                 metaMethod.setMethodName(readStringBinding(ast: script)); | 
| 317 |             } else if (name == QLatin1String("type" )) { | 
| 318 |                 metaMethod.setReturnType(readStringBinding(ast: script)); | 
| 319 |             } else if (name == QLatin1String("revision" )) { | 
| 320 |                 metaMethod.setRevision(readIntBinding(ast: script)); | 
| 321 |             } else { | 
| 322 |                 addWarning(loc: script->firstSourceLocation(), | 
| 323 |                            message: tr(sourceText: "Expected only name and type script bindings." )); | 
| 324 |             } | 
| 325 |         } else { | 
| 326 |             addWarning(loc: member->firstSourceLocation(), | 
| 327 |                        message: tr(sourceText: "Expected only script bindings and object definitions." )); | 
| 328 |         } | 
| 329 |     } | 
| 330 |  | 
| 331 |     if (metaMethod.methodName().isEmpty()) { | 
| 332 |         addError(loc: ast->firstSourceLocation(), | 
| 333 |                  message: tr(sourceText: "Method or signal is missing a name script binding." )); | 
| 334 |         return; | 
| 335 |     } | 
| 336 |  | 
| 337 |     scope->addMethod(method: metaMethod); | 
| 338 | } | 
| 339 |  | 
| 340 | void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, const ScopeTree::Ptr &scope) | 
| 341 | { | 
| 342 |     QString name; | 
| 343 |     QString type; | 
| 344 |     bool isPointer = false; | 
| 345 |     bool isReadonly = false; | 
| 346 |     bool isList = false; | 
| 347 |     int revision = 0; | 
| 348 |  | 
| 349 |     for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { | 
| 350 |         UiObjectMember *member = it->member; | 
| 351 |         auto *script = cast<UiScriptBinding *>(ast: member); | 
| 352 |         if (!script) { | 
| 353 |             addWarning(loc: member->firstSourceLocation(), message: tr(sourceText: "Expected script binding." )); | 
| 354 |             continue; | 
| 355 |         } | 
| 356 |  | 
| 357 |         QString id = toString(qualifiedId: script->qualifiedId); | 
| 358 |         if (id == QLatin1String("name" )) { | 
| 359 |             name = readStringBinding(ast: script); | 
| 360 |         } else if (id == QLatin1String("type" )) { | 
| 361 |             type = readStringBinding(ast: script); | 
| 362 |         } else if (id == QLatin1String("isPointer" )) { | 
| 363 |             isPointer = readBoolBinding(ast: script); | 
| 364 |         } else if (id == QLatin1String("isReadonly" )) { | 
| 365 |             isReadonly = readBoolBinding(ast: script); | 
| 366 |         } else if (id == QLatin1String("isList" )) { | 
| 367 |             isList = readBoolBinding(ast: script); | 
| 368 |         } else if (id == QLatin1String("revision" )) { | 
| 369 |             revision = readIntBinding(ast: script); | 
| 370 |         } else { | 
| 371 |             addWarning(loc: script->firstSourceLocation(), | 
| 372 |                        message: tr(sourceText: "Expected only type, name, revision, isPointer, isReadonly and"  | 
| 373 |                           " isList script bindings." )); | 
| 374 |         } | 
| 375 |     } | 
| 376 |  | 
| 377 |     if (name.isEmpty() || type.isEmpty()) { | 
| 378 |         addError(loc: ast->firstSourceLocation(), | 
| 379 |                  message: tr(sourceText: "Property object is missing a name or type script binding." )); | 
| 380 |         return; | 
| 381 |     } | 
| 382 |  | 
| 383 |     scope->addProperty(prop: MetaProperty(name, type, isList, !isReadonly, isPointer, false, revision)); | 
| 384 | } | 
| 385 |  | 
| 386 | void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, const ScopeTree::Ptr &scope) | 
| 387 | { | 
| 388 |     MetaEnum metaEnum; | 
| 389 |  | 
| 390 |     for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { | 
| 391 |         UiObjectMember *member = it->member; | 
| 392 |         auto *script = cast<UiScriptBinding *>(ast: member); | 
| 393 |         if (!script) { | 
| 394 |             addWarning(loc: member->firstSourceLocation(), message: tr(sourceText: "Expected script binding." )); | 
| 395 |             continue; | 
| 396 |         } | 
| 397 |  | 
| 398 |         QString name = toString(qualifiedId: script->qualifiedId); | 
| 399 |         if (name == QLatin1String("name" )) { | 
| 400 |             metaEnum.setName(readStringBinding(ast: script)); | 
| 401 |         } else if (name == QLatin1String("alias" )) { | 
| 402 |             metaEnum.setAlias(readStringBinding(ast: script)); | 
| 403 |         } else if (name == QLatin1String("isFlag" )) { | 
| 404 |             metaEnum.setIsFlag(readBoolBinding(ast: script)); | 
| 405 |         } else if (name == QLatin1String("values" )) { | 
| 406 |             readEnumValues(ast: script, metaEnum: &metaEnum); | 
| 407 |         } else { | 
| 408 |             addWarning(loc: script->firstSourceLocation(), | 
| 409 |                        message: tr(sourceText: "Expected only name and values script bindings." )); | 
| 410 |         } | 
| 411 |     } | 
| 412 |  | 
| 413 |     scope->addEnum(fakeEnum: metaEnum); | 
| 414 | } | 
| 415 |  | 
| 416 | void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, MetaMethod *metaMethod) | 
| 417 | { | 
| 418 |     QString name; | 
| 419 |     QString type; | 
| 420 |  | 
| 421 |     for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { | 
| 422 |         UiObjectMember *member = it->member; | 
| 423 |         auto *script = cast<UiScriptBinding *>(ast: member); | 
| 424 |         if (!script) { | 
| 425 |             addWarning(loc: member->firstSourceLocation(), message: tr(sourceText: "Expected script binding." )); | 
| 426 |             continue; | 
| 427 |         } | 
| 428 |  | 
| 429 |         const QString id = toString(qualifiedId: script->qualifiedId); | 
| 430 |         if (id == QLatin1String("name" )) { | 
| 431 |             name = readStringBinding(ast: script); | 
| 432 |         } else if (id == QLatin1String("type" )) { | 
| 433 |             type = readStringBinding(ast: script); | 
| 434 |         } else if (id == QLatin1String("isPointer" )) { | 
| 435 |             // ### unhandled | 
| 436 |         } else if (id == QLatin1String("isReadonly" )) { | 
| 437 |             // ### unhandled | 
| 438 |         } else if (id == QLatin1String("isList" )) { | 
| 439 |             // ### unhandled | 
| 440 |         } else { | 
| 441 |             addWarning(loc: script->firstSourceLocation(), | 
| 442 |                        message: tr(sourceText: "Expected only name and type script bindings." )); | 
| 443 |         } | 
| 444 |     } | 
| 445 |  | 
| 446 |     metaMethod->addParameter(name, type); | 
| 447 | } | 
| 448 |  | 
| 449 | QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast) | 
| 450 | { | 
| 451 |     Q_ASSERT(ast); | 
| 452 |  | 
| 453 |     if (!ast->statement) { | 
| 454 |         addError(loc: ast->colonToken, message: tr(sourceText: "Expected string after colon." )); | 
| 455 |         return QString(); | 
| 456 |     } | 
| 457 |  | 
| 458 |     auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement); | 
| 459 |     if (!expStmt) { | 
| 460 |         addError(loc: ast->statement->firstSourceLocation(), message: tr(sourceText: "Expected string after colon." )); | 
| 461 |         return QString(); | 
| 462 |     } | 
| 463 |  | 
| 464 |     auto *stringLit = cast<StringLiteral *>(ast: expStmt->expression); | 
| 465 |     if (!stringLit) { | 
| 466 |         addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected string after colon." )); | 
| 467 |         return QString(); | 
| 468 |     } | 
| 469 |  | 
| 470 |     return stringLit->value.toString(); | 
| 471 | } | 
| 472 |  | 
| 473 | bool TypeDescriptionReader::readBoolBinding(UiScriptBinding *ast) | 
| 474 | { | 
| 475 |     Q_ASSERT(ast); | 
| 476 |  | 
| 477 |     if (!ast->statement) { | 
| 478 |         addError(loc: ast->colonToken, message: tr(sourceText: "Expected boolean after colon." )); | 
| 479 |         return false; | 
| 480 |     } | 
| 481 |  | 
| 482 |     auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement); | 
| 483 |     if (!expStmt) { | 
| 484 |         addError(loc: ast->statement->firstSourceLocation(), message: tr(sourceText: "Expected boolean after colon." )); | 
| 485 |         return false; | 
| 486 |     } | 
| 487 |  | 
| 488 |     auto *trueLit = cast<TrueLiteral *>(ast: expStmt->expression); | 
| 489 |     auto *falseLit = cast<FalseLiteral *>(ast: expStmt->expression); | 
| 490 |     if (!trueLit && !falseLit) { | 
| 491 |         addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected true or false after colon." )); | 
| 492 |         return false; | 
| 493 |     } | 
| 494 |  | 
| 495 |     return trueLit; | 
| 496 | } | 
| 497 |  | 
| 498 | double TypeDescriptionReader::readNumericBinding(UiScriptBinding *ast) | 
| 499 | { | 
| 500 |     Q_ASSERT(ast); | 
| 501 |  | 
| 502 |     if (!ast->statement) { | 
| 503 |         addError(loc: ast->colonToken, message: tr(sourceText: "Expected numeric literal after colon." )); | 
| 504 |         return 0; | 
| 505 |     } | 
| 506 |  | 
| 507 |     auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement); | 
| 508 |     if (!expStmt) { | 
| 509 |         addError(loc: ast->statement->firstSourceLocation(), | 
| 510 |                  message: tr(sourceText: "Expected numeric literal after colon." )); | 
| 511 |         return 0; | 
| 512 |     } | 
| 513 |  | 
| 514 |     auto *numericLit = cast<NumericLiteral *>(ast: expStmt->expression); | 
| 515 |     if (!numericLit) { | 
| 516 |         addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected numeric literal after colon." )); | 
| 517 |         return 0; | 
| 518 |     } | 
| 519 |  | 
| 520 |     return numericLit->value; | 
| 521 | } | 
| 522 |  | 
| 523 | ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast) | 
| 524 | { | 
| 525 |     ComponentVersion invalidVersion; | 
| 526 |  | 
| 527 |     if (!ast || !ast->statement) { | 
| 528 |         addError(loc: (ast ? ast->colonToken : SourceLocation()), | 
| 529 |                  message: tr(sourceText: "Expected numeric literal after colon." )); | 
| 530 |         return invalidVersion; | 
| 531 |     } | 
| 532 |  | 
| 533 |     auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement); | 
| 534 |     if (!expStmt) { | 
| 535 |         addError(loc: ast->statement->firstSourceLocation(), | 
| 536 |                  message: tr(sourceText: "Expected numeric literal after colon." )); | 
| 537 |         return invalidVersion; | 
| 538 |     } | 
| 539 |  | 
| 540 |     auto *numericLit = cast<NumericLiteral *>(ast: expStmt->expression); | 
| 541 |     if (!numericLit) { | 
| 542 |         addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected numeric literal after colon." )); | 
| 543 |         return invalidVersion; | 
| 544 |     } | 
| 545 |  | 
| 546 |     return ComponentVersion(m_source.mid(position: numericLit->literalToken.begin(), | 
| 547 |                                          n: numericLit->literalToken.length)); | 
| 548 | } | 
| 549 |  | 
| 550 | int TypeDescriptionReader::readIntBinding(UiScriptBinding *ast) | 
| 551 | { | 
| 552 |     double v = readNumericBinding(ast); | 
| 553 |     int i = static_cast<int>(v); | 
| 554 |  | 
| 555 |     if (i != v) { | 
| 556 |         addError(loc: ast->firstSourceLocation(), message: tr(sourceText: "Expected integer after colon." )); | 
| 557 |         return 0; | 
| 558 |     } | 
| 559 |  | 
| 560 |     return i; | 
| 561 | } | 
| 562 |  | 
| 563 | void TypeDescriptionReader::readExports(UiScriptBinding *ast, const ScopeTree::Ptr &scope) | 
| 564 | { | 
| 565 |     Q_ASSERT(ast); | 
| 566 |  | 
| 567 |     if (!ast->statement) { | 
| 568 |         addError(loc: ast->colonToken, message: tr(sourceText: "Expected array of strings after colon." )); | 
| 569 |         return; | 
| 570 |     } | 
| 571 |  | 
| 572 |     auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement); | 
| 573 |     if (!expStmt) { | 
| 574 |         addError(loc: ast->statement->firstSourceLocation(), | 
| 575 |                  message: tr(sourceText: "Expected array of strings after colon." )); | 
| 576 |         return; | 
| 577 |     } | 
| 578 |  | 
| 579 |     auto *arrayLit = cast<ArrayPattern *>(ast: expStmt->expression); | 
| 580 |     if (!arrayLit) { | 
| 581 |         addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected array of strings after colon." )); | 
| 582 |         return; | 
| 583 |     } | 
| 584 |  | 
| 585 |     for (PatternElementList *it = arrayLit->elements; it; it = it->next) { | 
| 586 |         auto *stringLit = cast<StringLiteral *>(ast: it->element->initializer); | 
| 587 |         if (!stringLit) { | 
| 588 |             addError(loc: arrayLit->firstSourceLocation(), | 
| 589 |                      message: tr(sourceText: "Expected array literal with only string literal members." )); | 
| 590 |             return; | 
| 591 |         } | 
| 592 |         QString exp = stringLit->value.toString(); | 
| 593 |         int slashIdx = exp.indexOf(c: QLatin1Char('/')); | 
| 594 |         int spaceIdx = exp.indexOf(c: QLatin1Char(' ')); | 
| 595 |         ComponentVersion version(exp.mid(position: spaceIdx + 1)); | 
| 596 |  | 
| 597 |         if (spaceIdx == -1 || !version.isValid()) { | 
| 598 |             addError(loc: stringLit->firstSourceLocation(), | 
| 599 |                      message: tr(sourceText: "Expected string literal to contain 'Package/Name major.minor' "  | 
| 600 |                         "or 'Name major.minor'." )); | 
| 601 |             continue; | 
| 602 |         } | 
| 603 |         QString package; | 
| 604 |         if (slashIdx != -1) | 
| 605 |             package = exp.left(n: slashIdx); | 
| 606 |         QString name = exp.mid(position: slashIdx + 1, n: spaceIdx - (slashIdx+1)); | 
| 607 |  | 
| 608 |         // ### relocatable exports where package is empty? | 
| 609 |         scope->addExport(name, package, version); | 
| 610 |     } | 
| 611 | } | 
| 612 |  | 
| 613 | void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, | 
| 614 |                                                     const ScopeTree::Ptr &scope) | 
| 615 | { | 
| 616 |     Q_ASSERT(ast); | 
| 617 |  | 
| 618 |     if (!ast->statement) { | 
| 619 |         addError(loc: ast->colonToken, message: tr(sourceText: "Expected array of numbers after colon." )); | 
| 620 |         return; | 
| 621 |     } | 
| 622 |  | 
| 623 |     auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement); | 
| 624 |     if (!expStmt) { | 
| 625 |         addError(loc: ast->statement->firstSourceLocation(), | 
| 626 |                  message: tr(sourceText: "Expected array of numbers after colon." )); | 
| 627 |         return; | 
| 628 |     } | 
| 629 |  | 
| 630 |     auto *arrayLit = cast<ArrayPattern *>(ast: expStmt->expression); | 
| 631 |     if (!arrayLit) { | 
| 632 |         addError(loc: expStmt->firstSourceLocation(), message: tr(sourceText: "Expected array of numbers after colon." )); | 
| 633 |         return; | 
| 634 |     } | 
| 635 |  | 
| 636 |     int exportIndex = 0; | 
| 637 |     const int exportCount = scope->exports().size(); | 
| 638 |     for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) { | 
| 639 |         auto *numberLit = cast<NumericLiteral *>(ast: it->element->initializer); | 
| 640 |         if (!numberLit) { | 
| 641 |             addError(loc: arrayLit->firstSourceLocation(), | 
| 642 |                      message: tr(sourceText: "Expected array literal with only number literal members." )); | 
| 643 |             return; | 
| 644 |         } | 
| 645 |  | 
| 646 |         if (exportIndex >= exportCount) { | 
| 647 |             addError(loc: numberLit->firstSourceLocation(), | 
| 648 |                      message: tr(sourceText: "Meta object revision without matching export." )); | 
| 649 |             return; | 
| 650 |         } | 
| 651 |  | 
| 652 |         const double v = numberLit->value; | 
| 653 |         const int metaObjectRevision = static_cast<int>(v); | 
| 654 |         if (metaObjectRevision != v) { | 
| 655 |             addError(loc: numberLit->firstSourceLocation(), message: tr(sourceText: "Expected integer." )); | 
| 656 |             return; | 
| 657 |         } | 
| 658 |  | 
| 659 |         scope->setExportMetaObjectRevision(exportIndex, metaObjectRevision); | 
| 660 |     } | 
| 661 | } | 
| 662 |  | 
| 663 | void TypeDescriptionReader::readEnumValues(UiScriptBinding *ast, MetaEnum *metaEnum) | 
| 664 | { | 
| 665 |     if (!ast) | 
| 666 |         return; | 
| 667 |     if (!ast->statement) { | 
| 668 |         addError(loc: ast->colonToken, message: tr(sourceText: "Expected object literal after colon." )); | 
| 669 |         return; | 
| 670 |     } | 
| 671 |  | 
| 672 |     auto *expStmt = cast<ExpressionStatement *>(ast: ast->statement); | 
| 673 |     if (!expStmt) { | 
| 674 |         addError(loc: ast->statement->firstSourceLocation(), message: tr(sourceText: "Expected expression after colon." )); | 
| 675 |         return; | 
| 676 |     } | 
| 677 |  | 
| 678 |     if (auto *objectLit = cast<ObjectPattern *>(ast: expStmt->expression)) { | 
| 679 |         for (PatternPropertyList *it = objectLit->properties; it; it = it->next) { | 
| 680 |             if (PatternProperty *assignement = it->property) { | 
| 681 |                 if (auto *name = cast<StringLiteralPropertyName *>(ast: assignement->name)) { | 
| 682 |                     metaEnum->addKey(key: name->id.toString()); | 
| 683 |                     continue; | 
| 684 |                 } | 
| 685 |             } | 
| 686 |             addError(loc: it->firstSourceLocation(), message: tr(sourceText: "Expected strings as enum keys." )); | 
| 687 |         } | 
| 688 |     } else if (auto *arrayLit = cast<ArrayPattern *>(ast: expStmt->expression)) { | 
| 689 |         for (PatternElementList *it = arrayLit->elements; it; it = it->next) { | 
| 690 |             if (PatternElement *element = it->element) { | 
| 691 |                 if (auto *name = cast<StringLiteral *>(ast: element->initializer)) { | 
| 692 |                     metaEnum->addKey(key: name->value.toString()); | 
| 693 |                     continue; | 
| 694 |                 } | 
| 695 |             } | 
| 696 |             addError(loc: it->firstSourceLocation(), message: tr(sourceText: "Expected strings as enum keys." )); | 
| 697 |         } | 
| 698 |     } else { | 
| 699 |         addError(loc: ast->statement->firstSourceLocation(), | 
| 700 |                  message: tr(sourceText: "Expected either array or object literal as enum definition." )); | 
| 701 |     } | 
| 702 | } | 
| 703 |  |