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