qmljstypedescriptionreader.cpp 24.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Tobias Hunger's avatar
Tobias Hunger committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
Tobias Hunger's avatar
Tobias Hunger committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Tobias Hunger's avatar
Tobias Hunger committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
Tobias Hunger's avatar
Tobias Hunger committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
25
26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
Tobias Hunger's avatar
Tobias Hunger committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
Tobias Hunger's avatar
Tobias Hunger committed
30

31
32
33
34
35
36
#include "qmljstypedescriptionreader.h"

#include "parser/qmljsparser_p.h"
#include "parser/qmljslexer_p.h"
#include "parser/qmljsengine_p.h"

37
#include "qmljsinterpreter.h"
38
#include "qmljsutils.h"
39

40
41
#include <utils/qtcassert.h>

42
43
#include <QDir>

44
45
46
47
using namespace QmlJS;
using namespace QmlJS::AST;
using namespace LanguageUtils;

48
49
TypeDescriptionReader::TypeDescriptionReader(const QString &fileName, const QString &data)
    : _fileName (fileName), _source(data), _objects(0)
50
51
52
53
54
55
56
{
}

TypeDescriptionReader::~TypeDescriptionReader()
{
}

57
58
59
bool TypeDescriptionReader::operator()(
        QHash<QString, FakeMetaObject::ConstPtr> *objects,
        QList<ModuleApiInfo> *moduleApis)
60
61
62
63
64
65
{
    Engine engine;

    Lexer lexer(&engine);
    Parser parser(&engine);

66
    lexer.setCode(_source, /*line = */ 1, /*qmlMode = */true);
67
68

    if (!parser.parse()) {
69
        _errorMessage = QString::fromLatin1("%1:%2: %3").arg(
70
71
72
73
74
75
76
                    QString::number(parser.errorLineNumber()),
                    QString::number(parser.errorColumnNumber()),
                    parser.errorMessage());
        return false;
    }

    _objects = objects;
77
    _moduleApis = moduleApis;
78
79
80
81
82
83
84
85
86
87
    readDocument(parser.ast());

    return _errorMessage.isEmpty();
}

QString TypeDescriptionReader::errorMessage() const
{
    return _errorMessage;
}

88
89
90
91
92
QString TypeDescriptionReader::warningMessage() const
{
    return _warningMessage;
}

93
94
95
void TypeDescriptionReader::readDocument(UiProgram *ast)
{
    if (!ast) {
Leena Miettinen's avatar
Leena Miettinen committed
96
        addError(SourceLocation(), tr("Could not parse document."));
97
98
99
        return;
    }

Fawzi Mohamed's avatar
Fawzi Mohamed committed
100
    if (!ast->headers || ast->headers->next || !AST::cast<AST::UiImport *>(ast->headers->headerItem)) {
Leena Miettinen's avatar
Leena Miettinen committed
101
        addError(SourceLocation(), tr("Expected a single import."));
102
103
104
        return;
    }

Fawzi Mohamed's avatar
Fawzi Mohamed committed
105
    UiImport *import = AST::cast<AST::UiImport *>(ast->headers->headerItem);
106
    if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
Leena Miettinen's avatar
Leena Miettinen committed
107
        addError(import->importToken, tr("Expected import of QtQuick.tooling."));
108
109
110
111
112
113
114
115
116
117
        return;
    }

    ComponentVersion version;
    const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
    const int dotIdx = versionString.indexOf(QLatin1Char('.'));
    if (dotIdx != -1) {
        version = ComponentVersion(versionString.left(dotIdx).toInt(),
                                   versionString.mid(dotIdx + 1).toInt());
    }
118
119
    if (version.majorVersion() != 1) {
        addError(import->versionToken, tr("Major version different from 1 not supported."));
120
121
        return;
    }
122
123
    if (version.minorVersion() > 1)
        addWarning(import->versionToken, tr("Reading only version 1.1 parts."));
