1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Suppress GCC 11 warning about maybe-uninitialized copy of
4// another Data. We're not sure if the compiler is actually right,
5// but in this type of warning, it often isn't.
6//#if defined(Q_CC_GNU) && Q_CC_GNU >= 1100
7//QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
8#include "qqmldomconstants_p.h"
9#include "qqmldompath_p.h"
10#if defined(__GNUC__) && __GNUC__ >= 11
11# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
12#endif
13
14#include "qqmldomelements_p.h"
15#include "qqmldomcomments_p.h"
16#include "qqmldomastdumper_p.h"
17#include "qqmldommock_p.h"
18#include "qqmldomreformatter_p.h"
19#include "qqmldomoutwriter_p.h"
20#include "qqmldomlinewriter_p.h"
21#include "qqmldomtop_p.h"
22#include "qqmldomexternalitems_p.h"
23
24#include <QtQml/private/qqmljslexer_p.h>
25#include <QtQml/private/qqmljsparser_p.h>
26#include <QtQml/private/qqmljsengine_p.h>
27#include <QtQml/private/qqmljsastvisitor_p.h>
28#include <QtQml/private/qqmljsast_p.h>
29
30#include <QtCore/QScopeGuard>
31#include <QtCore/QRegularExpression>
32#include <QtCore/QDir>
33#include <QtCore/QBasicMutex>
34#include <QtCore/QUrl>
35
36#include <optional>
37#include <limits>
38
39QT_BEGIN_NAMESPACE
40
41using namespace Qt::StringLiterals;
42
43namespace QQmlJS {
44namespace Dom {
45
46namespace Paths {
47
48Path moduleIndexPath(const QString &uri, int majorVersion, const ErrorHandler &errorHandler)
49{
50 QString version = QString::number(majorVersion);
51 if (majorVersion == Version::Latest)
52 version = QLatin1String("Latest");
53 else if (majorVersion == Version::Undefined)
54 version = QString();
55 QRegularExpression moduleRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
56 auto m = moduleRe.match(subject: uri);
57 if (!m.isValid())
58 Path::myErrors()
59 .error(message: Path::tr(sourceText: "Invalid module name in import %1").arg(a: uri))
60 .handle(errorHandler);
61 return Path::Root(r: PathRoot::Env).field(name: Fields::moduleIndexWithUri).key(name: uri).key(name: version);
62}
63
64Path moduleScopePath(const QString &uri, Version version, const ErrorHandler &)
65{
66 return Path::Root(r: PathRoot::Env)
67 .field(name: Fields::moduleIndexWithUri)
68 .key(name: uri)
69 .key(name: version.majorSymbolicString())
70 .field(name: Fields::moduleScope)
71 .key(name: version.minorString());
72}
73
74Path moduleScopePath(const QString &uri, const QString &version, const ErrorHandler &errorHandler)
75{
76 Version v = Version::fromString(v: version);
77 if (!version.isEmpty() && !(v.isValid() || v.isLatest()))
78 Path::myErrors().error(message: Path::tr(sourceText: "Invalid Version %1").arg(a: version)).handle(errorHandler);
79 return moduleScopePath(uri, version: v, errorHandler);
80}
81
82} // end namespace Paths
83
84static ErrorGroups domParsingErrors()
85{
86 static ErrorGroups res = { .groups: { DomItem::domErrorGroup, NewErrorGroup("Parsing") } };
87 return res;
88}
89
90bool CommentableDomElement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
91{
92 bool cont = true;
93 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: m_comments);
94 return cont;
95}
96
97void Component::updatePathFromOwner(const Path &newPath)
98{
99 DomElement::updatePathFromOwner(newPath);
100 updatePathFromOwnerMultiMap(mmap&: m_enumerations, newPath: newPath.field(name: Fields::enumerations));
101 updatePathFromOwnerQList(list&: m_objects, newPath: newPath.field(name: Fields::objects));
102}
103
104Component::Component(const QString &name) : CommentableDomElement(Path()), m_name(name) { }
105
106Component::Component(const Path &pathFromOwner) : CommentableDomElement(pathFromOwner) { }
107
108bool Component::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
109{
110 bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
111 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name());
112 cont = cont && self.dvWrapField(visitor, f: Fields::enumerations, obj: m_enumerations);
113 cont = cont && self.dvWrapField(visitor, f: Fields::objects, obj: m_objects);
114 cont = cont && self.dvValueField(visitor, f: Fields::isSingleton, value: isSingleton());
115 cont = cont && self.dvValueField(visitor, f: Fields::isCreatable, value: isCreatable());
116 cont = cont && self.dvValueField(visitor, f: Fields::isComposite, value: isComposite());
117 cont = cont && self.dvValueField(visitor, f: Fields::attachedTypeName, value: attachedTypeName());
118 cont = cont && self.dvReferenceField(visitor, f: Fields::attachedType, referencedObject: attachedTypePath(self));
119 return cont;
120}
121
122DomItem Component::field(const DomItem &self, QStringView name) const
123{
124 if (name == Fields::name)
125 return self.wrapField(f: Fields::name, obj: m_name);
126 if (name == Fields::objects)
127 return self.wrapField(f: Fields::objects, obj: m_objects);
128
129 return DomBase::field(self, name);
130}
131
132Path Component::addObject(const QmlObject &object, QmlObject **oPtr)
133{
134 return appendUpdatableElementInQList(listPathFromOwner: pathFromOwner().field(name: Fields::objects), list&: m_objects, value: object,
135 vPtr: oPtr);
136}
137
138bool QmlComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
139{
140 bool cont = Component::iterateDirectSubpaths(self, visitor);
141 cont = cont && self.dvWrapField(visitor, f: Fields::ids, obj: m_ids);
142 cont = cont && self.dvValueLazyField(visitor, f: Fields::subComponents, valueF: [this, &self]() {
143 return this->subComponents(self);
144 });
145 if (m_nameIdentifiers) {
146 cont = cont && self.dvItemField(visitor, f: Fields::nameIdentifiers, it: [this, &self]() {
147 return self.subScriptElementWrapperItem(obj: m_nameIdentifiers);
148 });
149 }
150 return cont;
151}
152
153void QmlComponent::updatePathFromOwner(const Path &newPath)
154{
155 Component::updatePathFromOwner(newPath);
156 updatePathFromOwnerMultiMap(mmap&: m_ids, newPath: newPath.field(name: Fields::annotations));
157}
158
159void QmlComponent::writeOut(const DomItem &self, OutWriter &lw) const
160{
161 if (name().contains(c: QLatin1Char('.'))) {
162 // inline component
163 lw.ensureNewline()
164 .writeRegion(region: ComponentKeywordRegion)
165 .ensureSpace()
166 .writeRegion(region: IdentifierRegion, toWrite: name().split(sep: QLatin1Char('.')).last())
167 .writeRegion(region: ColonTokenRegion)
168 .ensureSpace();
169 }
170 self.field(name: Fields::objects).index(0).writeOut(lw);
171}
172
173QList<QString> QmlComponent::subComponentsNames(const DomItem &self) const
174{
175 DomItem components = self.owner().field(name: Fields::components);
176 const QSet<QString> cNames = components.keys();
177 QString myNameDot = self.pathFromOwner()[1].headName();
178 if (!myNameDot.isEmpty())
179 myNameDot += QLatin1Char('.');
180 QList<QString> subNames;
181 for (const QString &cName : cNames)
182 if (cName.startsWith(s: myNameDot)
183 && !QStringView(cName).mid(pos: myNameDot.size()).contains(c: QLatin1Char('.'))
184 && !cName.isEmpty())
185 subNames.append(t: cName);
186 std::sort(first: subNames.begin(), last: subNames.end());
187 return subNames;
188}
189
190QList<DomItem> QmlComponent::subComponents(const DomItem &self) const
191{
192 DomItem components = self.owner().field(name: Fields::components);
193 QList<DomItem> res;
194 for (const QString &cName : subComponentsNames(self))
195 for (const DomItem &comp : components.key(name: cName).values())
196 res.append(t: comp);
197 return res;
198}
199
200Version Version::fromString(QStringView v)
201{
202 if (v.isEmpty())
203 return Version(Latest, Latest);
204 QRegularExpression r(
205 QRegularExpression::anchoredPattern(QStringLiteral(uR"(([0-9]*)(?:\.([0-9]*))?)")));
206 auto m = r.matchView(subjectView: v);
207 if (m.hasMatch()) {
208 bool ok;
209 int majorV = m.capturedView(nth: 1).toInt(ok: &ok);
210 if (!ok)
211 majorV = Version::Undefined;
212 int minorV = m.capturedView(nth: 2).toInt(ok: &ok);
213 if (!ok)
214 minorV = Version::Undefined;
215 return Version(majorV, minorV);
216 }
217 return {};
218}
219
220Version::Version(qint32 majorV, qint32 minorV) : majorVersion(majorV), minorVersion(minorV) { }
221
222bool Version::isLatest() const
223{
224 return majorVersion == Latest && minorVersion == Latest;
225}
226
227bool Version::isValid() const
228{
229 return majorVersion >= 0 && minorVersion >= 0;
230}
231
232QString Version::stringValue() const
233{
234 if (isLatest())
235 return QString();
236 if (minorVersion < 0) {
237 if (majorVersion < 0)
238 return QLatin1String(".");
239 else
240 return QString::number(majorVersion);
241 }
242 if (majorVersion < 0)
243 return QLatin1String(".") + QString::number(minorVersion);
244 return QString::number(majorVersion) + QChar::fromLatin1(c: '.') + QString::number(minorVersion);
245}
246
247bool Version::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
248{
249 bool cont = true;
250 cont = cont && self.dvWrapField(visitor, f: Fields::majorVersion, obj: majorVersion);
251 cont = cont && self.dvWrapField(visitor, f: Fields::minorVersion, obj: minorVersion);
252 cont = cont && self.dvValueField(visitor, f: Fields::isLatest, value: isLatest());
253 cont = cont && self.dvValueField(visitor, f: Fields::isValid, value: isValid());
254 cont = cont && self.dvValueLazyField(visitor, f: Fields::stringValue, valueF: [this]() {
255 return this->stringValue();
256 });
257 return cont;
258}
259
260QRegularExpression Import::importRe()
261{
262 static QRegularExpression res(QRegularExpression::anchoredPattern(QStringLiteral(
263 uR"((?<uri>\w+(?:\.\w+)*)(?:\W+(?<version>[0-9]+(?:\.[0-9]*)?))?(?:\W+as\W+(?<id>\w+))?$)")));
264 return res;
265}
266
267Import Import::fromUriString(
268 const QString &importStr, Version v, const QString &importId, const ErrorHandler &handler)
269{
270 auto m = importRe().match(subject: importStr);
271 if (m.hasMatch()) {
272 if (v.majorVersion == Version::Undefined && v.minorVersion == Version::Undefined)
273 v = Version::fromString(v: m.captured(nth: 2));
274 else if (!m.captured(name: u"version").isEmpty())
275 domParsingErrors()
276 .warning(message: tr(sourceText: "Version %1 in import string '%2' overridden by explicit "
277 "version %3")
278 .arg(args: m.captured(nth: 2), args: importStr, args: v.stringValue()))
279 .handle(errorHandler: handler);
280 QString resolvedImportId;
281 if (importId.isEmpty()) {
282 resolvedImportId = m.captured(name: u"importId");
283 } else {
284 if (!m.captured(name: u"importId").isEmpty()) {
285 domParsingErrors()
286 .warning(message: tr(sourceText: "namespace %1 in import string '%2' overridden by explicit "
287 "importId %3")
288 .arg(args: m.captured(name: u"importId"), args: importStr, args: importId))
289 .handle(errorHandler: handler);
290 }
291 resolvedImportId = importId;
292 }
293
294 return Import(QmlUri::fromUriString(importStr: m.captured(name: u"uri").trimmed()), v, resolvedImportId);
295 }
296 domParsingErrors()
297 .error(message: tr(sourceText: "Unexpected URI format in import '%1'").arg(a: importStr))
298 .handle(errorHandler: handler);
299 return Import();
300}
301
302Import Import::fromFileString(
303 const QString &importStr, const QString &importId, const ErrorHandler &)
304{
305 return Import(QmlUri::fromDirectoryString(importStr), Version(), importId);
306}
307
308bool Import::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
309{
310 bool cont = true;
311 cont = cont && self.dvValueField(visitor, f: Fields::uri, value: uri.toString());
312 cont = cont && self.dvWrapField(visitor, f: Fields::version, obj: version);
313 if (!importId.isEmpty())
314 cont = cont && self.dvValueField(visitor, f: Fields::importId, value: importId);
315 if (implicit)
316 cont = cont && self.dvValueField(visitor, f: Fields::implicit, value: implicit);
317 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: comments);
318 return cont;
319}
320
321void Import::writeOut(const DomItem &self, OutWriter &ow) const
322{
323 if (implicit)
324 return;
325
326 QString code;
327 const DomItem owner = self.owner();
328 if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
329 code = qmlFilePtr->code();
330
331 // check for an empty line before the import, and preserve it
332 int preNewlines = 0;
333
334 const FileLocations::Tree elLoc = FileLocations::findAttachedInfo(item: self).foundTree;
335
336 quint32 start = elLoc->info().fullRegion.offset;
337 if (size_t(code.size()) >= start) {
338 while (start != 0) {
339 QChar c = code.at(i: --start);
340 if (c == u'\n') {
341 if (++preNewlines == 2)
342 break;
343 } else if (!c.isSpace())
344 break;
345 }
346 }
347 if (preNewlines == 0)
348 ++preNewlines;
349
350 ow.ensureNewline(nNewlines: preNewlines);
351 ow.writeRegion(region: ImportTokenRegion).ensureSpace();
352 ow.writeRegion(region: ImportUriRegion, toWrite: uri.toString());
353 if (uri.isModule()) {
354 QString vString = version.stringValue();
355 if (!vString.isEmpty())
356 ow.ensureSpace().write(v: vString);
357 }
358 if (!importId.isEmpty()) {
359 ow.ensureSpace().writeRegion(region: AsTokenRegion).ensureSpace().writeRegion(region: IdNameRegion,
360 toWrite: importId);
361 }
362}
363
364Id::Id(const QString &idName, const Path &referredObject) : name(idName), referredObjectPath(referredObject) { }
365
366bool Id::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
367{
368 bool cont = true;
369 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name);
370 cont = cont && self.dvReferenceField(visitor, f: Fields::referredObject, referencedObject: referredObjectPath);
371 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: comments);
372 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: annotations);
373 cont = cont && self.dvWrapField(visitor, f: Fields::value, obj: value);
374 return cont;
375}
376
377void Id::updatePathFromOwner(const Path &newPath)
378{
379 updatePathFromOwnerQList(list&: annotations, newPath: newPath.field(name: Fields::annotations));
380}
381
382Path Id::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
383{
384 return appendUpdatableElementInQList(listPathFromOwner: selfPathFromOwner.field(name: Fields::annotations), list&: annotations,
385 value: annotation, vPtr: aPtr);
386}
387
388QmlObject::QmlObject(const Path &pathFromOwner) : CommentableDomElement(pathFromOwner) { }
389
390bool QmlObject::iterateBaseDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
391{
392 bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
393 if (!idStr().isEmpty())
394 cont = cont && self.dvValueField(visitor, f: Fields::idStr, value: idStr());
395 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name());
396 if (!prototypePaths().isEmpty())
397 cont = cont && self.dvReferencesField(visitor, f: Fields::prototypes, paths: m_prototypePaths);
398 if (nextScopePath())
399 cont = cont && self.dvReferenceField(visitor, f: Fields::nextScope, referencedObject: nextScopePath());
400 cont = cont && self.dvWrapField(visitor, f: Fields::propertyDefs, obj: m_propertyDefs);
401 cont = cont && self.dvWrapField(visitor, f: Fields::bindings, obj: m_bindings);
402 cont = cont && self.dvWrapField(visitor, f: Fields::methods, obj: m_methods);
403 cont = cont && self.dvWrapField(visitor, f: Fields::children, obj: m_children);
404 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: m_annotations);
405 cont = cont && self.dvItemField(visitor, f: Fields::propertyInfos, it: [this, &self]() {
406 return self.subMapItem(map: Map(
407 pathFromOwner().field(name: Fields::propertyInfos),
408 [&self](const DomItem &map, const QString &k) {
409 auto pInfo = self.propertyInfoWithName(name: k);
410 return map.wrap(c: PathEls::Key(k), obj: pInfo);
411 },
412 [&self](const DomItem &) { return self.propertyInfoNames(); },
413 QLatin1String("PropertyInfo")));
414 });
415 if (m_nameIdentifiers) {
416 cont = cont && self.dvItemField(visitor, f: Fields::nameIdentifiers, it: [this, &self]() {
417 return self.subScriptElementWrapperItem(obj: m_nameIdentifiers);
418 });
419 }
420 return cont;
421}
422
423QList<QString> QmlObject::fields() const
424{
425 static QList<QString> myFields(
426 { QString::fromUtf16(Fields::comments), QString::fromUtf16(Fields::idStr),
427 QString::fromUtf16(Fields::name), QString::fromUtf16(Fields::prototypes),
428 QString::fromUtf16(Fields::nextScope), QString::fromUtf16(Fields::propertyDefs),
429 QString::fromUtf16(Fields::bindings), QString::fromUtf16(Fields::methods),
430 QString::fromUtf16(Fields::children), QString::fromUtf16(Fields::annotations),
431 QString::fromUtf16(Fields::propertyInfos) });
432 return myFields;
433}
434
435bool QmlObject::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
436{
437 bool cont = iterateBaseDirectSubpaths(self, visitor);
438 cont = cont && self.dvValueLazyField(visitor, f: Fields::defaultPropertyName, valueF: [this, &self]() {
439 return defaultPropertyName(self);
440 });
441 return cont;
442}
443
444DomItem QmlObject::field(const DomItem &self, QStringView name) const
445{
446 if (name == Fields::name)
447 return self.subDataItem(c: PathEls::Field(Fields::name), value: this->name());
448 if (name == Fields::idStr) {
449 if (idStr().isEmpty())
450 return DomItem();
451 return self.subDataItem(c: PathEls::Field(Fields::idStr), value: idStr());
452 }
453 if (name == Fields::methods)
454 return self.wrapField(f: Fields::methods, obj: m_methods);
455 if (name == Fields::bindings)
456 return self.wrapField(f: Fields::bindings, obj: m_bindings);
457 if (name == Fields::comments)
458 return CommentableDomElement::field(self, name);
459 if (name == Fields::children)
460 return self.wrapField(f: Fields::children, obj: m_children);
461
462 if (name == Fields::nextScope) {
463 if (nextScopePath())
464 return self.subReferenceItem(c: PathEls::Field(Fields::nextScope), referencedObject: nextScopePath());
465 else
466 return DomItem();
467 }
468 if (name == Fields::prototypes) {
469 if (prototypePaths().isEmpty())
470 return DomItem();
471 return self.subReferencesItem(c: PathEls::Field(Fields::prototypes), paths: m_prototypePaths);
472 }
473 if (name == Fields::annotations)
474 return self.wrapField(f: Fields::annotations, obj: m_annotations);
475 if (name == Fields::propertyDefs)
476 return self.wrapField(f: Fields::propertyDefs, obj: m_propertyDefs);
477 if (name == Fields::propertyInfos) {
478 // Need to explicitly copy self here since we might store this and call it later.
479 return self.subMapItem(map: Map(
480 pathFromOwner().field(name: Fields::propertyInfos),
481 [copiedSelf = self](const DomItem &map, const QString &k) {
482 return map.wrap(c: PathEls::Key(k), obj: copiedSelf.propertyInfoWithName(name: k));
483 },
484 [copiedSelf = self](const DomItem &) { return copiedSelf.propertyInfoNames(); },
485 QLatin1String("PropertyInfo")));
486 }
487 if (name == Fields::nameIdentifiers && m_nameIdentifiers) {
488 return self.subScriptElementWrapperItem(obj: m_nameIdentifiers);
489 }
490 if (name == Fields::defaultPropertyName) {
491 return self.subDataItem(c: PathEls::Field(Fields::defaultPropertyName),
492 value: defaultPropertyName(self));
493 }
494 static QStringList knownLookups({ QString::fromUtf16(Fields::fileLocationsTree) });
495 if (!knownLookups.contains(str: name)) {
496 qCWarning(domLog()) << "Asked non existing field " << name << " in QmlObject "
497 << pathFromOwner();
498 }
499 return DomItem();
500}
501
502void QmlObject::updatePathFromOwner(const Path &newPath)
503{
504 DomElement::updatePathFromOwner(newPath);
505 updatePathFromOwnerMultiMap(mmap&: m_propertyDefs, newPath: newPath.field(name: Fields::propertyDefs));
506 updatePathFromOwnerMultiMap(mmap&: m_bindings, newPath: newPath.field(name: Fields::bindings));
507 updatePathFromOwnerMultiMap(mmap&: m_methods, newPath: newPath.field(name: Fields::methods));
508 updatePathFromOwnerQList(list&: m_children, newPath: newPath.field(name: Fields::children));
509 updatePathFromOwnerQList(list&: m_annotations, newPath: newPath.field(name: Fields::annotations));
510}
511
512QString QmlObject::localDefaultPropertyName() const
513{
514 if (!m_defaultPropertyName.isEmpty())
515 return m_defaultPropertyName;
516 for (const PropertyDefinition &pDef : m_propertyDefs)
517 if (pDef.isDefaultMember)
518 return pDef.name;
519 return QString();
520}
521
522QString QmlObject::defaultPropertyName(const DomItem &self) const
523{
524 QString dProp = localDefaultPropertyName();
525 if (!dProp.isEmpty())
526 return dProp;
527 QString res = QStringLiteral(u"data");
528 self.visitPrototypeChain(
529 visitor: [&res](const DomItem &obj) {
530 if (const QmlObject *objPtr = obj.as<QmlObject>()) {
531 QString dProp = objPtr->localDefaultPropertyName();
532 if (!dProp.isEmpty()) {
533 res = dProp;
534 return false;
535 }
536 }
537 return true;
538 },
539 options: VisitPrototypesOption::SkipFirst);
540 return res;
541}
542
543bool QmlObject::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &)> visitor) const
544{
545 bool cont = self.field(name: Fields::bindings).visitKeys(visitor: [visitor](const QString &, const DomItem &bs) {
546 return bs.visitIndexes(visitor: [visitor](const DomItem &b) {
547 DomItem v = b.field(name: Fields::value);
548 if (std::shared_ptr<ScriptExpression> vPtr = v.ownerAs<ScriptExpression>()) {
549 if (!visitor(v))
550 return false;
551 return v.iterateSubOwners(visitor); // currently not needed, avoid?
552 }
553 return true;
554 });
555 });
556 cont = cont && self.field(name: Fields::children).visitIndexes(visitor: [visitor](const DomItem &qmlObj) {
557 if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>()) {
558 return qmlObjPtr->iterateSubOwners(self: qmlObj, visitor);
559 }
560 Q_ASSERT(false);
561 return true;
562 });
563 return cont;
564}
565
566static QStringList dotExpressionToList(const std::shared_ptr<ScriptExpression> &expr)
567{
568 QStringList res;
569 AST::Node *node = (expr ? expr->ast() : nullptr);
570 while (node) {
571 switch (node->kind) {
572 case AST::Node::Kind_IdentifierExpression: {
573 AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast: node);
574 res.prepend(t: id->name.toString());
575 return res;
576 }
577 case AST::Node::Kind_FieldMemberExpression: {
578 AST::FieldMemberExpression *id = AST::cast<AST::FieldMemberExpression *>(ast: node);
579 res.prepend(t: id->name.toString());
580 node = id->base;
581 break;
582 }
583 default:
584 qCDebug(writeOutLog).noquote() << "Could not convert dot expression to list for:\n"
585 << expr->astRelocatableDump();
586 return QStringList();
587 }
588 }
589 return res;
590}
591
592LocallyResolvedAlias QmlObject::resolveAlias(const DomItem &self,
593 std::shared_ptr<ScriptExpression> accessSequence) const
594{
595 QStringList accessSequenceList = dotExpressionToList(expr: accessSequence);
596 return resolveAlias(self, accessSequence: accessSequenceList);
597}
598
599LocallyResolvedAlias QmlObject::resolveAlias(const DomItem &self, const QStringList &accessSequence) const
600{
601 LocallyResolvedAlias res;
602 QSet<QString> visitedAlias;
603 if (accessSequence.isEmpty()) {
604 return res;
605 } else if (accessSequence.size() > 3) {
606 res.status = LocallyResolvedAlias::Status::TooDeep;
607 return res;
608 }
609 QString idName = accessSequence.first();
610 DomItem idTarget = self.component()
611 .field(name: Fields::ids)
612 .key(name: idName)
613 .index(0)
614 .field(name: Fields::referredObject)
615 .get();
616 if (!idTarget)
617 return res;
618 res.baseObject = idTarget;
619 res.accessedPath = accessSequence.mid(pos: 1);
620 res.typeName = idTarget.name();
621 res.status = LocallyResolvedAlias::Status::ResolvedObject;
622 // check if it refers to locally defined props/objs
623 while (!res.accessedPath.isEmpty()) {
624 QString pNow = res.accessedPath.first();
625 DomItem defNow = res.baseObject.propertyDefs().key(name: pNow).index(0);
626 if (const PropertyDefinition *defNowPtr = defNow.as<PropertyDefinition>()) {
627 if (defNowPtr->isAlias()) {
628 res.typeName = QString();
629 ++res.nAliases;
630 QString aliasPath = defNow.canonicalPath().toString();
631 if (visitedAlias.contains(value: aliasPath)) {
632 res.status = LocallyResolvedAlias::Status::Loop;
633 return res;
634 }
635 visitedAlias.insert(value: aliasPath);
636 DomItem valNow = res.baseObject.bindings().key(name: pNow).index(0);
637 if (std::shared_ptr<ScriptExpression> exp =
638 valNow.field(name: Fields::value).ownerAs<ScriptExpression>()) {
639 QStringList expList = dotExpressionToList(expr: exp);
640 if (expList.isEmpty()) {
641 res.status = LocallyResolvedAlias::Status::Invalid;
642 return res;
643 } else if (expList.size() > 3) {
644 res.status = LocallyResolvedAlias::Status::TooDeep;
645 return res;
646 }
647 idName = expList.first();
648 idTarget = self.component()
649 .field(name: Fields::ids)
650 .key(name: idName)
651 .index(0)
652 .field(name: Fields::referredObject)
653 .get();
654 res.baseObject = idTarget;
655 res.accessedPath = expList.mid(pos: 1) + res.accessedPath.mid(pos: 1);
656 if (!idTarget) {
657 res.status = LocallyResolvedAlias::Status::Invalid;
658 return res;
659 }
660 res.status = LocallyResolvedAlias::Status::ResolvedObject;
661 res.typeName = idTarget.name();
662 } else {
663 res.status = LocallyResolvedAlias::Status::Invalid;
664 return res;
665 }
666 } else {
667 res.localPropertyDef = defNow;
668 res.typeName = defNowPtr->typeName;
669 res.accessedPath = res.accessedPath.mid(pos: 1);
670 DomItem valNow = res.baseObject.bindings().key(name: pNow).index(0).field(name: Fields::value);
671 if (valNow.internalKind() == DomType::QmlObject) {
672 res.baseObject = valNow;
673 res.typeName = valNow.name();
674 res.status = LocallyResolvedAlias::Status::ResolvedObject;
675 } else {
676 res.status = LocallyResolvedAlias::Status::ResolvedProperty;
677 return res;
678 }
679 }
680 } else {
681 return res;
682 }
683 }
684 return res;
685}
686
687MutableDomItem QmlObject::addPropertyDef(
688 MutableDomItem &self, const PropertyDefinition &propertyDef, AddOption option)
689{
690 Path p = addPropertyDef(propertyDef, option);
691 if (p.last().headIndex(defaultValue: 0) > 1)
692 self.owningItemPtr()->addErrorLocal(msg: domParsingErrors().error(
693 message: tr(sourceText: "Repeated PropertyDefinition with name %1").arg(a: propertyDef.name)));
694 return self.owner().path(p);
695}
696
697MutableDomItem QmlObject::addBinding(MutableDomItem &self, Binding binding, AddOption option)
698{
699 Path p = addBinding(binding, option);
700 if (p && p.last().headIndex(defaultValue: 0) > 1)
701 self.owningItemPtr()->addErrorLocal(
702 msg: domParsingErrors().error(message: tr(sourceText: "Repeated binding with name %1").arg(a: binding.name())));
703 return self.owner().path(p);
704}
705
706MutableDomItem QmlObject::addMethod(
707 MutableDomItem &self, const MethodInfo &functionDef, AddOption option)
708{
709 Path p = addMethod(functionDef, option);
710 if (p.last().headIndex(defaultValue: 0) > 1)
711 self.owningItemPtr()->addErrorLocal(
712 msg: domParsingErrors().error(message: tr(sourceText: "Repeated Method with name %1").arg(a: functionDef.name)));
713 return self.owner().path(p);
714}
715
716void QmlObject::writeOut(const DomItem &self, OutWriter &ow, const QString &onTarget) const
717{
718 const quint32 posOfNewElements = std::numeric_limits<quint32>::max();
719 bool isRootObject = pathFromOwner().length() == 5
720 && pathFromOwner()[0] == Path::Field(s: Fields::components)
721 && pathFromOwner()[3] == Path::Field(s: Fields::objects);
722 QString code;
723 DomItem owner = self.owner();
724 if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
725 code = qmlFilePtr->code();
726 ow.writeRegion(region: IdentifierRegion, toWrite: name());
727 if (!onTarget.isEmpty()) {
728 ow.ensureSpace().writeRegion(region: OnTokenRegion).ensureSpace().writeRegion(region: OnTargetRegion,
729 toWrite: onTarget);
730 }
731 ow.writeRegion(region: LeftBraceRegion, toWrite: u" {");
732 int baseIndent = ow.increaseIndent();
733 int spacerId = 0;
734 if (!idStr().isEmpty()) { // *always* put id first
735 DomItem myId = self.component().field(name: Fields::ids).key(name: idStr()).index(0);
736 if (myId)
737 myId.writeOutPre(lw&: ow);
738 ow.ensureNewline()
739 .writeRegion(region: IdTokenRegion)
740 .writeRegion(region: IdColonTokenRegion)
741 .ensureSpace()
742 .writeRegion(region: IdNameRegion, toWrite: idStr());
743 if (ow.lineWriter.options().attributesSequence
744 == LineWriterOptions::AttributesSequence::Normalize) {
745 ow.ensureNewline(nNewlines: 2);
746 }
747 if (myId) {
748 myId.writeOutPost(lw&: ow);
749 ow.ensureNewline(nNewlines: 1);
750 }
751 }
752 quint32 counter = ow.counter();
753 DomItem component;
754 if (isRootObject)
755 component = self.containingObject();
756 auto startLoc = [&](const FileLocations::Tree &l) {
757 if (l)
758 return l->info().fullRegion;
759 return SourceLocation(posOfNewElements, 0, 0, 0);
760 };
761 if (ow.lineWriter.options().attributesSequence
762 == LineWriterOptions::AttributesSequence::Preserve) {
763 QList<QPair<SourceLocation, DomItem>> attribs;
764 AttachedInfoLookupResult<FileLocations::Tree> objLoc =
765 FileLocations::findAttachedInfo(item: self);
766 FileLocations::Tree componentLoc;
767 if (isRootObject && objLoc.foundTree)
768 componentLoc = objLoc.foundTree->parent()->parent();
769 auto addMMap
770 = [&attribs, &startLoc](const DomItem &base, const FileLocations::Tree &baseLoc) {
771 if (!base)
772 return;
773 const auto values = base.values();
774 for (const auto &els : values) {
775 FileLocations::Tree elsLoc =
776 FileLocations::find(self: baseLoc, p: els.pathFromOwner().last());
777 const auto elsValues = els.values();
778 for (const auto &el : elsValues) {
779 FileLocations::Tree elLoc =
780 FileLocations::find(self: elsLoc, p: el.pathFromOwner().last());
781 attribs.append(t: std::make_pair(x: startLoc(elLoc), y: el));
782 }
783 }
784 };
785 auto addMyMMap = [this, &objLoc, &self, &addMMap](QStringView fieldName) {
786 DomItem base = this->field(self, name: fieldName);
787 addMMap(base, FileLocations::find(self: objLoc.foundTree, p: base.pathFromOwner().last()));
788 };
789 auto addSingleLevel
790 = [&attribs, &startLoc](const DomItem &base, const FileLocations::Tree &baseLoc) {
791 if (!base)
792 return;
793 const auto baseValues = base.values();
794 for (const auto &el : baseValues) {
795 FileLocations::Tree elLoc = FileLocations::find(self: baseLoc, p: el.pathFromOwner().last());
796 attribs.append(t: std::make_pair(x: startLoc(elLoc), y: el));
797 }
798 };
799 if (isRootObject) {
800 DomItem enums = component.field(name: Fields::enumerations);
801 addMMap(enums, FileLocations::find(self: componentLoc, p: enums.pathFromOwner().last()));
802 }
803 addMyMMap(Fields::propertyDefs);
804 addMyMMap(Fields::bindings);
805 addMyMMap(Fields::methods);
806 DomItem children = field(self, name: Fields::children);
807 addSingleLevel(children,
808 FileLocations::find(self: objLoc.foundTree, p: children.pathFromOwner().last()));
809 if (isRootObject) {
810 DomItem subCs = component.field(name: Fields::subComponents);
811 for (const DomItem &c : subCs.values()) {
812 AttachedInfoLookupResult<FileLocations::Tree> subLoc =
813 FileLocations::findAttachedInfo(item: c);
814 Q_ASSERT(subLoc.foundTree);
815 attribs.append(t: std::make_pair(x: startLoc(subLoc.foundTree), y: c));
816 }
817 }
818 std::stable_sort(first: attribs.begin(), last: attribs.end(),
819 comp: [](const std::pair<SourceLocation, DomItem> &el1,
820 const std::pair<SourceLocation, DomItem> &el2) {
821 if (el1.first.offset < el2.first.offset)
822 return true;
823 if (el1.first.offset > el2.first.offset)
824 return false;
825 int i = int(el1.second.internalKind())
826 - int(el2.second.internalKind());
827 return i < 0;
828 });
829 qsizetype iAttr = 0;
830 while (iAttr != attribs.size()) {
831 auto &el = attribs[iAttr++];
832 // check for an empty line before the current element, and preserve it
833 int preNewlines = 0;
834 quint32 start = el.first.offset;
835 if (start != posOfNewElements && size_t(code.size()) >= start) {
836 while (start != 0) {
837 QChar c = code.at(i: --start);
838 if (c == u'\n') {
839 if (++preNewlines == 2)
840 break;
841 } else if (!c.isSpace())
842 break;
843 }
844 }
845 if (preNewlines == 0)
846 ++preNewlines;
847 ow.ensureNewline(nNewlines: preNewlines);
848 if (el.second.internalKind() == DomType::PropertyDefinition && iAttr != attribs.size()
849 && el.first.offset != ~quint32(0)) {
850 DomItem b;
851 auto &bPair = attribs[iAttr];
852 if (bPair.second.internalKind() == DomType::Binding
853 && bPair.first.begin() < el.first.end()
854 && bPair.second.name() == el.second.name()) {
855 b = bPair.second;
856 ++iAttr;
857 b.writeOutPre(lw&: ow);
858 }
859 el.second.writeOut(lw&: ow);
860 if (b) {
861 ow.write(v: u": ");
862 if (const Binding *bPtr = b.as<Binding>())
863 bPtr->writeOutValue(self: b, lw&: ow);
864 else {
865 qWarning() << "Internal error casting binding to Binding in"
866 << b.canonicalPath();
867 ow.writeRegion(region: LeftBraceRegion).writeRegion(region: RightBraceRegion);
868 }
869 b.writeOutPost(lw&: ow);
870 }
871 } else {
872 el.second.writeOut(lw&: ow);
873 }
874 ow.ensureNewline();
875 }
876 ow.decreaseIndent(level: 1, expectedIndent: baseIndent);
877 ow.writeRegion(region: RightBraceRegion);
878
879 return;
880 }
881 DomItem bindings = field(self, name: Fields::bindings);
882 DomItem propertyDefs = field(self, name: Fields::propertyDefs);
883
884 if (isRootObject) {
885 const auto descs = component.field(name: Fields::enumerations).values();
886 for (const auto &enumDescs : descs) {
887 const auto values = enumDescs.values();
888 for (const auto &enumDesc : values) {
889 ow.ensureNewline(nNewlines: 1);
890 enumDesc.writeOut(lw&: ow);
891 ow.ensureNewline(nNewlines: 1);
892 }
893 }
894 }
895 if (counter != ow.counter() || !idStr().isEmpty())
896 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
897 QSet<QString> mergedDefBinding;
898 for (const QString &defName : propertyDefs.sortedKeys()) {
899 const auto pDefs = propertyDefs.key(name: defName).values();
900 for (const auto &pDef : pDefs) {
901 const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>();
902 Q_ASSERT(pDefPtr);
903 DomItem b;
904 bool uniqueDeclarationWithThisName = pDefs.size() == 1;
905 if (uniqueDeclarationWithThisName && !pDefPtr->isRequired)
906 bindings.key(name: pDef.name()).visitIndexes(visitor: [&b, pDefPtr](const DomItem &el) {
907 const Binding *elPtr = el.as<Binding>();
908 if (elPtr && elPtr->bindingType() == BindingType::Normal) {
909 switch (elPtr->valueKind()) {
910 case BindingValueKind::ScriptExpression:
911 b = el;
912 break;
913 case BindingValueKind::Array:
914 if (!pDefPtr->isDefaultMember
915 && pDefPtr->isParametricType())
916 b = el;
917 break;
918 case BindingValueKind::Object:
919 if (!pDefPtr->isDefaultMember
920 && !pDefPtr->isParametricType())
921 b = el;
922 break;
923 case BindingValueKind::Empty:
924 break;
925 }
926 return false;
927 }
928 return true;
929 });
930 if (b) {
931 mergedDefBinding.insert(value: defName);
932 b.writeOutPre(lw&: ow);
933 }
934 pDef.writeOut(lw&: ow);
935 if (b) {
936 ow.write(v: u":");
937 ow.ensureSpace();
938 if (const Binding *bPtr = b.as<Binding>())
939 bPtr->writeOutValue(self: b, lw&: ow);
940 else {
941 qWarning() << "Internal error casting binding to Binding in"
942 << b.canonicalPath();
943 ow.writeRegion(region: LeftBraceRegion).writeRegion(region: RightBraceRegion);
944 }
945 b.writeOutPost(lw&: ow);
946 }
947 }
948 }
949 ow.removeTextAddCallback(i: spacerId);
950 QList<DomItem> signalList, methodList;
951 const auto fields = field(self, name: Fields::methods).values();
952 for (const auto &ms : fields) {
953 const auto values = ms.values();
954 for (const auto &m : values) {
955 const MethodInfo *mPtr = m.as<MethodInfo>();
956 if (mPtr && mPtr->methodType == MethodInfo::MethodType::Signal)
957 signalList.append(t: m);
958 else
959 methodList.append(t: m);
960 }
961 }
962 if (counter != ow.counter())
963 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
964 for (const auto &sig : std::as_const(t&: signalList)) {
965 ow.ensureNewline();
966 sig.writeOut(lw&: ow);
967 ow.ensureNewline();
968 }
969 ow.removeTextAddCallback(i: spacerId);
970 if (counter != ow.counter())
971 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
972 bool first = true;
973 for (const auto &method : std::as_const(t&: methodList)) {
974 if (!first && ow.lineWriter.options().functionsSpacing) {
975 ow.newline();
976 }
977 ow.ensureNewline();
978 first = false;
979 method.writeOut(lw&: ow);
980 ow.ensureNewline();
981 }
982 ow.removeTextAddCallback(i: spacerId);
983 QList<DomItem> normalBindings, signalHandlers, delayedBindings;
984 for (const auto &bName : bindings.sortedKeys()) {
985 bool skipFirstNormal = mergedDefBinding.contains(value: bName);
986 const auto values = bindings.key(name: bName).values();
987 for (const auto &b : values) {
988 const Binding *bPtr = b.as<Binding>();
989 if (skipFirstNormal) {
990 if (bPtr && bPtr->bindingType() == BindingType::Normal) {
991 skipFirstNormal = false;
992 continue;
993 }
994 }
995 if (bPtr->valueKind() == BindingValueKind::Array
996 || bPtr->valueKind() == BindingValueKind::Object)
997 delayedBindings.append(t: b);
998 else if (b.field(name: Fields::isSignalHandler).value().toBool(defaultValue: false))
999 signalHandlers.append(t: b);
1000 else
1001 normalBindings.append(t: b);
1002 }
1003 }
1004 if (counter != ow.counter())
1005 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1006 for (const auto &b : std::as_const(t&: normalBindings))
1007 b.writeOut(lw&: ow);
1008 ow.removeTextAddCallback(i: spacerId);
1009 if (counter != ow.counter())
1010 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1011 for (const auto &b : std::as_const(t&: delayedBindings))
1012 b.writeOut(lw&: ow);
1013 ow.removeTextAddCallback(i: spacerId);
1014 if (counter != ow.counter())
1015 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1016 for (const auto &b : std::as_const(t&: signalHandlers))
1017 b.writeOut(lw&: ow);
1018 ow.removeTextAddCallback(i: spacerId);
1019 if (counter != ow.counter())
1020 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1021 first = true;
1022 const auto values = field(self, name: Fields::children).values();
1023 for (const auto &c : values) {
1024 if (!first && ow.lineWriter.options().objectsSpacing) {
1025 ow.newline().newline();
1026 }
1027 first = false;
1028 ow.ensureNewline();
1029 c.writeOut(lw&: ow);
1030 }
1031 ow.removeTextAddCallback(i: spacerId);
1032 if (isRootObject) {
1033 // we are a root object, possibly add components
1034 DomItem subComps = component.field(name: Fields::subComponents);
1035 if (counter != ow.counter())
1036 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1037 const auto values = subComps.values();
1038 for (const auto &subC : values) {
1039 ow.ensureNewline();
1040 subC.writeOut(lw&: ow);
1041 }
1042 ow.removeTextAddCallback(i: spacerId);
1043 }
1044
1045 ow.decreaseIndent(level: 1, expectedIndent: baseIndent);
1046 ow.ensureNewline().writeRegion(region: RightBraceRegion);
1047}
1048
1049Binding::Binding(const QString &name) : Binding(name, std::unique_ptr<BindingValue>()) { }
1050
1051Binding::Binding(const QString &name, std::unique_ptr<BindingValue> value, BindingType bindingType)
1052 : m_bindingType(bindingType), m_name(name), m_value(std::move(value))
1053{
1054}
1055
1056Binding::Binding(
1057 const QString &name, const std::shared_ptr<ScriptExpression> &value,
1058 BindingType bindingType)
1059 : Binding(name, std::make_unique<BindingValue>(args: value), bindingType)
1060{
1061}
1062
1063Binding::Binding(const QString &name, const QString &scriptCode, BindingType bindingType)
1064 : Binding(name,
1065 std::make_unique<BindingValue>(args: std::make_shared<ScriptExpression>(
1066 args: scriptCode, args: ScriptExpression::ExpressionType::BindingExpression, args: 0,
1067 args: Binding::preCodeForName(n: name), args: Binding::postCodeForName(name))),
1068 bindingType)
1069{
1070}
1071
1072Binding::Binding(const QString &name, const QmlObject &value, BindingType bindingType)
1073 : Binding(name, std::make_unique<BindingValue>(args: value), bindingType)
1074{
1075}
1076
1077Binding::Binding(const QString &name, const QList<QmlObject> &value, BindingType bindingType)
1078 : Binding(name, std::make_unique<BindingValue>(args: value), bindingType)
1079{
1080}
1081
1082Binding::Binding(const Binding &o)
1083 : m_bindingType(o.m_bindingType),
1084 m_name(o.m_name),
1085 m_annotations(o.m_annotations),
1086 m_comments(o.m_comments),
1087 m_bindingIdentifiers(o.m_bindingIdentifiers)
1088{
1089 if (o.m_value) {
1090 m_value = std::make_unique<BindingValue>(args&: *o.m_value);
1091 }
1092}
1093
1094Binding::~Binding() { }
1095
1096Binding &Binding::operator=(const Binding &o)
1097{
1098 m_name = o.m_name;
1099 m_bindingType = o.m_bindingType;
1100 m_annotations = o.m_annotations;
1101 m_comments = o.m_comments;
1102 m_bindingIdentifiers = o.m_bindingIdentifiers;
1103 if (o.m_value) {
1104 if (!m_value)
1105 m_value = std::make_unique<BindingValue>(args&: *o.m_value);
1106 else
1107 *m_value = *o.m_value;
1108 } else {
1109 m_value.reset();
1110 }
1111 return *this;
1112}
1113
1114bool Binding::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1115{
1116 bool cont = true;
1117 cont = cont && self.dvValueField(visitor, f: Fields::name, value: m_name);
1118 cont = cont && self.dvValueField(visitor, f: Fields::isSignalHandler, value: isSignalHandler());
1119 if (!m_value)
1120 cont = cont && visitor(PathEls::Field(Fields::value), []() { return DomItem(); });
1121 else
1122 cont = cont && self.dvItemField(visitor, f: Fields::value, it: [this, &self]() {
1123 return m_value->value(binding: self);
1124 });
1125 cont = cont && self.dvValueField(visitor, f: Fields::bindingType, value: int(m_bindingType));
1126 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: m_comments);
1127 cont = cont && self.dvValueLazyField(visitor, f: Fields::preCode, valueF: [this]() {
1128 return this->preCode();
1129 });
1130 cont = cont && self.dvValueLazyField(visitor, f: Fields::postCode, valueF: [this]() {
1131 return this->postCode();
1132 });
1133 if (m_bindingIdentifiers) {
1134 cont = cont && self.dvItemField(visitor, f: Fields::bindingIdentifiers, it: [this, &self]() {
1135 return self.subScriptElementWrapperItem(obj: m_bindingIdentifiers);
1136 });
1137 }
1138 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: m_annotations);
1139 return cont;
1140}
1141
1142DomItem Binding::valueItem(const DomItem &self) const
1143{
1144 if (!m_value)
1145 return DomItem();
1146 return m_value->value(binding: self);
1147}
1148
1149BindingValueKind Binding::valueKind() const
1150{
1151 if (!m_value)
1152 return BindingValueKind::Empty;
1153 return m_value->kind;
1154}
1155
1156QmlObject const *Binding::objectValue() const
1157{
1158 if (valueKind() == BindingValueKind::Object)
1159 return &(m_value->object);
1160 return nullptr;
1161}
1162
1163QmlObject *Binding::objectValue()
1164{
1165 if (valueKind() == BindingValueKind::Object)
1166 return &(m_value->object);
1167 return nullptr;
1168}
1169
1170QList<QmlObject> const *Binding::arrayValue() const
1171{
1172 if (valueKind() == BindingValueKind::Array)
1173 return &(m_value->array);
1174 return nullptr;
1175}
1176
1177QList<QmlObject> *Binding::arrayValue()
1178{
1179 if (valueKind() == BindingValueKind::Array)
1180 return &(m_value->array);
1181 return nullptr;
1182}
1183
1184std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue() const
1185{
1186 if (valueKind() == BindingValueKind::ScriptExpression)
1187 return m_value->scriptExpression;
1188 return nullptr;
1189}
1190
1191std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue()
1192{
1193 if (valueKind() == BindingValueKind::ScriptExpression)
1194 return m_value->scriptExpression;
1195 return nullptr;
1196}
1197
1198void Binding::setValue(std::unique_ptr<BindingValue> &&value)
1199{
1200 m_value = std::move(value);
1201}
1202
1203Path Binding::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
1204{
1205 return appendUpdatableElementInQList(listPathFromOwner: selfPathFromOwner.field(name: Fields::annotations),
1206 list&: m_annotations, value: annotation, vPtr: aPtr);
1207}
1208
1209void Binding::updatePathFromOwner(const Path &newPath)
1210{
1211 Path base = newPath.field(name: Fields::annotations);
1212 if (m_value)
1213 m_value->updatePathFromOwner(newPath: newPath.field(name: Fields::value));
1214 updatePathFromOwnerQList(list&: m_annotations, newPath: newPath.field(name: Fields::annotations));
1215}
1216
1217void Binding::writeOut(const DomItem &self, OutWriter &lw) const
1218{
1219 lw.ensureNewline();
1220 if (m_bindingType == BindingType::Normal) {
1221 lw.writeRegion(region: IdentifierRegion, toWrite: name());
1222 lw.writeRegion(region: ColonTokenRegion).ensureSpace();
1223 writeOutValue(self, lw);
1224 } else {
1225 DomItem v = valueItem(self);
1226 if (const QmlObject *vPtr = v.as<QmlObject>()) {
1227 v.writeOutPre(lw);
1228 vPtr->writeOut(self: v, ow&: lw, onTarget: name());
1229 v.writeOutPost(lw);
1230 } else {
1231 qCWarning(writeOutLog()) << "On Binding requires an QmlObject Value, not "
1232 << v.internalKindStr() << " at " << self.canonicalPath();
1233 }
1234 }
1235}
1236
1237void Binding::writeOutValue(const DomItem &self, OutWriter &lw) const
1238{
1239 DomItem v = valueItem(self);
1240 switch (valueKind()) {
1241 case BindingValueKind::Empty:
1242 qCWarning(writeOutLog()) << "Writing of empty binding " << name();
1243 lw.write(v: u"{}");
1244 break;
1245 case BindingValueKind::Array:
1246 if (const List *vPtr = v.as<List>()) {
1247 v.writeOutPre(lw);
1248 vPtr->writeOut(self: v, ow&: lw, compact: false);
1249 v.writeOutPost(lw);
1250 }
1251 break;
1252 case BindingValueKind::Object:
1253 case BindingValueKind::ScriptExpression:
1254 v.writeOut(lw);
1255 break;
1256 }
1257}
1258
1259bool QmltypesComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1260{
1261 bool cont = Component::iterateDirectSubpaths(self, visitor);
1262 cont = cont && self.dvWrapField(visitor, f: Fields::exports, obj: m_exports);
1263 cont = cont && self.dvValueField(visitor, f: Fields::metaRevisions, value: m_metaRevisions);
1264 if (!fileName().isEmpty())
1265 cont = cont && self.dvValueField(visitor, f: Fields::fileName, value: fileName()); // remove?
1266 cont = cont && self.dvValueField(visitor, f: Fields::interfaceNames, value: m_interfaceNames);
1267 cont = cont && self.dvValueField(visitor, f: Fields::hasCustomParser, value: m_hasCustomParser);
1268 cont = cont && self.dvValueField(visitor, f: Fields::valueTypeName, value: m_valueTypeName);
1269 cont = cont && self.dvValueField(visitor, f: Fields::extensionTypeName, value: m_extensionTypeName);
1270 cont = cont && self.dvValueField(visitor, f: Fields::accessSemantics, value: int(m_accessSemantics));
1271 return cont;
1272}
1273
1274Export Export::fromString(
1275 const Path &source, QStringView exp, const Path &typePath, const ErrorHandler &h)
1276{
1277 Export res;
1278 res.exportSourcePath = source;
1279 res.typePath = typePath;
1280 int slashIdx = exp.indexOf(c: QLatin1Char('/'));
1281 int spaceIdx = exp.indexOf(c: QLatin1Char(' '));
1282 if (spaceIdx == -1)
1283 spaceIdx = exp.size();
1284 else
1285 res.version = Version::fromString(v: exp.mid(pos: spaceIdx + 1));
1286 if (!res.version.isValid())
1287 domParsingErrors()
1288 .error(message: tr(sourceText: "Expected string literal to contain 'Package/Name major.minor' "
1289 "or 'Name major.minor' not '%1'.")
1290 .arg(a: exp))
1291 .handle(errorHandler: h);
1292 if (slashIdx != -1)
1293 res.uri = exp.left(n: slashIdx).toString();
1294 res.typeName = exp.mid(pos: slashIdx + 1, n: spaceIdx - (slashIdx + 1)).toString();
1295 return res;
1296}
1297
1298bool AttributeInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1299{
1300 bool cont = true;
1301 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name);
1302 cont = cont && self.dvValueField(visitor, f: Fields::access, value: int(access));
1303 cont = cont && self.dvValueField(visitor, f: Fields::typeName, value: typeName);
1304 cont = cont && self.dvValueField(visitor, f: Fields::isReadonly, value: isReadonly);
1305 cont = cont && self.dvValueField(visitor, f: Fields::isList, value: isList);
1306 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: comments);
1307 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: annotations);
1308 return cont;
1309}
1310
1311Path AttributeInfo::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation,
1312 QmlObject **aPtr)
1313{
1314 return appendUpdatableElementInQList(listPathFromOwner: selfPathFromOwner.field(name: Fields::annotations), list&: annotations,
1315 value: annotation, vPtr: aPtr);
1316}
1317
1318void AttributeInfo::updatePathFromOwner(const Path &newPath)
1319{
1320 Path base = newPath.field(name: Fields::annotations);
1321 updatePathFromOwnerQList(list&: annotations, newPath: newPath.field(name: Fields::annotations));
1322}
1323
1324bool EnumDecl::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1325{
1326 bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
1327 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name());
1328 cont = cont && self.dvWrapField(visitor, f: Fields::values, obj: m_values);
1329 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: m_annotations);
1330 return cont;
1331}
1332
1333void EnumDecl::updatePathFromOwner(const Path &newPath)
1334{
1335 DomElement::updatePathFromOwner(newPath);
1336 updatePathFromOwnerQList(list&: m_annotations, newPath: newPath.field(name: Fields::annotations));
1337}
1338
1339void EnumDecl::setAnnotations(const QList<QmlObject> &annotations)
1340{
1341 m_annotations = annotations;
1342}
1343
1344Path EnumDecl::addAnnotation(const QmlObject &annotation, QmlObject **aPtr)
1345{
1346 return appendUpdatableElementInQList(listPathFromOwner: pathFromOwner().field(name: Fields::annotations), list&: m_annotations,
1347 value: annotation, vPtr: aPtr);
1348}
1349
1350void EnumDecl::writeOut(const DomItem &self, OutWriter &ow) const
1351{
1352 ow.writeRegion(region: EnumKeywordRegion)
1353 .ensureSpace()
1354 .writeRegion(region: IdentifierRegion, toWrite: name())
1355 .ensureSpace()
1356 .writeRegion(region: LeftBraceRegion);
1357 int iLevel = ow.increaseIndent(level: 1);
1358 const auto values = self.field(name: Fields::values).values();
1359 for (const auto &value : values) {
1360 ow.ensureNewline();
1361 value.writeOut(lw&: ow);
1362 }
1363 ow.decreaseIndent(level: 1, expectedIndent: iLevel);
1364 ow.ensureNewline().writeRegion(region: RightBraceRegion);
1365}
1366
1367QList<Path> ImportScope::allSources(const DomItem &self) const
1368{
1369 DomItem top = self.top();
1370 DomItem env = top.environment();
1371 Path selfPath = self.canonicalPath().field(name: Fields::allSources);
1372 RefCacheEntry cached = (env ? RefCacheEntry::forPath(el: env, canonicalPath: selfPath) : RefCacheEntry());
1373 if (cached.cached == RefCacheEntry::Cached::All)
1374 return cached.canonicalPaths;
1375 QList<Path> res;
1376 QSet<Path> knownPaths;
1377 QList<Path> toDo(m_importSourcePaths.rbegin(), m_importSourcePaths.rend());
1378 while (!toDo.isEmpty()) {
1379 Path pNow = toDo.takeLast();
1380 if (knownPaths.contains(value: pNow))
1381 continue;
1382 knownPaths.insert(value: pNow);
1383 res.append(t: pNow);
1384 DomItem sourceBase = top.path(p: pNow);
1385 for (const DomItem &autoExp : sourceBase.field(name: Fields::autoExports).values()) {
1386 if (const ModuleAutoExport *autoExpPtr = autoExp.as<ModuleAutoExport>()) {
1387 Path newSource;
1388 if (autoExpPtr->inheritVersion) {
1389 Version v = autoExpPtr->import.version;
1390 DomItem sourceVersion = sourceBase.field(name: Fields::version);
1391 if (const Version *sourceVersionPtr = sourceVersion.as<Version>()) {
1392 if (v.majorVersion < 0)
1393 v.majorVersion = sourceVersionPtr->majorVersion;
1394 if (v.minorVersion < 0)
1395 v.minorVersion = sourceVersionPtr->minorVersion;
1396 } else {
1397 qWarning() << "autoExport with inherited version " << autoExp
1398 << " but missing version in source" << pNow;
1399 }
1400 Import toImport(autoExpPtr->import.uri, v);
1401 newSource = toImport.importedPath();
1402 } else {
1403 newSource = autoExpPtr->import.importedPath();
1404 }
1405 if (newSource && !knownPaths.contains(value: newSource))
1406 toDo.append(t: newSource);
1407 } else {
1408 qWarning() << "expected ModuleAutoExport not " << autoExp.internalKindStr()
1409 << "looking up autoExports of" << sourceBase;
1410 Q_ASSERT(false);
1411 }
1412 }
1413 }
1414 if (env)
1415 RefCacheEntry::addForPath(el: env, canonicalPath: selfPath, entry: RefCacheEntry { .cached: RefCacheEntry::Cached::All, .canonicalPaths: res });
1416 return res;
1417}
1418
1419bool ImportScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1420{
1421 bool cont = true;
1422 cont = cont && self.dvReferencesField(visitor, f: Fields::importSources, paths: m_importSourcePaths);
1423 cont = cont && self.dvItemField(visitor, f: Fields::allSources, it: [this, &self]() -> DomItem {
1424 return self.subListItem(list: List::fromQList<Path>(
1425 pathFromOwner: self.pathFromOwner().field(name: Fields::allSources), list: allSources(self),
1426 elWrapper: [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
1427 return list.subDataItem(c: p, value: el.toString());
1428 }));
1429 });
1430 cont = cont && self.dvWrapField(visitor, f: Fields::qualifiedImports, obj: m_subImports);
1431 cont = cont && self.dvItemField(visitor, f: Fields::imported, it: [this, &self]() -> DomItem {
1432 return self.subMapItem(map: Map(
1433 self.pathFromOwner().field(name: Fields::imported),
1434 [this, &self](const DomItem &map, const QString &key) {
1435 return map.subListItem(list: List::fromQList<DomItem>(
1436 pathFromOwner: map.pathFromOwner().key(name: key), list: importedItemsWithName(self, name: key),
1437 elWrapper: [](const DomItem &, const PathEls::PathComponent &, const DomItem &el) {
1438 return el;
1439 }));
1440 },
1441 [this, &self](const DomItem &) { return this->importedNames(self); },
1442 QLatin1String("List<Export>")));
1443 });
1444 return cont;
1445}
1446
1447bool PropertyInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1448{
1449 bool cont = true;
1450 cont = cont && self.dvValueField(visitor, f: Fields::propertyDefs, value: propertyDefs);
1451 cont = cont && self.dvValueField(visitor, f: Fields::bindings, value: bindings);
1452 return cont;
1453}
1454
1455BindingValue::BindingValue() : kind(BindingValueKind::Empty) { }
1456
1457BindingValue::BindingValue(const QmlObject &o) : kind(BindingValueKind::Object)
1458{
1459 new (&object) QmlObject(o);
1460}
1461
1462BindingValue::BindingValue(const std::shared_ptr<ScriptExpression> &o)
1463 : kind(BindingValueKind::ScriptExpression)
1464{
1465 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o);
1466}
1467
1468BindingValue::BindingValue(const QList<QmlObject> &l) : kind(BindingValueKind::Array)
1469{
1470 new (&array) QList<QmlObject>(l);
1471}
1472
1473BindingValue::~BindingValue()
1474{
1475 clearValue();
1476}
1477
1478BindingValue::BindingValue(const BindingValue &o) : kind(o.kind)
1479{
1480 switch (o.kind) {
1481 case BindingValueKind::Empty:
1482 break;
1483 case BindingValueKind::Object:
1484 new (&object) QmlObject(o.object);
1485 break;
1486 case BindingValueKind::ScriptExpression:
1487 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
1488 break;
1489 case BindingValueKind::Array:
1490 new (&array) QList<QmlObject>(o.array);
1491 }
1492}
1493
1494BindingValue &BindingValue::operator=(const BindingValue &o)
1495{
1496 clearValue();
1497 kind = o.kind;
1498 switch (o.kind) {
1499 case BindingValueKind::Empty:
1500 break;
1501 case BindingValueKind::Object:
1502 new (&object) QmlObject(o.object);
1503 break;
1504 case BindingValueKind::ScriptExpression:
1505 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
1506 break;
1507 case BindingValueKind::Array:
1508 new (&array) QList<QmlObject>(o.array);
1509 }
1510 return *this;
1511}
1512
1513DomItem BindingValue::value(const DomItem &binding) const
1514{
1515 switch (kind) {
1516 case BindingValueKind::Empty:
1517 break;
1518 case BindingValueKind::Object:
1519 return binding.copy(base: &object);
1520 case BindingValueKind::ScriptExpression:
1521 return binding.subOwnerItem(c: PathEls::Field(Fields::value), o: scriptExpression);
1522 case BindingValueKind::Array:
1523 return binding.subListItem(list: List::fromQListRef<QmlObject>(
1524 pathFromOwner: binding.pathFromOwner().field(name: u"value"), list: array,
1525 elWrapper: [](const DomItem &self, const PathEls::PathComponent &, const QmlObject &obj) {
1526 return self.copy(base: &obj);
1527 }));
1528 }
1529 return DomItem();
1530}
1531
1532void BindingValue::updatePathFromOwner(const Path &newPath)
1533{
1534 switch (kind) {
1535 case BindingValueKind::Empty:
1536 break;
1537 case BindingValueKind::Object:
1538 object.updatePathFromOwner(newPath);
1539 break;
1540 case BindingValueKind::ScriptExpression:
1541 break;
1542 case BindingValueKind::Array:
1543 updatePathFromOwnerQList(list&: array, newPath);
1544 break;
1545 }
1546}
1547
1548void BindingValue::clearValue()
1549{
1550 switch (kind) {
1551 case BindingValueKind::Empty:
1552 break;
1553 case BindingValueKind::Object:
1554 object.~QmlObject();
1555 break;
1556 case BindingValueKind::ScriptExpression:
1557 scriptExpression.~shared_ptr();
1558 break;
1559 case BindingValueKind::Array:
1560 array.~QList<QmlObject>();
1561 break;
1562 }
1563 kind = BindingValueKind::Empty;
1564}
1565
1566ScriptExpression::ScriptExpression(
1567 QStringView code, const std::shared_ptr<QQmlJS::Engine> &engine, AST::Node *ast,
1568 const std::shared_ptr<AstComments> &comments, ExpressionType expressionType,
1569 SourceLocation localOffset, int derivedFrom, QStringView preCode, QStringView postCode)
1570 : OwningItem(derivedFrom),
1571 m_expressionType(expressionType),
1572 m_code(code),
1573 m_preCode(preCode),
1574 m_postCode(postCode),
1575 m_engine(engine),
1576 m_ast(ast),
1577 m_astComments(comments),
1578 m_localOffset(localOffset)
1579{
1580 if (m_expressionType == ExpressionType::BindingExpression)
1581 if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(ast: m_ast))
1582 m_ast = exp->expression;
1583 Q_ASSERT(m_astComments);
1584}
1585
1586ScriptExpression::ScriptExpression(const ScriptExpression &e) : OwningItem(e)
1587{
1588 QMutexLocker l(mutex());
1589 m_expressionType = e.m_expressionType;
1590 m_engine = e.m_engine;
1591 m_ast = e.m_ast;
1592 if (m_codeStr.isEmpty()) {
1593 m_code = e.m_code;
1594 } else {
1595 m_codeStr = e.m_codeStr;
1596 m_code = m_codeStr;
1597 }
1598 m_localOffset = e.m_localOffset;
1599 m_astComments = e.m_astComments;
1600}
1601
1602std::shared_ptr<ScriptExpression> ScriptExpression::copyWithUpdatedCode(
1603 const DomItem &self, const QString &code) const
1604{
1605 std::shared_ptr<ScriptExpression> copy = makeCopy(self);
1606 DomItem container = self.containingObject();
1607 QString preCodeStr = container.field(name: Fields::preCode).value().toString(defaultValue: m_preCode.toString());
1608 QString postCodeStr = container.field(name: Fields::postCode).value().toString(defaultValue: m_postCode.toString());
1609 copy->setCode(code, preCode: preCodeStr, postCode: postCodeStr);
1610 return copy;
1611}
1612
1613bool ScriptExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1614{
1615 bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
1616 cont = cont && self.dvValueField(visitor, f: Fields::code, value: code());
1617 if (!preCode().isEmpty())
1618 cont = cont
1619 && self.dvValueField(visitor, f: Fields::preCode, value: preCode(),
1620 options: ConstantData::Options::MapIsMap);
1621 if (!postCode().isEmpty())
1622 cont = cont
1623 && self.dvValueField(visitor, f: Fields::postCode, value: postCode(),
1624 options: ConstantData::Options::MapIsMap);
1625 cont = cont
1626 && self.dvValueLazyField(
1627 visitor, f: Fields::localOffset,
1628 valueF: [this]() { return sourceLocationToQCborValue(loc: localOffset()); },
1629 options: ConstantData::Options::MapIsMap);
1630 cont = cont && self.dvValueLazyField(visitor, f: Fields::astRelocatableDump, valueF: [this]() {
1631 return astRelocatableDump();
1632 });
1633 cont = cont && self.dvValueField(visitor, f: Fields::expressionType, value: int(expressionType()));
1634 if (m_element) {
1635 cont = cont && self.dvItemField(visitor, f: Fields::scriptElement, it: [this, &self]() {
1636 return self.subScriptElementWrapperItem(obj: m_element);
1637 });
1638 }
1639 return cont;
1640}
1641
1642class FirstNodeVisitor : public VisitAll
1643{
1644public:
1645 quint32 minStart = 0;
1646 quint32 maxEnd = std::numeric_limits<quint32>::max();
1647 AST::Node *firstNodeInRange = nullptr;
1648
1649 FirstNodeVisitor(quint32 minStart = 0, quint32 maxEnd = std::numeric_limits<quint32>::max())
1650 : minStart(minStart), maxEnd(maxEnd)
1651 {
1652 }
1653
1654 bool preVisit(AST::Node *n) override
1655 {
1656 if (!VisitAll::uiKinds().contains(value: n->kind)) {
1657 quint32 start = n->firstSourceLocation().begin();
1658 quint32 end = n->lastSourceLocation().end();
1659 if (!firstNodeInRange && minStart <= start && end <= maxEnd && start < end)
1660 firstNodeInRange = n;
1661 }
1662 return !firstNodeInRange;
1663 }
1664};
1665
1666AST::Node *firstNodeInRange(AST::Node *n, quint32 minStart = 0, quint32 maxEnd = ~quint32(0))
1667{
1668 FirstNodeVisitor visitor(minStart, maxEnd);
1669 AST::Node::accept(node: n, visitor: &visitor);
1670 return visitor.firstNodeInRange;
1671}
1672
1673void ScriptExpression::setCode(const QString &code, const QString &preCode, const QString &postCode)
1674{
1675 // TODO QTBUG-121933
1676 m_codeStr = code;
1677 QString resolvedPreCode, resolvedPostCode;
1678 if (m_expressionType == ExpressionType::BindingExpression && preCode.isEmpty()) {
1679 resolvedPreCode = Binding::preCodeForName(n: u"binding");
1680 resolvedPostCode = Binding::postCodeForName(u"binding");
1681 } else {
1682 resolvedPreCode = preCode;
1683 resolvedPostCode = postCode;
1684 }
1685 if (!resolvedPreCode.isEmpty() || !resolvedPostCode.isEmpty())
1686 m_codeStr = resolvedPreCode + code + resolvedPostCode;
1687 m_code = QStringView(m_codeStr).mid(pos: resolvedPreCode.size(), n: code.size());
1688 m_preCode = QStringView(m_codeStr).mid(pos: 0, n: resolvedPreCode.size());
1689 m_postCode = QStringView(m_codeStr).mid(
1690 pos: resolvedPreCode.size() + code.size(), n: resolvedPostCode.size());
1691 m_engine = nullptr;
1692 m_ast = nullptr;
1693 m_localOffset = SourceLocation();
1694 if (!m_code.isEmpty()) {
1695 IndentInfo preChange(m_preCode, 4);
1696 m_localOffset.offset = m_preCode.size();
1697 m_localOffset.length = m_code.size();
1698 m_localOffset.startColumn = preChange.trailingString.size();
1699 m_localOffset.startLine = preChange.nNewlines;
1700 m_engine = std::make_shared<QQmlJS::Engine>();
1701 m_astComments = std::make_shared<AstComments>(args&: m_engine);
1702 m_ast = parse(mode: resolveParseMode());
1703
1704 if (AST::Program *programPtr = AST::cast<AST::Program *>(ast: m_ast)) {
1705 m_ast = programPtr->statements;
1706 }
1707 if (!m_preCode.isEmpty())
1708 m_ast = firstNodeInRange(n: m_ast, minStart: m_preCode.size(),
1709 maxEnd: m_preCode.size() + m_code.size());
1710 if (auto *sList = AST::cast<AST::FormalParameterList *>(ast: m_ast)) {
1711 m_ast = sList->element;
1712 }
1713 if (m_expressionType != ExpressionType::FunctionBody) {
1714 if (AST::StatementList *sList = AST::cast<AST::StatementList *>(ast: m_ast)) {
1715 if (!sList->next)
1716 m_ast = sList->statement;
1717 }
1718 }
1719 if (m_expressionType == ExpressionType::BindingExpression)
1720 if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(ast: m_ast))
1721 m_ast = exp->expression;
1722
1723 CommentCollector collector;
1724 collector.collectComments(engine: m_engine, rootNode: m_ast, astComments: m_astComments);
1725 }
1726}
1727
1728AST::Node *ScriptExpression::parse(const ParseMode mode)
1729{
1730 QQmlJS::Lexer lexer(m_engine.get());
1731 lexer.setCode(code: m_codeStr, /*lineno = */ 1, /*qmlMode=*/mode == ParseMode::QML);
1732 QQmlJS::Parser parser(m_engine.get());
1733 const bool parserSucceeded = [mode, &parser]() {
1734 switch (mode) {
1735 case ParseMode::QML:
1736 return parser.parse();
1737 case ParseMode::JS:
1738 return parser.parseScript();
1739 case ParseMode::ESM:
1740 return parser.parseModule();
1741 default:
1742 Q_UNREACHABLE_RETURN(false);
1743 }
1744 }();
1745 if (!parserSucceeded) {
1746 addErrorLocal(msg: domParsingErrors().error(message: tr(sourceText: "Parsing of code failed")));
1747 }
1748 const auto messages = parser.diagnosticMessages();
1749 for (const DiagnosticMessage &msg : messages) {
1750 ErrorMessage err = domParsingErrors().errorMessage(msg);
1751 err.location.offset -= m_localOffset.offset;
1752 err.location.startLine -= m_localOffset.startLine;
1753 if (err.location.startLine == 1)
1754 err.location.startColumn -= m_localOffset.startColumn;
1755 addErrorLocal(std::move(err));
1756 }
1757 return parser.rootNode();
1758}
1759
1760void ScriptExpression::astDumper(const Sink &s, AstDumperOptions options) const
1761{
1762 astNodeDumper(s, n: ast(), opt: options, indent: 1, baseIndent: 0, loc2str: [this](SourceLocation astL) {
1763 SourceLocation l = this->locationToLocal(x: astL);
1764 return this->code().mid(pos: l.offset, n: l.length);
1765 });
1766}
1767
1768QString ScriptExpression::astRelocatableDump() const
1769{
1770 return dumperToString(writer: [this](const Sink &s) {
1771 this->astDumper(s, options: AstDumperOption::NoLocations | AstDumperOption::SloppyCompare);
1772 });
1773}
1774
1775void ScriptExpression::writeOut(const DomItem &self, OutWriter &lw) const
1776{
1777 OutWriter *ow = &lw;
1778
1779 std::optional<PendingSourceLocationId> codeLoc;
1780 if (lw.lineWriter.options().updateOptions & LineWriterOptions::Update::Expressions)
1781 codeLoc = lw.lineWriter.startSourceLocation([this, self, ow](SourceLocation myLoc) mutable {
1782 QStringView reformattedCode =
1783 QStringView(ow->writtenStr).mid(pos: myLoc.offset, n: myLoc.length);
1784 if (reformattedCode != code()) {
1785 // If some reformatting of the expression took place,
1786 // it will be saved as an intermediate step.
1787 // then it will be used to restore writtenOut fileItem
1788 // in the OutWriter::restoreWrittenFile
1789
1790 //Interestingly enough, this copyWithUpdatedCode will
1791 //instantiate Engine and Parser and will parse "reformattedCode"
1792 //because it calls ScriptExpression::setCode function
1793 std::shared_ptr<ScriptExpression> copy =
1794 copyWithUpdatedCode(self, code: reformattedCode.toString());
1795 ow->addReformattedScriptExpression(p: self.canonicalPath(), exp: copy);
1796 }
1797 });
1798 reformatAst(
1799 lw, comments: m_astComments,
1800 loc2Str: [this](SourceLocation astL) {
1801 SourceLocation l = this->locationToLocal(x: astL); // use engine->code() instead?
1802 return this->code().mid(pos: l.offset, n: l.length);
1803 },
1804 n: ast());
1805 if (codeLoc)
1806 lw.lineWriter.endSourceLocation(*codeLoc);
1807}
1808
1809SourceLocation ScriptExpression::globalLocation(const DomItem &self) const
1810{
1811 if (const FileLocations::Tree tree = FileLocations::treeOf(self)) {
1812 return FileLocations::region(fLoc: tree, region: MainRegion);
1813 }
1814 return SourceLocation();
1815}
1816
1817bool PropertyDefinition::isParametricType() const
1818{
1819 return typeName.contains(c: QChar(u'<'));
1820}
1821
1822void PropertyDefinition::writeOut(const DomItem &, OutWriter &lw) const
1823{
1824 lw.ensureNewline();
1825 if (isDefaultMember)
1826 lw.writeRegion(region: DefaultKeywordRegion).ensureSpace();
1827 if (isRequired)
1828 lw.writeRegion(region: RequiredKeywordRegion).ensureSpace();
1829 if (isReadonly)
1830 lw.writeRegion(region: ReadonlyKeywordRegion).ensureSpace();
1831 if (!typeName.isEmpty()) {
1832 lw.writeRegion(region: PropertyKeywordRegion).ensureSpace();
1833 lw.writeRegion(region: TypeIdentifierRegion, toWrite: typeName).ensureSpace();
1834 }
1835 lw.writeRegion(region: IdentifierRegion, toWrite: name);
1836}
1837
1838bool MethodInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1839{
1840 bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor);
1841 cont = cont && self.dvWrapField(visitor, f: Fields::parameters, obj: parameters);
1842 cont = cont && self.dvValueField(visitor, f: Fields::methodType, value: int(methodType));
1843 if (!typeName.isEmpty())
1844 cont = cont && self.dvReferenceField(visitor, f: Fields::type, referencedObject: typePath(self));
1845 if (methodType == MethodType::Method) {
1846 cont = cont && self.dvValueField(visitor, f: Fields::preCode, value: preCode(self));
1847 cont = cont && self.dvValueField(visitor, f: Fields::postCode, value: postCode(self));
1848 cont = cont && self.dvValueField(visitor, f: Fields::isConstructor, value: isConstructor);
1849 }
1850 if (returnType)
1851 cont = cont && self.dvItemField(visitor, f: Fields::returnType, it: [this, &self]() {
1852 return self.subOwnerItem(c: PathEls::Field(Fields::returnType), o: returnType);
1853 });
1854 if (body)
1855 cont = cont && self.dvItemField(visitor, f: Fields::body, it: [this, &self]() {
1856 return self.subOwnerItem(c: PathEls::Field(Fields::body), o: body);
1857 });
1858 return cont;
1859}
1860
1861QString MethodInfo::preCode(const DomItem &self) const
1862{
1863 QString res;
1864 LineWriter lw([&res](QStringView s) { res.append(v: s); }, QLatin1String("*preCode*"));
1865 OutWriter ow(lw);
1866 ow.indentNextlines = true;
1867 ow.skipComments = true;
1868 MockObject standinObj(self.pathFromOwner());
1869 DomItem standin = self.copy(base: &standinObj);
1870 ow.itemStart(it: standin);
1871 ow.writeRegion(region: FunctionKeywordRegion).ensureSpace().writeRegion(region: IdentifierRegion, toWrite: name);
1872 bool first = true;
1873 ow.writeRegion(region: LeftParenthesisRegion);
1874 for (const MethodParameter &mp : parameters) {
1875 if (first) {
1876 first = false;
1877 } else {
1878 ow.write(v: u",");
1879 ow.ensureSpace();
1880 }
1881 ow.write(v: mp.value->code());
1882 }
1883 ow.writeRegion(region: RightParenthesisRegion);
1884 ow.ensureSpace().writeRegion(region: LeftBraceRegion);
1885 ow.itemEnd(it: standin);
1886 ow.eof();
1887 return res;
1888}
1889
1890QString MethodInfo::postCode(const DomItem &) const
1891{
1892 return QLatin1String("\n}\n");
1893}
1894
1895void MethodInfo::writeOut(const DomItem &self, OutWriter &ow) const
1896{
1897 switch (methodType) {
1898 case MethodType::Signal: {
1899 if (body)
1900 qCWarning(domLog) << "signal should not have a body in" << self.canonicalPath();
1901 ow.writeRegion(region: SignalKeywordRegion).ensureSpace().writeRegion(region: IdentifierRegion, toWrite: name);
1902 if (parameters.isEmpty())
1903 return;
1904 bool first = true;
1905 ow.writeRegion(region: LeftParenthesisRegion);
1906 int baseIndent = ow.increaseIndent();
1907 for (const DomItem &arg : self.field(name: Fields::parameters).values()) {
1908 if (first)
1909 first = false;
1910 else
1911 ow.write(v: u", ");
1912
1913 if (const MethodParameter *argPtr = arg.as<MethodParameter>()) {
1914 if (argPtr->typeAnnotationStyle == MethodParameter::TypeAnnotationStyle::Prefix)
1915 argPtr->writeOutSignal(self: arg, ow);
1916 else
1917 argPtr->writeOut(self: arg, ow);
1918 } else
1919 qCWarning(domLog) << "failed to cast to MethodParameter";
1920 }
1921 ow.writeRegion(region: RightParenthesisRegion);
1922 ow.decreaseIndent(level: 1, expectedIndent: baseIndent);
1923 return;
1924 } break;
1925 case MethodType::Method: {
1926 ow.writeRegion(region: FunctionKeywordRegion).ensureSpace().writeRegion(region: IdentifierRegion, toWrite: name);
1927 bool first = true;
1928 ow.writeRegion(region: LeftParenthesisRegion);
1929 for (const DomItem &arg : self.field(name: Fields::parameters).values()) {
1930 if (first)
1931 first = false;
1932 else
1933 ow.write(v: u", ");
1934 arg.writeOut(lw&: ow);
1935 }
1936 ow.writeRegion(region: RightParenthesisRegion);
1937 if (!typeName.isEmpty()) {
1938 ow.writeRegion(region: ColonTokenRegion);
1939 ow.ensureSpace();
1940 ow.writeRegion(region: TypeIdentifierRegion, toWrite: typeName);
1941 }
1942 ow.ensureSpace().writeRegion(region: LeftBraceRegion);
1943 int baseIndent = ow.increaseIndent();
1944 if (DomItem b = self.field(name: Fields::body)) {
1945 ow.ensureNewline();
1946 b.writeOut(lw&: ow);
1947 }
1948 ow.decreaseIndent(level: 1, expectedIndent: baseIndent);
1949 ow.ensureNewline().writeRegion(region: RightBraceRegion);
1950 } break;
1951 }
1952}
1953
1954bool MethodParameter::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1955{
1956 bool cont = true;
1957 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name);
1958 if (!typeName.isEmpty()) {
1959 cont = cont
1960 && self.dvReferenceField(visitor, f: Fields::type, referencedObject: Paths::lookupTypePath(name: typeName));
1961 cont = cont && self.dvValueField(visitor, f: Fields::typeName, value: typeName);
1962 }
1963 cont = cont && self.dvValueField(visitor, f: Fields::isPointer, value: isPointer);
1964 cont = cont && self.dvValueField(visitor, f: Fields::isReadonly, value: isReadonly);
1965 cont = cont && self.dvValueField(visitor, f: Fields::isList, value: isList);
1966 cont = cont && self.dvWrapField(visitor, f: Fields::defaultValue, obj: defaultValue);
1967 cont = cont && self.dvWrapField(visitor, f: Fields::value, obj: value);
1968
1969 cont = cont && self.dvValueField(visitor, f: Fields::preCode, value: u"function f("_s);
1970 cont = cont && self.dvValueField(visitor, f: Fields::postCode, value: u") {}"_s);
1971
1972 if (!annotations.isEmpty())
1973 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: annotations);
1974 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: comments);
1975 return cont;
1976}
1977
1978void MethodParameter::writeOut(const DomItem &self, OutWriter &ow) const
1979{
1980 if (!name.isEmpty()) {
1981 if (isRestElement)
1982 ow.writeRegion(region: EllipsisTokenRegion);
1983 ow.writeRegion(region: IdentifierRegion, toWrite: name);
1984 if (!typeName.isEmpty())
1985 ow.writeRegion(region: ColonTokenRegion).ensureSpace().writeRegion(region: TypeIdentifierRegion, toWrite: typeName);
1986 if (defaultValue) {
1987 ow.ensureSpace().writeRegion(region: EqualTokenRegion).ensureSpace();
1988 self.subOwnerItem(c: PathEls::Field(Fields::defaultValue), o: defaultValue).writeOut(lw&: ow);
1989 }
1990 } else {
1991 if (value) {
1992 self.subOwnerItem(c: PathEls::Field(Fields::value), o: value).writeOut(lw&: ow);
1993 }
1994 }
1995}
1996
1997void MethodParameter::writeOutSignal(const DomItem &self, OutWriter &ow) const
1998{
1999 self.writeOutPre(lw&: ow);
2000 if (!typeName.isEmpty())
2001 ow.writeRegion(region: TypeIdentifierRegion, toWrite: typeName).ensureSpace();
2002 ow.writeRegion(region: IdentifierRegion, toWrite: name);
2003 self.writeOutPost(lw&: ow);
2004}
2005
2006void Pragma::writeOut(const DomItem &, OutWriter &ow) const
2007{
2008 ow.ensureNewline();
2009 ow.writeRegion(region: PragmaKeywordRegion).ensureSpace().writeRegion(region: IdentifierRegion, toWrite: name);
2010
2011 bool isFirst = true;
2012 for (const auto &value : values) {
2013 if (isFirst) {
2014 isFirst = false;
2015 ow.writeRegion(region: ColonTokenRegion).ensureSpace();
2016 ow.writeRegion(region: PragmaValuesRegion, toWrite: value);
2017 continue;
2018 }
2019
2020 ow.writeRegion(region: CommaTokenRegion).ensureSpace();
2021 ow.writeRegion(region: PragmaValuesRegion, toWrite: value);
2022 }
2023 ow.ensureNewline();
2024}
2025
2026bool EnumItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2027{
2028 bool cont = true;
2029 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name());
2030 cont = cont && self.dvValueField(visitor, f: Fields::value, value: value());
2031 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: m_comments);
2032 return cont;
2033}
2034
2035void EnumItem::writeOut(const DomItem &self, OutWriter &ow) const
2036{
2037 ow.ensureNewline();
2038 ow.writeRegion(region: IdentifierRegion, toWrite: name());
2039 index_type myIndex = self.pathFromOwner().last().headIndex();
2040 if (m_valueKind == ValueKind::ExplicitValue) {
2041 QString v = QString::number(value(), format: 'f', precision: 0);
2042 if (abs(x: value() - v.toDouble()) > 1.e-10)
2043 v = QString::number(value());
2044 ow.ensureSpace().writeRegion(region: EqualTokenRegion).ensureSpace().writeRegion(region: EnumValueRegion, toWrite: v);
2045 }
2046 if (myIndex >= 0 && self.container().indexes() != myIndex + 1)
2047 ow.writeRegion(region: CommaTokenRegion);
2048}
2049
2050QmlUri QmlUri::fromString(const QString &str)
2051{
2052 if (str.startsWith(c: u'"'))
2053 return fromDirectoryString(importStr: str.mid(position: 1, n: str.size() - 2)
2054 .replace(before: u"\\\""_s, after: u"\""_s)
2055 .replace(before: u"\\\\"_s, after: u"\\"_s));
2056 else
2057 return fromUriString(importStr: str);
2058}
2059
2060QmlUri QmlUri::fromUriString(const QString &str)
2061{
2062 QRegularExpression moduleUriRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
2063 return QmlUri((moduleUriRe.match(subject: str).hasMatch() ? Kind::ModuleUri : Kind::Invalid), str);
2064}
2065
2066QmlUri QmlUri::fromDirectoryString(const QString &str)
2067{
2068 QUrl url(str);
2069 if (url.isValid() && url.scheme().size() > 1)
2070 return QmlUri(url);
2071 if (!str.isEmpty()) {
2072 QFileInfo path(str);
2073 return QmlUri((path.isRelative() ? Kind::RelativePath : Kind::AbsolutePath), str);
2074 }
2075 return {};
2076}
2077
2078bool QmlUri::isValid() const
2079{
2080 return m_kind != Kind::Invalid;
2081}
2082
2083bool QmlUri::isDirectory() const
2084{
2085 switch (m_kind) {
2086 case Kind::Invalid:
2087 case Kind::ModuleUri:
2088 break;
2089 case Kind::DirectoryUrl:
2090 case Kind::RelativePath:
2091 case Kind::AbsolutePath:
2092 return true;
2093 }
2094 return false;
2095}
2096
2097bool QmlUri::isModule() const
2098{
2099 return m_kind == Kind::ModuleUri;
2100}
2101
2102QString QmlUri::moduleUri() const
2103{
2104 if (m_kind == Kind::ModuleUri)
2105 return std::get<QString>(v: m_value);
2106 return QString();
2107}
2108
2109QString QmlUri::localPath() const
2110{
2111 switch (m_kind) {
2112 case Kind::Invalid:
2113 case Kind::ModuleUri:
2114 break;
2115 case Kind::DirectoryUrl: {
2116 const QUrl &url = std::get<QUrl>(v: m_value);
2117 if (url.scheme().compare(s: u"file", cs: Qt::CaseInsensitive) == 0)
2118 return url.path();
2119 break;
2120 }
2121 case Kind::RelativePath:
2122 case Kind::AbsolutePath:
2123 return std::get<QString>(v: m_value);
2124 }
2125 return QString();
2126}
2127
2128QString QmlUri::absoluteLocalPath(const QString &basePath) const
2129{
2130 switch (m_kind) {
2131 case Kind::Invalid:
2132 case Kind::ModuleUri:
2133 break;
2134 case Kind::DirectoryUrl: {
2135 const QUrl &url = std::get<QUrl>(v: m_value);
2136 if (url.scheme().compare(s: u"file", cs: Qt::CaseInsensitive) == 0)
2137 return url.path();
2138 break;
2139 }
2140 case Kind::RelativePath: {
2141 if (!basePath.isEmpty())
2142 return QDir(basePath).filePath(fileName: std::get<QString>(v: m_value));
2143 break;
2144 }
2145 case Kind::AbsolutePath:
2146 return std::get<QString>(v: m_value);
2147 }
2148 return QString();
2149}
2150
2151QUrl QmlUri::directoryUrl() const
2152{
2153 if (m_kind == Kind::DirectoryUrl)
2154 return std::get<QUrl>(v: m_value);
2155 return QUrl {};
2156}
2157
2158QString QmlUri::directoryString() const
2159{
2160 switch (m_kind) {
2161 case Kind::Invalid:
2162 case Kind::ModuleUri:
2163 break;
2164 case Kind::DirectoryUrl:
2165 return std::get<QUrl>(v: m_value).toString(); // set formatting? options?
2166 case Kind::RelativePath:
2167 case Kind::AbsolutePath:
2168 return std::get<QString>(v: m_value);
2169 }
2170 return QString();
2171}
2172
2173QString QmlUri::toString() const
2174{
2175 switch (m_kind) {
2176 case Kind::Invalid:
2177 break;
2178 case Kind::ModuleUri:
2179 return std::get<QString>(v: m_value);
2180 case Kind::DirectoryUrl:
2181 case Kind::RelativePath:
2182 case Kind::AbsolutePath:
2183 return u"\""_s + directoryString().replace(c: u'\\', after: u"\\\\"_s).replace(c: u'"', after: u"\\\""_s)
2184 + u"\""_s;
2185 }
2186 return QString();
2187}
2188
2189QmlUri::Kind QmlUri::kind() const
2190{
2191 return m_kind;
2192}
2193
2194void ScriptExpression::setScriptElement(const ScriptElementVariant &p)
2195{
2196 m_element = p;
2197}
2198
2199} // end namespace Dom
2200} // end namespace QQmlJS
2201
2202QT_END_NAMESPACE
2203
2204#include "moc_qqmldomelements_p.cpp"
2205

source code of qtdeclarative/src/qmldom/qqmldomelements.cpp