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