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