1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
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.
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
31 **************************************************************************/
33 #include "qmljstypedescriptionreader.h"
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"
41 #include "qmljsbind.h"
42 #include "qmljsinterpreter.h"
43 #include "qmljsutils.h"
45 #include <QtCore/QIODevice>
46 #include <QtCore/QBuffer>
48 using namespace QmlJS;
49 using namespace QmlJS::AST;
50 using namespace LanguageUtils;
52 TypeDescriptionReader::TypeDescriptionReader(const QString &data)
58 TypeDescriptionReader::~TypeDescriptionReader()
62 bool TypeDescriptionReader::operator()(
63 QHash<QString, FakeMetaObject::ConstPtr> *objects,
64 QList<ModuleApiInfo> *moduleApis)
69 Parser parser(&engine);
71 lexer.setCode(_source, /*line = */ 1, /*qmlMode = */true);
73 if (!parser.parse()) {
74 _errorMessage = QString("%1:%2: %3").arg(
75 QString::number(parser.errorLineNumber()),
76 QString::number(parser.errorColumnNumber()),
77 parser.errorMessage());
82 _moduleApis = moduleApis;
83 readDocument(parser.ast());
85 return _errorMessage.isEmpty();
88 QString TypeDescriptionReader::errorMessage() const
93 QString TypeDescriptionReader::warningMessage() const
95 return _warningMessage;
98 void TypeDescriptionReader::readDocument(UiProgram *ast)
101 addError(SourceLocation(), "Could not parse document");
105 if (!ast->imports || ast->imports->next) {
106 addError(SourceLocation(), "Expected a single import");
110 UiImport *import = ast->imports->import;
111 if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
112 addError(import->importToken, "Expected import of QtQuick.tooling");
116 ComponentVersion version;
117 const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
118 const int dotIdx = versionString.indexOf(QLatin1Char('.'));
120 version = ComponentVersion(versionString.left(dotIdx).toInt(),
121 versionString.mid(dotIdx + 1).toInt());
123 if (version > ComponentVersion(1, 1)) {
124 addError(import->versionToken, "Expected version 1.1 or lower");
128 if (!ast->members || !ast->members->member || ast->members->next) {
129 addError(SourceLocation(), "Expected document to contain a single object definition");
133 UiObjectDefinition *module = dynamic_cast<UiObjectDefinition *>(ast->members->member);
135 addError(SourceLocation(), "Expected document to contain a single object definition");
139 if (toString(module->qualifiedTypeNameId) != "Module") {
140 addError(SourceLocation(), "Expected document to contain a Module {} member");
147 void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
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");
158 if (typeName == QLatin1String("Component")) {
159 readComponent(component);
160 } else if (typeName == QLatin1String("ModuleApi")) {
161 readModuleApi(component);
166 void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
168 _errorMessage += QString("%1:%2: %3\n").arg(
169 QString::number(loc.startLine),
170 QString::number(loc.startColumn),
174 void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
176 _warningMessage += QString("%1:%2: %3\n").arg(
177 QString::number(loc.startLine),
178 QString::number(loc.startColumn),
182 void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
184 FakeMetaObject::Ptr fmo(new FakeMetaObject);
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);
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);
199 addWarning(component->firstSourceLocation(), "Expected only Property, Method, Signal and Enum object definitions");
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));
216 addWarning(script->firstSourceLocation(),
217 "Expected only name, prototype, defaultProperty, attachedType, exports"
218 "and exportMetaObjectRevisions script bindings");
221 addWarning(member->firstSourceLocation(), "Expected only script bindings and object definitions");
225 if (fmo->className().isEmpty()) {
226 addError(ast->firstSourceLocation(), "Component definition is missing a name binding");
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);
235 void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast)
237 ModuleApiInfo apiInfo;
239 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
240 UiObjectMember *member = it->member;
241 UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
244 const QString name = toString(script->qualifiedId);
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);
252 addWarning(script->firstSourceLocation(),
253 "Expected only uri, version and name script bindings");
256 addWarning(member->firstSourceLocation(), "Expected only script bindings");
260 if (!apiInfo.version.isValid()) {
261 addError(ast->firstSourceLocation(), "ModuleApi definition has no or invalid 'version' binding");
266 _moduleApis->append(apiInfo);
269 void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo)
272 // ### confusion between Method and Slot. Method should be removed.
274 fmm.setMethodType(FakeMetaMethod::Slot);
276 fmm.setMethodType(FakeMetaMethod::Signal);
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);
283 QString name = toString(component->qualifiedTypeNameId);
284 if (name == "Parameter") {
285 readParameter(component, &fmm);
287 addWarning(component->firstSourceLocation(), "Expected only Parameter object definitions");
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));
298 addWarning(script->firstSourceLocation(), "Expected only name and type script bindings");
302 addWarning(member->firstSourceLocation(), "Expected only script bindings and object definitions");
306 if (fmm.methodName().isEmpty()) {
307 addError(ast->firstSourceLocation(), "Method or Signal is missing a name script binding");
314 void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
318 bool isPointer = false;
319 bool isReadonly = false;
323 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
324 UiObjectMember *member = it->member;
325 UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
327 addWarning(member->firstSourceLocation(), "Expected script binding");
331 QString id = toString(script->qualifiedId);
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);
345 addWarning(script->firstSourceLocation(), "Expected only type, name, revision, isPointer, isReadonly and isList script bindings");
349 if (name.isEmpty() || type.isEmpty()) {
350 addError(ast->firstSourceLocation(), "Property object is missing a name or type script binding");
354 fmo->addProperty(FakeMetaProperty(name, type, isList, !isReadonly, isPointer, revision));
357 void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
361 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
362 UiObjectMember *member = it->member;
363 UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
365 addWarning(member->firstSourceLocation(), "Expected script binding");
369 QString name = toString(script->qualifiedId);
370 if (name == "name") {
371 fme.setName(readStringBinding(script));
372 } else if (name == "values") {
373 readEnumValues(script, &fme);
375 addWarning(script->firstSourceLocation(), "Expected only name and values script bindings");
382 void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMethod *fmm)
387 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
388 UiObjectMember *member = it->member;
389 UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
391 addWarning(member->firstSourceLocation(), "Expected script binding");
395 const QString id = toString(script->qualifiedId);
397 name = readStringBinding(script);
398 } else if (id == "type") {
399 type = readStringBinding(script);
400 } else if (id == "isPointer") {
402 } else if (id == "isReadonly") {
404 } else if (id == "isList") {
407 addWarning(script->firstSourceLocation(), "Expected only name and type script bindings");
411 fmm->addParameter(name, type);
414 QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
416 if (!ast || !ast->statement) {
417 addError(ast->colonToken, "Expected string after colon");
421 ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
423 addError(ast->statement->firstSourceLocation(), "Expected string after colon");
427 StringLiteral *stringLit = dynamic_cast<StringLiteral *>(expStmt->expression);
429 addError(expStmt->firstSourceLocation(), "Expected string after colon");
433 return stringLit->value.toString();
436 bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast)
438 if (!ast || !ast->statement) {
439 addError(ast->colonToken, "Expected boolean after colon");
443 ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
445 addError(ast->statement->firstSourceLocation(), "Expected boolean after colon");
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");
459 double TypeDescriptionReader::readNumericBinding(AST::UiScriptBinding *ast)
461 if (!ast || !ast->statement) {
462 addError(ast->colonToken, "Expected numeric literal after colon");
466 ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
468 addError(ast->statement->firstSourceLocation(), "Expected numeric literal after colon");
472 NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
474 addError(expStmt->firstSourceLocation(), "Expected numeric literal after colon");
478 return numericLit->value;
481 ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
483 ComponentVersion invalidVersion;
485 if (!ast || !ast->statement) {
486 addError(ast->colonToken, "Expected numeric literal after colon");
487 return invalidVersion;
490 ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
492 addError(ast->statement->firstSourceLocation(), "Expected numeric literal after colon");
493 return invalidVersion;
496 NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression);
498 addError(expStmt->firstSourceLocation(), "Expected numeric literal after colon");
499 return invalidVersion;
502 return ComponentVersion(_source.mid(numericLit->literalToken.begin(), numericLit->literalToken.length));
505 int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast)
507 double v = readNumericBinding(ast);
508 int i = static_cast<int>(v);
511 addError(ast->firstSourceLocation(), "Expected integer after colon");
518 void TypeDescriptionReader::readExports(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
520 if (!ast || !ast->statement) {
521 addError(ast->colonToken, "Expected array of strings after colon");
525 ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
527 addError(ast->statement->firstSourceLocation(), "Expected array of strings after colon");
531 ArrayLiteral *arrayLit = dynamic_cast<ArrayLiteral *>(expStmt->expression);
533 addError(expStmt->firstSourceLocation(), "Expected array of strings after colon");
537 for (ElementList *it = arrayLit->elements; it; it = it->next) {
538 StringLiteral *stringLit = dynamic_cast<StringLiteral *>(it->expression);
540 addError(arrayLit->firstSourceLocation(), "Expected array literal with only string literal members");
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));
548 if (spaceIdx == -1 || !version.isValid()) {
549 addError(stringLit->firstSourceLocation(), "Expected string literal to contain 'Package/Name major.minor' or 'Name major.minor'");
554 package = exp.left(slashIdx);
555 QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
557 // ### relocatable exports where package is empty?
558 fmo->addExport(name, package, version);
562 void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMetaObject::Ptr fmo)
564 if (!ast || !ast->statement) {
565 addError(ast->colonToken, "Expected array of numbers after colon");
569 ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
571 addError(ast->statement->firstSourceLocation(), "Expected array of numbers after colon");
575 ArrayLiteral *arrayLit = dynamic_cast<ArrayLiteral *>(expStmt->expression);
577 addError(expStmt->firstSourceLocation(), "Expected array of numbers after colon");
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);
586 addError(arrayLit->firstSourceLocation(), "Expected array literal with only number literal members");
590 if (exportIndex >= exportCount) {
591 addError(numberLit->firstSourceLocation(), "Meta object revision without matching export");
595 const double v = numberLit->value;
596 const int metaObjectRevision = static_cast<int>(v);
597 if (metaObjectRevision != v) {
598 addError(numberLit->firstSourceLocation(), "Expected integer");
602 fmo->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
606 void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme)
608 if (!ast || !ast->statement) {
609 addError(ast->colonToken, "Expected object literal after colon");
613 ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
615 addError(ast->statement->firstSourceLocation(), "Expected object literal after colon");
619 ObjectLiteral *objectLit = dynamic_cast<ObjectLiteral *>(expStmt->expression);
621 addError(expStmt->firstSourceLocation(), "Expected object literal after colon");
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);
630 value = dynamic_cast<NumericLiteral *>(minus->expression);
631 if (!propName || !value) {
632 addError(objectLit->firstSourceLocation(), "Expected object literal to contain only 'string: number' elements");
636 double v = value->value;
639 fme->addKey(propName->id.toString(), v);