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