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