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

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