124
125

    if (!ast->members || !ast->members->member || ast->members->next) {
Leena Miettinen's avatar
Leena Miettinen committed
126
        addError(SourceLocation(), tr("Expected document to contain a single object definition."));
127
128
129
130
131
        return;
    }

    UiObjectDefinition *module = dynamic_cast<UiObjectDefinition *>(ast->members->member);
    if (!module) {
Leena Miettinen's avatar
Leena Miettinen committed
132
        addError(SourceLocation(), tr("Expected document to contain a single object definition."));
133
134
135
        return;
    }

136
    if (toString(module->qualifiedTypeNameId) != QLatin1String("Module")) {
Leena Miettinen's avatar
Leena Miettinen committed
137
        addError(SourceLocation(), tr("Expected document to contain a Module {} member."));
138
139
140
141
142
143
144
145
146
147
148
        return;
    }

    readModule(module);
}

void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
{
    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
149
150
151

        QString typeName;
        if (component)
152
            typeName = toString(component->qualifiedTypeNameId);
153

154
155
        if (!component || (typeName != QLatin1String("Component") && typeName != QLatin1String("ModuleApi"))) {
            addWarning(member->firstSourceLocation(),
Leena Miettinen's avatar
Leena Miettinen committed
156
                       tr("Expected only Component and ModuleApi object definitions."));
157
            continue;
158
159
        }

160
        if (typeName == QLatin1String("Component"))
161
            readComponent(component);
162
        else if (typeName == QLatin1String("ModuleApi"))
163
            readModuleApi(component);
164
165
166
167
168
    }
}

void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
{
169
170
    _errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
                QDir::toNativeSeparators(_fileName),
171
172
173
174
175
                QString::number(loc.startLine),
                QString::number(loc.startColumn),
                message);
}

176
177
void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
{
178
179
    _warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
                QDir::toNativeSeparators(_fileName),
180
181
182
183
184
                QString::number(loc.startLine),
                QString::number(loc.startColumn),
                message);
}

185
186
187
188
189
190
191
192
193
void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
{
    FakeMetaObject::Ptr fmo(new FakeMetaObject);

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (component) {
194
            QString name = toString(component->qualifiedTypeNameId);
195
            if (name == QLatin1String("Property"))
196
                readProperty(component, fmo);
197
            else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
198
                readSignalOrMethod(component, name == QLatin1String("Method"), fmo);
199
            else if (name == QLatin1String("Enum"))
200
                readEnum(component, fmo);
201
            else
202
                addWarning(component->firstSourceLocation(),
203
                           tr("Expected only Property, Method, Signal and Enum object definitions, not \"%1\".")
204
                           .arg(name));
205
        } else if (script) {
206
            QString name = toString(script->qualifiedId);
207
            if (name == QLatin1String("name")) {
208
                fmo->setClassName(readStringBinding(script));
209
            } else if (name == QLatin1String("prototype")) {
210
                fmo->setSuperclassName(readStringBinding(script));
211
            } else if (name == QLatin1String("defaultProperty")) {
212
                fmo->setDefaultPropertyName(readStringBinding(script));
213
            } else if (name == QLatin1String("exports")) {
214
                readExports(script, fmo);
215
            } else if (name == QLatin1String("exportMetaObjectRevisions")) {
216
                readMetaObjectRevisions(script, fmo);
217
            } else if (name == QLatin1String("attachedType")) {
218
                fmo->setAttachedTypeName(readStringBinding(script));
219
220
221
222
223
224
            } else if (name == QLatin1String("isSingleton")) {
                fmo->setIsSingleton(readBoolBinding(script));
            } else if (name == QLatin1String("isCreatable")) {
                fmo->setIsCreatable(readBoolBinding(script));
            } else if (name == QLatin1String("isComposite")) {
                fmo->setIsComposite(readBoolBinding(script));
225
            } else {
226
                addWarning(script->firstSourceLocation(),
Takumi Asaki's avatar
Takumi Asaki committed
227
                           tr("Expected only name, prototype, defaultProperty, attachedType, exports, "
228
                              "isSingleton, isCreatable, isComposite and exportMetaObjectRevisions "
229
                              "script bindings, not \"%1\".").arg(name));
230
231
            }
        } else {
Leena Miettinen's avatar
Leena Miettinen committed
232
            addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
233
234
235
236
        }
    }

    if (fmo->className().isEmpty()) {
Leena Miettinen's avatar
Leena Miettinen committed
237
        addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
238
239
240
        return;
    }

