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