diff options
Diffstat (limited to 'src/qml/qml/qqmltypedata.cpp')
-rw-r--r-- | src/qml/qml/qqmltypedata.cpp | 722 |
1 files changed, 493 insertions, 229 deletions
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index cfdcf6aad5..9acd801672 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -1,56 +1,25 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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/qqmltypedata_p.h> +#include <private/qqmlcomponentandaliasresolver_p.h> #include <private/qqmlengine_p.h> -#include <private/qqmlpropertycachecreator_p.h> -#include <private/qqmlpropertyvalidator_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 @@ -82,12 +51,7 @@ QQmlTypeData::~QQmlTypeData() m_resolvedTypes.clear(); } -const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const -{ - return m_scripts; -} - -QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const +QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const { return m_compiledData.data(); } @@ -105,19 +69,19 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) Q_ASSERT(!m_callbacks.contains(callback)); } -bool QQmlTypeData::tryLoadFromDiskCache() +QQmlType QQmlTypeData::qmlType(const QString &inlineComponentName) const { - if (diskCacheDisabled() && !diskCacheForced()) - return false; - - if (isDebugging()) - return false; + if (inlineComponentName.isEmpty()) + return m_qmlType; + return m_inlineComponentData[inlineComponentName].qmlType; +} - QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle(); - if (!v4) +bool QQmlTypeData::tryLoadFromDiskCache() +{ + if (!readCacheFile()) return false; - QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create(); + auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(); { QString error; if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { @@ -127,16 +91,23 @@ bool QQmlTypeData::tryLoadFromDiskCache() } if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) { - restoreIR(std::move(*unit)); + restoreIR(unit); return true; } - m_compiledData = unit; + m_compiledData = std::move(unit); - for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) - m_typeReferences.collectFromObject(m_compiledData->objectAt(i)); + 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()); + 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 @@ -152,10 +123,12 @@ bool QQmlTypeData::tryLoadFromDiskCache() const QV4::CompiledData::Import *import = m_compiledData->importAt(i); if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".") && import->qualifierIndex == 0 - && import->majorVersion == -1 - && import->minorVersion == -1) { + && !import->version.hasMajorVersion() + && !import->version.hasMinorVersion()) { QList<QQmlError> errors; - auto pendingImport = std::make_shared<PendingImport>(this, import); + auto pendingImport = std::make_shared<PendingImport>( + this, import, QQmlImports::ImportNoFlag); + pendingImport->precedence = QQmlImportInstance::Implicit; if (!fetchQmldir(qmldirUrl, pendingImport, 1, &errors)) { setError(errors); return false; @@ -169,61 +142,161 @@ bool QQmlTypeData::tryLoadFromDiskCache() 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)) { + if (!addImport(import, {}, &errors)) { Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); + 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; } -void QQmlTypeData::createTypeAndPropertyCaches( +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::ResolvedTypeReferenceMap &resolvedTypeCache) + 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::ExecutableCompilationUnit> propertyCacheCreator( + QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator( &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine, - m_compiledData.data(), &m_importCache); - QQmlJS::DiagnosticMessage error = propertyCacheCreator.buildMetaObjects(); - if (error.isValid()) { - setError(error); - return; - } - } + m_compiledData.data(), m_importCache.data(), typeClassName()); - QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator( - &m_compiledData->propertyCaches, m_compiledData.data()); - aliasCreator.appendAliasPropertiesToMetaObjects(engine); + QQmlError error = propertyCacheCreator.verifyNoICCycle(); + if (error.isValid()) + return error; - pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches); + 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, QCryptographicHash *hash, QQmlEngine *engine) +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 (typeRef.type.isValid()) { - const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject()); + 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(&ok)); + hash->addData(propertyCache->checksum(checksums, &ok)); if (!ok) return false; } @@ -231,28 +304,78 @@ static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeRefere 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()) - m_compiledData = nullptr; + 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.count(); ++ii) { + 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(script.location.line); - error.setColumn(script.location.column); + 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); @@ -261,48 +384,73 @@ void QQmlTypeData::done() } // Check all type dependencies for errors - for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; + 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()); + 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()); - - QList<QQmlError> errors = type.typeData->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(type.location.line); - error.setColumn(type.location.column); - error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); - errors.prepend(error); - setError(errors); + 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.count(); ++ii) { + 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(); - QList<QQmlError> errors = type.typeData->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(type.location.line); - error.setColumn(type.location.column); - error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); - errors.prepend(error); - setError(errors); + 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; - QV4::ResolvedTypeReferenceMap resolvedTypeCache; { - QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); + QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); if (error.isValid()) { setError(error); qDeleteAll(resolvedTypeCache); @@ -310,48 +458,81 @@ void QQmlTypeData::done() } } - QQmlEngine *const engine = typeLoader()->engine(); - - const auto dependencyHasher = [engine, &resolvedTypeCache, this]() { + const auto dependencyHasher = [&resolvedTypeCache, this]() { QCryptographicHash hash(QCryptographicHash::Md5); - return (resolvedTypeCache.addToHash(&hash, engine) - && ::addTypeReferenceChecksumsToHash(m_compositeSingletons, &hash, engine)) + 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() && !m_compiledData->verifyChecksum(dependencyHasher)) { - qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName(); - if (!loadFromSource()) - return; - m_backupSourceCode = SourceCodeData(); - m_compiledData = nullptr; + 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); - } else { - createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); + if (isError()) + return; + else + setCompileUnit(m_document); } - if (isError()) - return; - { - QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine); + QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); + m_compiledData->inlineComponentData = m_inlineComponentData; { // Sanity check property bindings - QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData); - QVector<QQmlJS::DiagnosticMessage> errors = validator.validate(); + QQmlPropertyValidator validator(enginePrivate, m_importCache.data(), m_compiledData); + QVector<QQmlError> errors = validator.validate(); if (!errors.isEmpty()) { setError(errors); return; } } - m_compiledData->finalizeCompositeType(enginePrivate); + m_compiledData->finalizeCompositeType(qmlType()); } { @@ -381,11 +562,11 @@ void QQmlTypeData::done() { // Collect imported scripts - m_compiledData->dependentScripts.reserve(m_scripts.count()); - for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { + m_compiledData->dependentScripts.reserve(m_scripts.size()); + for (int scriptIndex = 0; scriptIndex < m_scripts.size(); ++scriptIndex) { const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex); - QStringRef qualifier(&script.qualifier); + QStringView qualifier(script.qualifier); QString enclosingNamespace; const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); @@ -394,7 +575,8 @@ void QQmlTypeData::done() qualifier = qualifier.mid(lastDotIndex+1); } - m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + m_compiledData->typeNameCache->add( + qualifier.toString(), scriptIndex, enclosingNamespace); QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData(); m_compiledData->dependentScripts << scriptData; } @@ -414,14 +596,28 @@ 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()); + m_importCache->setBaseUrl(finalUrl(), finalUrlString()); - QQmlImportDatabase *importDatabase = typeLoader()->importDatabase(); // 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; - m_importCache.addImplicitImport(importDatabase, &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); @@ -457,14 +653,17 @@ void QQmlTypeData::dataReceived(const SourceCodeData &data) continueLoadFromIR(); } -void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) +void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit) { m_document.reset(new QmlIR::Document(isDebugging())); - QQmlIRLoader loader(unit, m_document.data()); + QQmlIRLoader loader(unit->qmlData, m_document.data()); loader.load(); m_document->jsModule.fileName = urlString(); m_document->jsModule.finalUrl = finalUrlString(); - m_document->javaScriptCompilationUnit = QV4::CompiledData::CompilationUnit(unit); + m_document->javaScriptCompilationUnit + = QQmlRefPointer<QV4::CompiledData::CompilationUnit>( + new QV4::CompiledData::CompilationUnit(unit->qmlData, unit->aotCompiledFunctions), + QQmlRefPointer<QV4::CompiledData::CompilationUnit>::Adopt); continueLoadFromIR(); } @@ -484,12 +683,12 @@ bool QQmlTypeData::loadFromSource() if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) { QList<QQmlError> errors; - errors.reserve(compiler.errors.count()); - for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) { + errors.reserve(compiler.errors.size()); + for (const QQmlJS::DiagnosticMessage &msg : std::as_const(compiler.errors)) { QQmlError e; e.setUrl(url()); - e.setLine(msg.line); - e.setColumn(msg.column); + e.setLine(qmlConvertSourceCoordinate<quint32, int>(msg.loc.startLine)); + e.setColumn(qmlConvertSourceCoordinate<quint32, int>(msg.loc.startColumn)); e.setDescription(msg.message); errors << e; } @@ -499,21 +698,31 @@ bool QQmlTypeData::loadFromSource() return true; } -void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit) +void QQmlTypeData::restoreIR(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit) { m_document.reset(new QmlIR::Document(isDebugging())); - QQmlIRLoader loader(unit.unitData(), m_document.data()); + QQmlIRLoader loader(unit->unitData(), m_document.data()); loader.load(); m_document->jsModule.fileName = urlString(); m_document->jsModule.finalUrl = finalUrlString(); - m_document->javaScriptCompilationUnit = std::move(unit); + 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()); + 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 @@ -526,8 +735,7 @@ void QQmlTypeData::continueLoadFromIR() // This qmldir is for the implicit import auto implicitImport = std::make_shared<PendingImport>(); implicitImport->uri = QLatin1String("."); - implicitImport->majorVersion = -1; - implicitImport->minorVersion = -1; + implicitImport->version = QTypeRevision(); QList<QQmlError> errors; if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) { @@ -539,15 +747,18 @@ void QQmlTypeData::continueLoadFromIR() QList<QQmlError> errors; - for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) { - if (!addImport(import, &errors)) { + for (const QV4::CompiledData::Import *import : std::as_const(m_document->imports)) { + if (!addImport(import, {}, &errors)) { Q_ASSERT(errors.size()); - QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); // put it back on the list after filling out information. - setError(errors); + + // 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; } } @@ -567,12 +778,14 @@ void QQmlTypeData::allDependenciesDone() for (auto keyIt = m_unresolvedImports.constBegin(), keyEnd = m_unresolvedImports.constEnd(); keyIt != keyEnd; ++keyIt) { - PendingImportPtr import = *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(import->location.line); - error.setColumn(import->location.column); + error.setUrl(m_importCache->baseUrl()); + error.setLine(qmlConvertSourceCoordinate<quint32, int>( + import->location.line())); + error.setColumn(qmlConvertSourceCoordinate<quint32, int>( + import->location.column())); errors.prepend(error); } } @@ -589,7 +802,7 @@ void QQmlTypeData::allDependenciesDone() void QQmlTypeData::downloadProgressChanged(qreal p) { - for (int ii = 0; ii < m_callbacks.count(); ++ii) { + for (int ii = 0; ii < m_callbacks.size(); ++ii) { TypeDataCallback *callback = m_callbacks.at(ii); callback->typeDataProgress(this, p); } @@ -603,43 +816,58 @@ QString QQmlTypeData::stringAt(int index) const } void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache, + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) { Q_ASSERT(m_compiledData.isNull()); - const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit.unitData() - && (m_document->javaScriptCompilationUnit.unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation); + 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(), typeNameCache, resolvedTypeCache, dependencyHasher); - m_compiledData = compiler.compile(); - if (!m_compiledData) { + 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 = (!diskCacheDisabled() || diskCacheForced()) - && !m_document->jsModule.debugMode && !typeRecompilation; + const bool trySaveToDisk = writeCacheFile() && !typeRecompilation; if (trySaveToDisk) { QString errorString; - if (m_compiledData->saveToDisk(url(), &errorString)) { + if (compilationUnit->saveToDisk(url(), &errorString)) { QString error; - if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &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" << m_compiledData->fileName() << "to disk:" << errorString; + 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(); + const auto resolvedScripts = m_importCache->resolvedScripts(); for (const QQmlImports::ScriptReference &script : resolvedScripts) { QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location); addDependency(blob.data()); @@ -660,7 +888,7 @@ void QQmlTypeData::resolveTypes() } // Lets handle resolved composite singleton types - const auto resolvedCompositeSingletons = m_importCache.resolvedCompositeSingletons(); + const auto resolvedCompositeSingletons = m_importCache->resolvedCompositeSingletons(); for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) { TypeReference ref; QString typeName; @@ -672,17 +900,15 @@ void QQmlTypeData::resolveTypes() typeName = csRef.typeName; } - int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1; - int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1; - - if (!resolveType(typeName, majorVersion, minorVersion, ref, -1, -1, true, - QQmlType::CompositeSingletonType)) + 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->status() == QQmlDataBlob::ResolvingDependencies || m_waitingOnMe.contains(ref.typeData.data())) { - // TODO: give an error message? If so, we should record and show the path of the cycle. + 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()); @@ -699,28 +925,36 @@ void QQmlTypeData::resolveTypes() const bool reportErrors = unresolvedRef->errorWhenNotFound; - int majorVersion = -1; - int minorVersion = -1; + QTypeRevision version; const QString name = stringAt(unresolvedRef.key()); - if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line, - unresolvedRef->location.column, reportErrors, - QQmlType::AnyRegistrationType) && reportErrors) + 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()) { + if (ref.type.isComposite() && !ref.selfReference) { ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); addDependency(ref.typeData.data()); } - ref.majorVersion = majorVersion; - ref.minorVersion = minorVersion; - - ref.location.line = unresolvedRef->location.line; - ref.location.column = unresolvedRef->location.column; + 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); } @@ -729,10 +963,9 @@ void QQmlTypeData::resolveTypes() loadImplicitImport(); } -QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( +QQmlError QQmlTypeData::buildTypeResolutionCaches( QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache - ) const + QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache) const { typeNameCache->adopt(new QQmlTypeNameCache(m_importCache)); @@ -743,59 +976,88 @@ QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix); - m_importCache.populateCache(typeNameCache->data()); - - QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + m_importCache->populateCache(typeNameCache->data()); for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { - QScopedPointer<QV4::ResolvedTypeReference> ref(new QV4::ResolvedTypeReference); + 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->compilationUnit = resolvedType->typeData->compilationUnit(); - } else if (qmlType.isValid()) { - ref->type = qmlType; - Q_ASSERT(ref->type.isValid()); + 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 && !ref->type.isCreatable()) { - QString reason = ref->type.noCreationReason(); + if (resolvedType->needsCreation && !qmlType.isCreatable()) { + QString reason = qmlType.noCreationReason(); if (reason.isEmpty()) reason = tr("Element is not creatable."); return qQmlCompileError(resolvedType->location, reason); } - if (ref->type.containsRevisionedAttributes()) { - ref->typePropertyCache = engine->cache(ref->type, - resolvedType->minorVersion); + 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->majorVersion = resolvedType->majorVersion; - ref->minorVersion = resolvedType->minorVersion; + ref->setVersion(resolvedType->version); ref->doDynamicTypeCheck(); - resolvedTypeCache->insert(resolvedType.key(), ref.take()); + resolvedTypeCache->insert(resolvedType.key(), ref.release()); } - QQmlJS::DiagnosticMessage noError; + QQmlError noError; return noError; } -bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, +bool QQmlTypeData::resolveType(const QString &typeName, QTypeRevision &version, TypeReference &ref, int lineNumber, int columnNumber, - bool reportErrors, QQmlType::RegistrationType registrationType) + bool reportErrors, QQmlType::RegistrationType registrationType, + bool *typeRecursionDetected) { QQmlImportNamespace *typeNamespace = nullptr; QList<QQmlError> errors; - bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion, - &typeNamespace, &errors, registrationType); + 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(typeName, &ref.type, &majorVersion, &minorVersion, - &typeNamespace, &errors, registrationType); + typeFound = m_importCache->resolveType( + typeLoader(), typeName, &ref.type, &version, &typeNamespace, &errors, + registrationType, typeRecursionDetected); } else { return false; //loadImplicitImport() hit an error, and called setError already } @@ -816,7 +1078,7 @@ bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int & // 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.setUrl(m_importCache->baseUrl()); error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description())); } @@ -833,12 +1095,14 @@ bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int & return true; } -void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/) +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; + ref.qualifier = qualifier.isEmpty() ? nameSpace : qualifier + QLatin1Char('.') + nameSpace; m_scripts << ref; } |