241
    // ### add implicit export into the package of c++ types
242
    fmo->addExport(fmo->className(), QmlJS::CppQmlTypes::cppPackage, ComponentVersion());
243
    fmo->updateFingerprint();
244
245
246
    _objects->insert(fmo->className(), fmo);
}

247
248
249
250
251
252
253
254
255
256
void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
{
    ModuleApiInfo apiInfo;

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);

        if (script) {
            const QString name = toString(script->qualifiedId);
257
            if (name == QLatin1String("uri")) {
258
                apiInfo.uri = readStringBinding(script);
259
            } else if (name == QLatin1String("version")) {
260
                apiInfo.version = readNumericVersionBinding(script);
261
            } else if (name == QLatin1String("name")) {
Christian Kamm's avatar
Christian Kamm committed
262
                apiInfo.cppName = readStringBinding(script);
263
264
            } else {
                addWarning(script->firstSourceLocation(),
Leena Miettinen's avatar
Leena Miettinen committed
265
                           tr("Expected only uri, version and name script bindings."));
266
267
            }
        } else {
Leena Miettinen's avatar
Leena Miettinen committed
268
            addWarning(member->firstSourceLocation(), tr("Expected only script bindings."));
269
270
271
272
        }
    }

    if (!apiInfo.version.isValid()) {
Leena Miettinen's avatar
Leena Miettinen committed
273
        addError(ast->firstSourceLocation(), tr("ModuleApi definition has no or invalid version binding."));
274
275
276
277
278
279
280
        return;
    }

    if (_moduleApis)
        _moduleApis->append(apiInfo);
}

281
282
283
284
285
286
287
288
289
290
291
292
293
294
void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo)
{
    FakeMetaMethod fmm;
    // ### confusion between Method and Slot. Method should be removed.
    if (isMethod)
        fmm.setMethodType(FakeMetaMethod::Slot);
    else
        fmm.setMethodType(FakeMetaMethod::Signal);

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (component) {
295
            QString name = toString(component->qualifiedTypeNameId);
296
            if (name == QLatin1String("Parameter"))
297
                readParameter(component, &fmm);
298
            else
Leena Miettinen's avatar
Leena Miettinen committed
299
                addWarning(component->firstSourceLocation(), tr("Expected only Parameter object definitions."));
300
        } else if (script) {
301
            QString name = toString(script->qualifiedId);
302
            if (name == QLatin1String("name"))
303
                fmm.setMethodName(readStringBinding(script));
304
            else if (name == QLatin1String("type"))
305
                fmm.setReturnType(readStringBinding(script));
306
            else if (name == QLatin1String("revision"))
307
                fmm.setRevision(readIntBinding(script));
308
            else
Leena Miettinen's avatar
Leena Miettinen committed
309
                addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
310
311

        } else {
Leena Miettinen's avatar
Leena Miettinen committed
312
            addWarning(member->firstSourceLocation(), tr("Expected only script bindings and object definitions."));
313
314
315
316
        }
    }

    if (fmm.methodName().isEmpty()) {
Leena Miettinen's avatar
Leena Miettinen committed
317
        addError(ast->firstSourceLocation(), tr("Method or signal is missing a name script binding."));
318
319
320
321
322
323
324
325
326
327
328
329
330
        return;
    }

    fmo->addMethod(fmm);
}

