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 | |
53 | Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) |
54 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | QQmlTypeData::TypeDataCallback::~TypeDataCallback() |
58 | { |
59 | } |
60 | |
61 | QString 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 | |
71 | QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) |
72 | : QQmlTypeLoader::Blob(url, QmlFile, manager), |
73 | m_typesResolved(false), m_implicitImportLoaded(false) |
74 | { |
75 | |
76 | } |
77 | |
78 | QQmlTypeData::~QQmlTypeData() |
79 | { |
80 | m_scripts.clear(); |
81 | m_compositeSingletons.clear(); |
82 | m_resolvedTypes.clear(); |
83 | } |
84 | |
85 | const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const |
86 | { |
87 | return m_scripts; |
88 | } |
89 | |
90 | QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const |
91 | { |
92 | return m_compiledData.data(); |
93 | } |
94 | |
95 | void QQmlTypeData::registerCallback(TypeDataCallback *callback) |
96 | { |
97 | Q_ASSERT(!m_callbacks.contains(callback)); |
98 | m_callbacks.append(callback); |
99 | } |
100 | |
101 | void 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 | |
108 | bool 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 | |
187 | void 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 | |
217 | static 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 | |
234 | void 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 | |
403 | void QQmlTypeData::completed() |
404 | { |
405 | // Notify callbacks |
406 | while (!m_callbacks.isEmpty()) { |
407 | TypeDataCallback *callback = m_callbacks.takeFirst(); |
408 | callback->typeDataReady(this); |
409 | } |
410 | } |
411 | |
412 | bool 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 | |
433 | void 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 | |
459 | void 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 | |
470 | bool 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 | |
501 | void 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 | |
512 | void 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 | |
555 | void 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 | |
589 | void 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 | |
597 | QString 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 | |
604 | void 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 | |
636 | void 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 | |
729 | QQmlJS::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 | |
780 | bool 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 | |
833 | void 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 | |
843 | QT_END_NAMESPACE |
844 | |