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