void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
{
    QString name;
    QString type;
    bool isPointer = false;
    bool isReadonly = false;
    bool isList = false;
331
    int revision = 0;
332
333
334
335
336

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (!script) {
Leena Miettinen's avatar
Leena Miettinen committed
337
            addWarning(member->firstSourceLocation(), tr("Expected script binding."));
338
            continue;
339
340
        }

341
        QString id = toString(script->qualifiedId);
342
        if (id == QLatin1String("name"))
343
            name = readStringBinding(script);
344
        else if (id == QLatin1String("type"))
345
            type = readStringBinding(script);
346
        else if (id == QLatin1String("isPointer"))
347
            isPointer = readBoolBinding(script);
348
        else if (id == QLatin1String("isReadonly"))
349
            isReadonly = readBoolBinding(script);
350
        else if (id == QLatin1String("isList"))
351
            isList = readBoolBinding(script);
352
        else if (id == QLatin1String("revision"))
353
            revision = readIntBinding(script);
354
        else
Leena Miettinen's avatar
Leena Miettinen committed
355
            addWarning(script->firstSourceLocation(), tr("Expected only type, name, revision, isPointer, isReadonly and isList script bindings."));
356
357
358
    }

    if (name.isEmpty() || type.isEmpty()) {
Leena Miettinen's avatar
Leena Miettinen committed
359
        addError(ast->firstSourceLocation(), tr("Property object is missing a name or type script binding."));
360
361
362
        return;
    }

363
    fmo->addProperty(FakeMetaProperty(name, type, isList, !isReadonly, isPointer, revision));
364
365
366
367
368
369
370
371
372
373
}

void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
{
    FakeMetaEnum fme;

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (!script) {
Leena Miettinen's avatar
Leena Miettinen committed
374
            addWarning(member->firstSourceLocation(), tr("Expected script binding."));
375
            continue;
376
377
        }

378
        QString name = toString(script->qualifiedId);
379
        if (name == QLatin1String("name"))
380
            fme.setName(readStringBinding(script));
381
        else if (name == QLatin1String("values"))
382
            readEnumValues(script, &fme);
383
        else
Fawzi Mohamed's avatar
Fawzi Mohamed committed
384
            addWarning(script->firstSourceLocation(), tr("Expected only name and values script bindings."));
385
386
387
388
389
390
391
392
393
394
395
396
397
398
    }

    fmo->addEnum(fme);
}

void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMethod *fmm)
{
    QString name;
    QString type;

    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
        UiObjectMember *member = it->member;
        UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
        if (!script) {
Leena Miettinen's avatar
Leena Miettinen committed
399
            addWarning(member->firstSourceLocation(), tr("Expected script binding."));
400
            continue;
401
402
        }

403
        const QString id = toString(script->qualifiedId);
404
        if (id == QLatin1String("name")) {
405
            name = readStringBinding(script);
406
        } else if (id == QLatin1String("type")) {
407
            type = readStringBinding(script);
408
        } else if (id == QLatin1String("isPointer")) {
409
            // ### unhandled
410
        } else if (id == QLatin1String("isReadonly")) {
411
            // ### unhandled
412
        } else if (id == QLatin1String("isList")) {
413
414
            // ### unhandled
        } else {
Leena Miettinen's avatar
Leena Miettinen committed
415
            addWarning(script->firstSourceLocation(), tr("Expected only name and type script bindings."));
416
417
418
419
420
421
422
423
        }
    }

    fmm->addParameter(name, type);
}

QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
{
424
425
426
    QTC_ASSERT(ast, return QString());

    if (!ast->statement) {
Leena Miettinen's avatar
Leena Miettinen committed
427
        addError(ast->colonToken, tr("Expected string after colon."));
428
429
430
431
432
        return QString();
    }

    ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
    if (!expStmt) {
Leena Miettinen's avatar
Leena Miettinen committed
433
        addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
434
435
436
437
438
        return QString();
    }

    StringLiteral *stringLit = dynamic_cast<StringLiteral *>(expStmt->expression);
    if (!stringLit) {
Leena Miettinen's avatar
Leena Miettinen committed
439
        addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
440
441
442
        return QString();
    }

443
    return stringLit->value.toString();
444
445
446
447
}

bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast)
{
448
449
450
    QTC_ASSERT(ast, return false);

    if (!ast->statement) {
Leena Miettinen's avatar
Leena Miettinen committed
451
        addError(ast->colonToken, tr("Expected boolean after colon."));
452
453
454
455
456
        return false;
    }

    ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
    if (!expStmt) {
Leena Miettinen's avatar
Leena Miettinen committed
457
        addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
458
459
460
461
462
463
        return false;
    }

    TrueLiteral *trueLit = dynamic_cast<TrueLiteral *>(expStmt->expression);
    FalseLiteral *falseLit = dynamic_cast<FalseLiteral *>(expStmt->expression);
    if (!trueLit && !falseLit) {
Leena Miettinen's avatar
Leena Miettinen committed
464
        addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
465
466
467
468
469
470
        return false;
    }

    return trueLit;
}

471
472
double TypeDescriptionReader::readNumericBinding(AST::UiScriptBinding *ast)
{
473
474
475
    QTC_ASSERT(ast, return qQNaN());

    if (!ast->statement) {
Leena Miettinen's avatar
Leena Miettinen committed
476
        addError(ast->colonToken, tr("Expected numeric literal after colon."));
477
478
479
480
481
        return 0;
    }

    ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
    if (!expStmt) {
Leena Miettinen's avatar
Leena Miettinen committed
482
        addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
483
484
485
486
487
        return 0;
    }

    NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
    if (!numericLit) {
Leena Miettinen's avatar
Leena Miettinen committed
488
        addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
489
490
491
492
493
494
        return 0;
    }

    return numericLit->value;
}

495
496
497
498
499
ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
{
    ComponentVersion invalidVersion;

    if (!ast || !ast->statement) {
500
        addError((ast ? ast->colonToken : SourceLocation()), tr("Expected numeric literal after colon."));
501
502
503
504
505
        return invalidVersion;
    }

    ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
    if (!expStmt) {
Leena Miettinen's avatar
Leena Miettinen committed
506
        addError(ast->statement->firstSourceLocation(), tr("Expected numeric literal after colon."));
507
508
509
510
511
        return invalidVersion;
    }

    NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
    if (!numericLit) {
Leena Miettinen's avatar
Leena Miettinen committed
512
        addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
513
514
515
516
517
518
        return invalidVersion;
    }

    return ComponentVersion(_source.mid(numericLit->literalToken.begin(), numericLit->literalToken.length));
}

519
520
521
int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast)
{
    double v = readNumericBinding(ast);
Christian Kamm's avatar
Christian Kamm committed
522
    int i = static_cast<int>(v);
523
524

    if (i != v) {
Leena Miettinen's avatar
Leena Miettinen committed
525
        addError(ast->firstSourceLocation(), tr("Expected integer after colon."));
526
527
528
529
530
531
        return 0;
    }

    return i;
}

532
533
void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
{
534
535
536
    QTC_ASSERT(ast, return);

    if (!ast->statement) {
Leena Miettinen's avatar
Leena Miettinen committed
537
        addError(ast->colonToken, tr("Expected array of strings after colon."));
538
539
540
541
542
        return;
    }

    ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
    if (!expStmt) {
Leena Miettinen's avatar
Leena Miettinen committed
543
        addError(ast->statement->firstSourceLocation(), tr("Expected array of strings after colon."));
544
545
546
547
548
        return;
    }

    ArrayLiteral *arrayLit = dynamic_cast<ArrayLiteral *>(expStmt->expression);
    if (!arrayLit) {
Leena Miettinen's avatar
Leena Miettinen committed
549
        addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
550
551
552
553
554
555
        return;
    }

    for (ElementList *it = arrayLit->elements; it; it = it->next) {
        StringLiteral *stringLit = dynamic_cast<StringLiteral *>(it->expression);
        if (!stringLit) {
Leena Miettinen's avatar
Leena Miettinen committed
556
            addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only string literal members."));
557
558
            return;
        }
559
        QString exp = stringLit->value.toString();
560
561
562
563
564
        int slashIdx = exp.indexOf(QLatin1Char('/'));
        int spaceIdx = exp.indexOf(QLatin1Char(' '));
        ComponentVersion version(exp.mid(spaceIdx + 1));

        if (spaceIdx == -1 || !version.isValid()) {
Leena Miettinen's avatar
Leena Miettinen committed
565
            addError(stringLit->firstSourceLocation(), tr("Expected string literal to contain 'Package/Name major.minor' or 'Name major.minor'."));
566
567
568
569
570
571
572
573
574
575
576
577
            continue;
        }
        QString package;
        if (slashIdx != -1)
            package = exp.left(slashIdx);
        QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));

        // ### relocatable exports where package is empty?
        fmo->addExport(name, package, version);
    }
}

