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::fromRoot(r: PathRoot::Env).withField(name: Fields::moduleIndexWithUri).withKey(name: uri).withKey(name: version);
62}
63
64Path moduleScopePath(const QString &uri, Version version, const ErrorHandler &)
65{
66 return Path::fromRoot(r: PathRoot::Env)
67 .withField(name: Fields::moduleIndexWithUri)
68 .withKey(name: uri)
69 .withKey(name: version.majorSymbolicString())
70 .withField(name: Fields::moduleScope)
71 .withKey(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.withField(name: Fields::enumerations));
101 updatePathFromOwnerQList(list&: m_objects, newPath: newPath.withField(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().withField(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.withField(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::treeOf(self);
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().writeRegion(region: VersionRegion, toWrite: 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.withField(name: Fields::annotations));
380}
381
382Path Id::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
383{
384 return appendUpdatableElementInQList(listPathFromOwner: selfPathFromOwner.withField(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().withField(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 { Fields::comments.toString(), Fields::idStr.toString(),
427 Fields::name.toString(), Fields::prototypes.toString(),
428 Fields::nextScope.toString(), Fields::propertyDefs.toString(),
429 Fields::bindings.toString(), Fields::methods.toString(),
430 Fields::children.toString(), Fields::annotations.toString(),
431 Fields::propertyInfos.toString() });
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().withField(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({ Fields::fileLocationsTree.toString() });
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.withField(name: Fields::propertyDefs));
506 updatePathFromOwnerMultiMap(mmap&: m_bindings, newPath: newPath.withField(name: Fields::bindings));
507 updatePathFromOwnerMultiMap(mmap&: m_methods, newPath: newPath.withField(name: Fields::methods));
508 updatePathFromOwnerQList(list&: m_children, newPath: newPath.withField(name: Fields::children));
509 updatePathFromOwnerQList(list&: m_annotations, newPath: newPath.withField(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::writeOutId(const DomItem &self, OutWriter &ow) const
717{
718 if (!idStr().isEmpty()) { // *always* put id first
719 DomItem myId = self.component().field(name: Fields::ids).key(name: idStr()).index(0);
720 if (myId)
721 myId.writeOutPre(lw&: ow);
722 ow.ensureNewline()
723 .writeRegion(region: IdTokenRegion)
724 .writeRegion(region: IdColonTokenRegion)
725 .ensureSpace()
726 .writeRegion(region: IdNameRegion, toWrite: idStr());
727 if (ow.lineWriter.options().attributesSequence
728 == LineWriterOptions::AttributesSequence::Normalize) {
729 ow.ensureNewline(nNewlines: 2);
730 }
731 if (myId) {
732 myId.writeOutPost(lw&: ow);
733 ow.ensureNewline(nNewlines: 1);
734 }
735 }
736}
737
738QList<std::pair<SourceLocation, DomItem>> QmlObject::orderOfAttributes(const DomItem &self,
739 const DomItem &component) const
740{
741 auto startLoc = [&](const FileLocations::Tree &l) {
742 if (l)
743 return l->info().fullRegion;
744 return SourceLocation(posOfNewElements, 0, 0, 0);
745 };
746
747 QList<std::pair<SourceLocation, DomItem>> attribs;
748 const auto objLocPtr = FileLocations::treeOf(self);
749 FileLocations::Tree componentLoc;
750 if (component && objLocPtr)
751 componentLoc = objLocPtr->parent()->parent();
752 auto addMMap = [&attribs, &startLoc](const DomItem &base, const FileLocations::Tree &baseLoc) {
753 if (!base)
754 return;
755 const auto values = base.values();
756 for (const auto &els : values) {
757 FileLocations::Tree elsLoc = FileLocations::find(self: baseLoc, p: els.pathFromOwner().last());
758 const auto elsValues = els.values();
759 for (const auto &el : elsValues) {
760 FileLocations::Tree elLoc = FileLocations::find(self: elsLoc, p: el.pathFromOwner().last());
761 attribs.append(t: std::make_pair(x: startLoc(elLoc), y: el));
762 }
763 }
764 };
765 auto addMyMMap = [this, &objLocPtr, &self, &addMMap](QStringView fieldName) {
766 DomItem base = this->field(self, name: fieldName);
767 addMMap(base, FileLocations::find(self: objLocPtr, p: base.pathFromOwner().last()));
768 };
769 auto addSingleLevel = [&attribs, &startLoc](const DomItem &base,
770 const FileLocations::Tree &baseLoc) {
771 if (!base)
772 return;
773 const auto baseValues = base.values();
774 for (const auto &el : baseValues) {
775 FileLocations::Tree elLoc = FileLocations::find(self: baseLoc, p: el.pathFromOwner().last());
776 attribs.append(t: std::make_pair(x: startLoc(elLoc), y: el));
777 }
778 };
779 if (component) {
780 DomItem enums = component.field(name: Fields::enumerations);
781 addMMap(enums, FileLocations::find(self: componentLoc, p: enums.pathFromOwner().last()));
782 }
783 addMyMMap(Fields::propertyDefs);
784 addMyMMap(Fields::bindings);
785 addMyMMap(Fields::methods);
786 DomItem children = field(self, name: Fields::children);
787 addSingleLevel(children, FileLocations::find(self: objLocPtr, p: children.pathFromOwner().last()));
788 if (component) {
789 DomItem subCs = component.field(name: Fields::subComponents);
790 for (const DomItem &c : subCs.values()) {
791 const auto subLocPtr = FileLocations::treeOf(c);
792 Q_ASSERT(subLocPtr);
793 attribs.append(t: std::make_pair(x: startLoc(subLocPtr), y: c));
794 }
795 }
796 std::stable_sort(first: attribs.begin(), last: attribs.end(),
797 comp: [](const std::pair<SourceLocation, DomItem> &el1,
798 const std::pair<SourceLocation, DomItem> &el2) {
799 if (el1.first.offset < el2.first.offset)
800 return true;
801 if (el1.first.offset > el2.first.offset)
802 return false;
803 int i = int(el1.second.internalKind()) - int(el2.second.internalKind());
804 return i < 0;
805 });
806 return attribs;
807}
808
809void QmlObject::writeOutAttributes(const DomItem &self, OutWriter &ow, const DomItem &component,
810 const QString &code) const
811{
812 const QList<std::pair<SourceLocation, DomItem>> attribs = orderOfAttributes(self, component);
813 qsizetype iAttr = 0;
814 while (iAttr != attribs.size()) {
815 auto &el = attribs[iAttr++];
816 // check for an empty line before the current element, and preserve it
817 int preNewlines = 0;
818 quint32 start = el.first.offset;
819 if (start != posOfNewElements && size_t(code.size()) >= start) {
820 while (start != 0) {
821 QChar c = code.at(i: --start);
822 if (c == u'\n') {
823 if (++preNewlines == 2)
824 break;
825 } else if (!c.isSpace())
826 break;
827 }
828 }
829 if (preNewlines == 0)
830 ++preNewlines;
831 ow.ensureNewline(nNewlines: preNewlines);
832 if (el.second.internalKind() == DomType::PropertyDefinition && iAttr != attribs.size()
833 && el.first.offset != ~quint32(0)) {
834 DomItem b;
835 auto &bPair = attribs[iAttr];
836 if (bPair.second.internalKind() == DomType::Binding
837 && bPair.first.begin() < el.first.end()
838 && bPair.second.name() == el.second.name()) {
839 b = bPair.second;
840 ++iAttr;
841 b.writeOutPre(lw&: ow);
842 }
843 el.second.writeOut(lw&: ow);
844 if (b) {
845 ow.writeRegion(region: ColonTokenRegion);
846 ow.ensureSpace();
847 if (const Binding *bPtr = b.as<Binding>())
848 bPtr->writeOutValue(self: b, lw&: ow);
849 else {
850 qWarning() << "Internal error casting binding to Binding in"
851 << b.canonicalPath();
852 ow.writeRegion(region: LeftBraceRegion).writeRegion(region: RightBraceRegion);
853 }
854 b.writeOutPost(lw&: ow);
855 }
856 } else {
857 el.second.writeOut(lw&: ow);
858 }
859 ow.ensureNewline();
860 }
861}
862
863void QmlObject::writeOutSortedEnumerations(const DomItem &component, OutWriter &ow) const
864{
865 const auto descs = component.field(name: Fields::enumerations).values();
866 for (const auto &enumDescs : descs) {
867 const auto values = enumDescs.values();
868 for (const auto &enumDesc : values) {
869 ow.ensureNewline(nNewlines: 1);
870 enumDesc.writeOut(lw&: ow);
871 ow.ensureNewline(nNewlines: 1);
872 }
873 }
874}
875
876void QmlObject::writeOutSortedPropertyDefinition(const DomItem &self, OutWriter &ow,
877 QSet<QString> &mergedDefBinding) const
878{
879 DomItem propertyDefs = field(self, name: Fields::propertyDefs);
880 DomItem bindings = field(self, name: Fields::bindings);
881
882 for (const QString &defName : propertyDefs.sortedKeys()) {
883 const auto pDefs = propertyDefs.key(name: defName).values();
884 for (const auto &pDef : pDefs) {
885 const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>();
886 Q_ASSERT(pDefPtr);
887 DomItem b;
888 bool uniqueDeclarationWithThisName = pDefs.size() == 1;
889 if (uniqueDeclarationWithThisName && !pDefPtr->isRequired)
890 bindings.key(name: pDef.name()).visitIndexes(visitor: [&b, pDefPtr](const DomItem &el) {
891 const Binding *elPtr = el.as<Binding>();
892 if (elPtr && elPtr->bindingType() == BindingType::Normal) {
893 switch (elPtr->valueKind()) {
894 case BindingValueKind::ScriptExpression:
895 b = el;
896 break;
897 case BindingValueKind::Array:
898 if (!pDefPtr->isDefaultMember && pDefPtr->isParametricType())
899 b = el;
900 break;
901 case BindingValueKind::Object:
902 if (!pDefPtr->isDefaultMember && !pDefPtr->isParametricType())
903 b = el;
904 break;
905 case BindingValueKind::Empty:
906 break;
907 }
908 return false;
909 }
910 return true;
911 });
912 if (b) {
913 mergedDefBinding.insert(value: defName);
914 b.writeOutPre(lw&: ow);
915 }
916 pDef.writeOut(lw&: ow);
917 if (b) {
918 ow.writeRegion(region: ColonTokenRegion);
919 ow.ensureSpace();
920 if (const Binding *bPtr = b.as<Binding>())
921 bPtr->writeOutValue(self: b, lw&: ow);
922 else {
923 qWarning() << "Internal error casting binding to Binding in"
924 << b.canonicalPath();
925 ow.writeRegion(region: LeftBraceRegion).writeRegion(region: RightBraceRegion);
926 }
927 b.writeOutPost(lw&: ow);
928 }
929 }
930 }
931}
932
933static std::pair<QList<DomItem>, QList<DomItem>> splitSignalsAndMethods(const DomItem &methods)
934{
935 QList<DomItem> signalList, methodList;
936 const auto fields = methods.values();
937 for (const auto &ms : fields) {
938 const auto values = ms.values();
939 for (const auto &m : values) {
940 const MethodInfo *mPtr = m.as<MethodInfo>();
941 if (mPtr && mPtr->methodType == MethodInfo::MethodType::Signal)
942 signalList.append(t: m);
943 else
944 methodList.append(t: m);
945 }
946 }
947 return std::make_pair(x&: signalList, y&: methodList);
948}
949
950static std::tuple<QList<DomItem>, QList<DomItem>, QList<DomItem>>
951splitBindings(const DomItem &bindings, const QSet<QString> &mergedDefBinding)
952{
953 QList<DomItem> normalBindings, signalHandlers, delayedBindings;
954 for (const auto &bName : bindings.sortedKeys()) {
955 bool skipFirstNormal = mergedDefBinding.contains(value: bName);
956 const auto values = bindings.key(name: bName).values();
957 for (const auto &b : values) {
958 const Binding *bPtr = b.as<Binding>();
959 if (skipFirstNormal) {
960 if (bPtr && bPtr->bindingType() == BindingType::Normal) {
961 skipFirstNormal = false;
962 continue;
963 }
964 }
965 if (bPtr->valueKind() == BindingValueKind::Array
966 || bPtr->valueKind() == BindingValueKind::Object)
967 delayedBindings.append(t: b);
968 else if (b.field(name: Fields::isSignalHandler).value().toBool(defaultValue: false))
969 signalHandlers.append(t: b);
970 else
971 normalBindings.append(t: b);
972 }
973 }
974 return std::make_tuple(args&: normalBindings, args&: signalHandlers, args&: delayedBindings);
975}
976
977void QmlObject::writeOutSortedAttributes(const DomItem &self, OutWriter &ow,
978 const DomItem &component) const
979{
980 int spacerId = 0;
981 quint32 counter = ow.counter();
982
983 if (component)
984 writeOutSortedEnumerations(component, ow);
985
986 if (counter != ow.counter() || !idStr().isEmpty())
987 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
988
989 QSet<QString> mergedDefBinding;
990 writeOutSortedPropertyDefinition(self, ow, mergedDefBinding);
991
992 ow.removeTextAddCallback(i: spacerId);
993 if (counter != ow.counter())
994 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
995
996 const auto [signalList, methodList] = splitSignalsAndMethods(methods: field(self, name: Fields::methods));
997 for (const auto &sig : std::as_const(t: signalList)) {
998 ow.ensureNewline();
999 sig.writeOut(lw&: ow);
1000 ow.ensureNewline();
1001 }
1002
1003 ow.removeTextAddCallback(i: spacerId);
1004 if (counter != ow.counter())
1005 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1006
1007 bool first = true;
1008 for (const auto &method : std::as_const(t: methodList)) {
1009 if (!first && ow.lineWriter.options().functionsSpacing) {
1010 ow.newline();
1011 }
1012 ow.ensureNewline();
1013 first = false;
1014 method.writeOut(lw&: ow);
1015 ow.ensureNewline();
1016 }
1017 ow.removeTextAddCallback(i: spacerId);
1018
1019 DomItem bindings = field(self, name: Fields::bindings);
1020 const auto [normalBindings, signalHandlers, delayedBindings] =
1021 splitBindings(bindings, mergedDefBinding);
1022
1023 if (counter != ow.counter())
1024 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1025 for (const auto &b : std::as_const(t: normalBindings))
1026 b.writeOut(lw&: ow);
1027 ow.removeTextAddCallback(i: spacerId);
1028
1029 if (counter != ow.counter())
1030 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1031 for (const auto &b : std::as_const(t: delayedBindings))
1032 b.writeOut(lw&: ow);
1033 ow.removeTextAddCallback(i: spacerId);
1034
1035 if (counter != ow.counter())
1036 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1037 for (const auto &b : std::as_const(t: signalHandlers))
1038 b.writeOut(lw&: ow);
1039 ow.removeTextAddCallback(i: spacerId);
1040
1041 if (counter != ow.counter())
1042 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1043 first = true;
1044
1045 const auto values = field(self, name: Fields::children).values();
1046 for (const auto &c : values) {
1047 if (!first && ow.lineWriter.options().objectsSpacing) {
1048 ow.newline().newline();
1049 }
1050 first = false;
1051 ow.ensureNewline();
1052 c.writeOut(lw&: ow);
1053 }
1054 ow.removeTextAddCallback(i: spacerId);
1055
1056 if (component) {
1057 // we are a root object, possibly add components
1058 DomItem subComps = component.field(name: Fields::subComponents);
1059 if (counter != ow.counter())
1060 spacerId = ow.addNewlinesAutospacerCallback(nLines: 2);
1061 const auto values = subComps.values();
1062 for (const auto &subC : values) {
1063 ow.ensureNewline();
1064 subC.writeOut(lw&: ow);
1065 }
1066 ow.removeTextAddCallback(i: spacerId);
1067 }
1068 ow.ensureNewline();
1069}
1070
1071void QmlObject::writeOut(const DomItem &self, OutWriter &ow, const QString &onTarget) const
1072{
1073 bool isRootObject = pathFromOwner().length() == 5
1074 && pathFromOwner()[0] == Path::fromField(s: Fields::components)
1075 && pathFromOwner()[3] == Path::fromField(s: Fields::objects);
1076 ow.writeRegion(region: IdentifierRegion, toWrite: name());
1077 if (!onTarget.isEmpty()) {
1078 ow.ensureSpace().writeRegion(region: OnTokenRegion).ensureSpace().writeRegion(region: OnTargetRegion,
1079 toWrite: onTarget);
1080 }
1081 ow.ensureSpace();
1082 ow.writeRegion(region: LeftBraceRegion);
1083 int baseIndent = ow.increaseIndent();
1084
1085 // *always* put id first
1086 writeOutId(self, ow);
1087
1088 DomItem component;
1089 if (isRootObject)
1090 component = self.containingObject();
1091 if (ow.lineWriter.options().attributesSequence
1092 == LineWriterOptions::AttributesSequence::Preserve) {
1093 QString code;
1094 if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
1095 code = qmlFilePtr->code();
1096 writeOutAttributes(self, ow, component, code);
1097 } else {
1098 writeOutSortedAttributes(self, ow, component);
1099 }
1100 ow.decreaseIndent(level: 1, expectedIndent: baseIndent);
1101 ow.writeRegion(region: RightBraceRegion);
1102}
1103
1104Binding::Binding(const QString &name) : Binding(name, std::unique_ptr<BindingValue>()) { }
1105
1106Binding::Binding(const QString &name, std::unique_ptr<BindingValue> value, BindingType bindingType)
1107 : m_bindingType(bindingType), m_name(name), m_value(std::move(value))
1108{
1109}
1110
1111Binding::Binding(
1112 const QString &name, const std::shared_ptr<ScriptExpression> &value,
1113 BindingType bindingType)
1114 : Binding(name, std::make_unique<BindingValue>(args: value), bindingType)
1115{
1116}
1117
1118Binding::Binding(const QString &name, const QString &scriptCode, BindingType bindingType)
1119 : Binding(name,
1120 std::make_unique<BindingValue>(args: std::make_shared<ScriptExpression>(
1121 args: scriptCode, args: ScriptExpression::ExpressionType::BindingExpression, args: 0,
1122 args: Binding::preCodeForName(n: name), args: Binding::postCodeForName(name))),
1123 bindingType)
1124{
1125}
1126
1127Binding::Binding(const QString &name, const QmlObject &value, BindingType bindingType)
1128 : Binding(name, std::make_unique<BindingValue>(args: value), bindingType)
1129{
1130}
1131
1132Binding::Binding(const QString &name, const QList<QmlObject> &value, BindingType bindingType)
1133 : Binding(name, std::make_unique<BindingValue>(args: value), bindingType)
1134{
1135}
1136
1137Binding::Binding(const Binding &o)
1138 : m_bindingType(o.m_bindingType),
1139 m_name(o.m_name),
1140 m_annotations(o.m_annotations),
1141 m_comments(o.m_comments),
1142 m_bindingIdentifiers(o.m_bindingIdentifiers)
1143{
1144 if (o.m_value) {
1145 m_value = std::make_unique<BindingValue>(args&: *o.m_value);
1146 }
1147}
1148
1149Binding::~Binding() { }
1150
1151Binding &Binding::operator=(const Binding &o)
1152{
1153 m_name = o.m_name;
1154 m_bindingType = o.m_bindingType;
1155 m_annotations = o.m_annotations;
1156 m_comments = o.m_comments;
1157 m_bindingIdentifiers = o.m_bindingIdentifiers;
1158 if (o.m_value) {
1159 if (!m_value)
1160 m_value = std::make_unique<BindingValue>(args&: *o.m_value);
1161 else
1162 *m_value = *o.m_value;
1163 } else {
1164 m_value.reset();
1165 }
1166 return *this;
1167}
1168
1169bool Binding::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1170{
1171 bool cont = true;
1172 cont = cont && self.dvValueField(visitor, f: Fields::name, value: m_name);
1173 cont = cont && self.dvValueField(visitor, f: Fields::isSignalHandler, value: isSignalHandler());
1174 if (!m_value)
1175 cont = cont && visitor(PathEls::Field(Fields::value), []() { return DomItem(); });
1176 else
1177 cont = cont && self.dvItemField(visitor, f: Fields::value, it: [this, &self]() {
1178 return m_value->value(binding: self);
1179 });
1180 cont = cont && self.dvValueField(visitor, f: Fields::bindingType, value: int(m_bindingType));
1181 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: m_comments);
1182 cont = cont && self.dvValueLazyField(visitor, f: Fields::preCode, valueF: [this]() {
1183 return this->preCode();
1184 });
1185 cont = cont && self.dvValueLazyField(visitor, f: Fields::postCode, valueF: [this]() {
1186 return this->postCode();
1187 });
1188 if (m_bindingIdentifiers) {
1189 cont = cont && self.dvItemField(visitor, f: Fields::bindingIdentifiers, it: [this, &self]() {
1190 return self.subScriptElementWrapperItem(obj: m_bindingIdentifiers);
1191 });
1192 }
1193 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: m_annotations);
1194 return cont;
1195}
1196
1197DomItem Binding::valueItem(const DomItem &self) const
1198{
1199 if (!m_value)
1200 return DomItem();
1201 return m_value->value(binding: self);
1202}
1203
1204BindingValueKind Binding::valueKind() const
1205{
1206 if (!m_value)
1207 return BindingValueKind::Empty;
1208 return m_value->kind;
1209}
1210
1211QmlObject const *Binding::objectValue() const
1212{
1213 if (valueKind() == BindingValueKind::Object)
1214 return &(m_value->object);
1215 return nullptr;
1216}
1217
1218QmlObject *Binding::objectValue()
1219{
1220 if (valueKind() == BindingValueKind::Object)
1221 return &(m_value->object);
1222 return nullptr;
1223}
1224
1225QList<QmlObject> const *Binding::arrayValue() const
1226{
1227 if (valueKind() == BindingValueKind::Array)
1228 return &(m_value->array);
1229 return nullptr;
1230}
1231
1232QList<QmlObject> *Binding::arrayValue()
1233{
1234 if (valueKind() == BindingValueKind::Array)
1235 return &(m_value->array);
1236 return nullptr;
1237}
1238
1239std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue() const
1240{
1241 if (valueKind() == BindingValueKind::ScriptExpression)
1242 return m_value->scriptExpression;
1243 return nullptr;
1244}
1245
1246std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue()
1247{
1248 if (valueKind() == BindingValueKind::ScriptExpression)
1249 return m_value->scriptExpression;
1250 return nullptr;
1251}
1252
1253void Binding::setValue(std::unique_ptr<BindingValue> &&value)
1254{
1255 m_value = std::move(value);
1256}
1257
1258Path Binding::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
1259{
1260 return appendUpdatableElementInQList(listPathFromOwner: selfPathFromOwner.withField(name: Fields::annotations),
1261 list&: m_annotations, value: annotation, vPtr: aPtr);
1262}
1263
1264void Binding::updatePathFromOwner(const Path &newPath)
1265{
1266 Path base = newPath.withField(name: Fields::annotations);
1267 if (m_value)
1268 m_value->updatePathFromOwner(newPath: newPath.withField(name: Fields::value));
1269 updatePathFromOwnerQList(list&: m_annotations, newPath: newPath.withField(name: Fields::annotations));
1270}
1271
1272void Binding::writeOut(const DomItem &self, OutWriter &lw) const
1273{
1274 lw.ensureNewline();
1275 if (m_bindingType == BindingType::Normal) {
1276 lw.writeRegion(region: IdentifierRegion, toWrite: name());
1277 lw.writeRegion(region: ColonTokenRegion).ensureSpace();
1278 writeOutValue(self, lw);
1279 } else {
1280 DomItem v = valueItem(self);
1281 if (const QmlObject *vPtr = v.as<QmlObject>()) {
1282 v.writeOutPre(lw);
1283 vPtr->writeOut(self: v, ow&: lw, onTarget: name());
1284 v.writeOutPost(lw);
1285 } else {
1286 qCWarning(writeOutLog()) << "On Binding requires an QmlObject Value, not "
1287 << v.internalKindStr() << " at " << self.canonicalPath();
1288 }
1289 }
1290}
1291
1292void Binding::writeOutValue(const DomItem &self, OutWriter &lw) const
1293{
1294 DomItem v = valueItem(self);
1295 switch (valueKind()) {
1296 case BindingValueKind::Empty:
1297 qCWarning(writeOutLog()) << "Writing of empty binding " << name();
1298 lw.writeRegion(region: LeftBraceRegion);
1299 lw.writeRegion(region: RightBraceRegion);
1300 break;
1301 case BindingValueKind::Array:
1302 if (const List *vPtr = v.as<List>()) {
1303 v.writeOutPre(lw);
1304 vPtr->writeOut(self: v, ow&: lw, compact: false);
1305 v.writeOutPost(lw);
1306 }
1307 break;
1308 case BindingValueKind::Object:
1309 case BindingValueKind::ScriptExpression:
1310 v.writeOut(lw);
1311 break;
1312 }
1313}
1314
1315bool QmltypesComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1316{
1317 bool cont = Component::iterateDirectSubpaths(self, visitor);
1318 cont = cont && self.dvWrapField(visitor, f: Fields::exports, obj: m_exports);
1319 cont = cont && self.dvValueField(visitor, f: Fields::metaRevisions, value: m_metaRevisions);
1320 if (!fileName().isEmpty())
1321 cont = cont && self.dvValueField(visitor, f: Fields::fileName, value: fileName()); // remove?
1322 cont = cont && self.dvValueField(visitor, f: Fields::interfaceNames, value: m_interfaceNames);
1323 cont = cont && self.dvValueField(visitor, f: Fields::hasCustomParser, value: m_hasCustomParser);
1324 cont = cont && self.dvValueField(visitor, f: Fields::valueTypeName, value: m_valueTypeName);
1325 cont = cont && self.dvValueField(visitor, f: Fields::extensionTypeName, value: m_extensionTypeName);
1326 cont = cont && self.dvValueField(visitor, f: Fields::accessSemantics, value: int(m_accessSemantics));
1327 return cont;
1328}
1329
1330Export Export::fromString(
1331 const Path &source, QStringView exp, const Path &typePath, const ErrorHandler &h)
1332{
1333 Export res;
1334 res.exportSourcePath = source;
1335 res.typePath = typePath;
1336 int slashIdx = exp.indexOf(c: QLatin1Char('/'));
1337 int spaceIdx = exp.indexOf(c: QLatin1Char(' '));
1338 if (spaceIdx == -1)
1339 spaceIdx = exp.size();
1340 else
1341 res.version = Version::fromString(v: exp.mid(pos: spaceIdx + 1));
1342 if (!res.version.isValid())
1343 domParsingErrors()
1344 .error(message: tr(sourceText: "Expected string literal to contain 'Package/Name major.minor' "
1345 "or 'Name major.minor' not '%1'.")
1346 .arg(a: exp))
1347 .handle(errorHandler: h);
1348 if (slashIdx != -1)
1349 res.uri = exp.left(n: slashIdx).toString();
1350 res.typeName = exp.mid(pos: slashIdx + 1, n: spaceIdx - (slashIdx + 1)).toString();
1351 return res;
1352}
1353
1354bool AttributeInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1355{
1356 bool cont = true;
1357 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name);
1358 cont = cont && self.dvValueField(visitor, f: Fields::access, value: int(access));
1359 cont = cont && self.dvValueField(visitor, f: Fields::typeName, value: typeName);
1360 cont = cont && self.dvValueField(visitor, f: Fields::isReadonly, value: isReadonly);
1361 cont = cont && self.dvValueField(visitor, f: Fields::isList, value: isList);
1362 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: comments);
1363 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: annotations);
1364 return cont;
1365}
1366
1367Path AttributeInfo::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation,
1368 QmlObject **aPtr)
1369{
1370 return appendUpdatableElementInQList(listPathFromOwner: selfPathFromOwner.withField(name: Fields::annotations), list&: annotations,
1371 value: annotation, vPtr: aPtr);
1372}
1373
1374void AttributeInfo::updatePathFromOwner(const Path &newPath)
1375{
1376 Path base = newPath.withField(name: Fields::annotations);
1377 updatePathFromOwnerQList(list&: annotations, newPath: newPath.withField(name: Fields::annotations));
1378}
1379
1380bool EnumDecl::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1381{
1382 bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
1383 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name());
1384 cont = cont && self.dvWrapField(visitor, f: Fields::values, obj: m_values);
1385 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: m_annotations);
1386 return cont;
1387}
1388
1389void EnumDecl::updatePathFromOwner(const Path &newPath)
1390{
1391 DomElement::updatePathFromOwner(newPath);
1392 updatePathFromOwnerQList(list&: m_annotations, newPath: newPath.withField(name: Fields::annotations));
1393}
1394
1395void EnumDecl::setAnnotations(const QList<QmlObject> &annotations)
1396{
1397 m_annotations = annotations;
1398}
1399
1400Path EnumDecl::addAnnotation(const QmlObject &annotation, QmlObject **aPtr)
1401{
1402 return appendUpdatableElementInQList(listPathFromOwner: pathFromOwner().withField(name: Fields::annotations), list&: m_annotations,
1403 value: annotation, vPtr: aPtr);
1404}
1405
1406void EnumDecl::writeOut(const DomItem &self, OutWriter &ow) const
1407{
1408 ow.writeRegion(region: EnumKeywordRegion)
1409 .ensureSpace()
1410 .writeRegion(region: IdentifierRegion, toWrite: name())
1411 .ensureSpace()
1412 .writeRegion(region: LeftBraceRegion);
1413 int iLevel = ow.increaseIndent(level: 1);
1414 const auto values = self.field(name: Fields::values).values();
1415 for (const auto &value : values)
1416 value.writeOut(lw&: ow);
1417 ow.decreaseIndent(level: 1, expectedIndent: iLevel);
1418 ow.ensureNewline().writeRegion(region: RightBraceRegion);
1419}
1420
1421QList<Path> ImportScope::allSources(const DomItem &self) const
1422{
1423 DomItem top = self.top();
1424 DomItem env = top.environment();
1425 Path selfPath = self.canonicalPath().withField(name: Fields::allSources);
1426 RefCacheEntry cached = (env ? RefCacheEntry::forPath(el: env, canonicalPath: selfPath) : RefCacheEntry());
1427 if (cached.cached == RefCacheEntry::Cached::All)
1428 return cached.canonicalPaths;
1429 QList<Path> res;
1430 QSet<Path> knownPaths;
1431 QList<Path> toDo(m_importSourcePaths.rbegin(), m_importSourcePaths.rend());
1432 while (!toDo.isEmpty()) {
1433 Path pNow = toDo.takeLast();
1434 if (knownPaths.contains(value: pNow))
1435 continue;
1436 knownPaths.insert(value: pNow);
1437 res.append(t: pNow);
1438 DomItem sourceBase = top.path(p: pNow);
1439 for (const DomItem &autoExp : sourceBase.field(name: Fields::autoExports).values()) {
1440 if (const ModuleAutoExport *autoExpPtr = autoExp.as<ModuleAutoExport>()) {
1441 Path newSource;
1442 if (autoExpPtr->inheritVersion) {
1443 Version v = autoExpPtr->import.version;
1444 DomItem sourceVersion = sourceBase.field(name: Fields::version);
1445 if (const Version *sourceVersionPtr = sourceVersion.as<Version>()) {
1446 if (v.majorVersion < 0)
1447 v.majorVersion = sourceVersionPtr->majorVersion;
1448 if (v.minorVersion < 0)
1449 v.minorVersion = sourceVersionPtr->minorVersion;
1450 } else {
1451 qWarning() << "autoExport with inherited version " << autoExp
1452 << " but missing version in source" << pNow;
1453 }
1454 Import toImport(autoExpPtr->import.uri, v);
1455 newSource = toImport.importedPath();
1456 } else {
1457 newSource = autoExpPtr->import.importedPath();
1458 }
1459 if (newSource && !knownPaths.contains(value: newSource))
1460 toDo.append(t: newSource);
1461 } else {
1462 qWarning() << "expected ModuleAutoExport not " << autoExp.internalKindStr()
1463 << "looking up autoExports of" << sourceBase;
1464 Q_ASSERT(false);
1465 }
1466 }
1467 }
1468 if (env)
1469 RefCacheEntry::addForPath(el: env, canonicalPath: selfPath, entry: RefCacheEntry { .cached: RefCacheEntry::Cached::All, .canonicalPaths: res });
1470 return res;
1471}
1472
1473bool ImportScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1474{
1475 bool cont = true;
1476 cont = cont && self.dvReferencesField(visitor, f: Fields::importSources, paths: m_importSourcePaths);
1477 cont = cont && self.dvItemField(visitor, f: Fields::allSources, it: [this, &self]() -> DomItem {
1478 return self.subListItem(list: List::fromQList<Path>(
1479 pathFromOwner: self.pathFromOwner().withField(name: Fields::allSources), list: allSources(self),
1480 elWrapper: [](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
1481 return list.subDataItem(c: p, value: el.toString());
1482 }));
1483 });
1484 cont = cont && self.dvWrapField(visitor, f: Fields::qualifiedImports, obj: m_subImports);
1485 cont = cont && self.dvItemField(visitor, f: Fields::imported, it: [this, &self]() -> DomItem {
1486 return self.subMapItem(map: Map(
1487 self.pathFromOwner().withField(name: Fields::imported),
1488 [this, &self](const DomItem &map, const QString &key) {
1489 return map.subListItem(list: List::fromQList<DomItem>(
1490 pathFromOwner: map.pathFromOwner().withKey(name: key), list: importedItemsWithName(self, name: key),
1491 elWrapper: [](const DomItem &, const PathEls::PathComponent &, const DomItem &el) {
1492 return el;
1493 }));
1494 },
1495 [this, &self](const DomItem &) { return this->importedNames(self); },
1496 QLatin1String("List<Export>")));
1497 });
1498 return cont;
1499}
1500
1501bool PropertyInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1502{
1503 bool cont = true;
1504 cont = cont && self.dvValueField(visitor, f: Fields::propertyDefs, value: propertyDefs);
1505 cont = cont && self.dvValueField(visitor, f: Fields::bindings, value: bindings);
1506 return cont;
1507}
1508
1509BindingValue::BindingValue() : kind(BindingValueKind::Empty) { }
1510
1511BindingValue::BindingValue(const QmlObject &o) : kind(BindingValueKind::Object)
1512{
1513 new (&object) QmlObject(o);
1514}
1515
1516BindingValue::BindingValue(const std::shared_ptr<ScriptExpression> &o)
1517 : kind(BindingValueKind::ScriptExpression)
1518{
1519 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o);
1520}
1521
1522BindingValue::BindingValue(const QList<QmlObject> &l) : kind(BindingValueKind::Array)
1523{
1524 new (&array) QList<QmlObject>(l);
1525}
1526
1527BindingValue::~BindingValue()
1528{
1529 clearValue();
1530}
1531
1532BindingValue::BindingValue(const BindingValue &o) : kind(o.kind)
1533{
1534 switch (o.kind) {
1535 case BindingValueKind::Empty:
1536 break;
1537 case BindingValueKind::Object:
1538 new (&object) QmlObject(o.object);
1539 break;
1540 case BindingValueKind::ScriptExpression:
1541 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
1542 break;
1543 case BindingValueKind::Array:
1544 new (&array) QList<QmlObject>(o.array);
1545 }
1546}
1547
1548BindingValue &BindingValue::operator=(const BindingValue &o)
1549{
1550 clearValue();
1551 kind = o.kind;
1552 switch (o.kind) {
1553 case BindingValueKind::Empty:
1554 break;
1555 case BindingValueKind::Object:
1556 new (&object) QmlObject(o.object);
1557 break;
1558 case BindingValueKind::ScriptExpression:
1559 new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
1560 break;
1561 case BindingValueKind::Array:
1562 new (&array) QList<QmlObject>(o.array);
1563 }
1564 return *this;
1565}
1566
1567DomItem BindingValue::value(const DomItem &binding) const
1568{
1569 switch (kind) {
1570 case BindingValueKind::Empty:
1571 break;
1572 case BindingValueKind::Object:
1573 return binding.copy(base: &object);
1574 case BindingValueKind::ScriptExpression:
1575 return binding.subOwnerItem(c: PathEls::Field(Fields::value), o: scriptExpression);
1576 case BindingValueKind::Array:
1577 return binding.subListItem(list: List::fromQListRef<QmlObject>(
1578 pathFromOwner: binding.pathFromOwner().withField(name: u"value"), list: array,
1579 elWrapper: [](const DomItem &self, const PathEls::PathComponent &, const QmlObject &obj) {
1580 return self.copy(base: &obj);
1581 }));
1582 }
1583 return DomItem();
1584}
1585
1586void BindingValue::updatePathFromOwner(const Path &newPath)
1587{
1588 switch (kind) {
1589 case BindingValueKind::Empty:
1590 break;
1591 case BindingValueKind::Object:
1592 object.updatePathFromOwner(newPath);
1593 break;
1594 case BindingValueKind::ScriptExpression:
1595 break;
1596 case BindingValueKind::Array:
1597 updatePathFromOwnerQList(list&: array, newPath);
1598 break;
1599 }
1600}
1601
1602void BindingValue::clearValue()
1603{
1604 switch (kind) {
1605 case BindingValueKind::Empty:
1606 break;
1607 case BindingValueKind::Object:
1608 object.~QmlObject();
1609 break;
1610 case BindingValueKind::ScriptExpression:
1611 scriptExpression.~shared_ptr();
1612 break;
1613 case BindingValueKind::Array:
1614 array.~QList<QmlObject>();
1615 break;
1616 }
1617 kind = BindingValueKind::Empty;
1618}
1619
1620ScriptExpression::ScriptExpression(
1621 QStringView code, const std::shared_ptr<QQmlJS::Engine> &engine, AST::Node *ast,
1622 const std::shared_ptr<AstComments> &comments, ExpressionType expressionType,
1623 SourceLocation localOffset, int derivedFrom, QStringView preCode, QStringView postCode)
1624 : OwningItem(derivedFrom),
1625 m_expressionType(expressionType),
1626 m_code(code),
1627 m_preCode(preCode),
1628 m_postCode(postCode),
1629 m_engine(engine),
1630 m_ast(ast),
1631 m_astComments(comments),
1632 m_localOffset(localOffset)
1633{
1634 if (m_expressionType == ExpressionType::BindingExpression)
1635 if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(ast: m_ast))
1636 m_ast = exp->expression;
1637 Q_ASSERT(m_astComments);
1638}
1639
1640ScriptExpression::ScriptExpression(const ScriptExpression &e) : OwningItem(e)
1641{
1642 QMutexLocker l(mutex());
1643 m_expressionType = e.m_expressionType;
1644 m_engine = e.m_engine;
1645 m_ast = e.m_ast;
1646 if (m_codeStr.isEmpty()) {
1647 m_code = e.m_code;
1648 } else {
1649 m_codeStr = e.m_codeStr;
1650 m_code = m_codeStr;
1651 }
1652 m_localOffset = e.m_localOffset;
1653 m_astComments = e.m_astComments;
1654}
1655
1656std::shared_ptr<ScriptExpression> ScriptExpression::copyWithUpdatedCode(
1657 const DomItem &self, const QString &code) const
1658{
1659 std::shared_ptr<ScriptExpression> copy = makeCopy(self);
1660 DomItem container = self.containingObject();
1661 QString preCodeStr = container.field(name: Fields::preCode).value().toString(defaultValue: m_preCode.toString());
1662 QString postCodeStr = container.field(name: Fields::postCode).value().toString(defaultValue: m_postCode.toString());
1663 copy->setCode(code, preCode: preCodeStr, postCode: postCodeStr);
1664 return copy;
1665}
1666
1667bool ScriptExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1668{
1669 bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
1670 cont = cont && self.dvValueField(visitor, f: Fields::code, value: code());
1671 if (!preCode().isEmpty())
1672 cont = cont
1673 && self.dvValueField(visitor, f: Fields::preCode, value: preCode(),
1674 options: ConstantData::Options::MapIsMap);
1675 if (!postCode().isEmpty())
1676 cont = cont
1677 && self.dvValueField(visitor, f: Fields::postCode, value: postCode(),
1678 options: ConstantData::Options::MapIsMap);
1679 cont = cont
1680 && self.dvValueLazyField(
1681 visitor, f: Fields::localOffset,
1682 valueF: [this]() { return sourceLocationToQCborValue(loc: localOffset()); },
1683 options: ConstantData::Options::MapIsMap);
1684 cont = cont && self.dvValueLazyField(visitor, f: Fields::astRelocatableDump, valueF: [this]() {
1685 return astRelocatableDump();
1686 });
1687 cont = cont && self.dvValueField(visitor, f: Fields::expressionType, value: int(expressionType()));
1688 if (m_element) {
1689 cont = cont && self.dvItemField(visitor, f: Fields::scriptElement, it: [this, &self]() {
1690 return self.subScriptElementWrapperItem(obj: m_element);
1691 });
1692 }
1693 return cont;
1694}
1695
1696class FirstNodeVisitor : public VisitAll
1697{
1698public:
1699 qsizetype minStart = 0;
1700 qsizetype maxEnd = std::numeric_limits<qint32>::max(); // see also Lexer::checkFileLength().
1701 AST::Node *firstNodeInRange = nullptr;
1702
1703 FirstNodeVisitor(qsizetype minStart, qsizetype maxEnd) : minStart(minStart), maxEnd(maxEnd) { }
1704
1705 bool preVisit(AST::Node *n) override
1706 {
1707 if (!VisitAll::uiKinds().contains(value: n->kind)) {
1708 qsizetype start = n->firstSourceLocation().begin();
1709 qsizetype end = n->lastSourceLocation().end();
1710 if (!firstNodeInRange && minStart <= start && end <= maxEnd && start < end)
1711 firstNodeInRange = n;
1712 }
1713 return !firstNodeInRange;
1714 }
1715};
1716
1717AST::Node *firstNodeInRange(AST::Node *n, qsizetype minStart = 0, qsizetype maxEnd = std::numeric_limits<qint32>::max())
1718{
1719 FirstNodeVisitor visitor(minStart, maxEnd);
1720 AST::Node::accept(node: n, visitor: &visitor);
1721 return visitor.firstNodeInRange;
1722}
1723
1724void ScriptExpression::setCode(const QString &code, const QString &preCode, const QString &postCode)
1725{
1726 // TODO QTBUG-121933
1727 m_codeStr = code;
1728 QString resolvedPreCode, resolvedPostCode;
1729 if (m_expressionType == ExpressionType::BindingExpression && preCode.isEmpty()) {
1730 resolvedPreCode = Binding::preCodeForName(n: u"binding");
1731 resolvedPostCode = Binding::postCodeForName(u"binding");
1732 } else {
1733 resolvedPreCode = preCode;
1734 resolvedPostCode = postCode;
1735 }
1736 if (!resolvedPreCode.isEmpty() || !resolvedPostCode.isEmpty())
1737 m_codeStr = resolvedPreCode + code + resolvedPostCode;
1738 m_code = QStringView(m_codeStr).mid(pos: resolvedPreCode.size(), n: code.size());
1739 m_preCode = QStringView(m_codeStr).mid(pos: 0, n: resolvedPreCode.size());
1740 m_postCode = QStringView(m_codeStr).mid(
1741 pos: resolvedPreCode.size() + code.size(), n: resolvedPostCode.size());
1742 m_engine = nullptr;
1743 m_ast = nullptr;
1744 m_localOffset = SourceLocation();
1745 if (!m_code.isEmpty()) {
1746 IndentInfo preChange(m_preCode, 4);
1747 m_localOffset.offset = m_preCode.size();
1748 m_localOffset.length = m_code.size();
1749 m_localOffset.startColumn = preChange.trailingString.size();
1750 m_localOffset.startLine = preChange.nNewlines;
1751 m_engine = std::make_shared<QQmlJS::Engine>();
1752 m_astComments = std::make_shared<AstComments>(args&: m_engine);
1753 m_ast = parse(mode: resolveParseMode());
1754
1755 if (AST::Program *programPtr = AST::cast<AST::Program *>(ast: m_ast)) {
1756 m_ast = programPtr->statements;
1757 }
1758 if (!m_preCode.isEmpty())
1759 m_ast = firstNodeInRange(n: m_ast, minStart: m_preCode.size(),
1760 maxEnd: m_preCode.size() + m_code.size());
1761 if (auto *sList = AST::cast<AST::FormalParameterList *>(ast: m_ast)) {
1762 m_ast = sList->element;
1763 }
1764 if (m_expressionType != ExpressionType::FunctionBody) {
1765 if (AST::StatementList *sList = AST::cast<AST::StatementList *>(ast: m_ast)) {
1766 if (!sList->next)
1767 m_ast = sList->statement;
1768 }
1769 }
1770 if (m_expressionType == ExpressionType::BindingExpression)
1771 if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(ast: m_ast))
1772 m_ast = exp->expression;
1773
1774 CommentCollector collector;
1775 collector.collectComments(engine: m_engine, rootNode: m_ast, astComments: m_astComments);
1776 }
1777}
1778
1779AST::Node *ScriptExpression::parse(const ParseMode mode)
1780{
1781 QQmlJS::Lexer lexer(m_engine.get());
1782 lexer.setCode(code: m_codeStr, /*lineno = */ 1, /*qmlMode=*/mode == ParseMode::QML);
1783 QQmlJS::Parser parser(m_engine.get());
1784 const bool parserSucceeded = [mode, &parser]() {
1785 switch (mode) {
1786 case ParseMode::QML:
1787 return parser.parse();
1788 case ParseMode::JS:
1789 return parser.parseScript();
1790 case ParseMode::ESM:
1791 return parser.parseModule();
1792 default:
1793 Q_UNREACHABLE_RETURN(false);
1794 }
1795 }();
1796 if (!parserSucceeded) {
1797 addErrorLocal(msg: domParsingErrors().error(message: tr(sourceText: "Parsing of code failed")));
1798 }
1799 const auto messages = parser.diagnosticMessages();
1800 for (const DiagnosticMessage &msg : messages) {
1801 ErrorMessage err = domParsingErrors().errorMessage(msg);
1802 err.location.offset -= m_localOffset.offset;
1803 err.location.startLine -= m_localOffset.startLine;
1804 if (err.location.startLine == 1)
1805 err.location.startColumn -= m_localOffset.startColumn;
1806 addErrorLocal(std::move(err));
1807 }
1808 return parser.rootNode();
1809}
1810
1811void ScriptExpression::astDumper(const Sink &s, AstDumperOptions options) const
1812{
1813 astNodeDumper(s, n: ast(), opt: options, indent: 1, baseIndent: 0, loc2str: [this](SourceLocation astL) {
1814 SourceLocation l = this->locationToLocal(x: astL);
1815 return this->code().mid(pos: l.offset, n: l.length);
1816 });
1817}
1818
1819QString ScriptExpression::astRelocatableDump() const
1820{
1821 return dumperToString(writer: [this](const Sink &s) {
1822 this->astDumper(s, options: AstDumperOption::NoLocations | AstDumperOption::SloppyCompare);
1823 });
1824}
1825
1826void ScriptExpression::writeOut(const DomItem &, OutWriter &lw) const
1827{
1828 reformatAst(lw, script: this);
1829}
1830
1831QStringView ScriptExpression::loc2Str(SourceLocation astL) const
1832{
1833 SourceLocation l = this->locationToLocal(x: astL); // use engine->code() instead?
1834 return this->code().mid(pos: l.offset, n: l.length);
1835}
1836
1837SourceLocation ScriptExpression::globalLocation(const DomItem &self) const
1838{
1839 if (const FileLocations::Tree tree = FileLocations::treeOf(self)) {
1840 return FileLocations::region(fLoc: tree, region: MainRegion);
1841 }
1842 return SourceLocation();
1843}
1844
1845bool PropertyDefinition::isParametricType() const
1846{
1847 return typeName.contains(c: QChar(u'<'));
1848}
1849
1850void PropertyDefinition::writeOut(const DomItem &, OutWriter &lw) const
1851{
1852 lw.ensureNewline();
1853 if (isDefaultMember)
1854 lw.writeRegion(region: DefaultKeywordRegion).ensureSpace();
1855 if (isFinal)
1856 lw.writeRegion(region: FinalKeywordRegion).ensureSpace();
1857 if (isRequired)
1858 lw.writeRegion(region: RequiredKeywordRegion).ensureSpace();
1859 if (isReadonly)
1860 lw.writeRegion(region: ReadonlyKeywordRegion).ensureSpace();
1861 if (!typeName.isEmpty()) {
1862 lw.writeRegion(region: PropertyKeywordRegion).ensureSpace();
1863 lw.writeRegion(region: TypeIdentifierRegion, toWrite: typeName).ensureSpace();
1864 }
1865 lw.writeRegion(region: IdentifierRegion, toWrite: name);
1866}
1867
1868bool MethodInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
1869{
1870 bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor);
1871 cont = cont && self.dvWrapField(visitor, f: Fields::parameters, obj: parameters);
1872 cont = cont && self.dvValueField(visitor, f: Fields::methodType, value: int(methodType));
1873 if (!typeName.isEmpty())
1874 cont = cont && self.dvReferenceField(visitor, f: Fields::type, referencedObject: typePath(self));
1875 if (methodType == MethodType::Method) {
1876 cont = cont && self.dvValueField(visitor, f: Fields::preCode, value: preCode(self));
1877 cont = cont && self.dvValueField(visitor, f: Fields::postCode, value: postCode(self));
1878 cont = cont && self.dvValueField(visitor, f: Fields::isConstructor, value: isConstructor);
1879 }
1880 if (returnType)
1881 cont = cont && self.dvItemField(visitor, f: Fields::returnType, it: [this, &self]() {
1882 return self.subOwnerItem(c: PathEls::Field(Fields::returnType), o: returnType);
1883 });
1884 if (body)
1885 cont = cont && self.dvItemField(visitor, f: Fields::body, it: [this, &self]() {
1886 return self.subOwnerItem(c: PathEls::Field(Fields::body), o: body);
1887 });
1888 return cont;
1889}
1890
1891QString MethodInfo::preCode(const DomItem &self) const
1892{
1893 QString res;
1894 LineWriter lw([&res](QStringView s) { res.append(v: s); }, QLatin1String("*preCode*"));
1895 OutWriter ow(lw);
1896 ow.indentNextlines = true;
1897 ow.skipComments = true;
1898 MockObject standinObj(self.pathFromOwner());
1899 DomItem standin = self.copy(base: &standinObj);
1900 ow.itemStart(it: standin);
1901 ow.writeRegion(region: FunctionKeywordRegion).ensureSpace().writeRegion(region: IdentifierRegion, toWrite: name);
1902 bool first = true;
1903 ow.writeRegion(region: LeftParenthesisRegion);
1904 for (const MethodParameter &mp : parameters) {
1905 if (first) {
1906 first = false;
1907 } else {
1908 ow.writeRegion(region: CommaTokenRegion);
1909 ow.ensureSpace();
1910 }
1911 ow.write(v: mp.value->code());
1912 }
1913 ow.writeRegion(region: RightParenthesisRegion);
1914 ow.ensureSpace().writeRegion(region: LeftBraceRegion);
1915 ow.itemEnd();
1916 ow.eof();
1917 return res;
1918}
1919
1920QString MethodInfo::postCode(const DomItem &) const
1921{
1922 return QLatin1String("\n}\n");
1923}
1924
1925void MethodInfo::writeOutArguments(const DomItem &self, OutWriter &ow) const
1926{
1927 if (parameters.isEmpty() && methodType == MethodType::Signal)
1928 return;
1929
1930 ow.writeRegion(region: LeftParenthesisRegion);
1931 bool first = true;
1932 for (const DomItem &arg : self.field(name: Fields::parameters).values()) {
1933 if (first)
1934 first = false;
1935 else
1936 ow.writeRegion(region: CommaTokenRegion).ensureSpace();
1937 arg.writeOut(lw&: ow);
1938 }
1939 ow.writeRegion(region: RightParenthesisRegion);
1940}
1941
1942void MethodInfo::writeOutReturnType(OutWriter &ow) const
1943{
1944 if (typeName.isEmpty())
1945 return;
1946
1947 ow.writeRegion(region: ColonTokenRegion);
1948 ow.ensureSpace();
1949 ow.writeRegion(region: TypeIdentifierRegion, toWrite: typeName);
1950}
1951
1952void MethodInfo::writeOutBody(const DomItem &self, OutWriter &ow) const
1953{
1954 ow.ensureSpace().writeRegion(region: LeftBraceRegion);
1955 int baseIndent = ow.increaseIndent();
1956 if (DomItem b = self.field(name: Fields::body)) {
1957 ow.ensureNewline();
1958 b.writeOut(lw&: ow);
1959 }
1960 ow.decreaseIndent(level: 1, expectedIndent: baseIndent);
1961 ow.ensureNewline().writeRegion(region: RightBraceRegion);
1962}
1963
1964void MethodInfo::writeOut(const DomItem &self, OutWriter &ow) const
1965{
1966 if (methodType == MethodType::Signal) {
1967 ow.writeRegion(region: SignalKeywordRegion).ensureSpace();
1968 } else {
1969 ow.writeRegion(region: FunctionKeywordRegion).ensureSpace();
1970 }
1971 ow.writeRegion(region: IdentifierRegion, toWrite: name);
1972 writeOutArguments(self, ow);
1973 if (methodType == MethodType::Signal) {
1974 // signal doesn't have returnType or body
1975 return;
1976 }
1977 writeOutReturnType(ow);
1978 writeOutBody(self, ow);
1979}
1980
1981QString MethodInfo::signature(const DomItem &self) const
1982{
1983 QString resultStr;
1984 QTextStream res(&resultStr);
1985 LineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"));
1986 OutWriter ow(lw);
1987 ow.indentNextlines = true;
1988 ow.skipComments = true;
1989
1990 ow.itemStart(it: self);
1991 writeOutArguments(self, ow);
1992
1993 writeOutReturnType(ow);
1994
1995 ow.itemEnd();
1996 lw.eof(ensureNewline: false);
1997 res.flush();
1998 return resultStr.simplified();
1999}
2000
2001bool MethodParameter::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2002{
2003 bool cont = true;
2004 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name);
2005 if (!typeName.isEmpty()) {
2006 cont = cont
2007 && self.dvReferenceField(visitor, f: Fields::type, referencedObject: Paths::lookupTypePath(name: typeName));
2008 cont = cont && self.dvValueField(visitor, f: Fields::typeName, value: typeName);
2009 }
2010 cont = cont && self.dvValueField(visitor, f: Fields::isPointer, value: isPointer);
2011 cont = cont && self.dvValueField(visitor, f: Fields::isReadonly, value: isReadonly);
2012 cont = cont && self.dvValueField(visitor, f: Fields::isList, value: isList);
2013 cont = cont && self.dvWrapField(visitor, f: Fields::defaultValue, obj: defaultValue);
2014 cont = cont && self.dvWrapField(visitor, f: Fields::value, obj: value);
2015
2016 cont = cont && self.dvValueField(visitor, f: Fields::preCode, value: u"function f("_s);
2017 cont = cont && self.dvValueField(visitor, f: Fields::postCode, value: u") {}"_s);
2018
2019 if (!annotations.isEmpty())
2020 cont = cont && self.dvWrapField(visitor, f: Fields::annotations, obj: annotations);
2021 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: comments);
2022 return cont;
2023}
2024
2025void MethodParameter::writeOut(const DomItem &self, OutWriter &ow) const
2026{
2027 if (typeAnnotationStyle == MethodParameter::TypeAnnotationStyle::Prefix) {
2028 writeOutSignal(self, ow);
2029 return;
2030 }
2031
2032 if (!name.isEmpty()) {
2033 if (isRestElement)
2034 ow.writeRegion(region: EllipsisTokenRegion);
2035 ow.writeRegion(region: IdentifierRegion, toWrite: name);
2036 if (!typeName.isEmpty())
2037 ow.writeRegion(region: ColonTokenRegion).ensureSpace().writeRegion(region: TypeIdentifierRegion, toWrite: typeName);
2038 if (defaultValue) {
2039 ow.ensureSpace().writeRegion(region: EqualTokenRegion).ensureSpace();
2040 self.subOwnerItem(c: PathEls::Field(Fields::defaultValue), o: defaultValue).writeOut(lw&: ow);
2041 }
2042 } else {
2043 if (value) {
2044 self.subOwnerItem(c: PathEls::Field(Fields::value), o: value).writeOut(lw&: ow);
2045 }
2046 }
2047}
2048
2049void MethodParameter::writeOutSignal(const DomItem &self, OutWriter &ow) const
2050{
2051 self.writeOutPre(lw&: ow);
2052 if (!typeName.isEmpty())
2053 ow.writeRegion(region: TypeIdentifierRegion, toWrite: typeName).ensureSpace();
2054 ow.writeRegion(region: IdentifierRegion, toWrite: name);
2055 self.writeOutPost(lw&: ow);
2056}
2057
2058void Pragma::writeOut(const DomItem &, OutWriter &ow) const
2059{
2060 ow.ensureNewline();
2061 ow.writeRegion(region: PragmaKeywordRegion).ensureSpace().writeRegion(region: IdentifierRegion, toWrite: name);
2062
2063 bool isFirst = true;
2064 for (const auto &value : values) {
2065 if (isFirst) {
2066 isFirst = false;
2067 ow.writeRegion(region: ColonTokenRegion).ensureSpace();
2068 ow.writeRegion(region: PragmaValuesRegion, toWrite: value);
2069 continue;
2070 }
2071
2072 ow.writeRegion(region: CommaTokenRegion).ensureSpace();
2073 ow.writeRegion(region: PragmaValuesRegion, toWrite: value);
2074 }
2075 ow.ensureNewline();
2076}
2077
2078bool EnumItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2079{
2080 bool cont = true;
2081 cont = cont && self.dvValueField(visitor, f: Fields::name, value: name());
2082 cont = cont && self.dvValueField(visitor, f: Fields::value, value: value());
2083 cont = cont && self.dvWrapField(visitor, f: Fields::comments, obj: m_comments);
2084 return cont;
2085}
2086
2087void EnumItem::writeOut(const DomItem &self, OutWriter &ow) const
2088{
2089 index_type myIndex = self.pathFromOwner().last().headIndex();
2090 if (myIndex != 0)
2091 ow.writeRegion(region: CommaTokenRegion);
2092 ow.ensureNewline();
2093 ow.writeRegion(region: IdentifierRegion, toWrite: name());
2094 if (m_valueKind == ValueKind::ExplicitValue) {
2095 QString v = QString::number(value(), format: 'f', precision: 0);
2096 if (abs(x: value() - v.toDouble()) > 1.e-10)
2097 v = QString::number(value());
2098 ow.ensureSpace().writeRegion(region: EqualTokenRegion).ensureSpace().writeRegion(region: EnumValueRegion, toWrite: v);
2099 }
2100}
2101
2102QmlUri QmlUri::fromString(const QString &str)
2103{
2104 if (str.startsWith(c: u'"'))
2105 return fromDirectoryString(importStr: str.mid(position: 1, n: str.size() - 2)
2106 .replace(before: u"\\\""_s, after: u"\""_s)
2107 .replace(before: u"\\\\"_s, after: u"\\"_s));
2108 else
2109 return fromUriString(importStr: str);
2110}
2111
2112QmlUri QmlUri::fromUriString(const QString &str)
2113{
2114 QRegularExpression moduleUriRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
2115 return QmlUri((moduleUriRe.match(subject: str).hasMatch() ? Kind::ModuleUri : Kind::Invalid), str);
2116}
2117
2118QmlUri QmlUri::fromDirectoryString(const QString &str)
2119{
2120 QUrl url(str);
2121 if (url.isValid() && url.scheme().size() > 1)
2122 return QmlUri(url);
2123 if (!str.isEmpty()) {
2124 QFileInfo path(str);
2125 return QmlUri((path.isRelative() ? Kind::RelativePath : Kind::AbsolutePath), str);
2126 }
2127 return {};
2128}
2129
2130bool QmlUri::isValid() const
2131{
2132 return m_kind != Kind::Invalid;
2133}
2134
2135bool QmlUri::isDirectory() const
2136{
2137 switch (m_kind) {
2138 case Kind::Invalid:
2139 case Kind::ModuleUri:
2140 break;
2141 case Kind::DirectoryUrl:
2142 case Kind::RelativePath:
2143 case Kind::AbsolutePath:
2144 return true;
2145 }
2146 return false;
2147}
2148
2149bool QmlUri::isModule() const
2150{
2151 return m_kind == Kind::ModuleUri;
2152}
2153
2154QString QmlUri::moduleUri() const
2155{
2156 if (m_kind == Kind::ModuleUri)
2157 return std::get<QString>(v: m_value);
2158 return QString();
2159}
2160
2161QString QmlUri::localPath() const
2162{
2163 switch (m_kind) {
2164 case Kind::Invalid:
2165 case Kind::ModuleUri:
2166 break;
2167 case Kind::DirectoryUrl: {
2168 const QUrl &url = std::get<QUrl>(v: m_value);
2169 if (url.scheme().compare(s: u"file", cs: Qt::CaseInsensitive) == 0)
2170 return url.path();
2171 break;
2172 }
2173 case Kind::RelativePath:
2174 case Kind::AbsolutePath:
2175 return std::get<QString>(v: m_value);
2176 }
2177 return QString();
2178}
2179
2180QString QmlUri::absoluteLocalPath(const QString &basePath) const
2181{
2182 switch (m_kind) {
2183 case Kind::Invalid:
2184 case Kind::ModuleUri:
2185 break;
2186 case Kind::DirectoryUrl: {
2187 const QUrl &url = std::get<QUrl>(v: m_value);
2188 if (url.scheme().compare(s: u"file", cs: Qt::CaseInsensitive) == 0)
2189 return url.path();
2190 break;
2191 }
2192 case Kind::RelativePath: {
2193 if (!basePath.isEmpty())
2194 return QDir(basePath).filePath(fileName: std::get<QString>(v: m_value));
2195 break;
2196 }
2197 case Kind::AbsolutePath:
2198 return std::get<QString>(v: m_value);
2199 }
2200 return QString();
2201}
2202
2203QUrl QmlUri::directoryUrl() const
2204{
2205 if (m_kind == Kind::DirectoryUrl)
2206 return std::get<QUrl>(v: m_value);
2207 return QUrl {};
2208}
2209
2210QString QmlUri::directoryString() const
2211{
2212 switch (m_kind) {
2213 case Kind::Invalid:
2214 case Kind::ModuleUri:
2215 break;
2216 case Kind::DirectoryUrl:
2217 return std::get<QUrl>(v: m_value).toString(); // set formatting? options?
2218 case Kind::RelativePath:
2219 case Kind::AbsolutePath:
2220 return std::get<QString>(v: m_value);
2221 }
2222 return QString();
2223}
2224
2225QString QmlUri::toString() const
2226{
2227 switch (m_kind) {
2228 case Kind::Invalid:
2229 break;
2230 case Kind::ModuleUri:
2231 return std::get<QString>(v: m_value);
2232 case Kind::DirectoryUrl:
2233 case Kind::RelativePath:
2234 case Kind::AbsolutePath:
2235 return u"\""_s + directoryString().replace(c: u'\\', after: u"\\\\"_s).replace(c: u'"', after: u"\\\""_s)
2236 + u"\""_s;
2237 }
2238 return QString();
2239}
2240
2241QmlUri::Kind QmlUri::kind() const
2242{
2243 return m_kind;
2244}
2245
2246void ScriptExpression::setScriptElement(const ScriptElementVariant &p)
2247{
2248 m_element = p;
2249}
2250
2251} // end namespace Dom
2252} // end namespace QQmlJS
2253
2254QT_END_NAMESPACE
2255
2256#include "moc_qqmldomelements_p.cpp"
2257

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