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