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
toString(const UiQualifiedId * qualifiedId,QChar delimiter=QLatin1Char ('.'))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
operator ()(QHash<QString,ScopeTree::ConstPtr> * objects,QList<ModuleApiInfo> * moduleApis,QStringList * dependencies)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(m_source, /*lineno = */ 1, /*qmlMode = */true);
65
66 if (!parser.parse()) {
67 m_errorMessage = QString::fromLatin1("%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(parser.ast());
78
79 return m_errorMessage.isEmpty();
80 }
81
readDocument(UiProgram * ast)82 void TypeDescriptionReader::readDocument(UiProgram *ast)
83 {
84 if (!ast) {
85 addError(SourceLocation(), tr("Could not parse document."));
86 return;
87 }
88
89 if (!ast->headers || ast->headers->next || !cast<UiImport *>(ast->headers->headerItem)) {
90 addError(SourceLocation(), tr("Expected a single import."));
91 return;
92 }
93
94 auto *import = cast<UiImport *>(ast->headers->headerItem);
95 if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
96 addError(import->importToken, tr("Expected import of QtQuick.tooling."));
97 return;
98 }
99
100 if (!import->version) {
101 addError(import->firstSourceLocation(), tr("Import statement without version."));
102 return;
103 }
104
105 if (import->version->majorVersion != 1) {
106 addError(import->version->firstSourceLocation(),
107 tr("Major version different from 1 not supported."));
108 return;
109 }
110
111 if (!ast->members || !ast->members->member || ast->members->next) {
112 addError(SourceLocation(), tr("Expected document to contain a single object definition."));
113 return;
114 }
115
116 auto *module = cast<UiObjectDefinition *>(ast->members->member);
117 if (!module) {
118 addError(SourceLocation(), tr("Expected document to contain a single object definition."));
119 return;
120 }
121
122 if (toString(module->qualifiedTypeNameId) != QLatin1String("Module")) {
123 addError(SourceLocation(), tr("Expected document to contain a Module {} member."));
124 return;
125 }
126
127 readModule(module);
128 }
129
readModule(UiObjectDefinition * ast)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 *>(member);
135
136 auto *script = cast<UiScriptBinding *>(member);
137 if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) {
138 readDependencies(script);
139 continue;
140 }
141
142 QString typeName;
143 if (component)
144 typeName = toString(component->qualifiedTypeNameId);
145
146 if (!component || (typeName != QLatin1String("Component")
147 && typeName != QLatin1String("ModuleApi"))) {
148 continue;
149 }
150
151 if (typeName == QLatin1String("Component"))
152 readComponent(component);
153 else if (typeName == QLatin1String("ModuleApi"))
154 readModuleApi(component);
155 }
156 }
157
addError(const SourceLocation & loc,const QString & message)158 void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
159 {
160 m_errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
161 QDir::toNativeSeparators(m_fileName),
162 QString::number(loc.startLine),
163 QString::number(loc.startColumn),
164 message);
165 }
166
addWarning(const SourceLocation & loc,const QString & message)167 void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
168 {
169 m_warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
170 QDir::toNativeSeparators(m_fileName),
171 QString::number(loc.startLine),
172 QString::number(loc.startColumn),
173 message);
174 }
175
readDependencies(UiScriptBinding * ast)176 void TypeDescriptionReader::readDependencies(UiScriptBinding *ast)
177 {
178 auto *stmt = cast<ExpressionStatement*>(ast->statement);
179 if (!stmt) {
180 addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions"));
181 return;
182 }
183 auto *exp = cast<ArrayPattern *>(stmt->expression);
184 if (!exp) {
185 addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions"));
186 return;
187 }
188 for (PatternElementList *l = exp->elements; l; l = l->next) {
189 auto *str = cast<StringLiteral *>(l->element->initializer);
190 *m_dependencies << str->value.toString();
191 }
192 }
193
readComponent(UiObjectDefinition * ast)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 *>(member);
201 auto *script = cast<UiScriptBinding *>(member);
202 if (component) {
203 QString name = toString(component->qualifiedTypeNameId);
204 if (name == QLatin1String("Property"))
205 readProperty(component, scope);
206 else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
207 readSignalOrMethod(component, name == QLatin1String("Method"), scope);
208 else if (name == QLatin1String("Enum"))
209 readEnum(component, scope);
210 else
211 addWarning(component->firstSourceLocation(),
212 tr("Expected only Property, Method, Signal and Enum object definitions, "
213 "not \"%1\".").arg(name));
214 } else if (script) {
215 QString name = toString(script->qualifiedId);
216 if (name == QLatin1String("name")) {
217 scope->setClassName(readStringBinding(script));
218 } else if (name == QLatin1String("prototype")) {
219 scope->setSuperclassName(readStringBinding(script));
220 } else if (name == QLatin1String("defaultProperty")) {
221 scope->setDefaultPropertyName(readStringBinding(script));
222 } else if (name == QLatin1String("exports")) {
223 readExports(script, scope);
224 } else if (name == QLatin1String("exportMetaObjectRevisions")) {
225 readMetaObjectRevisions(script, scope);
226 } else if (name == QLatin1String("attachedType")) {
227 scope->setAttachedTypeName(readStringBinding(script));
228 } else if (name == QLatin1String("isSingleton")) {
229 scope->setIsSingleton(readBoolBinding(script));
230 } else if (name == QLatin1String("isCreatable")) {
231 scope->setIsCreatable(readBoolBinding(script));
232 } else if (name == QLatin1String("isComposite")) {
233 scope->setIsComposite(readBoolBinding(script));
234 } else {
235 addWarning(script->firstSourceLocation(),
236 tr("Expected only name, prototype, defaultProperty, attachedType, "
237 "exports, isSingleton, isCreatable, isComposite and "
238 "exportMetaObjectRevisions script bindings, not \"%1\".").arg(name));
239 }
240 } else {
241 addWarning(member->firstSourceLocation(),
242 tr("Expected only script bindings and object definitions."));
243 }
244 }
245
246 if (scope->className().isEmpty()) {
247 addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
248 return;
249 }
250
251 // ### add implicit export into the package of c++ types
252 scope->addExport(scope->className(), QStringLiteral("<cpp>"), ComponentVersion());
253 m_objects->insert(scope->className(), scope);
254 }
255
readModuleApi(UiObjectDefinition * ast)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 *>(member);
263
264 if (script) {
265 const QString name = toString(script->qualifiedId);
266 if (name == QLatin1String("uri")) {
267 apiInfo.uri = readStringBinding(script);
268 } else if (name == QLatin1String("version")) {
269 apiInfo.version = readNumericVersionBinding(script);
270 } else if (name == QLatin1String("name")) {
271 apiInfo.cppName = readStringBinding(script);
272 } else {
273 addWarning(script->firstSourceLocation(),
274 tr("Expected only uri, version and name script bindings."));
275 }
276 } else {
277 addWarning(member->firstSourceLocation(), tr("Expected only script bindings."));
278 }
279 }
280
281 if (!apiInfo.version.isValid()) {
282 addError(ast->firstSourceLocation(),
283 tr("ModuleApi definition has no or invalid version binding."));
284 return;
285 }
286
287 if (m_moduleApis)
288 m_moduleApis->append(apiInfo);
289 }
290
readSignalOrMethod(UiObjectDefinition * ast,bool isMethod,const ScopeTree::Ptr & scope)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 *>(member);
304 auto *script = cast<UiScriptBinding *>(member);
305 if (component) {
306 QString name = toString(component->qualifiedTypeNameId);
307 if (name == QLatin1String("Parameter")) {
308 readParameter(component, &metaMethod);
309 } else {
310 addWarning(component->firstSourceLocation(),
311 tr("Expected only Parameter object definitions."));
312 }
313 } else if (script) {
314 QString name = toString(script->qualifiedId);
315 if (name == QLatin1String("name")) {
316 metaMethod.setMethodName(readStringBinding(script));
317 } else if (name == QLatin1String("type")) {
318 metaMethod.setReturnType(readStringBinding(script));
319 } else if (name == QLatin1String("revision")) {
320 metaMethod.setRevision(readIntBinding(script));
321 } else {
322 addWarning(script->firstSourceLocation(),
323 tr("Expected only name and type script bindings."));
324 }
325 } else {
326 addWarning(member->firstSourceLocation(),
327 tr("Expected only script bindings and object definitions."));
328 }
329 }
330
331 if (metaMethod.methodName().isEmpty()) {
332 addError(ast->firstSourceLocation(),
333 tr("Method or signal is missing a name script binding."));
334 return;
335 }
336
337 scope->addMethod(metaMethod);
338 }
339
readProperty(UiObjectDefinition * ast,const ScopeTree::Ptr & scope)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 *>(member);
352 if (!script) {
353 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
354 continue;
355 }
356
357 QString id = toString(script->qualifiedId);
358 if (id == QLatin1String("name")) {
359 name = readStringBinding(script);
360 } else if (id == QLatin1String("type")) {
361 type = readStringBinding(script);
362 } else if (id == QLatin1String("isPointer")) {
363 isPointer = readBoolBinding(script);
364 } else if (id == QLatin1String("isReadonly")) {
365 isReadonly = readBoolBinding(script);
366 } else if (id == QLatin1String("isList")) {
367 isList = readBoolBinding(script);
368 } else if (id == QLatin1String("revision")) {
369 revision = readIntBinding(script);
370 } else {
371 addWarning(script->firstSourceLocation(),
372 tr("Expected only type, name, revision, isPointer, isReadonly and"
373 " isList script bindings."));
374 }
375 }
376
377 if (name.isEmpty() || type.isEmpty()) {
378 addError(ast->firstSourceLocation(),
379 tr("Property object is missing a name or type script binding."));
380 return;
381 }
382
383 scope->addProperty(MetaProperty(name, type, isList, !isReadonly, isPointer, false, revision));
384 }
385
readEnum(UiObjectDefinition * ast,const ScopeTree::Ptr & scope)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 *>(member);
393 if (!script) {
394 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
395 continue;
396 }
397
398 QString name = toString(script->qualifiedId);
399 if (name == QLatin1String("name")) {
400 metaEnum.setName(readStringBinding(script));
401 } else if (name == QLatin1String("alias")) {
402 metaEnum.setAlias(readStringBinding(script));
403 } else if (name == QLatin1String("isFlag")) {
404 metaEnum.setIsFlag(readBoolBinding(script));
405 } else if (name == QLatin1String("values")) {
406 readEnumValues(script, &metaEnum);
407 } else {
408 addWarning(script->firstSourceLocation(),
409 tr("Expected only name and values script bindings."));
410 }
411 }
412
413 scope->addEnum(metaEnum);
414 }
415
readParameter(UiObjectDefinition * ast,MetaMethod * metaMethod)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 *>(member);
424 if (!script) {
425 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
426 continue;
427 }
428
429 const QString id = toString(script->qualifiedId);
430 if (id == QLatin1String("name")) {
431 name = readStringBinding(script);
432 } else if (id == QLatin1String("type")) {
433 type = readStringBinding(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(script->firstSourceLocation(),
442 tr("Expected only name and type script bindings."));
443 }
444 }
445
446 metaMethod->addParameter(name, type);
447 }
448
readStringBinding(UiScriptBinding * ast)449 QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
450 {
451 Q_ASSERT(ast);
452
453 if (!ast->statement) {
454 addError(ast->colonToken, tr("Expected string after colon."));
455 return QString();
456 }
457
458 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
459 if (!expStmt) {
460 addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
461 return QString();
462 }
463
464 auto *stringLit = cast<StringLiteral *>(expStmt->expression);
465 if (!stringLit) {
466 addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
467 return QString();
468 }
469
470 return stringLit->value.toString();
471 }
472
readBoolBinding(UiScriptBinding * ast)473 bool TypeDescriptionReader::readBoolBinding(UiScriptBinding *ast)
474 {
475 Q_ASSERT(ast);
476
477 if (!ast->statement) {
478 addError(ast->colonToken, tr("Expected boolean after colon."));
479 return false;
480 }
481
482 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
483 if (!expStmt) {
484 addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
485 return false;
486 }
487
488 auto *trueLit = cast<TrueLiteral *>(expStmt->expression);
489 auto *falseLit = cast<FalseLiteral *>(expStmt->expression);
490 if (!trueLit && !falseLit) {
491 addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
492 return false;
493 }
494
495 return trueLit;
496 }
497
readNumericBinding(UiScriptBinding * ast)498 double TypeDescriptionReader::readNumericBinding(UiScriptBinding *ast)
499 {
500 Q_ASSERT(ast);
501
502 if (!ast->statement) {
503 addError(ast->colonToken, tr("Expected numeric literal after colon."));
504 return 0;
505 }
506
507 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
508 if (!expStmt) {
509 addError(ast->statement->firstSourceLocation(),
510 tr("Expected numeric literal after colon."));
511 return 0;
512 }
513
514 auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
515 if (!numericLit) {
516 addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
517 return 0;
518 }
519
520 return numericLit->value;
521 }
522
readNumericVersionBinding(UiScriptBinding * ast)523 ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
524 {
525 ComponentVersion invalidVersion;
526
527 if (!ast || !ast->statement) {
528 addError((ast ? ast->colonToken : SourceLocation()),
529 tr("Expected numeric literal after colon."));
530 return invalidVersion;
531 }
532
533 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
534 if (!expStmt) {
535 addError(ast->statement->firstSourceLocation(),
536 tr("Expected numeric literal after colon."));
537 return invalidVersion;
538 }
539
540 auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
541 if (!numericLit) {
542 addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
543 return invalidVersion;
544 }
545
546 return ComponentVersion(m_source.mid(numericLit->literalToken.begin(),
547 numericLit->literalToken.length));
548 }
549
readIntBinding(UiScriptBinding * ast)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(ast->firstSourceLocation(), tr("Expected integer after colon."));
557 return 0;
558 }
559
560 return i;
561 }
562
readExports(UiScriptBinding * ast,const ScopeTree::Ptr & scope)563 void TypeDescriptionReader::readExports(UiScriptBinding *ast, const ScopeTree::Ptr &scope)
564 {
565 Q_ASSERT(ast);
566
567 if (!ast->statement) {
568 addError(ast->colonToken, tr("Expected array of strings after colon."));
569 return;
570 }
571
572 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
573 if (!expStmt) {
574 addError(ast->statement->firstSourceLocation(),
575 tr("Expected array of strings after colon."));
576 return;
577 }
578
579 auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
580 if (!arrayLit) {
581 addError(expStmt->firstSourceLocation(), tr("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 *>(it->element->initializer);
587 if (!stringLit) {
588 addError(arrayLit->firstSourceLocation(),
589 tr("Expected array literal with only string literal members."));
590 return;
591 }
592 QString exp = stringLit->value.toString();
593 int slashIdx = exp.indexOf(QLatin1Char('/'));
594 int spaceIdx = exp.indexOf(QLatin1Char(' '));
595 ComponentVersion version(exp.mid(spaceIdx + 1));
596
597 if (spaceIdx == -1 || !version.isValid()) {
598 addError(stringLit->firstSourceLocation(),
599 tr("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(slashIdx);
606 QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
607
608 // ### relocatable exports where package is empty?
609 scope->addExport(name, package, version);
610 }
611 }
612
readMetaObjectRevisions(UiScriptBinding * ast,const ScopeTree::Ptr & scope)613 void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast,
614 const ScopeTree::Ptr &scope)
615 {
616 Q_ASSERT(ast);
617
618 if (!ast->statement) {
619 addError(ast->colonToken, tr("Expected array of numbers after colon."));
620 return;
621 }
622
623 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
624 if (!expStmt) {
625 addError(ast->statement->firstSourceLocation(),
626 tr("Expected array of numbers after colon."));
627 return;
628 }
629
630 auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
631 if (!arrayLit) {
632 addError(expStmt->firstSourceLocation(), tr("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 *>(it->element->initializer);
640 if (!numberLit) {
641 addError(arrayLit->firstSourceLocation(),
642 tr("Expected array literal with only number literal members."));
643 return;
644 }
645
646 if (exportIndex >= exportCount) {
647 addError(numberLit->firstSourceLocation(),
648 tr("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(numberLit->firstSourceLocation(), tr("Expected integer."));
656 return;
657 }
658
659 scope->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
660 }
661 }
662
readEnumValues(UiScriptBinding * ast,MetaEnum * metaEnum)663 void TypeDescriptionReader::readEnumValues(UiScriptBinding *ast, MetaEnum *metaEnum)
664 {
665 if (!ast)
666 return;
667 if (!ast->statement) {
668 addError(ast->colonToken, tr("Expected object literal after colon."));
669 return;
670 }
671
672 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
673 if (!expStmt) {
674 addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
675 return;
676 }
677
678 if (auto *objectLit = cast<ObjectPattern *>(expStmt->expression)) {
679 for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
680 if (PatternProperty *assignement = it->property) {
681 if (auto *name = cast<StringLiteralPropertyName *>(assignement->name)) {
682 metaEnum->addKey(name->id.toString());
683 continue;
684 }
685 }
686 addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
687 }
688 } else if (auto *arrayLit = cast<ArrayPattern *>(expStmt->expression)) {
689 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
690 if (PatternElement *element = it->element) {
691 if (auto *name = cast<StringLiteral *>(element->initializer)) {
692 metaEnum->addKey(name->value.toString());
693 continue;
694 }
695 }
696 addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
697 }
698 } else {
699 addError(ast->statement->firstSourceLocation(),
700 tr("Expected either array or object literal as enum definition."));
701 }
702 }
703