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("resolvedFile")) {
200 scope->setResolvedFilePath(readStringBinding(script));
201 } else if (name == QLatin1String("lineNumberInResolvedFile")) {
202 scope->setLineNumberInResolvedFile(readNumericBinding(script));
203 } else if (name == QLatin1String("name")) {
204 scope->setInternalName(readStringBinding(script));
205 } else if (name == QLatin1String("prototype")) {
206 scope->setBaseTypeName(readStringBinding(script));
207 } else if (name == QLatin1String("defaultProperty")) {
208 scope->setOwnDefaultPropertyName(readStringBinding(script));
209 } else if (name == QLatin1String("parentProperty")) {
210 scope->setOwnParentPropertyName(readStringBinding(script));
211 } else if (name == QLatin1String("exports")) {
212 exports = readExports(script);
213 } else if (name == QLatin1String("aliases")) {
214 readAliases(script, scope);
215 } else if (name == QLatin1String("interfaces")) {
216 readInterfaces(script, scope);
217 } else if (name == QLatin1String("exportMetaObjectRevisions")) {
218 metaObjectRevisions = script;
219 } else if (name == QLatin1String("attachedType")) {
220 scope->setOwnAttachedTypeName(readStringBinding(script));
221 } else if (name == QLatin1String("valueType")) {
222 scope->setElementTypeName(readStringBinding(script));
223 } else if (name == QLatin1String("isSingleton")) {
224 scope->setIsSingleton(readBoolBinding(script));
225 } else if (name == QLatin1String("isCreatable")) {
226 scope->setCreatableFlag(readBoolBinding(script));
227 } else if (name == QLatin1String("isStructured")) {
228 scope->setStructuredFlag(readBoolBinding(script));
229 } else if (name == QLatin1String("isComposite")) {
230 scope->setIsComposite(readBoolBinding(script));
231 } else if (name == QLatin1String("hasCustomParser")) {
232 scope->setHasCustomParser(readBoolBinding(script));
233 } else if (name == QLatin1String("enforcesScopedEnums")) {
234 scope->setEnforcesScopedEnumsFlag(readBoolBinding(script));
235 } else if (name == QLatin1String("accessSemantics")) {
236 const QString semantics = readStringBinding(script);
237 if (semantics == QLatin1String("reference")) {
238 scope->setAccessSemantics(QQmlJSScope::AccessSemantics::Reference);
239 } else if (semantics == QLatin1String("value")) {
240 scope->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
241 } else if (semantics == QLatin1String("none")) {
242 scope->setAccessSemantics(QQmlJSScope::AccessSemantics::None);
243 } else if (semantics == QLatin1String("sequence")) {
244 scope->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
245 } else {
246 addWarning(script->firstSourceLocation(),
247 tr("Unknown access semantics \"%1\".").arg(semantics));
248 }
249 } else if (name == QLatin1String("extension")) {
250 scope->setExtensionTypeName(readStringBinding(script));
251 } else if (name == QLatin1String("extensionIsJavaScript")) {
252 scope->setExtensionIsJavaScript(readBoolBinding(script));
253 } else if (name == QLatin1String("extensionIsNamespace")) {
254 scope->setExtensionIsNamespace(readBoolBinding(script));
255 } else if (name == QLatin1String("isSelfExtension")) {
256 scope->setIsSelfExtension(readBoolBinding(script));
257 } else if (name == QLatin1String("deferredNames")) {
258 readDeferredNames(script, scope);
259 } else if (name == QLatin1String("immediateNames")) {
260 readImmediateNames(script, scope);
261 } else if (name == QLatin1String("isJavaScriptBuiltin")) {
262 scope->setIsJavaScriptBuiltin(readBoolBinding(script));
263 } else if (name == QLatin1String("isTypeOpaque")) {
264 scope->setIsTypeOpaque(readBoolBinding(script));
265 } else {
266 addWarning(script->firstSourceLocation(),
267 tr("Expected only lineNumber, name, prototype, defaultProperty, "
268 "attachedType, "
269 "valueType, exports, interfaces, isSingleton, isCreatable, "
270 "isStructured, isComposite, hasCustomParser, enforcesScopedEnums, "
271 "aliases, exportMetaObjectRevisions, deferredNames, and "
272 "immediateNames in script bindings, not \"%1\".")
273 .arg(name));
274 }
275 } else {
276 addWarning(member->firstSourceLocation(),
277 tr("Expected only script bindings and object definitions."));
278 }
279 }
280
281 if (scope->internalName().isEmpty()) {
282 addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
283 return;
284 }
285
286 if (metaObjectRevisions)
287 checkMetaObjectRevisions(metaObjectRevisions, &exports);
288 m_objects->append({scope, exports});
289}
290
291void QQmlJSTypeDescriptionReader::readSignalOrMethod(
292 UiObjectDefinition *ast, bool isMethod, const QQmlJSScope::Ptr &scope)
293{
294 QQmlJSMetaMethod metaMethod;
295 // ### confusion between Method and Slot. Method should be removed.
296 if (isMethod)
297 metaMethod.setMethodType(QQmlJSMetaMethodType::Slot);
298 else
299 metaMethod.setMethodType(QQmlJSMetaMethodType::Signal);
300
301 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
302 UiObjectMember *member = it->member;
303 auto *component = cast<UiObjectDefinition *>(member);
304 auto *script = cast<UiScriptBinding *>(member);
305 if (component) {
306 QString name = toString(component->qualifiedTypeNameId);
307 if (name == QLatin1String("Parameter")) {
308 readParameter(component, &metaMethod);
309 } else {
310 addWarning(component->firstSourceLocation(),
311 tr("Expected only Parameter in object definitions."));
312 }
313 } else if (script) {
314 QString name = toString(script->qualifiedId);
315 if (name == QLatin1String("name")) {
316 metaMethod.setMethodName(readStringBinding(script));
317 } else if (name == QLatin1String("lineNumber")) {
318 metaMethod.setSourceLocation(
319 SourceLocation::fromQSizeType(0, 0, readIntBinding(script), 1));
320 } else if (name == QLatin1String("type")) {
321 metaMethod.setReturnTypeName(readStringBinding(script));
322 } else if (name == QLatin1String("revision")) {
323 metaMethod.setRevision(readIntBinding(script));
324 } else if (name == QLatin1String("isCloned")) {
325 metaMethod.setIsCloned(readBoolBinding(script));
326 } else if (name == QLatin1String("isConstructor")) {
327 // The constructors in the moc json output are ordered the same
328 // way as the ones in the metaobject. qmltyperegistrar moves them into
329 // the same list as the other members, but maintains their order.
330 if (readBoolBinding(script)) {
331 metaMethod.setIsConstructor(true);
332 metaMethod.setConstructorIndex(
333 QQmlJSMetaMethod::RelativeFunctionIndex(m_currentCtorIndex++));
334 }
335 } else if (name == QLatin1String("isJavaScriptFunction")) {
336 metaMethod.setIsJavaScriptFunction(readBoolBinding(script));
337 } else if (name == QLatin1String("isList")) {
338 auto metaReturnType = metaMethod.returnValue();
339 metaReturnType.setIsList(readBoolBinding(script));
340 metaMethod.setReturnValue(metaReturnType);
341 } else if (name == QLatin1String("isPointer")) {
342 // TODO: We don't need this information. We can probably drop all isPointer members
343 // once we make sure that the type information is always complete. The
344 // description of the type being referenced has access semantics after all.
345 auto metaReturnType = metaMethod.returnValue();
346 metaReturnType.setIsPointer(readBoolBinding(script));
347 metaMethod.setReturnValue(metaReturnType);
348 } else if (name == QLatin1String("isTypeConstant")
349 || name == QLatin1String("isConstant")) {
350 // note: isConstant is only read for backwards compatibility
351 auto metaReturnType = metaMethod.returnValue();
352 metaReturnType.setTypeQualifier(readBoolBinding(script)
353 ? QQmlJSMetaParameter::Const
354 : QQmlJSMetaParameter::NonConst);
355 metaMethod.setReturnValue(metaReturnType);
356 } else if (name == QLatin1String("isMethodConstant")) {
357 metaMethod.setIsConst(readBoolBinding(script));
358 } else {
359 addWarning(script->firstSourceLocation(),
360 tr("Expected only name, lineNumber, type, revision, isPointer, "
361 "isTypeConstant, "
362 "isList, isCloned, isConstructor, isMethodConstant, and "
363 "isJavaScriptFunction in script bindings."));
364 }
365 } else {
366 addWarning(member->firstSourceLocation(),
367 tr("Expected only script bindings and object definitions."));
368 }
369 }
370
371 if (metaMethod.methodName().isEmpty()) {
372 addError(ast->firstSourceLocation(),
373 tr("Method or signal is missing a name script binding."));
374 return;
375 }
376
377 // Signals, slots and method share one index space. Constructors are separate.
378 // We also assume that the order and therefore the indexing of all methods is retained from
379 // moc's JSON output.
380 if (!metaMethod.isConstructor())
381 metaMethod.setMethodIndex(QQmlJSMetaMethod::RelativeFunctionIndex(m_currentMethodIndex++));
382
383 if (metaMethod.returnTypeName().isEmpty())
384 metaMethod.setReturnTypeName(QLatin1String("void"));
385
386 scope->addOwnMethod(metaMethod);
387}
388
389void QQmlJSTypeDescriptionReader::readProperty(UiObjectDefinition *ast, const QQmlJSScope::Ptr &scope)
390{
391 QQmlJSMetaProperty property;
392 property.setIsWritable(true); // default is writable
393 bool isRequired = false;
394
395 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
396 UiObjectMember *member = it->member;
397 auto *script = cast<UiScriptBinding *>(member);
398 if (!script) {
399 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
400 continue;
401 }
402
403 QString id = toString(script->qualifiedId);
404 if (id == QLatin1String("name")) {
405 property.setPropertyName(readStringBinding(script));
406 } else if (id == QLatin1String("lineNumber")) {
407 property.setSourceLocation(
408 SourceLocation::fromQSizeType(0, 0, readIntBinding(script), 1));
409 } else if (id == QLatin1String("type")) {
410 property.setTypeName(readStringBinding(script));
411 } else if (id == QLatin1String("isPointer")) {
412 property.setIsPointer(readBoolBinding(script));
413 } else if (id == QLatin1String("isReadonly")) {
414 property.setIsWritable(!readBoolBinding(script));
415 } else if (id == QLatin1String("isRequired")) {
416 isRequired = readBoolBinding(script);
417 } else if (id == QLatin1String("isList")) {
418 property.setIsList(readBoolBinding(script));
419 } else if (id == QLatin1String("isFinal")) {
420 property.setIsFinal(readBoolBinding(script));
421 } else if (id == QLatin1String("isVirtual")) {
422 property.setIsVirtual(readBoolBinding(script));
423 } else if (id == QLatin1String("isOverride")) {
424 property.setIsOverride(readBoolBinding(script));
425 } else if (id == QLatin1String("isTypeConstant")) {
426 property.setIsTypeConstant(readBoolBinding(script));
427 } else if (id == QLatin1String("isPropertyConstant")) {
428 property.setIsPropertyConstant(readBoolBinding(script));
429 } else if (id == QLatin1String("isConstant")) {
430 // support old "isConstant" for backwards compatibility
431 property.setIsPropertyConstant(readBoolBinding(script));
432 } else if (id == QLatin1String("revision")) {
433 property.setRevision(readIntBinding(script));
434 } else if (id == QLatin1String("bindable")) {
435 property.setBindable(readStringBinding(script));
436 } else if (id == QLatin1String("read")) {
437 property.setRead(readStringBinding(script));
438 } else if (id == QLatin1String("write")) {
439 property.setWrite(readStringBinding(script));
440 } else if (id == QLatin1String("reset")) {
441 property.setReset(readStringBinding(script));
442 } else if (id == QLatin1String("notify")) {
443 property.setNotify(readStringBinding(script));
444 } else if (id == QLatin1String("index")) {
445 property.setIndex(readIntBinding(script));
446 } else if (id == QLatin1String("privateClass")) {
447 property.setPrivateClass(readStringBinding(script));
448 } else {
449 addWarning(script->firstSourceLocation(),
450 tr("Expected only type, name, lineNumber, revision, isPointer, "
451 "isTypeConstant, isReadonly, isRequired, "
452 "isFinal, isList, bindable, read, write, isPropertyConstant, reset, "
453 "notify, index, privateClass and script bindings."));
454 }
455 }
456
457 if (property.propertyName().isEmpty()) {
458 addError(ast->firstSourceLocation(),
459 tr("Property object is missing a name script binding."));
460 return;
461 }
462
463 scope->addOwnProperty(property);
464 if (isRequired)
465 scope->setPropertyLocallyRequired(property.propertyName(), true);
466}
467
468void QQmlJSTypeDescriptionReader::readEnum(UiObjectDefinition *ast, const QQmlJSScope::Ptr &scope)
469{
470 QQmlJSMetaEnum metaEnum;
471
472 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
473 UiObjectMember *member = it->member;
474 auto *script = cast<UiScriptBinding *>(member);
475 if (!script) {
476 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
477 continue;
478 }
479
480 QString name = toString(script->qualifiedId);
481 if (name == QLatin1String("name")) {
482 metaEnum.setName(readStringBinding(script));
483 } else if (name == QLatin1String("alias")) {
484 metaEnum.setAlias(readStringBinding(script));
485 } else if (name == QLatin1String("isFlag")) {
486 metaEnum.setIsFlag(readBoolBinding(script));
487 } else if (name == QLatin1String("values")) {
488 readEnumValues(script, &metaEnum);
489 } else if (name == QLatin1String("isScoped")) {
490 metaEnum.setIsScoped(readBoolBinding(script));
491 } else if (name == QLatin1String("type")) {
492 metaEnum.setTypeName(readStringBinding(script));
493 } else if (name == QLatin1String("lineNumber")) {
494 metaEnum.setLineNumber(readIntBinding(script));
495 } else {
496 addWarning(script->firstSourceLocation(),
497 tr("Expected only name, alias, isFlag, values, isScoped, type, or "
498 "lineNumber."));
499 }
500 }
501
502 scope->addOwnEnumeration(metaEnum);
503}
504
505void QQmlJSTypeDescriptionReader::readParameter(UiObjectDefinition *ast, QQmlJSMetaMethod *metaMethod)
506{
507 QString name;
508 QString type;
509 bool isConstant = false;
510 bool isPointer = false;
511 bool isList = false;
512
513 for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
514 UiObjectMember *member = it->member;
515 auto *script = cast<UiScriptBinding *>(member);
516 if (!script) {
517 addWarning(member->firstSourceLocation(), tr("Expected script binding."));
518 continue;
519 }
520
521 const QString id = toString(script->qualifiedId);
522 if (id == QLatin1String("name")) {
523 name = readStringBinding(script);
524 } else if (id == QLatin1String("type")) {
525 type = readStringBinding(script);
526 } else if (id == QLatin1String("isPointer")) {
527 isPointer = readBoolBinding(script);
528 } else if (id == QLatin1String("isTypeConstant") || id == QLatin1String("isConstant")) {
529 // note: isConstant is only read for backwards compatibility
530 isConstant = readBoolBinding(script);
531 } else if (id == QLatin1String("isReadonly")) {
532 // ### unhandled
533 } else if (id == QLatin1String("isList")) {
534 isList = readBoolBinding(script);
535 } else {
536 addWarning(script->firstSourceLocation(),
537 tr("Expected only name, type, isPointer, isTypeConstant, isReadonly, "
538 "or IsList script bindings."));
539 }
540 }
541
542 QQmlJSMetaParameter p(name, type);
543 p.setTypeQualifier(isConstant ? QQmlJSMetaParameter::Const : QQmlJSMetaParameter::NonConst);
544 p.setIsPointer(isPointer);
545 p.setIsList(isList);
546 metaMethod->addParameter(std::move(p));
547}
548
549QString QQmlJSTypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
550{
551 Q_ASSERT(ast);
552
553 if (!ast->statement) {
554 addError(ast->colonToken, tr("Expected string after colon."));
555 return QString();
556 }
557
558 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
559 if (!expStmt) {
560 addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
561 return QString();
562 }
563
564 auto *stringLit = cast<StringLiteral *>(expStmt->expression);
565 if (!stringLit) {
566 addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
567 return QString();
568 }
569
570 return stringLit->value.toString();
571}
572
573bool QQmlJSTypeDescriptionReader::readBoolBinding(UiScriptBinding *ast)
574{
575 Q_ASSERT(ast);
576
577 if (!ast->statement) {
578 addError(ast->colonToken, tr("Expected boolean after colon."));
579 return false;
580 }
581
582 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
583 if (!expStmt) {
584 addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
585 return false;
586 }
587
588 auto *trueLit = cast<TrueLiteral *>(expStmt->expression);
589 auto *falseLit = cast<FalseLiteral *>(expStmt->expression);
590 if (!trueLit && !falseLit) {
591 addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
592 return false;
593 }
594
595 return trueLit;
596}
597
598double QQmlJSTypeDescriptionReader::readNumericBinding(UiScriptBinding *ast)
599{
600 Q_ASSERT(ast);
601
602 if (!ast->statement) {
603 addError(ast->colonToken, tr("Expected numeric literal after colon."));
604 return 0;
605 }
606
607 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
608 if (!expStmt) {
609 addError(ast->statement->firstSourceLocation(),
610 tr("Expected numeric literal after colon."));
611 return 0;
612 }
613
614 auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
615 if (!numericLit) {
616 addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
617 return 0;
618 }
619
620 return numericLit->value;
621}
622
623static QTypeRevision parseVersion(const QString &versionString)
624{
625 const int dotIdx = versionString.indexOf(QLatin1Char('.'));
626 if (dotIdx == -1)
627 return QTypeRevision();
628 bool ok = false;
629 const int maybeMajor = QStringView{versionString}.left(dotIdx).toInt(&ok);
630 if (!ok)
631 return QTypeRevision();
632 const int maybeMinor = QStringView{versionString}.mid(dotIdx + 1).toInt(&ok);
633 if (!ok)
634 return QTypeRevision();
635 return QTypeRevision::fromVersion(maybeMajor, maybeMinor);
636}
637
638QTypeRevision QQmlJSTypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
639{
640 QTypeRevision invalidVersion;
641
642 if (!ast || !ast->statement) {
643 addError((ast ? ast->colonToken : SourceLocation()),
644 tr("Expected numeric literal after colon."));
645 return invalidVersion;
646 }
647
648 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
649 if (!expStmt) {
650 addError(ast->statement->firstSourceLocation(),
651 tr("Expected numeric literal after colon."));
652 return invalidVersion;
653 }
654
655 auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
656 if (!numericLit) {
657 addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
658 return invalidVersion;
659 }
660
661 return parseVersion(m_source.mid(numericLit->literalToken.begin(),
662 numericLit->literalToken.length));
663}
664
665int QQmlJSTypeDescriptionReader::readIntBinding(UiScriptBinding *ast)
666{
667 double v = readNumericBinding(ast);
668 int i = static_cast<int>(v);
669
670 if (i != v) {
671 addError(ast->firstSourceLocation(), tr("Expected integer after colon."));
672 return 0;
673 }
674
675 return i;
676}
677
678ArrayPattern* QQmlJSTypeDescriptionReader::getArray(UiScriptBinding *ast)
679{
680 Q_ASSERT(ast);
681
682 if (!ast->statement) {
683 addError(ast->colonToken, tr("Expected array of strings after colon."));
684 return nullptr;
685 }
686
687 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
688 if (!expStmt) {
689 addError(ast->statement->firstSourceLocation(),
690 tr("Expected array of strings after colon."));
691 return nullptr;
692 }
693
694 auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
695 if (!arrayLit) {
696 addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
697 return nullptr;
698 }
699
700 return arrayLit;
701}
702
703QList<QQmlJSScope::Export> QQmlJSTypeDescriptionReader::readExports(UiScriptBinding *ast)
704{
705 QList<QQmlJSScope::Export> exports;
706 auto *arrayLit = getArray(ast);
707
708 if (!arrayLit)
709 return exports;
710
711 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
712 auto *stringLit = cast<StringLiteral *>(it->element->initializer);
713
714 if (!stringLit) {
715 addError(arrayLit->firstSourceLocation(),
716 tr("Expected array literal with only string literal members."));
717 return exports;
718 }
719
720 QString exp = stringLit->value.toString();
721 int slashIdx = exp.indexOf(QLatin1Char('/'));
722 int spaceIdx = exp.indexOf(QLatin1Char(' '));
723 const QTypeRevision version = parseVersion(exp.mid(spaceIdx + 1));
724
725 if (spaceIdx == -1 || !version.isValid()) {
726 addError(stringLit->firstSourceLocation(),
727 tr("Expected string literal to contain 'Package/Name major.minor' "
728 "or 'Name major.minor'."));
729 continue;
730 }
731 QString package;
732 if (slashIdx != -1)
733 package = exp.left(slashIdx);
734 QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
735
736 // ### relocatable exports where package is empty?
737 exports.append(QQmlJSScope::Export(package, name, version, version));
738 }
739
740 return exports;
741}
742
743void QQmlJSTypeDescriptionReader::readAliases(
744 QQmlJS::AST::UiScriptBinding *ast, const QQmlJSScope::Ptr &scope)
745{
746 scope->setAliases(readStringList(ast));
747}
748
749void QQmlJSTypeDescriptionReader::readInterfaces(UiScriptBinding *ast, const QQmlJSScope::Ptr &scope)
750{
751 auto *arrayLit = getArray(ast);
752
753 if (!arrayLit)
754 return;
755
756 QStringList list;
757
758 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
759 auto *stringLit = cast<StringLiteral *>(it->element->initializer);
760 if (!stringLit) {
761 addError(arrayLit->firstSourceLocation(),
762 tr("Expected array literal with only string literal members."));
763 return;
764 }
765
766 list << stringLit->value.toString();
767 }
768
769 scope->setInterfaceNames(list);
770}
771
772void QQmlJSTypeDescriptionReader::checkMetaObjectRevisions(
773 UiScriptBinding *ast, QList<QQmlJSScope::Export> *exports)
774{
775 Q_ASSERT(ast);
776
777 if (!ast->statement) {
778 addError(ast->colonToken, tr("Expected array of numbers after colon."));
779 return;
780 }
781
782 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
783 if (!expStmt) {
784 addError(ast->statement->firstSourceLocation(),
785 tr("Expected array of numbers after colon."));
786 return;
787 }
788
789 auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
790 if (!arrayLit) {
791 addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
792 return;
793 }
794
795 int exportIndex = 0;
796 const int exportCount = exports->size();
797 for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
798 auto *numberLit = cast<NumericLiteral *>(it->element->initializer);
799 if (!numberLit) {
800 addError(arrayLit->firstSourceLocation(),
801 tr("Expected array literal with only number literal members."));
802 return;
803 }
804
805 if (exportIndex >= exportCount) {
806 addError(numberLit->firstSourceLocation(),
807 tr("Meta object revision without matching export."));
808 return;
809 }
810
811 const double v = numberLit->value;
812 const int metaObjectRevision = static_cast<int>(v);
813 if (metaObjectRevision != v) {
814 addError(numberLit->firstSourceLocation(), tr("Expected integer."));
815 return;
816 }
817
818 const QTypeRevision metaObjectVersion
819 = QTypeRevision::fromEncodedVersion(metaObjectRevision);
820 const QQmlJSScope::Export &entry = exports->at(exportIndex);
821 const QTypeRevision exportVersion = entry.version();
822 if (metaObjectVersion != exportVersion) {
823 addWarning(numberLit->firstSourceLocation(),
824 tr("Meta object revision and export version differ.\n"
825 "Revision %1 corresponds to version %2.%3; it should be %4.%5.")
826 .arg(metaObjectRevision)
827 .arg(metaObjectVersion.majorVersion()).arg(metaObjectVersion.minorVersion())
828 .arg(exportVersion.majorVersion()).arg(exportVersion.minorVersion()));
829 (*exports)[exportIndex] = QQmlJSScope::Export(entry.package(), entry.type(),
830 exportVersion, metaObjectVersion);
831 }
832 }
833}
834
835QStringList QQmlJSTypeDescriptionReader::readStringList(UiScriptBinding *ast)
836{
837 auto *arrayLit = getArray(ast);
838 if (!arrayLit)
839 return {};
840
841 QStringList list;
842
843 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
844 auto *stringLit = cast<StringLiteral *>(it->element->initializer);
845 if (!stringLit) {
846 addError(arrayLit->firstSourceLocation(),
847 tr("Expected array literal with only string literal members."));
848 return {};
849 }
850
851 list << stringLit->value.toString();
852 }
853
854 return list;
855}
856
857void QQmlJSTypeDescriptionReader::readDeferredNames(UiScriptBinding *ast,
858 const QQmlJSScope::Ptr &scope)
859{
860 scope->setOwnDeferredNames(readStringList(ast));
861}
862
863void QQmlJSTypeDescriptionReader::readImmediateNames(UiScriptBinding *ast,
864 const QQmlJSScope::Ptr &scope)
865{
866 scope->setOwnImmediateNames(readStringList(ast));
867}
868
869void QQmlJSTypeDescriptionReader::readEnumValues(UiScriptBinding *ast, QQmlJSMetaEnum *metaEnum)
870{
871 if (!ast)
872 return;
873 if (!ast->statement) {
874 addError(ast->colonToken, tr("Expected object literal after colon."));
875 return;
876 }
877
878 auto *expStmt = cast<ExpressionStatement *>(ast->statement);
879 if (!expStmt) {
880 addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
881 return;
882 }
883
884 if (auto *objectLit = cast<ObjectPattern *>(expStmt->expression)) {
885 int currentValue = -1;
886 for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
887 if (PatternProperty *assignement = it->property) {
888 if (auto *name = cast<StringLiteralPropertyName *>(assignement->name)) {
889 metaEnum->addKey(name->id.toString());
890
891 if (auto *value = AST::cast<NumericLiteral *>(assignement->initializer)) {
892 currentValue = int(value->value);
893 } else if (auto *minus = AST::cast<UnaryMinusExpression *>(
894 assignement->initializer)) {
895 if (auto *value = AST::cast<NumericLiteral *>(minus->expression))
896 currentValue = -int(value->value);
897 else
898 ++currentValue;
899 } else {
900 ++currentValue;
901 }
902
903 metaEnum->addValue(currentValue);
904 continue;
905 }
906 }
907 addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
908 }
909 } else if (auto *arrayLit = cast<ArrayPattern *>(expStmt->expression)) {
910 for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
911 if (PatternElement *element = it->element) {
912 if (auto *name = cast<StringLiteral *>(element->initializer)) {
913 metaEnum->addKey(name->value.toString());
914 continue;
915 }
916 }
917 addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
918 }
919 } else {
920 addError(ast->statement->firstSourceLocation(),
921 tr("Expected either array or object literal as enum definition."));
922 }
923}
924
925QT_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('.'))