Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmljstypedescriptionreader.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant
4
6
7#include <QtQml/private/qqmljsparser_p.h>
8#include <QtQml/private/qqmljslexer_p.h>
9#include <QtQml/private/qqmljsengine_p.h>
10
11#include <QtCore/qdir.h>
12#include <QtCore/qstring.h>
13#include <QtCore/qtyperevision.h>
14
16
17using namespace QQmlJS;
18using namespace QQmlJS::AST;
19using namespace Qt::StringLiterals;
20
21QString toString(const UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
22{
23 QString result;
24
25 for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
26 if (iter != qualifiedId)
27 result += delimiter;
28
29 result += iter->name;
30 }
31
32 return result;
33}
34
35bool QQmlJSTypeDescriptionReader::operator()(
36 QList<QQmlJSExportedScope> *objects, QStringList *dependencies)
37{
38 Engine engine;
39
40 Lexer lexer(&engine);
41 Parser parser(&engine);
42
43 lexer.setCode(m_source, /*lineno = */ 1, /*qmlMode = */true);
44
45 if (!parser.parse()) {
46 m_errorMessage = QString::fromLatin1("%1:%2: %3").arg(
47 QString::number(parser.errorLineNumber()),
48 QString::number(parser.errorColumnNumber()),
49 parser.errorMessage());
50 return false;
51 }
52
53 m_objects = objects;
54 m_dependencies = dependencies;
55 readDocument(parser.ast());
56
57 return m_errorMessage.isEmpty();
58}
59
60void QQmlJSTypeDescriptionReader::readDocument(UiProgram *ast)
61{
62 if (!ast) {
63 addError(SourceLocation(), tr("Could not parse document."));
64 return;
65 }
66
67 if (!ast->headers || ast->headers->next || !cast<UiImport *>(ast->headers->headerItem)) {
68 addError(SourceLocation(), tr("Expected a single import."));
69 return;
70 }
71
72 auto *import = cast<UiImport *>(ast->headers->headerItem);
73 if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
74 addError(import->importToken, tr("Expected import of QtQuick.tooling."));
75 return;
76 }
77
78 if (!import->version) {
79 addError(import->firstSourceLocation(), tr("Import statement without version."));
80 return;
81 }
82
83 if (import->version->version.majorVersion() != 1) {
84 addError(import->version->firstSourceLocation(),
85 tr("Major version different from 1 not supported."));
86 return;
87 }
88
89 if (!ast->members || !ast->members->member || ast->members->next) {
90 addError(SourceLocation(), tr("Expected document to contain a single object definition."));
91 return;
92 }
93
94 auto *module = cast<UiObjectDefinition *>(ast->members->member);
95 if (!module) {
96 addError(SourceLocation(), tr("Expected document to contain a single object definition."));
97 return;
98 }
99
100 if (toString(module->qualifiedTypeNameId) != QLatin1String("Module")) {
101 addError(SourceLocation(), tr("Expected document to contain a Module {} member."));
102 return;
103 }
104
105 readModule(module);
106}
107
108void QQmlJSTypeDescriptionReader::readModule(UiObjectDefinition *ast)
109{
110 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
111 UiObjectMember *member = it->member;
112 auto *component = cast<UiObjectDefinition *>(member);
113
114 auto *script = cast<UiScriptBinding *>(member);
115 if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) {
116 readDependencies(script);
117 continue;
118 }
119
120 QString typeName;
121 if (component)
122 typeName = toString(component->qualifiedTypeNameId);
123
124 if (!component || typeName != QLatin1String("Component")) {
125 continue;
126 }
127
128 if (typeName == QLatin1String("Component"))
129 readComponent(component);
130 }
131}
132
133void QQmlJSTypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
134{
135 m_errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
136 QDir::toNativeSeparators(m_fileName),
137 QString::number(loc.startLine),
138 QString::number(loc.startColumn),
139 message);
140}
141
142void QQmlJSTypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
143{
144 m_warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
145 QDir::toNativeSeparators(m_fileName),
146 QString::number(loc.startLine),
147 QString::number(loc.startColumn),
148 message);
149}
150
151void QQmlJSTypeDescriptionReader::readDependencies(UiScriptBinding *ast)
152{
153 auto *stmt = cast<ExpressionStatement*>(ast->statement);
154 if (!stmt) {
155 addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions"));
156 return;
157 }
158 auto *exp = cast<ArrayPattern *>(stmt->expression);
159 if (!exp) {
160 addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions"));
161 return;
162 }
163 for (PatternElementList *l = exp->elements; l; l = l->next) {
164 auto *str = cast<StringLiteral *>(l->element->initializer);
165 *m_dependencies << str->value.toString();
166 }
167}
168
169void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
170{
171 m_currentCtorIndex = 0;
172 m_currentMethodIndex = 0;
173 QQmlJSScope::Ptr scope = QQmlJSScope::create();
174 QList<QQmlJSScope::Export> exports;
175
176 UiScriptBinding *metaObjectRevisions = nullptr;
177 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
178 UiObjectMember *member = it->member;
179 auto *component = cast<UiObjectDefinition *>(member);
180 auto *script = cast<UiScriptBinding *>(member);
181 if (component) {
182 QString name = toString(component->qualifiedTypeNameId);
183 if (name == QLatin1String("Property"))
184 readProperty(component, scope);
185 else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
186 readSignalOrMethod(component, name == QLatin1String("Method"), scope);
187 else if (name == QLatin1String("Enum"))
188 readEnum(component, scope);
189 else
190 addWarning(component->firstSourceLocation(),
191 tr("Expected only Property, Method, Signal and Enum object definitions, "
192 "not \"%1\".").arg(name));
193 } else if (script) {
194 QString name = toString(script->qualifiedId);
195 if (name == QLatin1String("file")) {
196 scope->setFilePath(readStringBinding(script));
197 } else if (name == QLatin1String("lineNumber")) {
198 scope->setLineNumber(readNumericBinding(script));
199 } else if (name == QLatin1String("name")) {
200 scope->setInternalName(readStringBinding(script));
201 } else if (name == QLatin1String("prototype")) {
202 scope->setBaseTypeName(readStringBinding(script));
203 } else if (name == QLatin1String("defaultProperty")) {
204 scope->setOwnDefaultPropertyName(readStringBinding(script));
205 } else if (name == QLatin1String("parentProperty")) {
206 scope->setOwnParentPropertyName(readStringBinding(script));
207 } else if (name == QLatin1String("exports")) {
208 exports = readExports(script);
209 } else if (name == QLatin1String("aliases")) {
210 readAliases(script, scope);
211 } else if (name == QLatin1String("interfaces")) {
212 readInterfaces(script, scope);
213 } else if (name == QLatin1String("exportMetaObjectRevisions")) {
214 metaObjectRevisions = script;
215 } else if (name == QLatin1String("attachedType")) {
216 scope->setOwnAttachedTypeName(readStringBinding(script));
217 } else if (name == QLatin1String("valueType")) {
218 scope->setElementTypeName(readStringBinding(script));
219 } else if (name == QLatin1String("isSingleton")) {
220 scope->setIsSingleton(readBoolBinding(script));
221 } else if (name == QLatin1String("isCreatable")) {
222 scope->setCreatableFlag(readBoolBinding(script));
223 } else if (name == QLatin1String("isStructured")) {
224 scope->setStructuredFlag(readBoolBinding(script));
225 } else if (name == QLatin1String("isComposite")) {
226 scope->setIsComposite(readBoolBinding(script));
227 } else if (name == QLatin1String("hasCustomParser")) {
228 scope->setHasCustomParser(readBoolBinding(script));
229 } else if (name == QLatin1String("enforcesScopedEnums")) {
230 scope->setEnforcesScopedEnumsFlag(readBoolBinding(script));
231 } else if (name == QLatin1String("accessSemantics")) {
232 const QString semantics = readStringBinding(script);
233 if (semantics == QLatin1String("reference")) {
234 scope->setAccessSemantics(QQmlJSScope::AccessSemantics::Reference);
235 } else if (semantics == QLatin1String("value")) {
236 scope->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
237 } else if (semantics == QLatin1String("none")) {
238 scope->setAccessSemantics(QQmlJSScope::AccessSemantics::None);
239 } else if (semantics == QLatin1String("sequence")) {
240 scope->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
241 } else {
242 addWarning(script->firstSourceLocation(),
243 tr("Unknown access semantics \"%1\".").arg(semantics));
244 }
245 } else if (name == QLatin1String("extension")) {
246 scope->setExtensionTypeName(readStringBinding(script));
247 } else if (name == QLatin1String("extensionIsJavaScript")) {
248 scope->setExtensionIsJavaScript(readBoolBinding(script));
249 } else if (name == QLatin1String("extensionIsNamespace")) {
250 scope->setExtensionIsNamespace(readBoolBinding(script));
251 } else if (name == QLatin1String("deferredNames")) {
252 readDeferredNames(script, scope);
253 } else if (name == QLatin1String("immediateNames")) {
254 readImmediateNames(script, scope);
255 } else if (name == QLatin1String("isJavaScriptBuiltin")) {
256 scope->setIsJavaScriptBuiltin(readBoolBinding(script));
257 } else {
258 addWarning(script->firstSourceLocation(),
259 tr("Expected only lineNumber, name, prototype, defaultProperty, "
260 "attachedType, "
261 "valueType, exports, interfaces, isSingleton, isCreatable, "
262 "isStructured, isComposite, hasCustomParser, enforcesScopedEnums, "
263 "aliases, exportMetaObjectRevisions, deferredNames, and "
264 "immediateNames in script bindings, not \"%1\".")
265 .arg(name));
266 }
267 } else {
268 addWarning(member->firstSourceLocation(),
269 tr("Expected only script bindings and object definitions."));
270 }
271 }
272
273 if (scope->internalName().isEmpty()) {
274 addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
275 return;
276 }
277
278 if (metaObjectRevisions)
279 checkMetaObjectRevisions(metaObjectRevisions, &exports);
280 m_objects->append({scope, exports});
281}
282
283void QQmlJSTypeDescriptionReader::readSignalOrMethod(
284 UiObjectDefinition *ast, bool isMethod, const QQmlJSScope::Ptr &scope)
285{
286 QQmlJSMetaMethod metaMethod;
287 // ### confusion between Method and Slot. Method should be removed.
288 if (isMethod)
289 metaMethod.setMethodType(QQmlJSMetaMethodType::Slot);
290 else
291 metaMethod.setMethodType(QQmlJSMetaMethodType::Signal);
292
293 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
294 UiObjectMember *member = it->member;
295 auto *component = cast<UiObjectDefinition *>(member);
296 auto *script = cast<UiScriptBinding *>(member);
297 if (component) {
298 QString name = toString(component->qualifiedTypeNameId);
299 if (name == QLatin1String("Parameter")) {
300 readParameter(component, &metaMethod);
301 } else {
302 addWarning(component->firstSourceLocation(),
303 tr("Expected only Parameter in object definitions."));
304 }
305 } else if (script) {
306 QString name = toString(script->qualifiedId);
307 if (name == QLatin1String("name")) {
308 metaMethod.setMethodName(readStringBinding(script));
309 } else if (name == QLatin1String("lineNumber")) {
310 metaMethod.setSourceLocation(
311 SourceLocation::fromQSizeType(0, 0, readIntBinding(script), 1));
312 } else if (name == QLatin1String("type")) {
313 metaMethod.setReturnTypeName(readStringBinding(script));
314 } else if (name == QLatin1String("revision")) {
315 metaMethod.setRevision(readIntBinding(script));
316 } else if (name == QLatin1String("isCloned")) {
317 metaMethod.setIsCloned(readBoolBinding(script));
318 } else if (name == QLatin1String("isConstructor")) {
319 // The constructors in the moc json output are ordered the same
320 // way as the ones in the metaobject. qmltyperegistrar moves them into
321 // the same list as the other members, but maintains their order.
322 if (readBoolBinding(script)) {
323 metaMethod.setIsConstructor(true);
324 metaMethod.setConstructorIndex(
325 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentCtorIndex++));
326 }
327 } else if (name == QLatin1String("isJavaScriptFunction")) {
328 metaMethod.setIsJavaScriptFunction(readBoolBinding(script));
329 } else if (name == QLatin1String("isList")) {
330 auto metaReturnType = metaMethod.returnValue();
331 metaReturnType.setIsList(readBoolBinding(script));
332 metaMethod.setReturnValue(metaReturnType);
333 } else if (name == QLatin1String("isPointer")) {
334 // TODO: We don't need this information. We can probably drop all isPointer members
335 // once we make sure that the type information is always complete. The
336 // description of the type being referenced has access semantics after all.
337 auto metaReturnType = metaMethod.returnValue();
338 metaReturnType.setIsPointer(readBoolBinding(script));
339 metaMethod.setReturnValue(metaReturnType);
340 } else if (name == QLatin1String("isTypeConstant")
341 || name == QLatin1String("isConstant")) {
342 // note: isConstant is only read for backwards compatibility
343 auto metaReturnType = metaMethod.returnValue();
344 metaReturnType.setTypeQualifier(readBoolBinding(script)
345 ? QQmlJSMetaParameter::Const
346 : QQmlJSMetaParameter::NonConst);
347 metaMethod.setReturnValue(metaReturnType);
348 } else if (name == QLatin1String("isMethodConstant")) {
349 metaMethod.setIsConst(readBoolBinding(script));
350 } else {
351 addWarning(script->firstSourceLocation(),
352 tr("Expected only name, lineNumber, type, revision, isPointer, "
353 "isTypeConstant, "
354 "isList, isCloned, isConstructor, isMethodConstant, and "
355 "isJavaScriptFunction in script bindings."));
356 }
357 } else {
358 addWarning(member->firstSourceLocation(),
359 tr("Expected only script bindings and object definitions."));
360 }
361 }
362
363 if (metaMethod.methodName().isEmpty()) {
364 addError(ast->firstSourceLocation(),
365 tr("Method or signal is missing a name script binding."));
366 return;
367 }
368
369 // Signals, slots and method share one index space. Constructors are separate.
370 // We also assume that the order and therefore the indexing of all methods is retained from
371 // moc's JSON output.
372 if (!metaMethod.isConstructor())
373 metaMethod.setMethodIndex(QQmlJSMetaMethod::RelativeFunctionIndex(m_currentMethodIndex++));
374
375 if (metaMethod.returnTypeName().isEmpty())
376 metaMethod.setReturnTypeName(QLatin1String("void"));
377
378 scope->addOwnMethod(metaMethod);
379}
380
381void QQmlJSTypeDescriptionReader::readProperty(UiObjectDefinition *ast, const QQmlJSScope::Ptr &scope)
382{
383 QQmlJSMetaProperty property;
384 property.setIsWritable(true); // default is writable
385 bool isRequired = false;
386
387 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
388 UiObjectMember *member = it->member;
389 auto *script = cast<UiScriptBinding *>(member);
390 if (!script) {
391 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
392 continue;
393 }
394
395 QString id = toString(script->qualifiedId);
396 if (id == QLatin1String("name")) {
397 property.setPropertyName(readStringBinding(script));
398 } else if (id == QLatin1String("lineNumber")) {
399 property.setSourceLocation(
400 SourceLocation::fromQSizeType(0, 0, readIntBinding(script), 1));
401 } else if (id == QLatin1String("type")) {
402 property.setTypeName(readStringBinding(script));
403 } else if (id == QLatin1String("isPointer")) {
404 property.setIsPointer(readBoolBinding(script));
405 } else if (id == QLatin1String("isReadonly")) {
406 property.setIsWritable(!readBoolBinding(script));
407 } else if (id == QLatin1String("isRequired")) {
408 isRequired = readBoolBinding(script);
409 } else if (id == QLatin1String("isList")) {
410 property.setIsList(readBoolBinding(script));
411 } else if (id == QLatin1String("isFinal")) {
412 property.setIsFinal(readBoolBinding(script));
413 } else if (id == QLatin1String("isVirtual")) {
414 property.setIsVirtual(readBoolBinding(script));
415 } else if (id == QLatin1String("isOverride")) {
416 property.setIsOverride(readBoolBinding(script));
417 } else if (id == QLatin1String("isTypeConstant")) {
418 property.setIsTypeConstant(readBoolBinding(script));
419 } else if (id == QLatin1String("isPropertyConstant")) {
420 property.setIsPropertyConstant(readBoolBinding(script));
421 } else if (id == QLatin1String("isConstant")) {
422 // support old "isConstant" for backwards compatibility
423 property.setIsPropertyConstant(readBoolBinding(script));
424 } else if (id == QLatin1String("revision")) {
425 property.setRevision(readIntBinding(script));
426 } else if (id == QLatin1String("bindable")) {
427 property.setBindable(readStringBinding(script));
428 } else if (id == QLatin1String("read")) {
429 property.setRead(readStringBinding(script));
430 } else if (id == QLatin1String("write")) {
431 property.setWrite(readStringBinding(script));
432 } else if (id == QLatin1String("reset")) {
433 property.setReset(readStringBinding(script));
434 } else if (id == QLatin1String("notify")) {
435 property.setNotify(readStringBinding(script));
436 } else if (id == QLatin1String("index")) {
437 property.setIndex(readIntBinding(script));
438 } else if (id == QLatin1String("privateClass")) {
439 property.setPrivateClass(readStringBinding(script));
440 } else {
441 addWarning(script->firstSourceLocation(),
442 tr("Expected only type, name, lineNumber, revision, isPointer, "
443 "isTypeConstant, isReadonly, isRequired, "
444 "isFinal, isList, bindable, read, write, isPropertyConstant, reset, "
445 "notify, index, privateClass and script bindings."));
446 }
447 }
448
449 if (property.propertyName().isEmpty()) {
450 addError(ast->firstSourceLocation(),
451 tr("Property object is missing a name script binding."));
452 return;
453 }
454
455 scope->addOwnProperty(property);
456 if (isRequired)
457 scope->setPropertyLocallyRequired(property.propertyName(), true);
458}
459
460void QQmlJSTypeDescriptionReader::readEnum(UiObjectDefinition *ast, const QQmlJSScope::Ptr &scope)
461{
462 QQmlJSMetaEnum metaEnum;
463
464 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
465 UiObjectMember *member = it->member;
466 auto *script = cast<UiScriptBinding *>(member);
467 if (!script) {
468 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
469 continue;
470 }
471
472 QString name = toString(script->qualifiedId);
473 if (name == QLatin1String("name")) {
474 metaEnum.setName(readStringBinding(script));
475 } else if (name == QLatin1String("alias")) {
476 metaEnum.setAlias(readStringBinding(script));
477 } else if (name == QLatin1String("isFlag")) {
478 metaEnum.setIsFlag(readBoolBinding(script));
479 } else if (name == QLatin1String("values")) {
480 readEnumValues(script, &metaEnum);
481 } else if (name == QLatin1String("isScoped")) {
482 metaEnum.setIsScoped(readBoolBinding(script));
483 } else if (name == QLatin1String("type")) {
484 metaEnum.setTypeName(readStringBinding(script));
485 } else if (name == QLatin1String("lineNumber")) {
486 metaEnum.setLineNumber(readIntBinding(script));
487 } else {
488 addWarning(script->firstSourceLocation(),
489 tr("Expected only name, alias, isFlag, values, isScoped, type, or "
490 "lineNumber."));
491 }
492 }
493
494 scope->addOwnEnumeration(metaEnum);
495}
496
497void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSMetaMethod *metaMethod)
498{
499 QString name;
500 QString type;
501 bool isConstant = false;
502 bool isPointer = false;
503 bool isList = false;
504
505 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
506 UiObjectMember *member = it->member;
507 auto *script = cast<UiScriptBinding *>(member);
508 if (!script) {
509 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
510 continue;
511 }
512
513 const QString id = toString(script->qualifiedId);
514 if (id == QLatin1String("name")) {
515 name = readStringBinding(script);
516 } else if (id == QLatin1String("type")) {
517 type = readStringBinding(script);
518 } else if (id == QLatin1String("isPointer")) {
519 isPointer = readBoolBinding(script);
520 } else if (id == QLatin1String("isTypeConstant") || id == QLatin1String("isConstant")) {
521 // note: isConstant is only read for backwards compatibility
522 isConstant = readBoolBinding(script);
523 } else if (id == QLatin1String("isReadonly")) {
524 // ### unhandled
525 } else if (id == QLatin1String("isList")) {
526 isList = readBoolBinding(script);
527 } else {
528 addWarning(script->firstSourceLocation(),
529 tr("Expected only name, type, isPointer, isTypeConstant, isReadonly, "
530 "or IsList script bindings."));
531 }
532 }
533
534 QQmlJSMetaParameter p(name, type);
535 p.setTypeQualifier(isConstant ? QQmlJSMetaParameter::Const : QQmlJSMetaParameter::NonConst);
536 p.setIsPointer(isPointer);
537 p.setIsList(isList);
538 metaMethod->addParameter(std::move(p));
539}
540
541QString QQmlJSTypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
542{
543 Q_ASSERT(ast);
544
545 if (!ast->statement) {
546 addError(ast->colonToken, tr("Expected string after colon."));
547 return QString();
548 }
549
550 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
551 if (!expStmt) {
552 addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
553 return QString();
554 }
555
556 auto *stringLit = cast<StringLiteral *>(expStmt->expression);
557 if (!stringLit) {
558 addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
559 return QString();
560 }
561
562 return stringLit->value.toString();
563}
564
565bool QQmlJSTypeDescriptionReader::readBoolBinding(UiScriptBinding *ast)
566{
567 Q_ASSERT(ast);
568
569 if (!ast->statement) {
570 addError(ast->colonToken, tr("Expected boolean after colon."));
571 return false;
572 }
573
574 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
575 if (!expStmt) {
576 addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
577 return false;
578 }
579
580 auto *trueLit = cast<TrueLiteral *>(expStmt->expression);
581 auto *falseLit = cast<FalseLiteral *>(expStmt->expression);
582 if (!trueLit && !falseLit) {
583 addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
584 return false;
585 }
586
587 return trueLit;
588}
589
590double QQmlJSTypeDescriptionReader::readNumericBinding(UiScriptBinding *ast)
591{
592 Q_ASSERT(ast);
593
594 if (!ast->statement) {
595 addError(ast->colonToken, tr("Expected numeric literal after colon."));
596 return 0;
597 }
598
599 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
600 if (!expStmt) {
601 addError(ast->statement->firstSourceLocation(),
602 tr("Expected numeric literal after colon."));
603 return 0;
604 }
605
606 auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
607 if (!numericLit) {
608 addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
609 return 0;
610 }
611
612 return numericLit->value;
613}
614
615static QTypeRevision parseVersion(const QString &versionString)
616{
617 const int dotIdx = versionString.indexOf(QLatin1Char('.'));
618 if (dotIdx == -1)
619 return QTypeRevision();
620 bool ok = false;
621 const int maybeMajor = QStringView{versionString}.left(dotIdx).toInt(&ok);
622 if (!ok)
623 return QTypeRevision();
624 const int maybeMinor = QStringView{versionString}.mid(dotIdx + 1).toInt(&ok);
625 if (!ok)
626 return QTypeRevision();
627 return QTypeRevision::fromVersion(maybeMajor, maybeMinor);
628}
629
630QTypeRevision QQmlJSTypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
631{
632 QTypeRevision invalidVersion;
633
634 if (!ast || !ast->statement) {
635 addError((ast ? ast->colonToken : SourceLocation()),
636 tr("Expected numeric literal after colon."));
637 return invalidVersion;
638 }
639
640 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
641 if (!expStmt) {
642 addError(ast->statement->firstSourceLocation(),
643 tr("Expected numeric literal after colon."));
644 return invalidVersion;
645 }
646
647 auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
648 if (!numericLit) {
649 addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
650 return invalidVersion;
651 }
652
653 return parseVersion(m_source.mid(numericLit->literalToken.begin(),
654 numericLit->literalToken.length));
655}
656
657int QQmlJSTypeDescriptionReader::readIntBinding(UiScriptBinding *ast)
658{
659 double v = readNumericBinding(ast);
660 int i = static_cast<int>(v);
661
662 if (i != v) {
663 addError(ast->firstSourceLocation(), tr("Expected integer after colon."));
664 return 0;
665 }
666
667 return i;
668}
669
670ArrayPattern* QQmlJSTypeDescriptionReader::getArray(UiScriptBinding *ast)
671{
672 Q_ASSERT(ast);
673
674 if (!ast->statement) {
675 addError(ast->colonToken, tr("Expected array of strings after colon."));
676 return nullptr;
677 }
678
679 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
680 if (!expStmt) {
681 addError(ast->statement->firstSourceLocation(),
682 tr("Expected array of strings after colon."));
683 return nullptr;
684 }
685
686 auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
687 if (!arrayLit) {
688 addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
689 return nullptr;
690 }
691
692 return arrayLit;
693}
694
695QList<QQmlJSScope::Export> QQmlJSTypeDescriptionReader::readExports(UiScriptBinding *ast)
696{
697 QList<QQmlJSScope::Export> exports;
698 auto *arrayLit = getArray(ast);
699
700 if (!arrayLit)
701 return exports;
702
703 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
704 auto *stringLit = cast<StringLiteral *>(it->element->initializer);
705
706 if (!stringLit) {
707 addError(arrayLit->firstSourceLocation(),
708 tr("Expected array literal with only string literal members."));
709 return exports;
710 }
711
712 QString exp = stringLit->value.toString();
713 int slashIdx = exp.indexOf(QLatin1Char('/'));
714 int spaceIdx = exp.indexOf(QLatin1Char(' '));
715 const QTypeRevision version = parseVersion(exp.mid(spaceIdx + 1));
716
717 if (spaceIdx == -1 || !version.isValid()) {
718 addError(stringLit->firstSourceLocation(),
719 tr("Expected string literal to contain 'Package/Name major.minor' "
720 "or 'Name major.minor'."));
721 continue;
722 }
723 QString package;
724 if (slashIdx != -1)
725 package = exp.left(slashIdx);
726 QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
727
728 // ### relocatable exports where package is empty?
729 exports.append(QQmlJSScope::Export(package, name, version, version));
730 }
731
732 return exports;
733}
734
735void QQmlJSTypeDescriptionReader::readAliases(
736 QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope)
737{
738 scope->setAliases(readStringList(ast));
739}
740
741void QQmlJSTypeDescriptionReader::readInterfaces(UiScriptBinding *ast, const QQmlJSScope::Ptr &scope)
742{
743 auto *arrayLit = getArray(ast);
744
745 if (!arrayLit)
746 return;
747
748 QStringList list;
749
750 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
751 auto *stringLit = cast<StringLiteral *>(it->element->initializer);
752 if (!stringLit) {
753 addError(arrayLit->firstSourceLocation(),
754 tr("Expected array literal with only string literal members."));
755 return;
756 }
757
758 list << stringLit->value.toString();
759 }
760
761 scope->setInterfaceNames(list);
762}
763
764void QQmlJSTypeDescriptionReader::checkMetaObjectRevisions(
765 UiScriptBinding *ast, QList<QQmlJSScope::Export> *exports)
766{
767 Q_ASSERT(ast);
768
769 if (!ast->statement) {
770 addError(ast->colonToken, tr("Expected array of numbers after colon."));
771 return;
772 }
773
774 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
775 if (!expStmt) {
776 addError(ast->statement->firstSourceLocation(),
777 tr("Expected array of numbers after colon."));
778 return;
779 }
780
781 auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
782 if (!arrayLit) {
783 addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
784 return;
785 }
786
787 int exportIndex = 0;
788 const int exportCount = exports->size();
789 for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
790 auto *numberLit = cast<NumericLiteral *>(it->element->initializer);
791 if (!numberLit) {
792 addError(arrayLit->firstSourceLocation(),
793 tr("Expected array literal with only number literal members."));
794 return;
795 }
796
797 if (exportIndex >= exportCount) {
798 addError(numberLit->firstSourceLocation(),
799 tr("Meta object revision without matching export."));
800 return;
801 }
802
803 const double v = numberLit->value;
804 const int metaObjectRevision = static_cast<int>(v);
805 if (metaObjectRevision != v) {
806 addError(numberLit->firstSourceLocation(), tr("Expected integer."));
807 return;
808 }
809
810 const QTypeRevision metaObjectVersion
811 = QTypeRevision::fromEncodedVersion(metaObjectRevision);
812 const QQmlJSScope::Export &entry = exports->at(exportIndex);
813 const QTypeRevision exportVersion = entry.version();
814 if (metaObjectVersion != exportVersion) {
815 addWarning(numberLit->firstSourceLocation(),
816 tr("Meta object revision and export version differ.\n"
817 "Revision %1 corresponds to version %2.%3; it should be %4.%5.")
818 .arg(metaObjectRevision)
819 .arg(metaObjectVersion.majorVersion()).arg(metaObjectVersion.minorVersion())
820 .arg(exportVersion.majorVersion()).arg(exportVersion.minorVersion()));
821 (*exports)[exportIndex] = QQmlJSScope::Export(entry.package(), entry.type(),
822 exportVersion, metaObjectVersion);
823 }
824 }
825}
826
827QStringList QQmlJSTypeDescriptionReader::readStringList(UiScriptBinding *ast)
828{
829 auto *arrayLit = getArray(ast);
830 if (!arrayLit)
831 return {};
832
833 QStringList list;
834
835 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
836 auto *stringLit = cast<StringLiteral *>(it->element->initializer);
837 if (!stringLit) {
838 addError(arrayLit->firstSourceLocation(),
839 tr("Expected array literal with only string literal members."));
840 return {};
841 }
842
843 list << stringLit->value.toString();
844 }
845
846 return list;
847}
848
849void QQmlJSTypeDescriptionReader::readDeferredNames(UiScriptBinding *ast,
850 const QQmlJSScope::Ptr &scope)
851{
852 scope->setOwnDeferredNames(readStringList(ast));
853}
854
855void QQmlJSTypeDescriptionReader::readImmediateNames(UiScriptBinding *ast,
856 const QQmlJSScope::Ptr &scope)
857{
858 scope->setOwnImmediateNames(readStringList(ast));
859}
860
861void QQmlJSTypeDescriptionReader::readEnumValues(UiScriptBinding *ast, QQmlJSMetaEnum *metaEnum)
862{
863 if (!ast)
864 return;
865 if (!ast->statement) {
866 addError(ast->colonToken, tr("Expected object literal after colon."));
867 return;
868 }
869
870 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
871 if (!expStmt) {
872 addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
873 return;
874 }
875
876 if (auto *objectLit = cast<ObjectPattern *>(expStmt->expression)) {
877 int currentValue = -1;
878 for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
879 if (PatternProperty *assignement = it->property) {
880 if (auto *name = cast<StringLiteralPropertyName *>(assignement->name)) {
881 metaEnum->addKey(name->id.toString());
882
883 if (auto *value = AST::cast<NumericLiteral *>(assignement->initializer)) {
884 currentValue = int(value->value);
885 } else if (auto *minus = AST::cast<UnaryMinusExpression *>(
886 assignement->initializer)) {
887 if (auto *value = AST::cast<NumericLiteral *>(minus->expression))
888 currentValue = -int(value->value);
889 else
890 ++currentValue;
891 } else {
892 ++currentValue;
893 }
894
895 metaEnum->addValue(currentValue);
896 continue;
897 }
898 }
899 addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
900 }
901 } else if (auto *arrayLit = cast<ArrayPattern *>(expStmt->expression)) {
902 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
903 if (PatternElement *element = it->element) {
904 if (auto *name = cast<StringLiteral *>(element->initializer)) {
905 metaEnum->addKey(name->value.toString());
906 continue;
907 }
908 }
909 addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
910 }
911 } else {
912 addError(ast->statement->firstSourceLocation(),
913 tr("Expected either array or object literal as enum definition."));
914 }
915}
916
917QT_END_NAMESPACE
\inmodule QtCore
Combined button and popup list for selecting options.
static QTypeRevision parseVersion(const QString &versionString)
QString toString(const UiQualifiedId *qualifiedId, QChar delimiter=QLatin1Char('.'))