578
579
void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
{
580
581
582
    QTC_ASSERT(ast, return);

    if (!ast->statement) {
Leena Miettinen's avatar
Leena Miettinen committed
583
        addError(ast->colonToken, tr("Expected array of numbers after colon."));
584
585
586
587
588
        return;
    }

    ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
    if (!expStmt) {
Leena Miettinen's avatar
Leena Miettinen committed
589
        addError(ast->statement->firstSourceLocation(), tr("Expected array of numbers after colon."));
590
591
592
593
594
        return;
    }

    ArrayLiteral *arrayLit = dynamic_cast<ArrayLiteral *>(expStmt->expression);
    if (!arrayLit) {
Leena Miettinen's avatar
Leena Miettinen committed
595
        addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
596
597
598
599
600
601
602
603
        return;
    }

    int exportIndex = 0;
    const int exportCount = fmo->exports().size();
    for (ElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
        NumericLiteral *numberLit = cast<NumericLiteral *>(it->expression);
        if (!numberLit) {
Leena Miettinen's avatar
Leena Miettinen committed
604
            addError(arrayLit->firstSourceLocation(), tr("Expected array literal with only number literal members."));
605
606
607
608
            return;
        }

        if (exportIndex >= exportCount) {
Leena Miettinen's avatar
Leena Miettinen committed
609
            addError(numberLit->firstSourceLocation(), tr("Meta object revision without matching export."));
610
611
612
613
614
615
            return;
        }

        const double v = numberLit->value;
        const int metaObjectRevision = static_cast<int>(v);
        if (metaObjectRevision != v) {
Leena Miettinen's avatar
Leena Miettinen committed
616
            addError(numberLit->firstSourceLocation(), tr("Expected integer."));
617
618
619
620
621
622
623
            return;
        }

        fmo->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
    }
}

624
625
626
void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme)
{
    if (!ast || !ast->statement) {
Leena Miettinen's avatar
Leena Miettinen committed
627
        addError(ast->colonToken, tr("Expected object literal after colon."));
628
629
630
631
632
        return;
    }

    ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
    if (!expStmt) {
Leena Miettinen's avatar
Leena Miettinen committed
633
        addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon."));
634
635
636
637
638
        return;
    }

    ObjectLiteral *objectLit = dynamic_cast<ObjectLiteral *>(expStmt->expression);
    if (!objectLit) {
Leena Miettinen's avatar
Leena Miettinen committed
639
        addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon."));
640
641
642
        return;
    }

Fawzi Mohamed's avatar
Fawzi Mohamed committed
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
    for (PropertyAssignmentList *it = objectLit->properties; it; it = it->next) {
        PropertyNameAndValue *assignement = AST::cast<PropertyNameAndValue *>(it->assignment);
        if (assignement) {
            StringLiteralPropertyName *propName = dynamic_cast<StringLiteralPropertyName *>(assignement->name);
            NumericLiteral *value = dynamic_cast<NumericLiteral *>(assignement->value);
            UnaryMinusExpression *minus = dynamic_cast<UnaryMinusExpression *>(assignement->value);
            if (minus)
                value = dynamic_cast<NumericLiteral *>(minus->expression);
            if (!propName || !value) {
                addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements."));
                continue;
            }

            double v = value->value;
            if (minus)
                v = -v;
            fme->addKey(propName->id.toString(), v);
660
661
            continue;
        }
Fawzi Mohamed's avatar
Fawzi Mohamed committed
662
        PropertyGetterSetter *getterSetter = AST::cast<PropertyGetterSetter *>(it->assignment);
Orgad Shaneh's avatar
Orgad Shaneh committed
663
        if (getterSetter)
Fawzi Mohamed's avatar
Fawzi Mohamed committed
664
            addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements."));
665
666
    }
}