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(s: 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 | QV4::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 | |
114 | void QQmlTypeData::registerCallback(TypeDataCallback *callback) |
115 | { |
116 | Q_ASSERT(!m_callbacks.contains(callback)); |
117 | m_callbacks.append(t: callback); |
118 | } |
119 | |
120 | void 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 | |
127 | CompositeMetaTypeIds QQmlTypeData::typeIds(int objectId) const |
128 | { |
129 | if (objectId != 0) |
130 | return m_inlineComponentData[objectId].typeIds; |
131 | return m_typeIds; |
132 | } |
133 | |
134 | bool 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 | |
225 | void 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 | |
255 | static 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 |
273 | namespace { |
274 | template<typename ObjectContainer> |
275 | void 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 | |
288 | template<typename Container> |
289 | void 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 | |
307 | void 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 | |
540 | void QQmlTypeData::completed() |
541 | { |
542 | // Notify callbacks |
543 | while (!m_callbacks.isEmpty()) { |
544 | TypeDataCallback *callback = m_callbacks.takeFirst(); |
545 | callback->typeDataReady(this); |
546 | } |
547 | } |
548 | |
549 | bool 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 | |
570 | void 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 | |
596 | void 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 | |
607 | bool 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 | |
638 | void 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 | |
649 | void 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 | |
708 | void 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 | |
742 | void 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 | |
750 | QString 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 | |
757 | void 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 | |
790 | void 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 | |
896 | QQmlError 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 | |
979 | bool 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 | |
1035 | void 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 | |
1045 | QT_END_NAMESPACE |
1046 | |