Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmltypecompiler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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// Qt-Security score:significant
4
6
7#include <private/qqmlobjectcreator_p.h>
8#include <private/qqmlcustomparser_p.h>
9#include <private/qqmlvmemetaobject_p.h>
10#include <private/qqmlcomponent_p.h>
11#include <private/qqmlpropertyresolver_p.h>
12#include <private/qqmlcomponentandaliasresolver_p.h>
13#include <private/qqmlsignalnames_p.h>
14
15#include <QtCore/qtyperevision.h>
16
17#define COMPILE_EXCEPTION(token, desc)
18 {
19 recordError((token)->location, desc);
20 return false;
21 }
22
24
25DEFINE_BOOL_CONFIG_OPTION(
26 disableInternalDeferredProperties, QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES);
27
28Q_LOGGING_CATEGORY(lcQmlTypeCompiler, "qt.qml.typecompiler");
29
30QQmlTypeCompiler::QQmlTypeCompiler(
31 QQmlTypeLoader *typeLoader, QQmlTypeData *typeData, QmlIR::Document *parsedQML,
32 QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
33 const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
34 : resolvedTypes(resolvedTypeCache)
35 , loader(typeLoader)
36 , dependencyHasher(dependencyHasher)
37 , document(parsedQML)
38 , typeData(typeData)
39{
40}
41
43{
44 // Build property caches and VME meta object data
45
46 for (auto it = resolvedTypes->constBegin(), end = resolvedTypes->constEnd();
47 it != end; ++it) {
48 QQmlCustomParser *customParser = (*it)->type().customParser();
49 if (customParser)
50 customParsers.insert(it.key(), customParser);
51 }
52
53 QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
54
55
56 {
57 QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(
58 &m_propertyCaches, &pendingGroupPropertyBindings,
59 loader, this, imports(), typeData->typeClassName());
60 QQmlError cycleError = propertyCacheBuilder.verifyNoICCycle();
61 if (cycleError.isValid()) {
62 recordError(cycleError);
63 return nullptr;
64 }
65 QQmlPropertyCacheCreatorBase::IncrementalResult result;
66 do {
67 result = propertyCacheBuilder.buildMetaObjectsIncrementally();
68 const QQmlError &error = result.error;
69 if (error.isValid()) {
70 recordError(error);
71 return nullptr;
72 } else {
73 // Resolve component boundaries and aliases
74
75 QQmlComponentAndAliasResolver resolver(this, &m_propertyCaches);
76 if (QQmlError error = resolver.resolve(result.processedRoot); error.isValid()) {
77 recordError(error);
78 return nullptr;
79 }
80 pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_propertyCaches);
81 pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
82 }
83 } while (result.canResume);
84 }
85
86 {
87 SignalHandlerResolver converter(this);
89 return nullptr;
90 }
91
92 {
93 QQmlEnumTypeResolver enumResolver(this);
94 if (!enumResolver.resolveEnumBindings())
95 return nullptr;
96 }
97
98 {
101 }
102
103 {
104 QQmlAliasAnnotator annotator(this);
106 }
107
108 {
109 QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this);
110 if (!deferredAndCustomParserBindingScanner.scanObject())
111 return nullptr;
112 }
113
114 if (!document->javaScriptCompilationUnit || !document->javaScriptCompilationUnit->unitData()) {
115 // Compile JS binding expressions and signal handlers if necessary
116 {
117 // We can compile script strings ahead of time, but they must be compiled
118 // without type optimizations as their scope is always entirely dynamic.
120 sss.scan();
121 }
122
123 Q_ASSERT(document->jsModule.fileName == typeData->urlString());
124 Q_ASSERT(document->jsModule.finalUrl == typeData->finalUrlString());
125 QmlIR::JSCodeGen v4CodeGenerator(document);
126 for (QmlIR::Object *object : std::as_const(document->objects)) {
127 if (!v4CodeGenerator.generateRuntimeFunctions(object)) {
128 Q_ASSERT(v4CodeGenerator.hasError());
129 recordError(v4CodeGenerator.error());
130 return nullptr;
131 }
132 }
133 document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false);
134 }
135
136 // Generate QML compiled type data structures
137
138 QmlIR::QmlUnitGenerator qmlGenerator;
139 qmlGenerator.generate(*document, dependencyHasher);
140
141 if (!errors.isEmpty())
142 return nullptr;
143
144 return std::move(document->javaScriptCompilationUnit);
145}
146
147void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
148{
149 QQmlError error;
150 error.setLine(qmlConvertSourceCoordinate<quint32, int>(location.line()));
151 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(location.column()));
152 error.setDescription(description);
153 error.setUrl(url());
154 errors << error;
155}
156
157void QQmlTypeCompiler::recordError(const QQmlJS::DiagnosticMessage &message)
158{
159 QQmlError error;
160 error.setDescription(message.message);
161 error.setLine(qmlConvertSourceCoordinate<quint32, int>(message.loc.startLine));
162 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(message.loc.startColumn));
163 error.setUrl(url());
164 errors << error;
165}
166
167void QQmlTypeCompiler::recordError(const QQmlError &e)
168{
169 QQmlError error = e;
170 error.setUrl(url());
171 errors << error;
172}
173
175{
176 return document->stringAt(idx);
177}
178
179int QQmlTypeCompiler::registerString(const QString &str)
180{
181 return document->jsGenerator.registerString(str);
182}
183
184int QQmlTypeCompiler::resolvedIndex(int index) const
185{
186 if (index < objectCount())
187 return index;
188 const auto *p = m_propertyCaches.at(index)->defaultProperty();
189 Q_ASSERT(p && p->isComponentWrapper());
190 return p->wrappedObjectIndex();
191}
192
194{
195 for (int i = objectCount(), end = m_propertyCaches.count(); i < end; ++i) {
196 const auto *p = m_propertyCaches.at(i)->defaultProperty();
197 Q_ASSERT(p && p->isComponentWrapper());
198 if (p->wrappedObjectIndex() == childIndex)
199 return i;
200 }
201 return -1;
202}
203
204int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
205{
206 return document->jsGenerator.registerConstant(v);
207}
208
210{
211 return document->javaScriptCompilationUnit->unitData();
212}
213
214const QQmlImports *QQmlTypeCompiler::imports() const
215{
216 return typeData->imports();
217}
218
220{
221 return &document->objects;
222}
223
225{
226 return &m_propertyCaches;
227}
228
230{
231 return &m_propertyCaches;
232}
233
235{
236 return document->jsParserEngine.pool();
237}
238
240{
241 return document->jsParserEngine.newStringRef(string);
242}
243
245{
246 return &document->jsGenerator.stringTable;
247}
248
249QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const
250{
251 return object->bindingAsString(document, scriptIndex);
252}
253
254void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, QTypeRevision version)
255{
256 const quint32 moduleIdx = registerString(module);
257 const quint32 qualifierIdx = registerString(qualifier);
258
259 for (int i = 0, count = document->imports.size(); i < count; ++i) {
260 const QV4::CompiledData::Import *existingImport = document->imports.at(i);
261 if (existingImport->type == QV4::CompiledData::Import::ImportLibrary
262 && existingImport->uriIndex == moduleIdx
263 && existingImport->qualifierIndex == qualifierIdx)
264 return;
265 }
266 auto pool = memoryPool();
267 QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>();
268 import->type = QV4::CompiledData::Import::ImportLibrary;
269 import->version = version;
270 import->uriIndex = moduleIdx;
271 import->qualifierIndex = qualifierIdx;
272 document->imports.append(import);
273}
274
275QQmlType QQmlTypeCompiler::qmlTypeForComponent(const QString &inlineComponentName) const
276{
277 return typeData->qmlType(inlineComponentName);
278}
279
281 : compiler(typeCompiler)
282{
283}
284
285SignalHandlerResolver::SignalHandlerResolver(QQmlTypeCompiler *typeCompiler)
286 : QQmlCompilePass(typeCompiler)
287 , typeLoader(typeCompiler->typeLoader())
288 , qmlObjects(*typeCompiler->qmlObjects())
289 , imports(typeCompiler->imports())
290 , customParsers(typeCompiler->customParserCache())
291 , propertyCaches(typeCompiler->propertyCaches())
292{
293}
294
296{
297 for (int objectIndex = 0; objectIndex < qmlObjects.size(); ++objectIndex) {
298 const QmlIR::Object * const obj = qmlObjects.at(objectIndex);
299 QQmlPropertyCache::ConstPtr cache = propertyCaches->at(objectIndex);
300 if (!cache)
301 continue;
302 if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) {
303 if (!(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers))
304 continue;
305 }
306 const QString elementName = stringAt(obj->inheritedTypeNameIndex);
307 if (!resolveSignalHandlerExpressions(obj, elementName, cache))
308 return false;
309 }
310 return true;
311}
312
313bool SignalHandlerResolver::resolveSignalHandlerExpressions(
314 const QmlIR::Object *obj, const QString &typeName,
315 const QQmlPropertyCache::ConstPtr &propertyCache,
316 QQmlPropertyResolver::RevisionCheck checkRevision)
317{
318 // map from signal name defined in qml itself to list of parameters
319 QHash<QString, QStringList> customSignals;
320
321 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
322 const QString bindingPropertyName = stringAt(binding->propertyNameIndex);
323 // Attached property?
324 const QV4::CompiledData::Binding::Type bindingType = binding->type();
325 if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) {
326 const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
327 auto *typeRef = resolvedType(binding->propertyNameIndex);
328 QQmlType type = typeRef ? typeRef->type() : QQmlType();
329 if (!type.isValid())
330 imports->resolveType(typeLoader, bindingPropertyName, &type, nullptr, nullptr);
331
332 const QMetaObject *attachedType = type.attachedPropertiesType(typeLoader);
333 if (!attachedType)
334 COMPILE_EXCEPTION(binding, tr("Non-existent attached object"));
335 QQmlPropertyCache::ConstPtr cache = QQmlMetaType::propertyCache(attachedType);
336
337 // Ignore revisions of signals on attached objects. They are not unqualified.
339 attachedObj, bindingPropertyName, cache,
340 QQmlPropertyResolver::IgnoreRevision)) {
341 return false;
342 }
343
344 continue;
345 }
346
347 QString qPropertyName;
348 QString signalName;
349 if (auto propertyName =
350 QQmlSignalNames::changedHandlerNameToPropertyName(bindingPropertyName)) {
351 qPropertyName = *propertyName;
352 signalName = *QQmlSignalNames::changedHandlerNameToSignalName(bindingPropertyName);
353 } else {
354 signalName = QQmlSignalNames::handlerNameToSignalName(bindingPropertyName)
355 .value_or(QString());
356 }
357 if (signalName.isEmpty())
358 continue;
359
360 QQmlPropertyResolver resolver(propertyCache);
361
362 bool notInRevision = false;
363 const QQmlPropertyData *const signal
364 = resolver.signal(signalName, &notInRevision, checkRevision);
365 const QQmlPropertyData *const signalPropertyData
366 = resolver.property(signalName, /*notInRevision ptr*/nullptr, checkRevision);
367 const QQmlPropertyData *const qPropertyData = !qPropertyName.isEmpty()
368 ? resolver.property(qPropertyName, nullptr, checkRevision)
369 : nullptr;
370 QString finalSignalHandlerPropertyName = signalName;
371 QV4::CompiledData::Binding::Flag flag
372 = QV4::CompiledData::Binding::IsSignalHandlerExpression;
373
374 const bool isPropertyObserver
375 = !signalPropertyData && qPropertyData && qPropertyData->notifiesViaBindable();
376 if (signal && !(qPropertyData && qPropertyData->isAlias() && isPropertyObserver)) {
377 int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex());
378 sigIndex = propertyCache->originalClone(sigIndex);
379
380 bool unnamedParameter = false;
381
382 QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex);
383 for (int i = 0; i < parameterNames.size(); ++i) {
384 const QString param = QString::fromUtf8(parameterNames.at(i));
385 if (param.isEmpty())
386 unnamedParameter = true;
387 else if (unnamedParameter) {
388 COMPILE_EXCEPTION(binding, tr("Signal uses unnamed parameter followed by named parameter."));
389 } else if (QV4::Compiler::Codegen::isNameGlobal(param)) {
390 COMPILE_EXCEPTION(binding, tr("Signal parameter \"%1\" hides global variable.").arg(param));
391 }
392 }
393 } else if (isPropertyObserver) {
394 finalSignalHandlerPropertyName = qPropertyName;
395 flag = QV4::CompiledData::Binding::IsPropertyObserver;
396 } else {
397 if (notInRevision) {
398 // Try assinging it as a property later
399 if (signalPropertyData)
400 continue;
401
402 const QString &originalPropertyName = stringAt(binding->propertyNameIndex);
403
404 auto *typeRef = resolvedType(obj->inheritedTypeNameIndex);
405 const QQmlType type = typeRef ? typeRef->type() : QQmlType();
406 if (type.isValid()) {
407 COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.")
408 .arg(typeName, originalPropertyName, type.module())
409 .arg(type.version().majorVersion())
410 .arg(type.version().minorVersion()));
411 } else {
413 binding,
414 tr("\"%1.%2\" is not available due to component versioning.")
415 .arg(typeName, originalPropertyName));
416 }
417 }
418
419 // Try to look up the signal parameter names in the object itself
420
421 // build cache if necessary
422 if (customSignals.isEmpty()) {
423 for (const QmlIR::Signal *signal = obj->firstSignal(); signal; signal = signal->next) {
424 const QString &signalName = stringAt(signal->nameIndex);
425 customSignals.insert(signalName, signal->parameterStringList(compiler->stringPool()));
426 }
427
428 for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) {
429 const QString propName = stringAt(property->nameIndex());
430 customSignals.insert(propName, QStringList());
431 }
432 }
433
434 QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(signalName);
435 if (entry == customSignals.constEnd() && !qPropertyName.isEmpty())
436 entry = customSignals.constFind(qPropertyName);
437
438 if (entry == customSignals.constEnd()) {
439 // Can't find even a custom signal, then just don't do anything and try
440 // keeping the binding as a regular property assignment.
441 continue;
442 }
443 }
444
445 // Binding object to signal means connect the signal to the object's default method.
446 if (bindingType == QV4::CompiledData::Binding::Type_Object) {
447 binding->setFlag(QV4::CompiledData::Binding::IsSignalHandlerObject);
448 continue;
449 }
450
451 if (bindingType != QV4::CompiledData::Binding::Type_Script) {
452 if (bindingType < QV4::CompiledData::Binding::Type_Script) {
453 COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)"));
454 } else {
455 COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment"));
456 }
457 }
458
459 binding->propertyNameIndex = compiler->registerString(finalSignalHandlerPropertyName);
460 binding->setFlag(flag);
461 }
462 return true;
463}
464
465QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler)
466 : QQmlCompilePass(typeCompiler)
467 , qmlObjects(*typeCompiler->qmlObjects())
468 , propertyCaches(typeCompiler->propertyCaches())
469 , imports(typeCompiler->imports())
470{
471}
472
474{
475 for (int i = 0; i < qmlObjects.size(); ++i) {
476 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i);
477 if (!propertyCache)
478 continue;
479 const QmlIR::Object *obj = qmlObjects.at(i);
480
481 QQmlPropertyResolver resolver(propertyCache);
482
483 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
484 const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags();
485 if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression
486 || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject
487 || bindingFlags & QV4::CompiledData::Binding::IsPropertyObserver)
488 continue;
489
490 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
491 continue;
492
493 const QString propertyName = stringAt(binding->propertyNameIndex);
494 bool notInRevision = false;
495 const QQmlPropertyData *pd = resolver.property(propertyName, &notInRevision);
496 if (!pd || pd->isQList())
497 continue;
498
499 if (!pd->isEnum() && pd->propType().id() != QMetaType::Int)
500 continue;
501
502 if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding))
503 return false;
504 }
505 }
506
507 return true;
508}
509
510bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, QStringView, int enumValue, bool)
511{
512 binding->setType(QV4::CompiledData::Binding::Type_Number);
513 binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue));
514// binding->setNumberValueInternal((double)enumValue);
515 binding->setFlag(QV4::CompiledData::Binding::IsResolvedEnum);
516 return true;
517}
518
519bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(
520 const QmlIR::Object *obj, const QQmlPropertyCache::ConstPtr &propertyCache,
521 const QQmlPropertyData *prop, QmlIR::Binding *binding)
522{
523 bool isIntProp = (prop->propType().id() == QMetaType::Int) && !prop->isEnum();
524 if (!prop->isEnum() && !isIntProp)
525 return true;
526
527 if (!prop->isWritable()
528 && !(binding->hasFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))) {
529 COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property")
530 .arg(stringAt(binding->propertyNameIndex)));
531 }
532
533 Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
534 const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
535 if (!string.constData()->isUpper())
536 return true;
537
538 // reject any "complex" expression (even simple arithmetic)
539 // we do this by excluding everything that is not part of a
540 // valid identifier or a dot
541 for (const QChar &c : string)
542 if (!(c.isLetterOrNumber() || c == u'.' || c == u'_' || c.isSpace()))
543 return true;
544
545 // we support one or two '.' in the enum phrase:
546 // * <TypeName>.<EnumValue>
547 // * <TypeName>.<ScopedEnumName>.<EnumValue>
548
549 int dot = string.indexOf(QLatin1Char('.'));
550 if (dot == -1 || dot == string.size()-1)
551 return true;
552
553 int dot2 = string.indexOf(QLatin1Char('.'), dot+1);
554 if (dot2 != -1 && dot2 != string.size()-1) {
555 if (!string.at(dot+1).isUpper())
556 return true;
557 if (string.indexOf(QLatin1Char('.'), dot2+1) != -1)
558 return true;
559 }
560
561 QHashedStringRef typeName(string.constData(), dot);
562 const bool isQtObject = (typeName == QLatin1String("Qt"));
563 const QStringView scopedEnumName = (dot2 != -1 ? QStringView{string}.mid(dot + 1, dot2 - dot - 1) : QStringView());
564 // ### consider supporting scoped enums in Qt namespace
565 const QStringView enumValue = QStringView{string}.mid(!isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1);
566
567 if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here?
568 // Allow enum assignment to ints.
569 bool ok;
570 int enumval = evaluateEnum(typeName.toString(), scopedEnumName, enumValue, &ok);
571 if (ok) {
572 if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject))
573 return false;
574 }
575 return true;
576 }
577 QQmlType type;
578 imports->resolveType(compiler->typeLoader(), typeName, &type, nullptr, nullptr);
579
580 if (!type.isValid() && !isQtObject)
581 return true;
582
583 int value = 0;
584 bool ok = false;
585
586 auto *tr = resolvedType(obj->inheritedTypeNameIndex);
587
588 // When these two match, we can short cut the search, unless...
589 bool useFastPath = type.isValid() && tr && tr->type() == type;
590 QMetaProperty mprop;
591 QMetaEnum menum;
592 if (useFastPath) {
593 mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex());
594 menum = mprop.enumerator();
595 // ...the enumerator merely comes from a related metaobject, but the enum scope does not match
596 // the typename we resolved
597 if (!menum.isScoped() && scopedEnumName.isEmpty() && typeName != QString::fromUtf8(menum.scope()))
598 useFastPath = false;;
599 }
600 if (useFastPath) {
601 QByteArray enumName = enumValue.toUtf8();
602 if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8())
603 return true;
604
605 if (mprop.isFlagType()) {
606 value = menum.keysToValue(enumName.constData(), &ok);
607 } else {
608 value = menum.keyToValue(enumName.constData(), &ok);
609 }
610 } else {
611 // Otherwise we have to search the whole type
612 if (type.isValid()) {
613 if (!scopedEnumName.isEmpty()) {
614 value = type.scopedEnumValue(
615 compiler->typeLoader(), scopedEnumName, enumValue, &ok);
616 } else {
617 value = type.enumValue(compiler->typeLoader(), QHashedStringRef(enumValue), &ok);
618 }
619 } else {
620 QByteArray enumName = enumValue.toUtf8();
621 const QMetaObject *metaObject = &Qt::staticMetaObject;
622 for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
623 QMetaEnum e = metaObject->enumerator(ii);
624 value = e.keyToValue(enumName.constData(), &ok);
625 }
626 }
627 }
628
629 if (!ok)
630 return true;
631
632 return assignEnumToBinding(binding, enumValue, value, isQtObject);
633}
634
635int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, QStringView enumName, QStringView enumValue, bool *ok) const
636{
637 Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer");
638 *ok = false;
639
640 if (scope != QLatin1String("Qt")) {
641 QQmlType type;
642 imports->resolveType(compiler->typeLoader(), scope, &type, nullptr, nullptr);
643 if (!type.isValid())
644 return -1;
645 if (!enumName.isEmpty())
646 return type.scopedEnumValue(compiler->typeLoader(), enumName, enumValue, ok);
647 return type.enumValue(
648 compiler->typeLoader(),
649 QHashedStringRef(enumValue.constData(), enumValue.size()), ok);
650 }
651
652 const QMetaObject *mo = &Qt::staticMetaObject;
653 int i = mo->enumeratorCount();
654 const QByteArray ba = enumValue.toUtf8();
655 while (i--) {
656 int v = mo->enumerator(i).keyToValue(ba.constData(), ok);
657 if (*ok)
658 return v;
659 }
660 return -1;
661}
662
669
671{
672 scanObjectRecursively(/*root object*/0);
673 for (int i = 0; i < qmlObjects.size(); ++i)
674 if (qmlObjects.at(i)->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
675 scanObjectRecursively(i);
676}
677
678void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings)
679{
680 const QmlIR::Object * const obj = qmlObjects.at(objectIndex);
681 if (!annotateScriptBindings)
682 annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex);
683 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
684 switch (binding->type()) {
685 case QV4::CompiledData::Binding::Type_Script:
686 if (annotateScriptBindings) {
687 binding->stringIndex = compiler->registerString(
688 compiler->bindingAsString(obj, binding->value.compiledScriptIndex));
689 }
690 break;
691 case QV4::CompiledData::Binding::Type_Object:
692 case QV4::CompiledData::Binding::Type_AttachedProperty:
693 case QV4::CompiledData::Binding::Type_GroupProperty:
694 scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings);
695 break;
696 default:
697 break;
698 }
699 }
700}
701
708
710{
711 for (int i = 0; i < qmlObjects.size(); ++i) {
712 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i);
713 if (!propertyCache)
714 continue;
715
716 const QmlIR::Object *obj = qmlObjects.at(i);
717
718 QQmlPropertyResolver resolver(propertyCache);
719 const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
720
721 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
722 if (!binding->isValueBinding())
723 continue;
724 bool notInRevision = false;
725 const QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
726 if (pd && pd->isAlias())
727 binding->setFlag(QV4::CompiledData::Binding::IsBindingToAlias);
728 }
729 }
730}
731
739
741{
742 const QMetaType scriptStringMetaType = QMetaType::fromType<QQmlScriptString>();
743 for (int i = 0; i < qmlObjects.size(); ++i) {
744 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(i);
745 if (!propertyCache)
746 continue;
747
748 const QmlIR::Object *obj = qmlObjects.at(i);
749
750 QQmlPropertyResolver resolver(propertyCache);
751 const QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
752
753 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
754 if (binding->type() != QV4::CompiledData::Binding::Type_Script)
755 continue;
756 bool notInRevision = false;
757 const QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
758 if (!pd || pd->propType() != scriptStringMetaType)
759 continue;
760
761 QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
762 binding->stringIndex = compiler->registerString(script);
763 }
764 }
765}
766
767template<>
768void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::allocateNamedObjects(
769 QmlIR::Object *object) const
770{
771 object->namedObjectsInComponent.allocate(m_compiler->memoryPool(), m_idToObjectIndex);
772}
773
774template<>
775bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::markAsComponent(int index) const
776{
777 m_compiler->qmlObjects()->at(index)->flags |= QV4::CompiledData::Object::IsComponent;
778 return true;
779}
780
781template<>
782void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::setObjectId(int index) const
783{
784 m_compiler->qmlObjects()->at(index)->id = m_idToObjectIndex.size();
785}
786
787template<>
788void QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveGeneralizedGroupProperty(
789 const CompiledObject &component, CompiledBinding *binding)
790{
791 Q_UNUSED(component);
792 // We cannot make it fail here. It might be a custom-parsed property
793 const int targetObjectIndex = m_idToObjectIndex.value(binding->propertyNameIndex, -1);
794 if (targetObjectIndex != -1)
795 m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(targetObjectIndex));
796}
797
798/*!
799 \internal
800
801 Attempts to resolve a "deep alias" — an alias whose sub-property path
802 goes through a QObject property, an inline component binding, or another
803 alias. For example: \c{alias foo: target.groupProp.innerProp}
804
805 First searches the target object's bindings and aliases for \a property,
806 then looks up \a subProperty on the bound/aliased object's property cache.
807 If no binding or alias matches, falls back to looking up \a subProperty
808 on the declared type's property cache.
809
810 On success, updates \a propIdx with the resolved value-type index and
811 returns \c true.
812*/
814 QQmlTypeCompiler *compiler, const QmlIR::Object *targetObject,
815 QStringView property, QStringView subProperty,
816 QQmlPropertyIndex &propIdx, QMetaType targetPropertyType,
817 const QQmlPropertyCacheVector *propertyCaches,
818 const QMap<int, int> &idToObjectIndex)
819{
820 for (auto it = targetObject->bindingsBegin(), end = targetObject->bindingsEnd();
821 it != end; ++it) {
822 auto binding = *it;
823 if (compiler->stringAt(binding.propertyNameIndex) != property)
824 continue;
825 const auto &cache = propertyCaches->at(binding.value.objectIndex);
826 if (!cache)
827 continue;
828 QQmlPropertyResolver resolver(cache);
829 const QQmlPropertyData *pd = resolver.property(subProperty.toString());
830 if (!pd)
831 continue;
832 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), pd->coreIndex());
833 return true;
834 }
835
836 for (auto it = targetObject->aliasesBegin(), end = targetObject->aliasesEnd();
837 it != end; ++it) {
838 auto innerAlias = *it;
839 if (compiler->stringAt(innerAlias.nameIndex()) != property)
840 continue;
841 const int innerObjectIndex = idToObjectIndex.value(innerAlias.idIndex(), -1);
842 if (innerObjectIndex == -1)
843 continue;
844 const auto &cache = propertyCaches->at(innerObjectIndex);
845 if (!cache)
846 continue;
847 QQmlPropertyResolver resolver(cache);
848 const QQmlPropertyData *pd = resolver.property(subProperty.toString());
849 if (!pd)
850 continue;
851 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), pd->coreIndex());
852 return true;
853 }
854
855 const QQmlPropertyCache::ConstPtr typeCache
856 = QQmlMetaType::propertyCacheForType(targetPropertyType);
857 if (typeCache) {
858 const QQmlPropertyResolver resolver(typeCache);
859 const QQmlPropertyData *pd = resolver.property(subProperty.toString());
860 if (pd) {
861 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), pd->coreIndex());
862 return true;
863 }
864 }
865
866 return false;
867}
868
869template<>
870typename QQmlComponentAndAliasResolver<QQmlTypeCompiler>::AliasResolutionResult
871QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
872 const CompiledObject &component, int objectIndex,
873 QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> *aliasCacheCreator, QQmlError *error)
874{
875 Q_UNUSED(component);
876
877 const QmlIR::Object *obj = m_compiler->objectAt(objectIndex);
878 if (!obj->aliasCount())
879 return AllAliasesResolved;
880
881 int aliasIndex = 0;
882 int numSkippedAliases = 0;
883 bool hasUnresolvedLocalAliases = false;
884
885 for (const QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next, ++aliasIndex) {
886 if (resolvedAliases.contains(alias)) {
887 ++numSkippedAliases;
888 continue;
889 }
890
891
892 const int idIndex = alias->idIndex();
893 const int targetObjectIndex = m_idToObjectIndex.value(idIndex, -1);
894 if (targetObjectIndex == -1) {
895 *error = qQmlCompileError(
896 alias->referenceLocation(),
897 QQmlComponentAndAliasResolverBase::tr(
898 "Invalid alias reference. Unable to find id \"%1\"")
899 .arg(stringAt(idIndex)));
900 break;
901 }
902
903 const QmlIR::Object *targetObject = m_compiler->objectAt(targetObjectIndex);
904 Q_ASSERT(targetObject->id >= 0);
905 const int resolvedTargetObjectId = targetObject->id;
906
907 const QString aliasPropertyValue = stringAt(alias->propertyNameIndex());
908
909 QStringView property;
910 QStringView subProperty;
911
912 const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
913 if (propertySeparator != -1) {
914 property = QStringView{aliasPropertyValue}.left(propertySeparator);
915 subProperty = QStringView{aliasPropertyValue}.mid(propertySeparator + 1);
916 } else
917 property = QStringView(aliasPropertyValue);
918
919 QQmlPropertyIndex propIdx;
920
921 if (property.isEmpty()) {
922 // Alias points to object. No need to resolve properties.
923 } else {
924 QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
925 if (!targetCache) {
926 *error = qQmlCompileError(
927 alias->referenceLocation(),
928 QQmlComponentAndAliasResolverBase::tr("Invalid alias target location: %1")
929 .arg(property.toString()));
930 break;
931 }
932
933 QQmlPropertyResolver resolver(targetCache);
934
935 const QQmlPropertyData *targetProperty = resolver.property(
936 property.toString(), nullptr, QQmlPropertyResolver::IgnoreRevision);
937
938 // If it's an alias that we haven't resolved yet, try again later.
939 if (!targetProperty) {
940 bool aliasPointsToOtherAlias = false;
941 auto targetAlias = targetObject->aliasesBegin();
942 for (const auto end = targetObject->aliasesEnd(); targetAlias != end;
943 ++targetAlias) {
944 if (stringAt(targetAlias->nameIndex()) == property) {
945 aliasPointsToOtherAlias = true;
946 break;
947 }
948 }
949 if (aliasPointsToOtherAlias) {
950 if (targetObjectIndex != objectIndex) {
951 // Don't continue, yet. We need to respect the order of objects.
952 return aliasIndex == numSkippedAliases
953 ? NoAliasResolved
954 : SomeAliasesResolved;
955 }
956
957 if (resolvedAliases.contains(targetAlias)) {
958 // Target already resolved. We can set the alias right away.
959 if (!appendAliasToPropertyCache(
960 &component, alias, objectIndex, aliasIndex, -1,
961 resolvedTargetObjectId, aliasCacheCreator, error)) {
962 break;
963 }
964 continue;
965 }
966
967 // Target isn't resolved yet, but it's in the same object.
968 // Continue with the other aliases.
969 // Try again later and resolve the target alias first.
970 ++numSkippedAliases;
971 hasUnresolvedLocalAliases = true;
972 continue;
973 }
974 }
975
976 if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) {
977 *error = qQmlCompileError(
978 alias->referenceLocation(),
979 QQmlComponentAndAliasResolverBase::tr("Invalid alias target location: %1")
980 .arg(property.toString()));
981 break;
982 }
983
984 propIdx = QQmlPropertyIndex(targetProperty->coreIndex());
985
986 if (!subProperty.isEmpty()) {
987 const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(targetProperty->propType());
988 if (!valueTypeMetaObject) {
989 // could be a deep alias
990 bool isDeepAlias = subProperty.at(0).isLower();
991 if (isDeepAlias) {
992 isDeepAlias = resolveDeepAlias(
993 m_compiler, targetObject, property, subProperty, propIdx,
994 targetProperty->propType(), m_propertyCaches, m_idToObjectIndex);
995 }
996 if (!isDeepAlias) {
997 *error = qQmlCompileError(
998 alias->referenceLocation(),
999 QQmlComponentAndAliasResolverBase::tr(
1000 "Invalid alias target location: %1")
1001 .arg(subProperty.toString()));
1002 break;
1003 }
1004 } else {
1005
1006 int valueTypeIndex =
1007 valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
1008 if (valueTypeIndex == -1) {
1009 *error = qQmlCompileError(
1010 alias->referenceLocation(),
1011 QQmlComponentAndAliasResolverBase::tr(
1012 "Invalid alias target location: %1")
1013 .arg(subProperty.toString()));
1014 break;
1015 }
1016 Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
1017
1018 propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
1019 }
1020 }
1021 }
1022
1023 if (!appendAliasToPropertyCache(
1024 &component, alias, objectIndex, aliasIndex, propIdx.toEncoded(),
1025 resolvedTargetObjectId, aliasCacheCreator, error)) {
1026 break;
1027 }
1028 }
1029
1030 if (numSkippedAliases == aliasIndex)
1031 return NoAliasResolved;
1032
1033 if (aliasIndex == obj->aliasCount() && !hasUnresolvedLocalAliases)
1034 return AllAliasesResolved;
1035
1036 return SomeAliasesResolved;
1037}
1038
1039QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler)
1040 : QQmlCompilePass(typeCompiler)
1041 , qmlObjects(typeCompiler->qmlObjects())
1042 , propertyCaches(typeCompiler->propertyCaches())
1043 , customParsers(typeCompiler->customParserCache())
1044 , _seenObjectWithId(false)
1045{
1046}
1047
1049{
1050 for (int i = 0; i < qmlObjects->size(); ++i) {
1051 if ((qmlObjects->at(i)->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
1052 && !scanObject(i, ScopeDeferred::False)) {
1053 return false;
1054 }
1055 }
1056 return scanObject(/*root object*/0, ScopeDeferred::False);
1057}
1058
1060 int objectIndex, ScopeDeferred scopeDeferred)
1061{
1062 using namespace QV4::CompiledData;
1063
1064 QmlIR::Object *obj = qmlObjects->at(objectIndex);
1065 if (obj->idNameIndex != 0)
1066 _seenObjectWithId = true;
1067
1068 if (obj->flags & Object::IsComponent) {
1069 Q_ASSERT(obj->bindingCount() == 1);
1070 const Binding *componentBinding = obj->firstBinding();
1071 Q_ASSERT(componentBinding->type() == Binding::Type_Object);
1072 // Components are separate from their surrounding scope. They cannot be deferred.
1073 return scanObject(componentBinding->value.objectIndex, ScopeDeferred::False);
1074 }
1075
1076 QQmlPropertyCache::ConstPtr propertyCache = propertyCaches->at(objectIndex);
1077 if (!propertyCache)
1078 return true;
1079
1080 QString defaultPropertyName;
1081 const QQmlPropertyData *defaultProperty = nullptr;
1082 if (obj->indexOfDefaultPropertyOrAlias != -1) {
1083 const QQmlPropertyCache *cache = propertyCache->parent().data();
1084 defaultPropertyName = cache->defaultPropertyName();
1085 defaultProperty = cache->defaultProperty();
1086 } else {
1087 defaultPropertyName = propertyCache->defaultPropertyName();
1088 defaultProperty = propertyCache->defaultProperty();
1089 }
1090
1091 QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex);
1092
1093 QQmlPropertyResolver propertyResolver(propertyCache);
1094
1095 QStringList deferredPropertyNames;
1096 QStringList immediatePropertyNames;
1097 {
1098 const QMetaObject *mo = propertyCache->firstCppMetaObject();
1099 const int deferredNamesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
1100 const int immediateNamesIndex = mo->indexOfClassInfo("ImmediatePropertyNames");
1101 if (deferredNamesIndex != -1) {
1102 if (immediateNamesIndex != -1) {
1103 COMPILE_EXCEPTION(obj, tr("You cannot define both DeferredPropertyNames and "
1104 "ImmediatePropertyNames on the same type."));
1105 }
1106 const QMetaClassInfo classInfo = mo->classInfo(deferredNamesIndex);
1107 deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(u',');
1108 } else if (immediateNamesIndex != -1) {
1109 const QMetaClassInfo classInfo = mo->classInfo(immediateNamesIndex);
1110 immediatePropertyNames = QString::fromUtf8(classInfo.value()).split(u',');
1111
1112 // If the property contains an empty string, all properties shall be deferred.
1113 if (immediatePropertyNames.isEmpty())
1114 immediatePropertyNames.append(QString());
1115 }
1116 }
1117
1118 for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
1119 QString name = stringAt(binding->propertyNameIndex);
1120
1121 if (customParser) {
1122 if (binding->type() == Binding::Type_AttachedProperty) {
1123 if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
1124 binding->setFlag(Binding::IsCustomParserBinding);
1125 obj->flags |= Object::HasCustomParserBindings;
1126 continue;
1127 }
1128 } else if (QQmlSignalNames::isHandlerName(name)
1129 && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
1130 obj->flags |= Object::HasCustomParserBindings;
1131 binding->setFlag(Binding::IsCustomParserBinding);
1132 continue;
1133 }
1134 }
1135
1136 const bool hasPropertyData = [&]() {
1137 if (name.isEmpty()) {
1138 name = defaultPropertyName;
1139 if (defaultProperty)
1140 return true;
1141 } else if (name.constData()->isUpper()) {
1142 // Upper case names cannot be custom-parsed unless they are attached properties
1143 // and the custom parser explicitly accepts them. See above for that case.
1144 return false;
1145 } else {
1146 bool notInRevision = false;
1147 if (propertyResolver.property(
1148 name, &notInRevision, QQmlPropertyResolver::CheckRevision)) {
1149 return true;
1150 }
1151 }
1152
1153 if (!customParser)
1154 return false;
1155
1156 const Binding::Flags bindingFlags = binding->flags();
1157 if (bindingFlags & Binding::IsSignalHandlerExpression
1158 || bindingFlags & Binding::IsSignalHandlerObject
1159 || bindingFlags & Binding::IsPropertyObserver) {
1160 // These signal handlers cannot be custom-parsed. We have already established
1161 // that the signal exists.
1162 return false;
1163 }
1164
1165 // If the property isn't found, we may want to custom-parse the binding.
1166 obj->flags |= Object::HasCustomParserBindings;
1167 binding->setFlag(Binding::IsCustomParserBinding);
1168 return false;
1169 }();
1170
1171 bool seenSubObjectWithId = false;
1172 bool isExternal = false;
1173 if (binding->type() >= Binding::Type_Object) {
1174 const bool isOwnProperty = hasPropertyData || binding->isAttachedProperty();
1175 isExternal = !isOwnProperty && binding->isGroupProperty();
1176 if (isOwnProperty || isExternal) {
1177 qSwap(_seenObjectWithId, seenSubObjectWithId);
1178
1179 // Implicit component wrappers are scope boundaries.
1180 const bool isImplicitComponent
1181 = compiler->implicitComponentForObject(binding->value.objectIndex) != -1;
1182
1183 const ScopeDeferred objectScope =
1184 !isImplicitComponent && (isExternal || scopeDeferred == ScopeDeferred::True)
1185 ? ScopeDeferred::True
1186 : ScopeDeferred::False;
1187 const bool subObjectValid = scanObject(binding->value.objectIndex, objectScope);
1188 qSwap(_seenObjectWithId, seenSubObjectWithId);
1189 if (!subObjectValid)
1190 return false;
1191 _seenObjectWithId |= seenSubObjectWithId;
1192 }
1193 }
1194
1195 bool isDeferred = false;
1196 if (!immediatePropertyNames.isEmpty() && !immediatePropertyNames.contains(name)) {
1197 if (seenSubObjectWithId) {
1198 COMPILE_EXCEPTION(binding, tr("You cannot assign an id to an object assigned "
1199 "to a deferred property."));
1200 }
1201 if (isExternal || !disableInternalDeferredProperties())
1202 isDeferred = true;
1203 } else if (!deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
1204 if (!seenSubObjectWithId && binding->type() != Binding::Type_GroupProperty) {
1205 if (isExternal || !disableInternalDeferredProperties())
1206 isDeferred = true;
1207 }
1208 }
1209
1210 if (binding->type() >= Binding::Type_Object) {
1211 if (isExternal && !isDeferred && !customParser) {
1213 binding, tr("Cannot assign to non-existent property \"%1\"").arg(name));
1214 }
1215 }
1216
1217 if (isDeferred) {
1218 binding->setFlag(Binding::IsDeferredBinding);
1219 obj->flags |= Object::HasDeferredBindings;
1220 }
1221 }
1222
1223 return true;
1224}
1225
1226QT_END_NAMESPACE
QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler)
QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler)
QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler)
\inmodule QtCore
Combined button and popup list for selecting options.
Definition qjsvalue.h:24
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define COMPILE_EXCEPTION(location, desc)
static bool resolveDeepAlias(QQmlTypeCompiler *compiler, const QmlIR::Object *targetObject, QStringView property, QStringView subProperty, QQmlPropertyIndex &propIdx, QMetaType targetPropertyType, const QQmlPropertyCacheVector *propertyCaches, const QMap< int, int > &idToObjectIndex)
QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
QQmlTypeCompiler * compiler
int implicitComponentForObject(int childIndex) const
int registerConstant(QV4::ReturnedValue v)
void recordError(const QQmlJS::DiagnosticMessage &message)
const QV4::CompiledData::Unit * qmlUnit() const
QQmlType qmlTypeForComponent(const QString &inlineComponentName=QString()) const
QList< QmlIR::Object * > * qmlObjects() const
QQmlPropertyCacheVector * propertyCaches()
QQmlJS::MemoryPool * memoryPool()
void recordError(const QQmlError &e)
QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const
const QQmlPropertyCacheVector * propertyCaches() const
int registerString(const QString &str)
const QV4::Compiler::StringTableGenerator * stringPool() const
void recordError(const QV4::CompiledData::Location &location, const QString &description)
QStringView newStringRef(const QString &string)
int resolvedIndex(int index) const
void addImport(const QString &module, const QString &qualifier, QTypeRevision version)
QQmlRefPointer< QV4::CompiledData::CompilationUnit > compile()
QString stringAt(int idx) const
const QQmlImports * imports() const