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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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