aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmltypedata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmltypedata.cpp')
-rw-r--r--src/qml/qml/qqmltypedata.cpp1110
1 files changed, 1110 insertions, 0 deletions
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
new file mode 100644
index 0000000000..9acd801672
--- /dev/null
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -0,0 +1,1110 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include <private/qqmlcomponentandaliasresolver_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlirbuilder_p.h>
+#include <private/qqmlirloader_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qqmlpropertyvalidator_p.h>
+#include <private/qqmlscriptblob_p.h>
+#include <private/qqmlscriptdata_p.h>
+#include <private/qqmltypecompiler_p.h>
+#include <private/qqmltypedata_p.h>
+#include <private/qqmltypeloaderqmldircontent_p.h>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qcryptographichash.h>
+
+#include <memory>
+
+Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
+Q_LOGGING_CATEGORY(lcCycle, "qt.qml.typeresolution.cycle", QtWarningMsg)
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeData::TypeDataCallback::~TypeDataCallback()
+{
+}
+
+QString QQmlTypeData::TypeReference::qualifiedName() const
+{
+ QString result;
+ if (!prefix.isEmpty()) {
+ result = prefix + QLatin1Char('.');
+ }
+ result.append(type.qmlTypeName());
+ return result;
+}
+
+QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager)
+ : QQmlTypeLoader::Blob(url, QmlFile, manager),
+ m_typesResolved(false), m_implicitImportLoaded(false)
+{
+
+}
+
+QQmlTypeData::~QQmlTypeData()
+{
+ m_scripts.clear();
+ m_compositeSingletons.clear();
+ m_resolvedTypes.clear();
+}
+
+QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const
+{
+ return m_compiledData.data();
+}
+
+void QQmlTypeData::registerCallback(TypeDataCallback *callback)
+{
+ Q_ASSERT(!m_callbacks.contains(callback));
+ m_callbacks.append(callback);
+}
+
+void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
+{
+ Q_ASSERT(m_callbacks.contains(callback));
+ m_callbacks.removeOne(callback);
+ Q_ASSERT(!m_callbacks.contains(callback));
+}
+
+QQmlType QQmlTypeData::qmlType(const QString &inlineComponentName) const
+{
+ if (inlineComponentName.isEmpty())
+ return m_qmlType;
+ return m_inlineComponentData[inlineComponentName].qmlType;
+}
+
+bool QQmlTypeData::tryLoadFromDiskCache()
+{
+ if (!readCacheFile())
+ return false;
+
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
+ {
+ QString error;
+ if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
+ qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error;
+ return false;
+ }
+ }
+
+ if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
+ restoreIR(unit);
+ return true;
+ }
+
+ m_compiledData = std::move(unit);
+
+ QVector<QV4::CompiledData::InlineComponent> ics;
+ for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) {
+ auto object = m_compiledData->objectAt(i);
+ m_typeReferences.collectFromObject(object);
+ const auto inlineComponentTable = object->inlineComponentTable();
+ for (auto i = 0; i != object->nInlineComponents; ++i) {
+ ics.push_back(inlineComponentTable[i]);
+ }
+ }
+
+ m_importCache->setBaseUrl(finalUrl(), finalUrlString());
+
+ // For remote URLs, we don't delay the loading of the implicit import
+ // because the loading probably requires an asynchronous fetch of the
+ // qmldir (so we can't load it just in time).
+ if (!finalUrl().scheme().isEmpty()) {
+ QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
+ if (!QQmlImports::isLocal(qmldirUrl)) {
+ if (!loadImplicitImport())
+ return false;
+
+ // find the implicit import
+ for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
+ if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".")
+ && import->qualifierIndex == 0
+ && !import->version.hasMajorVersion()
+ && !import->version.hasMinorVersion()) {
+ QList<QQmlError> errors;
+ auto pendingImport = std::make_shared<PendingImport>(
+ this, import, QQmlImports::ImportNoFlag);
+ pendingImport->precedence = QQmlImportInstance::Implicit;
+ if (!fetchQmldir(qmldirUrl, pendingImport, 1, &errors)) {
+ setError(errors);
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
+ QList<QQmlError> errors;
+ if (!addImport(import, {}, &errors)) {
+ Q_ASSERT(errors.size());
+ QQmlError error(errors.takeFirst());
+ error.setUrl(m_importCache->baseUrl());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
+ errors.prepend(error); // put it back on the list after filling out information.
+ setError(errors);
+ return false;
+ }
+ }
+
+ for (auto&& ic: ics) {
+ QString const nameString = m_compiledData->stringAt(ic.nameIndex);
+ auto importUrl = finalUrl();
+ importUrl.setFragment(nameString);
+ auto import = new QQmlImportInstance();
+ m_importCache->addInlineComponentImport(import, nameString, importUrl);
+ }
+
+ return true;
+}
+
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::allocateNamedObjects(
+ const QV4::CompiledData::Object *object) const
+{
+ Q_UNUSED(object);
+}
+
+template<>
+bool QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::markAsComponent(int index) const
+{
+ return m_compiler->objectAt(index)->hasFlag(QV4::CompiledData::Object::IsComponent);
+}
+
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::setObjectId(int index) const
+{
+ Q_UNUSED(index)
+ // we cannot sanity-check the index here because bindings are sorted in a different order
+ // in the CU vs the IR.
+}
+
+template<>
+void QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveGeneralizedGroupProperty(
+ const CompiledObject &component, CompiledBinding *binding)
+{
+ // We cannot make it fail here. It might be a custom-parsed property
+ for (int i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ if (m_compiler->objectAt(candidateIndex)->idNameIndex == binding->propertyNameIndex) {
+ m_propertyCaches->set(binding->value.objectIndex, m_propertyCaches->at(candidateIndex));
+ return;
+ }
+ }
+}
+
+template<>
+typename QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::AliasResolutionResult
+QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::resolveAliasesInObject(
+ const CompiledObject &component, int objectIndex, QQmlError *error)
+{
+ const CompiledObject *obj = m_compiler->objectAt(objectIndex);
+ for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
+ if (!alias->hasFlag(QV4::CompiledData::Alias::Resolved)) {
+ *error = qQmlCompileError( alias->referenceLocation, tr("Unresolved alias found"));
+ return NoAliasResolved;
+ }
+
+ if (alias->isAliasToLocalAlias() || alias->encodedMetaPropertyIndex == -1)
+ continue;
+
+ const int targetObjectIndex
+ = objectForId(m_compiler, component, alias->targetObjectId());
+ const int coreIndex
+ = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+
+ QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+
+ if (!targetCache->property(coreIndex))
+ return SomeAliasesResolved;
+ }
+
+ return AllAliasesResolved;
+}
+
+template<>
+bool QQmlComponentAndAliasResolver<QV4::CompiledData::CompilationUnit>::wrapImplicitComponent(
+ const QV4::CompiledData::Binding *binding)
+{
+ // This should have been done when creating the CU.
+ Q_UNUSED(binding);
+ return false;
+}
+
+QQmlError QQmlTypeData::createTypeAndPropertyCaches(
+ const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
+{
+ Q_ASSERT(m_compiledData);
+ m_compiledData->typeNameCache = typeNameCache;
+ m_compiledData->resolvedTypes = resolvedTypeCache;
+ m_compiledData->inlineComponentData = m_inlineComponentData;
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
+
+ {
+ QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(
+ &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine,
+ m_compiledData.data(), m_importCache.data(), typeClassName());
+
+ QQmlError error = propertyCacheCreator.verifyNoICCycle();
+ if (error.isValid())
+ return error;
+
+ QQmlPropertyCacheCreatorBase::IncrementalResult result;
+ do {
+ result = propertyCacheCreator.buildMetaObjectsIncrementally();
+ if (result.error.isValid()) {
+ return result.error;
+ } else {
+ QQmlComponentAndAliasResolver resolver(
+ m_compiledData.data(), engine, &m_compiledData->propertyCaches);
+ if (const QQmlError error = resolver.resolve(result.processedRoot);
+ error.isValid()) {
+ return error;
+ }
+ pendingGroupPropertyBindings.resolveMissingPropertyCaches(
+ &m_compiledData->propertyCaches);
+ pendingGroupPropertyBindings.clear(); // anything that can be processed is now processed
+ }
+
+ } while (result.canResume);
+ }
+
+ pendingGroupPropertyBindings.resolveMissingPropertyCaches(&m_compiledData->propertyCaches);
+ return QQmlError();
+}
+
+static bool addTypeReferenceChecksumsToHash(
+ const QList<QQmlTypeData::TypeReference> &typeRefs,
+ QHash<quintptr, QByteArray> *checksums, QCryptographicHash *hash)
+{
+ for (const auto &typeRef: typeRefs) {
+ if (typeRef.typeData) {
+ const auto unit = typeRef.typeData->compilationUnit()->unitData();
+ hash->addData({unit->md5Checksum, sizeof(unit->md5Checksum)});
+ } else if (const QMetaObject *mo = typeRef.type.metaObject()) {
+ const auto propertyCache = QQmlMetaType::propertyCache(mo);
+ bool ok = false;
+ hash->addData(propertyCache->checksum(checksums, &ok));
+ if (!ok)
+ return false;
+ }
+ }
+ return true;
+}
+
+// local helper function for inline components
+namespace {
+using InlineComponentData = QV4::CompiledData::InlineComponentData;
+
+template<typename ObjectContainer>
+void setupICs(
+ const ObjectContainer &container, QHash<QString, InlineComponentData> *icData,
+ const QUrl &baseUrl,
+ const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) {
+ Q_ASSERT(icData->empty());
+ for (int i = 0; i != container->objectCount(); ++i) {
+ auto root = container->objectAt(i);
+ for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) {
+ // We cannot re-use a previously finalized inline component type here. We need our own.
+ // We can and should re-use speculative type references, though.
+ InlineComponentData icDatum(
+ QQmlMetaType::findInlineComponentType(
+ baseUrl, container->stringAt(it->nameIndex), compilationUnit),
+ int(it->objectIndex), int(it->nameIndex), 0, 0, 0);
+
+ icData->insert(container->stringAt(it->nameIndex), icDatum);
+ }
+ }
+};
+}
+
+template<typename Container>
+void QQmlTypeData::setCompileUnit(const Container &container)
+{
+ for (int i = 0; i != container->objectCount(); ++i) {
+ auto const root = container->objectAt(i);
+ for (auto it = root->inlineComponentsBegin(); it != root->inlineComponentsEnd(); ++it) {
+ auto *typeRef = m_compiledData->resolvedType(it->nameIndex);
+
+ // We don't want the type reference to keep a strong reference to the compilation unit
+ // here. The compilation unit owns the type reference, and having a strong reference
+ // would prevent the compilation unit from ever getting deleted. We can still be sure
+ // that the compilation unit outlives the type reference, due to ownership.
+ typeRef->setReferencesCompilationUnit(false);
+
+ typeRef->setCompilationUnit(m_compiledData); // share compilation unit
+ }
+ }
+}
+
+void QQmlTypeData::done()
+{
+ auto cleanup = qScopeGuard([this]{
+ m_backupSourceCode = SourceCodeData();
+ m_document.reset();
+ m_typeReferences.clear();
+ if (isError()) {
+ const auto encounteredErrors = errors();
+ for (const QQmlError &e : encounteredErrors)
+ qCDebug(DBG_DISK_CACHE) << e.toString();
+ m_compiledData.reset();
+ }
+ });
+
+ if (isError())
+ return;
+
+ // Check all script dependencies for errors
+ for (int ii = 0; ii < m_scripts.size(); ++ii) {
+ const ScriptReference &script = m_scripts.at(ii);
+ Q_ASSERT(script.script->isCompleteOrError());
+ if (script.script->isError()) {
+ QList<QQmlError> errors = script.script->errors();
+ QQmlError error;
+ error.setUrl(url());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column()));
+ error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
+ errors.prepend(error);
+ setError(errors);
+ return;
+ }
+ }
+
+ // Check all type dependencies for errors
+ auto createError = [&](const TypeReference &type , const QString &message) {
+ QList<QQmlError> errors = type.typeData ? type.typeData->errors() : QList<QQmlError>{};
+ QQmlError error;
+ error.setUrl(url());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column()));
+ error.setDescription(message);
+ errors.prepend(error);
+ setError(errors);
+ };
+ for (auto it = std::as_const(m_resolvedTypes).begin(), end = std::as_const(m_resolvedTypes).end(); it != end;
+ ++it) {
+ const TypeReference &type = *it;
+ Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError() || type.type.isInlineComponentType());
+
+ if (type.type.isInlineComponentType()) {
+ const QUrl url = type.type.sourceUrl();
+ if (!QQmlMetaType::equalBaseUrls(url, finalUrl())
+ && !QQmlMetaType::obtainCompilationUnit(type.type.typeId())) {
+ const QString &typeName = stringAt(it.key());
+ int lastDot = typeName.lastIndexOf(u'.');
+ createError(
+ type,
+ QQmlTypeLoader::tr("Type %1 has no inline component type called %2")
+ .arg(QStringView{typeName}.left(lastDot), type.type.elementName()));
+ return;
+ }
+ }
+ if (type.typeData && type.typeData->isError()) {
+ const QString &typeName = stringAt(it.key());
+ createError(type, QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
+ return;
+ }
+ }
+
+ // Check all composite singleton type dependencies for errors
+ for (int ii = 0; ii < m_compositeSingletons.size(); ++ii) {
+ const TypeReference &type = m_compositeSingletons.at(ii);
+ Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
+ if (type.typeData && type.typeData->isError()) {
+ QString typeName = type.type.qmlTypeName();
+
+ createError(type, QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
+ return;
+ }
+ }
+
+ if (QQmlPropertyCacheCreatorBase::canCreateClassNameTypeByUrl(finalUrl())) {
+ const bool isSingleton = m_document
+ ? m_document.data()->isSingleton()
+ : (m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton);
+ m_qmlType = QQmlMetaType::findCompositeType(
+ finalUrl(), m_compiledData, isSingleton
+ ? QQmlMetaType::Singleton
+ : QQmlMetaType::NonSingleton);
+ m_typeClassName = QByteArray(m_qmlType.typeId().name()).chopped(1);
+ }
+
+ if (m_document)
+ setupICs(m_document, &m_inlineComponentData, finalUrl(), m_compiledData);
+ else
+ setupICs(m_compiledData, &m_inlineComponentData, finalUrl(), m_compiledData);
+
+ QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+ {
+ QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
+ if (error.isValid()) {
+ setError(error);
+ qDeleteAll(resolvedTypeCache);
+ return;
+ }
+ }
+
+ const auto dependencyHasher = [&resolvedTypeCache, this]() {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ return (resolvedTypeCache.addToHash(&hash, typeLoader()->checksumCache())
+ && ::addTypeReferenceChecksumsToHash(
+ m_compositeSingletons, typeLoader()->checksumCache(), &hash))
+ ? hash.result()
+ : QByteArray();
+ };
+
+ // verify if any dependencies changed if we're using a cache
+ if (m_document.isNull()) {
+ const QQmlError error = createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache);
+ if (!error.isValid() && m_compiledData->verifyChecksum(dependencyHasher)) {
+ setCompileUnit(m_compiledData);
+ } else {
+
+ if (error.isValid()) {
+ qCDebug(DBG_DISK_CACHE)
+ << "Failed to create property caches for"
+ << m_compiledData->fileName()
+ << "because" << error.description();
+ } else {
+ qCDebug(DBG_DISK_CACHE)
+ << "Checksum mismatch for cached version of"
+ << m_compiledData->fileName();
+ }
+
+ if (!loadFromSource())
+ return;
+
+ // We want to keep our resolve types ...
+ m_compiledData->resolvedTypes.clear();
+ // ... but we don't want the property caches we've created for the broken CU.
+ for (QV4::ResolvedTypeReference *ref: std::as_const(resolvedTypeCache)) {
+ const auto compilationUnit = ref->compilationUnit();
+ if (compilationUnit.isNull()) {
+ // Inline component references without CU belong to the surrounding CU.
+ // We have to clear them. Inline component references to other documents
+ // have a CU.
+ if (!ref->type().isInlineComponentType())
+ continue;
+ } else if (compilationUnit != m_compiledData) {
+ continue;
+ }
+ ref->setTypePropertyCache(QQmlPropertyCache::ConstPtr());
+ ref->setCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit>());
+ }
+
+ m_compiledData.reset();
+ }
+ }
+
+ if (!m_document.isNull()) {
+ // Compile component
+ compile(typeNameCache, &resolvedTypeCache, dependencyHasher);
+ if (isError())
+ return;
+ else
+ setCompileUnit(m_document);
+ }
+
+ {
+ QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
+ m_compiledData->inlineComponentData = m_inlineComponentData;
+ {
+ // Sanity check property bindings
+ QQmlPropertyValidator validator(enginePrivate, m_importCache.data(), m_compiledData);
+ QVector<QQmlError> errors = validator.validate();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ }
+
+ m_compiledData->finalizeCompositeType(qmlType());
+ }
+
+ {
+ QQmlType type = QQmlMetaType::qmlType(finalUrl(), true);
+ if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) {
+ if (!type.isValid()) {
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
+ setError(error);
+ return;
+ } else if (!type.isCompositeSingleton()) {
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName()));
+ setError(error);
+ return;
+ }
+ } else {
+ // If the type is CompositeSingleton but there was no pragma Singleton in the
+ // QML file, lets report an error.
+ if (type.isValid() && type.isCompositeSingleton()) {
+ QString typeName = type.qmlTypeName();
+ setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
+ return;
+ }
+ }
+ }
+
+ {
+ // Collect imported scripts
+ m_compiledData->dependentScripts.reserve(m_scripts.size());
+ for (int scriptIndex = 0; scriptIndex < m_scripts.size(); ++scriptIndex) {
+ const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex);
+
+ QStringView qualifier(script.qualifier);
+ QString enclosingNamespace;
+
+ const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
+ if (lastDotIndex != -1) {
+ enclosingNamespace = qualifier.left(lastDotIndex).toString();
+ qualifier = qualifier.mid(lastDotIndex+1);
+ }
+
+ m_compiledData->typeNameCache->add(
+ qualifier.toString(), scriptIndex, enclosingNamespace);
+ QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
+ m_compiledData->dependentScripts << scriptData;
+ }
+ }
+}
+
+void QQmlTypeData::completed()
+{
+ // Notify callbacks
+ while (!m_callbacks.isEmpty()) {
+ TypeDataCallback *callback = m_callbacks.takeFirst();
+ callback->typeDataReady(this);
+ }
+}
+
+bool QQmlTypeData::loadImplicitImport()
+{
+ m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error)
+
+ m_importCache->setBaseUrl(finalUrl(), finalUrlString());
+
+ // For local urls, add an implicit import "." as most overridden lookup.
+ // This will also trigger the loading of the qmldir and the import of any native
+ // types from available plugins.
+ QList<QQmlError> implicitImportErrors;
+ QString localQmldir;
+ m_importCache->addImplicitImport(typeLoader(), &localQmldir, &implicitImportErrors);
+
+ // When loading with QQmlImports::ImportImplicit, the imports are _appended_ to the namespace
+ // in the order they are loaded. Therefore, the addImplicitImport above gets the highest
+ // precedence. This is in contrast to normal priority imports. Those are _prepended_ in the
+ // order they are loaded.
+ if (!localQmldir.isEmpty()) {
+ const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(localQmldir);
+ const QList<QQmlDirParser::Import> moduleImports
+ = QQmlMetaType::moduleImports(qmldir.typeNamespace(), QTypeRevision())
+ + qmldir.imports();
+ loadDependentImports(moduleImports, QString(), QTypeRevision(),
+ QQmlImportInstance::Implicit + 1, QQmlImports::ImportNoFlag,
+ &implicitImportErrors);
+ }
+
+ if (!implicitImportErrors.isEmpty()) {
+ setError(implicitImportErrors);
+ return false;
+ }
+
+ return true;
+}
+
+void QQmlTypeData::dataReceived(const SourceCodeData &data)
+{
+ m_backupSourceCode = data;
+
+ if (tryLoadFromDiskCache())
+ return;
+
+ if (isError())
+ return;
+
+ if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) {
+ if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch)
+ setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile"));
+ else if (!m_backupSourceCode.exists())
+ setError(QQmlTypeLoader::tr("No such file or directory"));
+ else
+ setError(QQmlTypeLoader::tr("File is empty"));
+ return;
+ }
+
+ if (!loadFromSource())
+ return;
+
+ continueLoadFromIR();
+}
+
+void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
+{
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ QQmlIRLoader loader(unit->qmlData, m_document.data());
+ loader.load();
+ m_document->jsModule.fileName = urlString();
+ m_document->jsModule.finalUrl = finalUrlString();
+ m_document->javaScriptCompilationUnit
+ = QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
+ new QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions),
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit>::Adopt);
+ continueLoadFromIR();
+}
+
+bool QQmlTypeData::loadFromSource()
+{
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp();
+ QQmlEngine *qmlEngine = typeLoader()->engine();
+ QmlIR::IRBuilder compiler(qmlEngine->handle()->illegalNames());
+
+ QString sourceError;
+ const QString source = m_backupSourceCode.readAll(&sourceError);
+ if (!sourceError.isEmpty()) {
+ setError(sourceError);
+ return false;
+ }
+
+ if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) {
+ QList<QQmlError> errors;
+ errors.reserve(compiler.errors.size());
+ for (const QQmlJS::DiagnosticMessage &msg : std::as_const(compiler.errors)) {
+ QQmlError e;
+ e.setUrl(url());
+ e.setLine(qmlConvertSourceCoordinate<quint32, int>(msg.loc.startLine));
+ e.setColumn(qmlConvertSourceCoordinate<quint32, int>(msg.loc.startColumn));
+ e.setDescription(msg.message);
+ errors << e;
+ }
+ setError(errors);
+ return false;
+ }
+ return true;
+}
+
+void QQmlTypeData::restoreIR(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
+{
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ QQmlIRLoader loader(unit->unitData(), m_document.data());
+ loader.load();
+ m_document->jsModule.fileName = urlString();
+ m_document->jsModule.finalUrl = finalUrlString();
+ m_document->javaScriptCompilationUnit = unit;
+ continueLoadFromIR();
+}
+
+void QQmlTypeData::continueLoadFromIR()
+{
+ for (auto const& object: m_document->objects) {
+ for (auto it = object->inlineComponentsBegin(); it != object->inlineComponentsEnd(); ++it) {
+ QString const nameString = m_document->stringAt(it->nameIndex);
+ auto importUrl = finalUrl();
+ importUrl.setFragment(nameString);
+ auto import = new QQmlImportInstance(); // Note: The cache takes ownership of the QQmlImportInstance
+ m_importCache->addInlineComponentImport(import, nameString, importUrl);
+ }
+ }
+
+ m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd());
+ m_importCache->setBaseUrl(finalUrl(), finalUrlString());
+
+ // For remote URLs, we don't delay the loading of the implicit import
+ // because the loading probably requires an asynchronous fetch of the
+ // qmldir (so we can't load it just in time).
+ if (!finalUrl().scheme().isEmpty()) {
+ QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
+ if (!QQmlImports::isLocal(qmldirUrl)) {
+ if (!loadImplicitImport())
+ return;
+ // This qmldir is for the implicit import
+ auto implicitImport = std::make_shared<PendingImport>();
+ implicitImport->uri = QLatin1String(".");
+ implicitImport->version = QTypeRevision();
+ QList<QQmlError> errors;
+
+ if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) {
+ setError(errors);
+ return;
+ }
+ }
+ }
+
+ QList<QQmlError> errors;
+
+ for (const QV4::CompiledData::Import *import : std::as_const(m_document->imports)) {
+ if (!addImport(import, {}, &errors)) {
+ Q_ASSERT(errors.size());
+
+ // We're only interested in the chronoligically last error. The previous
+ // errors might be from unsuccessfully trying to load a module from the
+ // resource file system.
+ QQmlError error = errors.first();
+ error.setUrl(m_importCache->baseUrl());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column()));
+ setError(error);
+ return;
+ }
+ }
+}
+
+void QQmlTypeData::allDependenciesDone()
+{
+ QQmlTypeLoader::Blob::allDependenciesDone();
+
+ if (!m_typesResolved) {
+ // Check that all imports were resolved
+ QList<QQmlError> errors;
+ auto it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
+ for ( ; it != end; ++it) {
+ if ((*it)->priority == 0) {
+ // This import was not resolved
+ for (auto keyIt = m_unresolvedImports.constBegin(),
+ keyEnd = m_unresolvedImports.constEnd();
+ keyIt != keyEnd; ++keyIt) {
+ const PendingImportPtr &import = *keyIt;
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
+ error.setUrl(m_importCache->baseUrl());
+ error.setLine(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.line()));
+ error.setColumn(qmlConvertSourceCoordinate<quint32, int>(
+ import->location.column()));
+ errors.prepend(error);
+ }
+ }
+ }
+ if (errors.size()) {
+ setError(errors);
+ return;
+ }
+
+ resolveTypes();
+ m_typesResolved = true;
+ }
+}
+
+void QQmlTypeData::downloadProgressChanged(qreal p)
+{
+ for (int ii = 0; ii < m_callbacks.size(); ++ii) {
+ TypeDataCallback *callback = m_callbacks.at(ii);
+ callback->typeDataProgress(this, p);
+ }
+}
+
+QString QQmlTypeData::stringAt(int index) const
+{
+ if (m_compiledData)
+ return m_compiledData->stringAt(index);
+ return m_document->jsGenerator.stringTable.stringForIndex(index);
+}
+
+void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
+ const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+{
+ Q_ASSERT(m_compiledData.isNull());
+
+ const bool typeRecompilation = m_document
+ && m_document->javaScriptCompilationUnit
+ && m_document->javaScriptCompilationUnit->unitData()
+ && (m_document->javaScriptCompilationUnit->unitData()->flags
+ & QV4::CompiledData::Unit::PendingTypeCompilation);
+
+ QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
+ QQmlTypeCompiler compiler(
+ enginePrivate, this, m_document.data(), resolvedTypeCache, dependencyHasher);
+ auto compilationUnit = compiler.compile();
+ if (!compilationUnit) {
+ qDeleteAll(*resolvedTypeCache);
+ resolvedTypeCache->clear();
+ setError(compiler.compilationErrors());
+ return;
+ }
+
+ const bool trySaveToDisk = writeCacheFile() && !typeRecompilation;
+ if (trySaveToDisk) {
+ QString errorString;
+ if (compilationUnit->saveToDisk(url(), &errorString)) {
+ QString error;
+ if (!compilationUnit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) {
+ // ignore error, keep using the in-memory compilation unit.
+ }
+ } else {
+ qCDebug(DBG_DISK_CACHE) << "Error saving cached version of"
+ << compilationUnit->fileName() << "to disk:" << errorString;
+ }
+ }
+
+ m_compiledData = std::move(compilationUnit);
+ m_compiledData->typeNameCache = typeNameCache;
+ m_compiledData->resolvedTypes = *resolvedTypeCache;
+ m_compiledData->propertyCaches = std::move(*compiler.propertyCaches());
+ Q_ASSERT(m_compiledData->propertyCaches.count()
+ == static_cast<int>(m_compiledData->objectCount()));
+}
+
+void QQmlTypeData::resolveTypes()
+{
+ // Load the implicit import since it may have additional scripts.
+ if (!m_implicitImportLoaded && !loadImplicitImport())
+ return;
+
+ // Add any imported scripts to our resolved set
+ const auto resolvedScripts = m_importCache->resolvedScripts();
+ for (const QQmlImports::ScriptReference &script : resolvedScripts) {
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location);
+ addDependency(blob.data());
+
+ ScriptReference ref;
+ //ref.location = ...
+ if (!script.qualifier.isEmpty())
+ {
+ ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace;
+ // Add a reference to the enclosing namespace
+ m_namespaces.insert(script.qualifier);
+ } else {
+ ref.qualifier = script.nameSpace;
+ }
+
+ ref.script = blob;
+ m_scripts << ref;
+ }
+
+ // Lets handle resolved composite singleton types
+ const auto resolvedCompositeSingletons = m_importCache->resolvedCompositeSingletons();
+ for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) {
+ TypeReference ref;
+ QString typeName;
+ if (!csRef.prefix.isEmpty()) {
+ typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName;
+ // Add a reference to the enclosing namespace
+ m_namespaces.insert(csRef.prefix);
+ } else {
+ typeName = csRef.typeName;
+ }
+
+ QTypeRevision version = csRef.version;
+ if (!resolveType(typeName, version, ref, -1, -1, true, QQmlType::CompositeSingletonType))
+ return;
+
+ if (ref.type.isCompositeSingleton()) {
+ ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
+ if (ref.typeData->isWaiting() || m_waitingOnMe.contains(ref.typeData.data())) {
+ qCDebug(lcCycle) << "Possible cyclic dependency detected between"
+ << ref.typeData->urlString() << "and" << urlString();
+ continue;
+ }
+ addDependency(ref.typeData.data());
+ ref.prefix = csRef.prefix;
+
+ m_compositeSingletons << ref;
+ }
+ }
+
+ for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
+ unresolvedRef != end; ++unresolvedRef) {
+
+ TypeReference ref; // resolved reference
+
+ const bool reportErrors = unresolvedRef->errorWhenNotFound;
+
+ QTypeRevision version;
+
+ const QString name = stringAt(unresolvedRef.key());
+
+ bool *selfReferenceDetection = unresolvedRef->needsCreation ? nullptr : &ref.selfReference;
+
+ if (!resolveType(name, version, ref, unresolvedRef->location.line(),
+ unresolvedRef->location.column(), reportErrors,
+ QQmlType::AnyRegistrationType, selfReferenceDetection) && reportErrors)
+ return;
+
+ if (ref.type.isComposite() && !ref.selfReference) {
+ ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
+ addDependency(ref.typeData.data());
+ }
+ if (ref.type.isInlineComponentType()) {
+ QUrl containingTypeUrl = ref.type.sourceUrl();
+ containingTypeUrl.setFragment(QString());
+ if (!containingTypeUrl.isEmpty()) {
+ auto typeData = typeLoader()->getType(containingTypeUrl);
+ if (typeData.data() != this) {
+ ref.typeData = typeData;
+ addDependency(typeData.data());
+ }
+ }
+ }
+
+ ref.version = version;
+ ref.location = unresolvedRef->location;
+ ref.needsCreation = unresolvedRef->needsCreation;
+ m_resolvedTypes.insert(unresolvedRef.key(), ref);
+ }
+
+ // ### this allows enums to work without explicit import or instantiation of the type
+ if (!m_implicitImportLoaded)
+ loadImplicitImport();
+}
+
+QQmlError QQmlTypeData::buildTypeResolutionCaches(
+ QQmlRefPointer<QQmlTypeNameCache> *typeNameCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache) const
+{
+ typeNameCache->adopt(new QQmlTypeNameCache(m_importCache));
+
+ for (const QString &ns: m_namespaces)
+ (*typeNameCache)->add(ns);
+
+ // Add any Composite Singletons that were used to the import cache
+ for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
+ (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix);
+
+ m_importCache->populateCache(typeNameCache->data());
+
+ for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) {
+ auto ref = std::make_unique<QV4::ResolvedTypeReference>();
+ QQmlType qmlType = resolvedType->type;
+ if (resolvedType->typeData) {
+ if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) {
+ return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName()));
+ }
+ ref->setCompilationUnit(resolvedType->typeData->compilationUnit());
+ if (resolvedType->type.isInlineComponentType()) {
+ // Inline component which is part of an already resolved type
+ QString icName = qmlType.elementName();
+ Q_ASSERT(!icName.isEmpty());
+
+ const auto compilationUnit = resolvedType->typeData->compilationUnit();
+ ref->setTypePropertyCache(compilationUnit->propertyCaches.at(
+ compilationUnit->inlineComponentId(icName)));
+ ref->setType(std::move(qmlType));
+ Q_ASSERT(ref->type().isInlineComponentType());
+ }
+ } else if (resolvedType->type.isInlineComponentType()) {
+ ref->setType(qmlType);
+
+ // Inline component
+ // If it's defined in the same file we're currently compiling, we don't want to use it.
+ // We're going to fill in the property caches later after all.
+ if (qmlType.isValid()
+ && !QQmlMetaType::equalBaseUrls(finalUrl(), qmlType.sourceUrl())) {
+
+ // this is required for inline components in singletons
+ const QMetaType type = qmlType.typeId();
+ if (auto unit = QQmlMetaType::obtainCompilationUnit(type)) {
+ ref->setCompilationUnit(std::move(unit));
+ ref->setTypePropertyCache(QQmlMetaType::propertyCacheForType(type));
+ }
+ }
+ } else if (qmlType.isValid() && !resolvedType->selfReference) {
+ ref->setType(qmlType);
+ Q_ASSERT(ref->type().isValid());
+
+ if (resolvedType->needsCreation && !qmlType.isCreatable()) {
+ QString reason = qmlType.noCreationReason();
+ if (reason.isEmpty())
+ reason = tr("Element is not creatable.");
+ return qQmlCompileError(resolvedType->location, reason);
+ }
+
+ if (qmlType.containsRevisionedAttributes()) {
+ // It can only have (revisioned) properties or methods if it has a metaobject
+ Q_ASSERT(qmlType.metaObject());
+ ref->setTypePropertyCache(
+ QQmlMetaType::propertyCache(qmlType, resolvedType->version));
+ }
+ }
+ ref->setVersion(resolvedType->version);
+ ref->doDynamicTypeCheck();
+ resolvedTypeCache->insert(resolvedType.key(), ref.release());
+ }
+ QQmlError noError;
+ return noError;
+}
+
+bool QQmlTypeData::resolveType(const QString &typeName, QTypeRevision &version,
+ TypeReference &ref, int lineNumber, int columnNumber,
+ bool reportErrors, QQmlType::RegistrationType registrationType,
+ bool *typeRecursionDetected)
+{
+ QQmlImportNamespace *typeNamespace = nullptr;
+ QList<QQmlError> errors;
+
+ bool typeFound = m_importCache->resolveType(
+ typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors, registrationType,
+ typeRecursionDetected);
+ if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
+ // Lazy loading of implicit import
+ if (loadImplicitImport()) {
+ // Try again to find the type
+ errors.clear();
+ typeFound = m_importCache->resolveType(
+ typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors,
+ registrationType, typeRecursionDetected);
+ } else {
+ return false; //loadImplicitImport() hit an error, and called setError already
+ }
+ }
+
+ if ((!typeFound || typeNamespace) && reportErrors) {
+ // Known to not be a type:
+ // - known to be a namespace (Namespace {})
+ // - type with unknown namespace (UnknownNamespace.SomeType {})
+ QQmlError error;
+ if (typeNamespace) {
+ error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName));
+ } else {
+ if (errors.size()) {
+ error = errors.takeFirst();
+ } else {
+ // this should not be possible!
+ // Description should come from error provided by addImport() function.
+ error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
+ }
+ error.setUrl(m_importCache->baseUrl());
+ error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description()));
+ }
+
+ if (lineNumber != -1)
+ error.setLine(lineNumber);
+ if (columnNumber != -1)
+ error.setColumn(columnNumber);
+
+ errors.prepend(error);
+ setError(errors);
+ return false;
+ }
+
+ return true;
+}
+
+void QQmlTypeData::scriptImported(
+ const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location,
+ const QString &nameSpace, const QString &qualifier)
+{
+ ScriptReference ref;
+ ref.script = blob;
+ ref.location = location;
+ ref.qualifier = qualifier.isEmpty() ? nameSpace : qualifier + QLatin1Char('.') + nameSpace;
+
+ m_scripts << ref;
+}
+
+QT_END_NAMESPACE