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(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
95void QQmlTypeData::registerCallback(TypeDataCallback *callback)
96{
97 Q_ASSERT(!m_callbacks.contains(callback));
98 m_callbacks.append(callback);
99}
100
101void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
102{
103 Q_ASSERT(m_callbacks.contains(callback));
104 m_callbacks.removeOne(callback);
105 Q_ASSERT(!m_callbacks.contains(callback));
106}
107
108bool QQmlTypeData::tryLoadFromDiskCache()
109{
110 if (diskCacheDisabled() && !diskCacheForced())
111 return false;
112
113 if (isDebugging())
114 return false;
115
116 QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle();
117 if (!v4)
118 return false;
119
120 QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create();
121 {
122 QString error;
123 if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
124 qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error;
125 return false;
126 }
127 }
128
129 if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
130 restoreIR(std::move(*unit));
131 return true;
132 }
133
134 m_compiledData = unit;
135
136 for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i)
137 m_typeReferences.collectFromObject(m_compiledData->objectAt(i));
138
139 m_importCache.setBaseUrl(finalUrl(), finalUrlString());
140
141 // For remote URLs, we don't delay the loading of the implicit import
142 // because the loading probably requires an asynchronous fetch of the
143 // qmldir (so we can't load it just in time).
144 if (!finalUrl().scheme().isEmpty()) {
145 QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
146 if (!QQmlImports::isLocal(qmldirUrl)) {
147 if (!loadImplicitImport())
148 return false;
149
150 // find the implicit import
151 for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) {
152 const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
153 if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".")
154 && import->qualifierIndex == 0
155 && import->majorVersion == -1
156 && import->minorVersion == -1) {
157 QList<QQmlError> errors;
158 auto pendingImport = std::make_shared<PendingImport>(this, import);
159 if (!fetchQmldir(qmldirUrl, pendingImport, 1, &errors)) {
160 setError(errors);
161 return false;
162 }
163 break;
164 }
165 }
166 }
167 }
168
169 for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) {
170 const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
171 QList<QQmlError> errors;
172 if (!addImport(import, &errors)) {
173 Q_ASSERT(errors.size());
174 QQmlError error(errors.takeFirst());
175 error.setUrl(m_importCache.baseUrl());
176 error.setLine(import->location.line);
177 error.setColumn(import->location.column);
178 errors.prepend(error); // put it back on the list after filling out information.
179 setError(errors);
180 return false;
181 }
182 }
183
184 return true;
185}
186
187void QQmlTypeData::createTypeAndPropertyCaches(
188 const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
189 const QV4::ResolvedTypeReferenceMap &resolvedTypeCache)
190{
191 Q_ASSERT(m_compiledData);
192 m_compiledData->typeNameCache = typeNameCache;
193 m_compiledData->resolvedTypes = resolvedTypeCache;
194
195 QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
196
197 QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
198
199 {
200 QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator(
201 &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine,
202 m_compiledData.data(), &m_importCache);
203 QQmlJS::DiagnosticMessage error = propertyCacheCreator.buildMetaObjects();
204 if (error.isValid()) {
205 setError(error);
206 return;
207 }
208 }
209
210 QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator(
211 &m_compiledData->propertyCaches, m_compiledData.data());
212 aliasCreator.appendAliasPropertiesToMetaObjects();
213
214 pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches);
215}
216
217static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine)
218{
219 for (const auto &typeRef: typeRefs) {
220 if (typeRef.typeData) {
221 const auto unit = typeRef.typeData->compilationUnit()->unitData();
222 hash->addData(unit->md5Checksum, sizeof(unit->md5Checksum));
223 } else if (typeRef.type.isValid()) {
224 const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject());
225 bool ok = false;
226 hash->addData(propertyCache->checksum(&ok));
227 if (!ok)
228 return false;
229 }
230 }
231 return true;
232}
233
234void QQmlTypeData::done()
235{
236 auto cleanup = qScopeGuard([this]{
237 m_document.reset();
238 m_typeReferences.clear();
239 if (isError())
240 m_compiledData = nullptr;
241 });
242
243 if (isError())
244 return;
245
246 // Check all script dependencies for errors
247 for (int ii = 0; ii < m_scripts.count(); ++ii) {
248 const ScriptReference &script = m_scripts.at(ii);
249 Q_ASSERT(script.script->isCompleteOrError());
250 if (script.script->isError()) {
251 QList<QQmlError> errors = script.script->errors();
252 QQmlError error;
253 error.setUrl(url());
254 error.setLine(script.location.line);
255 error.setColumn(script.location.column);
256 error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
257 errors.prepend(error);
258 setError(errors);
259 return;
260 }
261 }
262
263 // Check all type dependencies for errors
264 for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end;
265 ++it) {
266 const TypeReference &type = *it;
267 Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
268 if (type.typeData && type.typeData->isError()) {
269 const QString typeName = stringAt(it.key());
270
271 QList<QQmlError> errors = type.typeData->errors();
272 QQmlError error;
273 error.setUrl(url());
274 error.setLine(type.location.line);
275 error.setColumn(type.location.column);
276 error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
277 errors.prepend(error);
278 setError(errors);
279 return;
280 }
281 }
282
283 // Check all composite singleton type dependencies for errors
284 for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) {
285 const TypeReference &type = m_compositeSingletons.at(ii);
286 Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
287 if (type.typeData && type.typeData->isError()) {
288 QString typeName = type.type.qmlTypeName();
289
290 QList<QQmlError> errors = type.typeData->errors();
291 QQmlError error;
292 error.setUrl(url());
293 error.setLine(type.location.line);
294 error.setColumn(type.location.column);
295 error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
296 errors.prepend(error);
297 setError(errors);
298 return;
299 }
300 }
301
302 QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
303 QV4::ResolvedTypeReferenceMap resolvedTypeCache;
304 {
305 QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
306 if (error.isValid()) {
307 setError(error);
308 return;
309 }
310 }
311
312 QQmlEngine *const engine = typeLoader()->engine();
313
314 const auto dependencyHasher = [engine, &resolvedTypeCache, this]() {
315 QCryptographicHash hash(QCryptographicHash::Md5);
316 return (resolvedTypeCache.addToHash(&hash, engine)
317 && ::addTypeReferenceChecksumsToHash(m_compositeSingletons, &hash, engine))
318 ? hash.result()
319 : QByteArray();
320 };
321
322 // verify if any dependencies changed if we're using a cache
323 if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) {
324 qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName();
325 if (!loadFromSource())
326 return;
327 m_backupSourceCode = SourceCodeData();
328 m_compiledData = nullptr;
329 }
330
331 if (!m_document.isNull()) {
332 // Compile component
333 compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
334 } else {
335 createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
336 }
337
338 if (isError())
339 return;
340
341 {
342 QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine);
343 {
344 // Sanity check property bindings
345 QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData);
346 QVector<QQmlJS::DiagnosticMessage> errors = validator.validate();
347 if (!errors.isEmpty()) {
348 setError(errors);
349 return;
350 }
351 }
352
353 m_compiledData->finalizeCompositeType(enginePrivate);
354 }
355
356 {
357 QQmlType type = QQmlMetaType::qmlType(finalUrl(), true);
358 if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) {
359 if (!type.isValid()) {
360 QQmlError error;
361 error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
362 setError(error);
363 return;
364 } else if (!type.isCompositeSingleton()) {
365 QQmlError error;
366 error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName()));
367 setError(error);
368 return;
369 }
370 } else {
371 // If the type is CompositeSingleton but there was no pragma Singleton in the
372 // QML file, lets report an error.
373 if (type.isValid() && type.isCompositeSingleton()) {
374 QString typeName = type.qmlTypeName();
375 setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
376 return;
377 }
378 }
379 }
380
381 {
382 // Collect imported scripts
383 m_compiledData->dependentScripts.reserve(m_scripts.count());
384 for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
385 const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex);
386
387 QStringRef qualifier(&script.qualifier);
388 QString enclosingNamespace;
389
390 const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
391 if (lastDotIndex != -1) {
392 enclosingNamespace = qualifier.left(lastDotIndex).toString();
393 qualifier = qualifier.mid(lastDotIndex+1);
394 }
395
396 m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
397 QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
398 m_compiledData->dependentScripts << scriptData;
399 }
400 }
401}
402
403void QQmlTypeData::completed()
404{
405 // Notify callbacks
406 while (!m_callbacks.isEmpty()) {
407 TypeDataCallback *callback = m_callbacks.takeFirst();
408 callback->typeDataReady(this);
409 }
410}
411
412bool QQmlTypeData::loadImplicitImport()
413{
414 m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error)
415
416 m_importCache.setBaseUrl(finalUrl(), finalUrlString());
417
418 QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
419 // For local urls, add an implicit import "." as most overridden lookup.
420 // This will also trigger the loading of the qmldir and the import of any native
421 // types from available plugins.
422 QList<QQmlError> implicitImportErrors;
423 m_importCache.addImplicitImport(importDatabase, &implicitImportErrors);
424
425 if (!implicitImportErrors.isEmpty()) {
426 setError(implicitImportErrors);
427 return false;
428 }
429
430 return true;
431}
432
433void QQmlTypeData::dataReceived(const SourceCodeData &data)
434{
435 m_backupSourceCode = data;
436
437 if (tryLoadFromDiskCache())
438 return;
439
440 if (isError())
441 return;
442
443 if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) {
444 if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch)
445 setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile"));
446 else if (!m_backupSourceCode.exists())
447 setError(QQmlTypeLoader::tr("No such file or directory"));
448 else
449 setError(QQmlTypeLoader::tr("File is empty"));
450 return;
451 }
452
453 if (!loadFromSource())
454 return;
455
456 continueLoadFromIR();
457}
458
459void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit)
460{
461 m_document.reset(new QmlIR::Document(isDebugging()));
462 QQmlIRLoader loader(unit, m_document.data());
463 loader.load();
464 m_document->jsModule.fileName = urlString();
465 m_document->jsModule.finalUrl = finalUrlString();
466 m_document->javaScriptCompilationUnit = QV4::CompiledData::CompilationUnit(unit);
467 continueLoadFromIR();
468}
469
470bool QQmlTypeData::loadFromSource()
471{
472 m_document.reset(new QmlIR::Document(isDebugging()));
473 m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp();
474 QQmlEngine *qmlEngine = typeLoader()->engine();
475 QmlIR::IRBuilder compiler(qmlEngine->handle()->illegalNames());
476
477 QString sourceError;
478 const QString source = m_backupSourceCode.readAll(&sourceError);
479 if (!sourceError.isEmpty()) {
480 setError(sourceError);
481 return false;
482 }
483
484 if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) {
485 QList<QQmlError> errors;
486 errors.reserve(compiler.errors.count());
487 for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) {
488 QQmlError e;
489 e.setUrl(url());
490 e.setLine(msg.line);
491 e.setColumn(msg.column);
492 e.setDescription(msg.message);
493 errors << e;
494 }
495 setError(errors);
496 return false;
497 }
498 return true;
499}
500
501void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit)
502{
503 m_document.reset(new QmlIR::Document(isDebugging()));
504 QQmlIRLoader loader(unit.unitData(), m_document.data());
505 loader.load();
506 m_document->jsModule.fileName = urlString();
507 m_document->jsModule.finalUrl = finalUrlString();
508 m_document->javaScriptCompilationUnit = std::move(unit);
509 continueLoadFromIR();
510}
511
512void QQmlTypeData::continueLoadFromIR()
513{
514 m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd());
515 m_importCache.setBaseUrl(finalUrl(), finalUrlString());
516
517 // For remote URLs, we don't delay the loading of the implicit import
518 // because the loading probably requires an asynchronous fetch of the
519 // qmldir (so we can't load it just in time).
520 if (!finalUrl().scheme().isEmpty()) {
521 QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
522 if (!QQmlImports::isLocal(qmldirUrl)) {
523 if (!loadImplicitImport())
524 return;
525 // This qmldir is for the implicit import
526 auto implicitImport = std::make_shared<PendingImport>();
527 implicitImport->uri = QLatin1String(".");
528 implicitImport->majorVersion = -1;
529 implicitImport->minorVersion = -1;
530 QList<QQmlError> errors;
531
532 if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) {
533 setError(errors);
534 return;
535 }
536 }
537 }
538
539 QList<QQmlError> errors;
540
541 for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) {
542 if (!addImport(import, &errors)) {
543 Q_ASSERT(errors.size());
544 QQmlError error(errors.takeFirst());
545 error.setUrl(m_importCache.baseUrl());
546 error.setLine(import->location.line);
547 error.setColumn(import->location.column);
548 errors.prepend(error); // put it back on the list after filling out information.
549 setError(errors);
550 return;
551 }
552 }
553}
554
555void QQmlTypeData::allDependenciesDone()
556{
557 QQmlTypeLoader::Blob::allDependenciesDone();
558
559 if (!m_typesResolved) {
560 // Check that all imports were resolved
561 QList<QQmlError> errors;
562 auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
563 for ( ; it != end; ++it) {
564 if ((*it)->priority == 0) {
565 // This import was not resolved
566 for (auto keyIt = m_unresolvedImports.constBegin(),
567 keyEnd = m_unresolvedImports.constEnd();
568 keyIt != keyEnd; ++keyIt) {
569 PendingImportPtr import = *keyIt;
570 QQmlError error;
571 error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
572 error.setUrl(m_importCache.baseUrl());
573 error.setLine(import->location.line);
574 error.setColumn(import->location.column);
575 errors.prepend(error);
576 }
577 }
578 }
579 if (errors.size()) {
580 setError(errors);
581 return;
582 }
583
584 resolveTypes();
585 m_typesResolved = true;
586 }
587}
588
589void QQmlTypeData::downloadProgressChanged(qreal p)
590{
591 for (int ii = 0; ii < m_callbacks.count(); ++ii) {
592 TypeDataCallback *callback = m_callbacks.at(ii);
593 callback->typeDataProgress(this, p);
594 }
595}
596
597QString QQmlTypeData::stringAt(int index) const
598{
599 if (m_compiledData)
600 return m_compiledData->stringAt(index);
601 return m_document->jsGenerator.stringTable.stringForIndex(index);
602}
603
604void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
605 QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
606 const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
607{
608 Q_ASSERT(m_compiledData.isNull());
609
610 const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit.unitData()
611 && (m_document->javaScriptCompilationUnit.unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation);
612
613 QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
614 QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher);
615 m_compiledData = compiler.compile();
616 if (!m_compiledData) {
617 setError(compiler.compilationErrors());
618 return;
619 }
620
621 const bool trySaveToDisk = (!diskCacheDisabled() || diskCacheForced())
622 && !m_document->jsModule.debugMode && !typeRecompilation;
623 if (trySaveToDisk) {
624 QString errorString;
625 if (m_compiledData->saveToDisk(url(), &errorString)) {
626 QString error;
627 if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
628 // ignore error, keep using the in-memory compilation unit.
629 }
630 } else {
631 qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->fileName() << "to disk:" << errorString;
632 }
633 }
634}
635
636void QQmlTypeData::resolveTypes()
637{
638 // Add any imported scripts to our resolved set
639 const auto resolvedScripts = m_importCache.resolvedScripts();
640 for (const QQmlImports::ScriptReference &script : resolvedScripts) {
641 QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location);
642 addDependency(blob.data());
643
644 ScriptReference ref;
645 //ref.location = ...
646 if (!script.qualifier.isEmpty())
647 {
648 ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace;
649 // Add a reference to the enclosing namespace
650 m_namespaces.insert(script.qualifier);
651 } else {
652 ref.qualifier = script.nameSpace;
653 }
654
655 ref.script = blob;
656 m_scripts << ref;
657 }
658
659 // Lets handle resolved composite singleton types
660 const auto resolvedCompositeSingletons = m_importCache.resolvedCompositeSingletons();
661 for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) {
662 TypeReference ref;
663 QString typeName;
664 if (!csRef.prefix.isEmpty()) {
665 typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName;
666 // Add a reference to the enclosing namespace
667 m_namespaces.insert(csRef.prefix);
668 } else {
669 typeName = csRef.typeName;
670 }
671
672 int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1;
673 int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1;
674
675 if (!resolveType(typeName, majorVersion, minorVersion, ref, -1, -1, true,
676 QQmlType::CompositeSingletonType))
677 return;
678
679 if (ref.type.isCompositeSingleton()) {
680 ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
681 if (ref.typeData->status() == QQmlDataBlob::ResolvingDependencies) {
682 // TODO: give an error message? If so, we should record and show the path of the cycle.
683 continue;
684 }
685 addDependency(ref.typeData.data());
686 ref.prefix = csRef.prefix;
687
688 m_compositeSingletons << ref;
689 }
690 }
691
692 for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
693 unresolvedRef != end; ++unresolvedRef) {
694
695 TypeReference ref; // resolved reference
696
697 const bool reportErrors = unresolvedRef->errorWhenNotFound;
698
699 int majorVersion = -1;
700 int minorVersion = -1;
701
702 const QString name = stringAt(unresolvedRef.key());
703
704 if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line,
705 unresolvedRef->location.column, reportErrors,
706 QQmlType::AnyRegistrationType) && reportErrors)
707 return;
708
709 if (ref.type.isComposite()) {
710 ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
711 addDependency(ref.typeData.data());
712 }
713 ref.majorVersion = majorVersion;
714 ref.minorVersion = minorVersion;
715
716 ref.location.line = unresolvedRef->location.line;
717 ref.location.column = unresolvedRef->location.column;
718
719 ref.needsCreation = unresolvedRef->needsCreation;
720
721 m_resolvedTypes.insert(unresolvedRef.key(), ref);
722 }
723
724 // ### this allows enums to work without explicit import or instantiation of the type
725 if (!m_implicitImportLoaded)
726 loadImplicitImport();
727}
728
729QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches(
730 QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
731 QV4::ResolvedTypeReferenceMap *resolvedTypeCache
732 ) const
733{
734 typeNameCache->adopt(new QQmlTypeNameCache(m_importCache));
735
736 for (const QString &ns: m_namespaces)
737 (*typeNameCache)->add(ns);
738
739 // Add any Composite Singletons that were used to the import cache
740 for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
741 (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix);
742
743 m_importCache.populateCache(typeNameCache->data());
744
745 QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
746
747 for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) {
748 QScopedPointer<QV4::ResolvedTypeReference> ref(new QV4::ResolvedTypeReference);
749 QQmlType qmlType = resolvedType->type;
750 if (resolvedType->typeData) {
751 if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) {
752 return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName()));
753 }
754 ref->compilationUnit = resolvedType->typeData->compilationUnit();
755 } else if (qmlType.isValid()) {
756 ref->type = qmlType;
757 Q_ASSERT(ref->type.isValid());
758
759 if (resolvedType->needsCreation && !ref->type.isCreatable()) {
760 QString reason = ref->type.noCreationReason();
761 if (reason.isEmpty())
762 reason = tr("Element is not creatable.");
763 return qQmlCompileError(resolvedType->location, reason);
764 }
765
766 if (ref->type.containsRevisionedAttributes()) {
767 ref->typePropertyCache = engine->cache(ref->type,
768 resolvedType->minorVersion);
769 }
770 }
771 ref->majorVersion = resolvedType->majorVersion;
772 ref->minorVersion = resolvedType->minorVersion;
773 ref->doDynamicTypeCheck();
774 resolvedTypeCache->insert(resolvedType.key(), ref.take());
775 }
776 QQmlJS::DiagnosticMessage noError;
777 return noError;
778}
779
780bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion,
781 TypeReference &ref, int lineNumber, int columnNumber,
782 bool reportErrors, QQmlType::RegistrationType registrationType)
783{
784 QQmlImportNamespace *typeNamespace = nullptr;
785 QList<QQmlError> errors;
786
787 bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion,
788 &typeNamespace, &errors, registrationType);
789 if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
790 // Lazy loading of implicit import
791 if (loadImplicitImport()) {
792 // Try again to find the type
793 errors.clear();
794 typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion,
795 &typeNamespace, &errors, registrationType);
796 } else {
797 return false; //loadImplicitImport() hit an error, and called setError already
798 }
799 }
800
801 if ((!typeFound || typeNamespace) && reportErrors) {
802 // Known to not be a type:
803 // - known to be a namespace (Namespace {})
804 // - type with unknown namespace (UnknownNamespace.SomeType {})
805 QQmlError error;
806 if (typeNamespace) {
807 error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName));
808 } else {
809 if (errors.size()) {
810 error = errors.takeFirst();
811 } else {
812 // this should not be possible!
813 // Description should come from error provided by addImport() function.
814 error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
815 }
816 error.setUrl(m_importCache.baseUrl());
817 error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description()));
818 }
819
820 if (lineNumber != -1)
821 error.setLine(lineNumber);
822 if (columnNumber != -1)
823 error.setColumn(columnNumber);
824
825 errors.prepend(error);
826 setError(errors);
827 return false;
828 }
829
830 return true;
831}
832
833void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
834{
835 ScriptReference ref;
836 ref.script = blob;
837 ref.location = location;
838 ref.qualifier = qualifier;
839
840 m_scripts << ref;
841}
842
843QT_END_NAMESPACE
844