OSDN Git Service

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