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

source code of qtdeclarative/src/qml/qml/qqmltypedata.cpp