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
qqmltypedata.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
5#include <private/qqmlcomponentandaliasresolver_p.h>
6#include <private/qqmlengine_p.h>
7#include <private/qqmlirbuilder_p.h>
8#include <private/qqmlirloader_p.h>
9#include <private/qqmlpropertycachecreator_p.h>
10#include <private/qqmlpropertyvalidator_p.h>
11#include <private/qqmlscriptblob_p.h>
12#include <private/qqmlscriptdata_p.h>
13#include <private/qqmltypecompiler_p.h>
14#include <private/qqmltypedata_p.h>
15#include <private/qqmltypeloaderqmldircontent_p.h>
16
17#include <QtCore/qloggingcategory.h>
18#include <QtCore/qcryptographichash.h>
19
20#include <memory>
21
23
24Q_LOGGING_CATEGORY(lcCycle, "qt.qml.typeresolution.cycle", QtWarningMsg)
25
26QString QQmlTypeData::TypeReference::qualifiedName() const
27{
28 QString result;
29 if (!prefix.isEmpty()) {
30 result = prefix + QLatin1Char('.');
31 }
32 result.append(type.qmlTypeName());
33 return result;
34}
35
36QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager)
37 : QQmlNotifyingBlob(url, QmlFile, manager),
38 m_typesResolved(false), m_implicitImportLoaded(false)
39{
40
41}
42
43QQmlTypeData::~QQmlTypeData()
44{
45 m_scripts.clear();
46 m_compositeSingletons.clear();
47 m_resolvedTypes.clear();
48}
49
50QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const
51{
52 return m_compiledData.data();
53}
54
55QQmlType QQmlTypeData::qmlType(const QString &inlineComponentName) const
56{
57 if (inlineComponentName.isEmpty())
58 return m_qmlType;
59 return m_inlineComponentData[inlineComponentName].qmlType;
60}
61
62bool QQmlTypeData::tryLoadFromDiskCache()
63{
64 assertTypeLoaderThread();
65
66 if (!m_backupSourceCode.isCacheable())
67 return false;
68
69 if (!m_typeLoader->readCacheFile())
70 return false;
71
72 auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
73 {
74 QString error;
75 const auto sourceChecksum = [this]() { return m_backupSourceCode.checksum(); };
76 if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), sourceChecksum,
77 &error)) {
78 qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error;
79 return false;
80 }
81 }
82
83 if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
84 restoreIR(unit);
85 return true;
86 }
87
88 return loadFromDiskCache(unit);
89}
90
91bool QQmlTypeData::loadFromDiskCache(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
92{
93 assertTypeLoaderThread();
94
95 m_compiledData = unit;
96
97 QList<QV4::CompiledData::InlineComponent> ics;
98 for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) {
99 auto object = m_compiledData->objectAt(i);
100 m_typeReferences.collectFromObject(object);
101 const auto inlineComponentTable = object->inlineComponentTable();
102 for (auto i = 0; i != object->nInlineComponents; ++i) {
103 ics.push_back(inlineComponentTable[i]);
104 }
105 }
106
107 m_importCache->setBaseUrl(finalUrl(), finalUrlString());
108
109 // For remote URLs, we don't delay the loading of the implicit import
110 // because the loading probably requires an asynchronous fetch of the
111 // qmldir (so we can't load it just in time).
112 if (!finalUrl().scheme().isEmpty()) {
113 QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
114 if (!QQmlImports::isLocal(qmldirUrl)) {
115 if (!loadImplicitImport())
116 return false;
117
118 // find the implicit import
119 for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) {
120 const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
121 if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".")
122 && import->qualifierIndex == 0
123 && !import->version.hasMajorVersion()
124 && !import->version.hasMinorVersion()) {
125 QList<QQmlError> errors;
126 auto pendingImport = std::make_shared<PendingImport>(
127 this, import, QQmlImports::ImportNoFlag);
128 pendingImport->precedence = QQmlImportInstance::Implicit;
129 if (!fetchQmldir(qmldirUrl, std::move(pendingImport), 1, &errors)) {
130 setError(errors);
131 return false;
132 }
133 break;
134 }
135 }
136 }
137 }
138
139 for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) {
140 const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
141 QList<QQmlError> errors;
142 if (!addImport(import, {}, &errors)) {
143 Q_ASSERT(errors.size());
144 QQmlError error(errors.takeFirst());
145 error.setUrl(m_importCache->baseUrl());
146 error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
147 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
148 errors.prepend(error); // put it back on the list after filling out information.
149 setError(errors);
150 return false;
151 }
152 }
153
154 for (auto&& ic: ics) {
155 QString const nameString = m_compiledData->stringAt(ic.nameIndex);
156 auto importUrl = finalUrl();
157 importUrl.setFragment(nameString);
158 auto import = new QQmlImportInstance();
159 m_importCache->addInlineComponentImport(import, nameString, importUrl);
160 }
161
162 return true;
163}
164
165template<>
166void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::allocateNamedObjects(
167 const QV4::CompiledData::Object *object) const
168{
169 Q_UNUSED(object);
170}
171
172template<>
173bool QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::markAsComponent(int index) const
174{
175 return m_compiler->objectAt(index)->hasFlag(QV4::CompiledData::Object::IsComponent);
176}
177
178template<>
179void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::setObjectId(int index) const
180{
181 Q_UNUSED(index)
182 // we cannot sanity-check the index here because bindings are sorted in a different order
183 // in the CU vs the IR.
184}
185
186template<>
187void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveGeneralizedGroupProperty(
188 const CompiledObject &component, CompiledBinding *binding)
189{
190 // We cannot make it fail here. It might be a custom-parsed property
191 for (int i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
192 const int candidateIndex = component.namedObjectsInComponentTable()[i];
193 if (m_compiler->objectAt(candidateIndex)->idNameIndex == binding->propertyNameIndex) {
194 m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(candidateIndex));
195 return;
196 }
197 }
198}
199
201
202template<>
203typename QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::AliasResolutionResult
204QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveAliasesInObject(
205 const CompiledObject &component, int objectIndex,
206 QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> *aliasCacheCreator,
207 QQmlError *error)
208{
209 const CompiledObject *obj = m_compiler->objectAt(objectIndex);
210 int aliasIndex = 0;
211 const auto doAppendAlias = [&](const QV4::CompiledData::Alias *alias, int encodedIndex,
212 int resolvedTargetObjectId) {
213 return appendAliasToPropertyCache(
214 &component, alias, objectIndex, aliasIndex++, encodedIndex,
215 resolvedTargetObjectId, aliasCacheCreator, error);
216 };
217
218 const auto handleDeepAlias = [&](
219 const QV4::CompiledData::Alias *alias, const QQmlPropertyCache::ConstPtr &propertyCache,
220 int coreIndex, QStringView subProperty, int resolvedTargetObjectId)
221 {
222 const QQmlPropertyResolver resolver = QQmlPropertyResolver(propertyCache);
223 const QQmlPropertyData *actualProperty = resolver.property(subProperty.toString());
224 if (!actualProperty)
225 return DeepAliasResult::NoProperty;
226
227 if (doAppendAlias(
228 alias, QQmlPropertyIndex(coreIndex, actualProperty->coreIndex()).toEncoded(),
229 resolvedTargetObjectId)) {
230 return DeepAliasResult::Success;
231 }
232
233 return DeepAliasResult::CannotAppend;
234 };
235
236 for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
237 if (resolvedAliases.contains(alias)) {
238 ++aliasIndex;
239 continue;
240 }
241
242 int targetObjectIndex = -1;
243 for (int i = 0, end = component.namedObjectsInComponentCount(); i < end; ++i) {
244 const int candidateIndex = component.namedObjectsInComponentTable()[i];
245 if (m_compiler->objectAt(candidateIndex)->idNameIndex == alias->idIndex()) {
246 targetObjectIndex = candidateIndex;
247 break;
248 }
249 }
250 if (targetObjectIndex == -1) {
251 *error = qQmlCompileError(
252 alias->referenceLocation(),
253 tr("Invalid alias reference. Unable to find id \"%1\"")
254 .arg(stringAt(alias->idIndex())));
255 break;
256 }
257 const QV4::CompiledData::Object *targetObject = m_compiler->objectAt(targetObjectIndex);
258 const int resolvedTargetObjectId = targetObject->objectId();
259
260 QStringView property;
261 QStringView subProperty;
262
263 const QString aliasPropertyValue = stringAt(alias->propertyNameIndex());
264 const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
265 if (propertySeparator != -1) {
266 property = QStringView{aliasPropertyValue}.left(propertySeparator);
267 subProperty = QStringView{aliasPropertyValue}.mid(propertySeparator + 1);
268 } else {
269 property = QStringView(aliasPropertyValue);
270 }
271
272 if (property.isEmpty()) {
273 if (doAppendAlias(alias, -1, resolvedTargetObjectId))
274 continue;
275 return SomeAliasesResolved;
276 }
277
278 Q_ASSERT(!property.isEmpty());
279 QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
280 Q_ASSERT(targetCache);
281
282 const QQmlPropertyResolver resolver(targetCache);
283 const QQmlPropertyData *targetProperty = resolver.property(property.toString());
284 if (!targetProperty)
285 return SomeAliasesResolved;
286
287 const int coreIndex = targetProperty->coreIndex();
288 if (subProperty.isEmpty()) {
289 if (doAppendAlias(
290 alias, QQmlPropertyIndex(coreIndex).toEncoded(), resolvedTargetObjectId)) {
291 continue;
292 }
293 return SomeAliasesResolved;
294 }
295
296 if (const QMetaObject *valueTypeMetaObject
297 = QQmlMetaType::metaObjectForValueType(targetProperty->propType())) {
298 const int valueTypeIndex = valueTypeMetaObject->indexOfProperty(
299 subProperty.toString().toUtf8().constData());
300 if (valueTypeIndex == -1)
301 return SomeAliasesResolved;
302
303 if (doAppendAlias(
304 alias, QQmlPropertyIndex(coreIndex, valueTypeIndex).toEncoded(),
305 resolvedTargetObjectId)) {
306 continue;
307 }
308
309 return SomeAliasesResolved;
310 }
311
312 Q_ASSERT(subProperty.at(0).isLower());
313
314 bool foundDeepAliasInBindings = false;
315 for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) {
316 if (m_compiler->stringAt(it->propertyNameIndex) != property)
317 continue;
318
319 const QQmlPropertyCache::ConstPtr bindingCache
320 = m_propertyCaches->at(it->value.objectIndex);
321 if (!bindingCache)
322 continue;
323
324 switch (handleDeepAlias(
325 alias, bindingCache, coreIndex, subProperty, resolvedTargetObjectId)) {
326 case DeepAliasResult::NoProperty:
327 continue;
328 case DeepAliasResult::CannotAppend:
329 return SomeAliasesResolved;
330 case DeepAliasResult::Success:
331 foundDeepAliasInBindings = true;
332 break;
333 }
334
335 break;
336 }
337
338 if (foundDeepAliasInBindings)
339 continue;
340
341 const QQmlPropertyCache::ConstPtr typeCache
342 = QQmlMetaType::propertyCacheForType(targetProperty->propType());
343 if (!typeCache)
344 return SomeAliasesResolved;
345
346 switch (handleDeepAlias(alias, typeCache, coreIndex, subProperty, resolvedTargetObjectId)) {
347 case DeepAliasResult::NoProperty:
348 case DeepAliasResult::CannotAppend:
349 return SomeAliasesResolved;
350 case DeepAliasResult::Success:
351 break;
352 }
353 }
354
355 return AllAliasesResolved;
356}
357
358QQmlError QQmlTypeData::createTypeAndPropertyCaches(
359 const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
360 const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
361{
362 assertTypeLoaderThread();
363
364 Q_ASSERT(m_compiledData);
365 m_compiledData->typeNameCache = typeNameCache;
366 m_compiledData->resolvedTypes = resolvedTypeCache;
367 m_compiledData->inlineComponentData = m_inlineComponentData;
368 m_compiledData->qmlType = m_qmlType;
369
370 QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
371
372 {
373 QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(
374 &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, m_typeLoader,
375 m_compiledData.data(), m_importCache.data(), typeClassName());
376
377 QQmlError error = propertyCacheCreator.verifyNoICCycle();
378 if (error.isValid())
379 return error;
380
381 QQmlPropertyCacheCreatorBase::IncrementalResult result;
382 do {
383 result = propertyCacheCreator.buildMetaObjectsIncrementally();
384 if (result.error.isValid()) {
385 return result.error;
386 } else {
387 QQmlComponentAndAliasResolver resolver(
388 m_compiledData.data(), &m_compiledData->propertyCaches);
389 if (const QQmlError error = resolver.resolve(result.processedRoot);
390 error.isValid()) {
391 return error;
392 }
393 pendingGroupPropertyBindings.resolveMissingPropertyCaches(
394 &m_compiledData->propertyCaches);
395 pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
396 }
397
398 } while (result.canResume);
399 }
400
401 pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_compiledData->propertyCaches);
402 return QQmlError();
403}
404
405// local helper function for inline components
406namespace {
407using InlineComponentData = QV4::CompiledData::InlineComponentData;
408
409template<typename ObjectContainer>
410void setupICs(
411 const ObjectContainer &container, QHash<QString, InlineComponentData> *icData,
412 const QUrl &baseUrl,
413 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) {
414 Q_ASSERT(icData->empty());
415 for (int i = 0; i != container->objectCount(); ++i) {
416 auto root = container->objectAt(i);
417 for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) {
418 // We cannot re-use a previously finalized inline component type here. We need our own.
419 // We can and should re-use speculative type references, though.
420 InlineComponentData icDatum(
421 QQmlMetaType::findOrCreateFactualInlineComponentType(
422 baseUrl, container->stringAt(it->nameIndex), compilationUnit),
423 int(it->objectIndex), int(it->nameIndex));
424
425 icData->insert(container->stringAt(it->nameIndex), icDatum);
426 }
427 }
428};
429}
430
431bool QQmlTypeData::checkScripts()
432{
433 // Check all script dependencies for errors
434 for (int ii = 0; ii < m_scripts.size(); ++ii) {
435 const ScriptReference &script = m_scripts.at(ii);
436 Q_ASSERT(script.script->isCompleteOrError());
437 if (script.script->isError()) {
438 createError(
439 script,
440 QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
441 return false;
442 }
443 }
444 return true;
445}
446
447void QQmlTypeData::createError(const TypeReference &type, const QString &message)
448{
449 createError(type, message, type.typeData ? type.typeData->errors() : QList<QQmlError>());
450}
451
452void QQmlTypeData::createError(const ScriptReference &script, const QString &message)
453{
454 createError(script, message, script.script ? script.script->errors() : QList<QQmlError>());
455}
456
457bool QQmlTypeData::checkDependencies()
458{
459 // Check all type dependencies for errors
460 for (auto it = std::as_const(m_resolvedTypes).begin(), end = std::as_const(m_resolvedTypes).end();
461 it != end; ++it) {
462 const TypeReference &type = *it;
463 Q_ASSERT(!type.typeData
464 || type.typeData->isCompleteOrError()
465 || type.type.isInlineComponent());
466
467 if (type.typeData && type.typeData->isError()) {
468 const QString &typeName = stringAt(it.key());
469 createError(type, QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
470 return false;
471 }
472
473 if (!type.selfReference && type.type.isInlineComponent()) {
474 const QString icName = type.type.elementName();
475 Q_ASSERT(!icName.isEmpty());
476
477 // We have a CU here. Check if the inline component exists.
478 if (type.typeData && type.typeData->compilationUnit()->inlineComponentId(icName) >= 0)
479 return true;
480
481 const QString typeName = stringAt(it.key());
482 const qsizetype lastDot = typeName.lastIndexOf(u'.');
483 createError(
484 type,
485 QQmlTypeLoader::tr("Type %1 has no inline component type called %2")
486 .arg(QStringView{typeName}.left(lastDot), icName));
487 return false;
488 }
489 }
490
491 return true;
492}
493
494bool QQmlTypeData::checkCompositeSingletons()
495{
496 // Check all composite singleton type dependencies for errors
497 for (int ii = 0; ii < m_compositeSingletons.size(); ++ii) {
498 const TypeReference &type = m_compositeSingletons.at(ii);
499 Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
500 if (type.typeData && type.typeData->isError()) {
501 QString typeName = type.type.qmlTypeName();
502 createError(type, QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
503 return false;
504 }
505 }
506
507 return true;
508}
509
510void QQmlTypeData::createQQmlType()
511{
512 if (QQmlPropertyCacheCreatorBase::canCreateClassNameTypeByUrl(finalUrl())) {
513 const bool isSingleton = m_document
514 ? m_document.data()->isSingleton()
515 : (m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton);
516 m_qmlType = QQmlMetaType::findCompositeType(
517 url(), m_compiledData, isSingleton
518 ? QQmlMetaType::Singleton
519 : QQmlMetaType::NonSingleton);
520 m_typeClassName = QByteArray(m_qmlType.typeId().name()).chopped(1);
521 }
522}
523
524bool QQmlTypeData::rebuildFromSource()
525{
526 // Clear and re-build everything.
527
528 m_typeReferences.clear();
529 m_scripts.clear();
530 m_namespaces.clear();
531 m_compositeSingletons.clear();
532
533 m_resolvedTypes.clear();
534 m_typesResolved = false;
535
536 m_qmlType = QQmlType();
537 m_typeClassName.clear();
538
539 m_inlineComponentData.clear();
540 m_compiledData.reset();
541
542 m_implicitImportLoaded = false;
543
544 m_importCache.adopt(new QQmlImports);
545 m_unresolvedImports.clear();
546
547 if (!loadFromSource())
548 return false;
549
550 continueLoadFromIR();
551
552 if (!resolveTypes())
553 return false;
554
555 if (!checkScripts())
556 return false;
557
558 if (!checkDependencies())
559 return false;
560
561 if (!checkCompositeSingletons())
562 return false;
563
564 createQQmlType();
565
566 setupICs(m_document, &m_inlineComponentData, finalUrl(), m_compiledData);
567 return true;
568}
569
570void QQmlTypeData::done()
571{
572 assertTypeLoaderThread();
573
574 auto cleanup = qScopeGuard([this]{
575 m_backupSourceCode = SourceCodeData();
576 m_document.reset();
577 m_typeReferences.clear();
578 if (isError()) {
579 const auto encounteredErrors = errors();
580 for (const QQmlError &e : encounteredErrors)
581 qCDebug(DBG_DISK_CACHE) << e.toString();
582 m_compiledData.reset();
583 // Clear resolved types, scripts, and composite singletons to break
584 // potential circular references (e.g., A depends on B, B depends on A)
585 m_resolvedTypes.clear();
586 m_compositeSingletons.clear();
587 m_scripts.clear();
588 }
589 });
590
591 if (isError())
592 return;
593
594 if (!checkScripts())
595 return;
596
597 if (!checkDependencies())
598 return;
599
600 if (!checkCompositeSingletons())
601 return;
602
603 createQQmlType();
604
605 if (m_document)
606 setupICs(m_document, &m_inlineComponentData, finalUrl(), m_compiledData);
607 else
608 setupICs(m_compiledData, &m_inlineComponentData, finalUrl(), m_compiledData);
609
610 QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache;
611 QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
612
613 // If we've pulled the CU from the memory cache, we don't need to do any verification.
614 const bool verifyCaches = !m_compiledData
615 || (m_compiledData->resolvedTypes.isEmpty() && !m_compiledData->typeNameCache);
616
617 if (verifyCaches) {
618 QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
619 if (error.isValid()) {
620 setError(error);
621 qDeleteAll(resolvedTypeCache);
622 return;
623 }
624 }
625
626 const auto dependencyHasher = [&resolvedTypeCache, this]() {
627 return typeLoader()->hashDependencies(&resolvedTypeCache, m_compositeSingletons);
628 };
629
630 // verify if any dependencies changed if we're using a cache
631 if (m_document.isNull() && verifyCaches) {
632 const QQmlError error = createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
633 if (error.isValid() || !m_compiledData->verifyChecksum(dependencyHasher)) {
634
635 if (error.isValid()) {
636 qCDebug(DBG_DISK_CACHE)
637 << "Failed to create property caches for"
638 << m_compiledData->fileName()
639 << "because" << error.description();
640 } else {
641 qCDebug(DBG_DISK_CACHE)
642 << "Checksum mismatch for cached version of"
643 << m_compiledData->fileName();
644 }
645
646 resolvedTypeCache.clear();
647 typeNameCache.reset();
648
649 if (!rebuildFromSource())
650 return;
651
652 const QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
653 if (error.isValid()) {
654 setError(error);
655 qDeleteAll(resolvedTypeCache);
656 return;
657 }
658 }
659 }
660
661 if (!m_document.isNull()) {
662 Q_ASSERT(verifyCaches);
663 // Compile component
664 compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
665 if (isError())
666 return;
667 }
668
669 {
670 m_compiledData->inlineComponentData = m_inlineComponentData;
671 {
672 // Sanity check property bindings
673 QQmlPropertyValidator validator(typeLoader(), m_importCache.data(), m_compiledData);
674 QList<QQmlError> errors = validator.validate();
675 if (!errors.isEmpty()) {
676 setError(errors);
677 return;
678 }
679 }
680
681 m_compiledData->finalizeCompositeType(qmlType());
682 }
683
684 {
685 QQmlType type = QQmlMetaType::qmlType(finalUrl());
686 if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) {
687 if (!type.isValid()) {
688 QQmlError error;
689 error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
690 setError(error);
691 return;
692 } else if (!type.isCompositeSingleton()) {
693 QQmlError error;
694 error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName()));
695 setError(error);
696 return;
697 }
698 } else {
699 // If the type is CompositeSingleton but there was no pragma Singleton in the
700 // QML file, lets report an error.
701 if (type.isValid() && type.isCompositeSingleton()) {
702 QString typeName = type.qmlTypeName();
703 setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
704 return;
705 }
706 }
707 }
708
709 {
710 // Collect imported scripts
711 m_compiledData->dependentScripts.reserve(m_scripts.size());
712 for (int scriptIndex = 0; scriptIndex < m_scripts.size(); ++scriptIndex) {
713 const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex);
714
715 QStringView qualifier(script.qualifier);
716 QString enclosingNamespace;
717
718 const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
719 if (lastDotIndex != -1) {
720 enclosingNamespace = qualifier.left(lastDotIndex).toString();
721 qualifier = qualifier.mid(lastDotIndex+1);
722 }
723
724 m_compiledData->typeNameCache->add(
725 qualifier.toString(), scriptIndex, enclosingNamespace);
726 QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
727 m_compiledData->dependentScripts << scriptData;
728 }
729 }
730}
731
732bool QQmlTypeData::loadImplicitImport()
733{
734 assertTypeLoaderThread();
735
736 m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error)
737
738 m_importCache->setBaseUrl(finalUrl(), finalUrlString());
739
740 // For local urls, add an implicit import "." as most overridden lookup.
741 // This will also trigger the loading of the qmldir and the import of any native
742 // types from available plugins.
743 QList<QQmlError> implicitImportErrors;
744 QString localQmldir;
745 m_importCache->addImplicitImport(typeLoader(), &localQmldir, &implicitImportErrors);
746
747 // When loading with QQmlImports::ImportImplicit, the imports are _appended_ to the namespace
748 // in the order they are loaded. Therefore, the addImplicitImport above gets the highest
749 // precedence. This is in contrast to normal priority imports. Those are _prepended_ in the
750 // order they are loaded.
751 if (!localQmldir.isEmpty()) {
752 const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(localQmldir);
753 const QList<QQmlDirParser::Import> moduleImports
754 = QQmlMetaType::moduleImports(qmldir.typeNamespace(), QTypeRevision())
755 + qmldir.imports();
756 loadDependentImports(moduleImports, QString(), QTypeRevision(),
757 QQmlImportInstance::Implicit + 1, QQmlImports::ImportNoFlag,
758 &implicitImportErrors);
759 }
760
761 if (!implicitImportErrors.isEmpty()) {
762 setError(implicitImportErrors);
763 return false;
764 }
765
766 return true;
767}
768
769void QQmlTypeData::dataReceived(const SourceCodeData &data)
770{
771 assertTypeLoaderThread();
772
773 m_backupSourceCode = data;
774
775 if (tryLoadFromDiskCache())
776 return;
777
778 if (isError())
779 return;
780
781 if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) {
782 if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch)
783 setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile"));
784 else if (!m_backupSourceCode.exists())
785 setError(QQmlTypeLoader::tr("No such file or directory"));
786 else
787 setError(QQmlTypeLoader::tr("File is empty"));
788 return;
789 }
790
791 if (!loadFromSource())
792 return;
793
794 continueLoadFromIR();
795}
796
797void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
798{
799 assertTypeLoaderThread();
800
801 if (unit->qmlData->qmlUnit()->nObjects == 0) {
802 setError(QQmlTypeLoader::tr("Cached QML Unit has no objects"));
803 return;
804 }
805
806 m_document.reset(
807 new QmlIR::Document(urlString(), finalUrlString(), m_typeLoader->isDebugging()));
808 QQmlIRLoader loader(unit->qmlData, m_document.data());
809 loader.load();
810 m_document->javaScriptCompilationUnit
811 = QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
812 new QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions, unit->validateLookupSignatures),
813 QQmlRefPointer<QV4::CompiledData::CompilationUnit>::Adopt);
814 continueLoadFromIR();
815}
816
817bool QQmlTypeData::loadFromSource()
818{
819 assertTypeLoaderThread();
820
821 m_document.reset(
822 new QmlIR::Document(urlString(), finalUrlString(), m_typeLoader->isDebugging()));
823 m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp();
824 QmlIR::IRBuilder compiler;
825
826 QString sourceError;
827 const QString source = m_backupSourceCode.readAll(&sourceError);
828 if (!sourceError.isEmpty()) {
829 setError(sourceError);
830 return false;
831 }
832
833 if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) {
834 QList<QQmlError> errors;
835 errors.reserve(compiler.errors.size());
836 for (const QQmlJS::DiagnosticMessage &msg : std::as_const(compiler.errors)) {
837 QQmlError e;
838 e.setUrl(url());
839 e.setLine(qmlConvertSourceCoordinate<quint32, int>(msg.loc.startLine));
840 e.setColumn(qmlConvertSourceCoordinate<quint32, int>(msg.loc.startColumn));
841 e.setDescription(msg.message);
842 errors << e;
843 }
844 setError(errors);
845 return false;
846 }
847 return true;
848}
849
850void QQmlTypeData::restoreIR(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
851{
852 assertTypeLoaderThread();
853
854 m_document.reset(
855 new QmlIR::Document(urlString(), finalUrlString(), m_typeLoader->isDebugging()));
856 QQmlIRLoader loader(unit->unitData(), m_document.data());
857 loader.load();
858 m_document->javaScriptCompilationUnit = unit;
859 continueLoadFromIR();
860}
861
862void QQmlTypeData::continueLoadFromIR()
863{
864 assertTypeLoaderThread();
865
866 for (auto const& object: std::as_const(m_document->objects)) {
867 for (auto it = object->inlineComponentsBegin(); it != object->inlineComponentsEnd(); ++it) {
868 QString const nameString = m_document->stringAt(it->nameIndex);
869 auto importUrl = finalUrl();
870 importUrl.setFragment(nameString);
871 auto import = new QQmlImportInstance(); // Note: The cache takes ownership of the QQmlImportInstance
872 m_importCache->addInlineComponentImport(import, nameString, importUrl);
873 }
874 }
875
876 m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd());
877 m_importCache->setBaseUrl(finalUrl(), finalUrlString());
878
879 // For remote URLs, we don't delay the loading of the implicit import
880 // because the loading probably requires an asynchronous fetch of the
881 // qmldir (so we can't load it just in time).
882 if (!finalUrl().scheme().isEmpty()) {
883 QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
884 if (!QQmlImports::isLocal(qmldirUrl)) {
885 if (!loadImplicitImport())
886 return;
887 // This qmldir is for the implicit import
888 auto implicitImport = std::make_shared<PendingImport>();
889 implicitImport->uri = QLatin1String(".");
890 implicitImport->version = QTypeRevision();
891 QList<QQmlError> errors;
892
893 if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) {
894 setError(errors);
895 return;
896 }
897 }
898 }
899
900 QList<QQmlError> errors;
901
902 for (const QV4::CompiledData::Import *import : std::as_const(m_document->imports)) {
903 if (!addImport(import, {}, &errors)) {
904 Q_ASSERT(errors.size());
905
906 // We're only interested in the chronoligically last error. The previous
907 // errors might be from unsuccessfully trying to load a module from the
908 // resource file system.
909 QQmlError error = errors.first();
910 error.setUrl(m_importCache->baseUrl());
911 error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
912 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
913 setError(error);
914 return;
915 }
916 }
917}
918
919void QQmlTypeData::allDependenciesDone()
920{
921 assertTypeLoaderThread();
922
923 QQmlTypeLoader::Blob::allDependenciesDone();
924
925 if (!m_typesResolved)
926 resolveTypes();
927}
928
929QString QQmlTypeData::stringAt(int index) const
930{
931 if (m_compiledData)
932 return m_compiledData->stringAt(index);
933 return m_document->jsGenerator.stringTable.stringForIndex(index);
934}
935
936void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
937 QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
938 const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
939{
940 assertTypeLoaderThread();
941
942 Q_ASSERT(m_compiledData.isNull());
943
944 const bool typeRecompilation = m_document
945 && m_document->javaScriptCompilationUnit
946 && m_document->javaScriptCompilationUnit->unitData()
947 && (m_document->javaScriptCompilationUnit->unitData()->flags
948 & QV4::CompiledData::Unit::PendingTypeCompilation);
949
950 QQmlTypeCompiler compiler(
951 typeLoader(), this, m_document.data(), resolvedTypeCache, dependencyHasher);
952 auto compilationUnit = compiler.compile();
953 if (!compilationUnit) {
954 qDeleteAll(*resolvedTypeCache);
955 resolvedTypeCache->clear();
956 setError(compiler.compilationErrors());
957 return;
958 }
959
960 const bool trySaveToDisk = m_typeLoader->writeCacheFile() && !typeRecompilation;
961 if (trySaveToDisk) {
962 QString errorString;
963
964 QByteArray cachedChecksum;
965 const auto sourceChecksum = [&]() {
966 if (cachedChecksum.isEmpty())
967 cachedChecksum = m_backupSourceCode.checksum();
968 return cachedChecksum;
969 };
970
971 if (compilationUnit->saveToDisk(url(), sourceChecksum, &errorString)) {
972 QString error;
973 if (!compilationUnit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(),
974 sourceChecksum, &error)) {
975 // log but ignore error, keep using the in-memory compilation unit.
976 qCDebug(DBG_DISK_CACHE)
977 << "Error re-loading cached version of" << compilationUnit->fileName()
978 << "from disk:" << errorString;
979 }
980 } else {
981 qCDebug(DBG_DISK_CACHE) << "Error saving cached version of"
982 << compilationUnit->fileName() << "to disk:" << errorString;
983 }
984 }
985
986 m_compiledData = std::move(compilationUnit);
987 m_compiledData->typeNameCache = typeNameCache;
988 m_compiledData->resolvedTypes = *resolvedTypeCache;
989 m_compiledData->propertyCaches = std::move(*compiler.propertyCaches());
990 Q_ASSERT(m_compiledData->propertyCaches.count()
991 >= static_cast<int>(m_compiledData->objectCount()));
992}
993
994bool QQmlTypeData::resolveTypes()
995{
996 assertTypeLoaderThread();
997
998 Q_ASSERT(!m_typesResolved);
999
1000 // Check that all imports were resolved
1001 QList<QQmlError> errors;
1002 auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
1003 for ( ; it != end; ++it) {
1004 const PendingImportPtr &import = *it;
1005 if (import->priority != 0)
1006 continue;
1007
1008 // If the import was potentially remote and all the network requests have failed,
1009 // we now know that there is no qmldir. We can register its types.
1010 if (registerPendingTypes(import))
1011 continue;
1012
1013 // This import was not resolved
1014 QQmlError error;
1015 error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
1016 error.setUrl(m_importCache->baseUrl());
1017 error.setLine(qmlConvertSourceCoordinate<quint32, int>(
1018 import->location.line()));
1019 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
1020 import->location.column()));
1021 errors.prepend(error);
1022 }
1023
1024 if (errors.size()) {
1025 setError(errors);
1026 return false;
1027 }
1028
1029 // Load the implicit import since it may have additional scripts.
1030 if (!m_implicitImportLoaded && !loadImplicitImport())
1031 return false;
1032
1033 // Add any imported scripts to our resolved set
1034 const auto resolvedScripts = m_importCache->resolvedScripts();
1035 for (const QQmlImports::ScriptReference &script : resolvedScripts) {
1036 QQmlRefPointer<QQmlScriptBlob> blob
1037 = typeLoader()->getScript(script.location, script.fileName);
1038 addDependency(blob.data());
1039
1040 ScriptReference ref;
1041 //ref.location = ...
1042 if (!script.qualifier.isEmpty())
1043 {
1044 ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace;
1045 // Add a reference to the enclosing namespace
1046 m_namespaces.insert(script.qualifier);
1047 } else {
1048 ref.qualifier = script.nameSpace;
1049 }
1050
1051 ref.script = blob;
1052 m_scripts << ref;
1053 }
1054
1055 // Lets handle resolved composite singleton types
1056 const auto resolvedCompositeSingletons = m_importCache->resolvedCompositeSingletons();
1057 for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) {
1058 TypeReference ref;
1059 QString typeName;
1060 if (!csRef.prefix.isEmpty()) {
1061 typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName;
1062 // Add a reference to the enclosing namespace
1063 m_namespaces.insert(csRef.prefix);
1064 } else {
1065 typeName = csRef.typeName;
1066 }
1067
1068 QTypeRevision version = csRef.version;
1069 if (!resolveType(typeName, version, ref, -1, -1, true, QQmlType::CompositeSingletonType))
1070 return false;
1071
1072 if (ref.type.isCompositeSingleton()) {
1073 ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
1074 if (ref.typeData->isWaiting() || m_waitingOnMe.contains(ref.typeData.data())) {
1075 qCDebug(lcCycle) << "Possible cyclic dependency detected between"
1076 << ref.typeData->urlString() << "and" << urlString();
1077 continue;
1078 }
1079 addDependency(ref.typeData.data());
1080 ref.prefix = csRef.prefix;
1081
1082 m_compositeSingletons << ref;
1083 }
1084 }
1085
1086 for (auto unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
1087 unresolvedRef != end; ++unresolvedRef) {
1088
1089 TypeReference ref; // resolved reference
1090
1091 const bool reportErrors = unresolvedRef->errorWhenNotFound;
1092
1093 QTypeRevision version;
1094
1095 const QString name = stringAt(unresolvedRef.key());
1096
1097 bool *selfReferenceDetection = unresolvedRef->needsCreation ? nullptr : &ref.selfReference;
1098
1099 if (!resolveType(name, version, ref, unresolvedRef->location.line(),
1100 unresolvedRef->location.column(), reportErrors,
1101 QQmlType::AnyRegistrationType, selfReferenceDetection) && reportErrors)
1102 return false;
1103
1104 if (ref.selfReference) {
1105 // nothing to do
1106 } else if (ref.type.isInlineComponent()) {
1107 QUrl containingTypeUrl = ref.type.sourceUrl();
1108 Q_ASSERT(!containingTypeUrl.isEmpty());
1109 if (QQmlMetaType::equalBaseUrls(finalUrl(), containingTypeUrl)) {
1110 ref.selfReference = true;
1111 } else {
1112 containingTypeUrl.setFragment(QString());
1113 auto typeData = typeLoader()->getType(containingTypeUrl);
1114 Q_ASSERT(typeData.data() != this);
1115 ref.typeData = typeData;
1116 addDependency(typeData.data());
1117 }
1118 } else if (ref.type.isComposite()) {
1119 ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
1120 addDependency(ref.typeData.data());
1121 }
1122
1123 ref.version = version;
1124 ref.location = unresolvedRef->location;
1125 ref.needsCreation = unresolvedRef->needsCreation;
1126 m_resolvedTypes.insert(unresolvedRef.key(), ref);
1127 }
1128
1129 m_typesResolved = true;
1130 return true;
1131}
1132
1133QQmlError QQmlTypeData::buildTypeResolutionCaches(
1134 QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
1135 QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache) const
1136{
1137 assertTypeLoaderThread();
1138
1139 typeNameCache->adopt(new QQmlTypeNameCache(m_importCache));
1140
1141 for (const QString &ns: m_namespaces)
1142 (*typeNameCache)->add(ns);
1143
1144 // Add any Composite Singletons that were used to the import cache
1145 for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
1146 (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix);
1147
1148 m_importCache->populateCache(typeNameCache->data());
1149
1150 for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) {
1151 auto ref = std::make_unique<QV4::ResolvedTypeReference>();
1152 QQmlType qmlType = resolvedType->type;
1153 ref->setType(qmlType);
1154 ref->setIsSelfReference(resolvedType->selfReference);
1155 if (resolvedType->typeData) {
1156 if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) {
1157 return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName()));
1158 }
1159 const auto compilationUnit = resolvedType->typeData->compilationUnit();
1160 if (qmlType.isInlineComponent()) {
1161 // Inline component which is part of an already resolved type
1162 QString icName = qmlType.elementName();
1163 Q_ASSERT(!icName.isEmpty());
1164
1165 ref->setTypePropertyCache(compilationUnit->propertyCaches.at(
1166 compilationUnit->inlineComponentId(icName)));
1167 Q_ASSERT(ref->type().isInlineComponent());
1168 } else {
1169 ref->setTypePropertyCache(compilationUnit->rootPropertyCache());
1170 }
1171 if (!resolvedType->selfReference)
1172 ref->setCompilationUnit(compilationUnit);
1173 } else if (qmlType.isInlineComponent()) {
1174 // Inline component.
1175 // If it's from a different file we have a typeData and can't get here.
1176 // If it's defined in the same file we're currently compiling, we don't want to use it.
1177 // We're going to fill in the property caches later after all.
1178 Q_ASSERT(resolvedType->selfReference);
1179 Q_ASSERT(ref->isSelfReference());
1180 } else if (qmlType.isValid() && !resolvedType->selfReference) {
1181 Q_ASSERT(ref->type().isValid());
1182
1183 if (resolvedType->needsCreation && !qmlType.isCreatable()) {
1184 QString reason = qmlType.noCreationReason();
1185 if (reason.isEmpty())
1186 reason = tr("Element is not creatable.");
1187 return qQmlCompileError(resolvedType->location, reason);
1188 }
1189
1190 if (qmlType.containsRevisionedAttributes()) {
1191 // It can only have (revisioned) properties or methods if it has a metaobject
1192 Q_ASSERT(qmlType.metaObject());
1193 ref->setTypePropertyCache(
1194 QQmlMetaType::propertyCache(qmlType, resolvedType->version));
1195 }
1196 }
1197 ref->setVersion(resolvedType->version);
1198 ref->doDynamicTypeCheck();
1199 resolvedTypeCache->insert(resolvedType.key(), ref.release());
1200 }
1201 QQmlError noError;
1202 return noError;
1203}
1204
1205bool QQmlTypeData::resolveType(const QString &typeName, QTypeRevision &version,
1206 TypeReference &ref, int lineNumber, int columnNumber,
1207 bool reportErrors, QQmlType::RegistrationType registrationType,
1208 bool *typeRecursionDetected)
1209{
1210 assertTypeLoaderThread();
1211
1212 QQmlImportNamespace *typeNamespace = nullptr;
1213 QList<QQmlError> errors;
1214
1215 bool typeFound = m_importCache->resolveType(
1216 typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors, registrationType,
1217 typeRecursionDetected);
1218 if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
1219 // Lazy loading of implicit import
1220 if (loadImplicitImport()) {
1221 // Try again to find the type
1222 errors.clear();
1223 typeFound = m_importCache->resolveType(
1224 typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors,
1225 registrationType, typeRecursionDetected);
1226 } else {
1227 return false; //loadImplicitImport() hit an error, and called setError already
1228 }
1229 }
1230
1231 if ((!typeFound || typeNamespace) && reportErrors) {
1232 // Known to not be a type:
1233 // - known to be a namespace (Namespace {})
1234 // - type with unknown namespace (UnknownNamespace.SomeType {})
1235 QQmlError error;
1236 if (typeNamespace) {
1237 error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName));
1238 } else {
1239 if (errors.size()) {
1240 error = errors.takeFirst();
1241 } else {
1242 // this should not be possible!
1243 // Description should come from error provided by addImport() function.
1244 error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
1245 }
1246 error.setUrl(m_importCache->baseUrl());
1247 error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName, error.description()));
1248 }
1249
1250 if (lineNumber != -1)
1251 error.setLine(lineNumber);
1252 if (columnNumber != -1)
1253 error.setColumn(columnNumber);
1254
1255 errors.prepend(error);
1256 setError(errors);
1257 return false;
1258 }
1259
1260 return true;
1261}
1262
1263void QQmlTypeData::scriptImported(
1264 const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location,
1265 const QString &nameSpace, const QString &qualifier)
1266{
1267 assertTypeLoaderThread();
1268
1269 ScriptReference ref;
1270 ref.script = blob;
1271 ref.location = location;
1272 ref.qualifier = qualifier.isEmpty() ? nameSpace : qualifier + QLatin1Char('.') + nameSpace;
1273
1274 m_scripts << ref;
1275}
1276
1277QT_END_NAMESPACE
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
DeepAliasResult