diff options
Diffstat (limited to 'src/qml/qml/qqmltypedata.cpp')
-rw-r--r-- | src/qml/qml/qqmltypedata.cpp | 283 |
1 files changed, 247 insertions, 36 deletions
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index 0fd5bc83e6..fc1d0cfbcf 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -92,6 +92,25 @@ QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const return m_compiledData.data(); } +QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnitForInlineComponent(unsigned int icObjectId) const +{ + Q_ASSERT(m_document || m_compiledData); + if (m_compiledData) + return m_compiledData.data(); + for (auto it = m_document->objects.begin(); it != m_document->objects.end(); ++it) { + auto object = *it; + auto icIt = std::find_if(object->inlineComponentsBegin(), object->inlineComponentsEnd(), [&](const QV4::CompiledData::InlineComponent &ic) { + return ic.objectIndex == icObjectId; + }); + if (icIt != object->inlineComponentsEnd()) { + Q_ASSERT(m_inlineComponentToCompiledData.contains(icIt->nameIndex)); + return m_inlineComponentToCompiledData[icIt->nameIndex].data(); + } + } + Q_UNREACHABLE(); + return nullptr; // make integrity happy +} + void QQmlTypeData::registerCallback(TypeDataCallback *callback) { Q_ASSERT(!m_callbacks.contains(callback)); @@ -105,6 +124,13 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) Q_ASSERT(!m_callbacks.contains(callback)); } +CompositeMetaTypeIds QQmlTypeData::typeIds(int objectId) const +{ + if (objectId != 0) + return m_inlineComponentData[objectId].typeIds; + return m_typeIds; +} + bool QQmlTypeData::tryLoadFromDiskCache() { if (!diskCacheEnabled()) @@ -130,8 +156,15 @@ bool QQmlTypeData::tryLoadFromDiskCache() m_compiledData = 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()); @@ -170,14 +203,28 @@ bool QQmlTypeData::tryLoadFromDiskCache() Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); + 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; } } + QQmlType containingType; + auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first(); + int major = -1, minor = -1; + QQmlImportNamespace *ns = nullptr; + m_importCache.resolveType(containingTypeName, &containingType, &major, &minor, &ns); + for (auto&& ic: ics) { + QString const nameString = m_compiledData->stringAt(ic.nameIndex); + QByteArray const name = nameString.toUtf8(); + auto importUrl = finalUrl(); + importUrl.setFragment(QString::number(ic.objectIndex)); + auto import = new QQmlImportInstance(); + m_importCache.addInlineComponentImport(import, nameString, importUrl, containingType); + } + return true; } @@ -196,8 +243,8 @@ void QQmlTypeData::createTypeAndPropertyCaches( { QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator( &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine, - m_compiledData.data(), &m_importCache); - QQmlJS::DiagnosticMessage error = propertyCacheCreator.buildMetaObjects(); + m_compiledData.data(), &m_importCache, typeClassName()); + QQmlError error = propertyCacheCreator.buildMetaObjects(); if (error.isValid()) { setError(error); return; @@ -228,6 +275,43 @@ static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeRefere return true; } +// local helper function for inline components +namespace { +template<typename ObjectContainer> +void setupICs(const ObjectContainer &container, QHash<int, InlineComponentData> *icData, const QUrl &finalUrl) { + 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) { + auto url = finalUrl; + url.setFragment(QString::number(it->objectIndex)); + const QByteArray &className = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url); + InlineComponentData icDatum(QQmlMetaType::registerInternalCompositeType(className), int(it->objectIndex), int(it->nameIndex), 0, 0, 0); + icData->insert(it->objectIndex, 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]{ @@ -248,8 +332,8 @@ void QQmlTypeData::done() 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); @@ -258,18 +342,38 @@ void QQmlTypeData::done() } // Check all type dependencies for errors - for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; + for (auto it = qAsConst(m_resolvedTypes).begin(), end = qAsConst(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() && !type.type.pendingResolutionName().isEmpty()) { + auto containingType = type.type.containingType(); + auto objectId = containingType.lookupInlineComponentIdByName(type.type.pendingResolutionName()); + if (objectId < 0) { // can be any negative number if we tentatively resolved it in QQmlImport but it actually was not an inline component + const QString typeName = stringAt(it.key()); + int lastDot = typeName.lastIndexOf('.'); + + 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(QQmlTypeLoader::tr("Type %1 has no inline component type called %2").arg(typeName.leftRef(lastDot), type.type.pendingResolutionName())); + errors.prepend(error); + setError(errors); + return; + } else { + type.type.setInlineComponentObjectId(objectId); + } + } 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.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line)); + error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column)); error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); errors.prepend(error); setError(errors); @@ -287,8 +391,8 @@ void QQmlTypeData::done() QList<QQmlError> errors = type.typeData->errors(); QQmlError error; error.setUrl(url()); - error.setLine(type.location.line); - error.setColumn(type.location.column); + error.setLine(qmlConvertSourceCoordinate<quint32, int>(type.location.line)); + error.setColumn(qmlConvertSourceCoordinate<quint32, int>(type.location.column)); error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); errors.prepend(error); setError(errors); @@ -296,10 +400,28 @@ void QQmlTypeData::done() } } - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + m_typeClassName = QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(finalUrl()); + if (!m_typeClassName.isEmpty()) + m_typeIds = QQmlMetaType::registerInternalCompositeType(m_typeClassName); + + if (m_document) { + setupICs(m_document, &m_inlineComponentData, finalUrl()); + } else { + setupICs(m_compiledData, &m_inlineComponentData, finalUrl()); + } + auto typeCleanupGuard = qScopeGuard([&]() { + if (isError() && m_typeIds.isValid()) { + QQmlMetaType::unregisterInternalCompositeType(m_typeIds); + for (auto&& icData: qAsConst(m_inlineComponentData)) { + QQmlMetaType::unregisterInternalCompositeType(icData.typeIds); + } + } + }); + QV4::ResolvedTypeReferenceMap resolvedTypeCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; { - QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); + QQmlError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); if (error.isValid()) { setError(error); qDeleteAll(resolvedTypeCache); @@ -329,8 +451,12 @@ void QQmlTypeData::done() if (!m_document.isNull()) { // Compile component compile(typeNameCache, &resolvedTypeCache, dependencyHasher); + if (!isError()) + setCompileUnit(m_document); } else { createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); + if (!isError()) + setCompileUnit(m_compiledData); } if (isError()) @@ -338,17 +464,18 @@ void QQmlTypeData::done() { QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine); + m_compiledData->inlineComponentData = m_inlineComponentData; { // Sanity check property bindings QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData); - QVector<QQmlJS::DiagnosticMessage> errors = validator.validate(); + QVector<QQmlError> errors = validator.validate(); if (!errors.isEmpty()) { setError(errors); return; } } - m_compiledData->finalizeCompositeType(enginePrivate); + m_compiledData->finalizeCompositeType(enginePrivate, typeIds()); } { @@ -376,6 +503,26 @@ void QQmlTypeData::done() } } + // associate inline components to root component + { + auto typeName = finalUrlString().splitRef('/').last().split('.').first().toString(); + // typeName can be empty if a QQmlComponent was constructed with an empty QUrl parameter + if (!typeName.isEmpty() && typeName.at(0).isUpper() && !m_inlineComponentData.isEmpty()) { + QHashedStringRef const hashedStringRef { typeName }; + QList<QQmlError> errors; + auto type = QQmlMetaType::typeForUrl(finalUrlString(), hashedStringRef, false, &errors); + Q_ASSERT(errors.empty()); + if (type.isValid()) { + for (auto const &icDatum : m_inlineComponentData) { + Q_ASSERT(icDatum.typeIds.isValid()); + QQmlType existingType = type.lookupInlineComponentById(type.lookupInlineComponentIdByName(m_compiledData->stringAt(icDatum.nameIndex))); + type.associateInlineComponent(m_compiledData->stringAt(icDatum.nameIndex), + icDatum.objectIndex, icDatum.typeIds, existingType); + } + } + } + } + { // Collect imported scripts m_compiledData->dependentScripts.reserve(m_scripts.count()); @@ -485,8 +632,8 @@ bool QQmlTypeData::loadFromSource() for (const QQmlJS::DiagnosticMessage &msg : qAsConst(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; } @@ -509,6 +656,22 @@ void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit) void QQmlTypeData::continueLoadFromIR() { + QQmlType containingType; + auto containingTypeName = finalUrl().fileName().split(QLatin1Char('.')).first(); + int major = -1, minor = -1; + QQmlImportNamespace *ns = nullptr; + m_importCache.resolveType(containingTypeName, &containingType, &major, &minor, &ns); + for (auto const& object: m_document->objects) { + for (auto it = object->inlineComponentsBegin(); it != object->inlineComponentsEnd(); ++it) { + QString const nameString = m_document->stringAt(it->nameIndex); + QByteArray const name = nameString.toUtf8(); + auto importUrl = finalUrl(); + importUrl.setFragment(QString::number(it->objectIndex)); + auto import = new QQmlImportInstance(); // Note: The cache takes ownership of the QQmlImportInstance + m_importCache.addInlineComponentImport(import, nameString, importUrl, containingType); + } + } + m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); m_importCache.setBaseUrl(finalUrl(), finalUrlString()); @@ -541,8 +704,8 @@ void QQmlTypeData::continueLoadFromIR() Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); + 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; @@ -568,8 +731,8 @@ void QQmlTypeData::allDependenciesDone() 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.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line)); + error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column)); errors.prepend(error); } } @@ -677,8 +840,9 @@ void QQmlTypeData::resolveTypes() 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())) { + qWarning() << "Cyclic dependency detected between" << ref.typeData->urlString() + << "and" << urlString(); continue; } addDependency(ref.typeData.data()); @@ -700,15 +864,28 @@ void QQmlTypeData::resolveTypes() const QString name = stringAt(unresolvedRef.key()); + bool *selfReferenceDetection = unresolvedRef->needsCreation ? nullptr : &ref.selfReference; + if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line, unresolvedRef->location.column, reportErrors, - QQmlType::AnyRegistrationType) && 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()); } + if (ref.type.isInlineComponentType()) { + auto containingType = ref.type.containingType(); + if (containingType.isValid()) { + auto const url = containingType.sourceUrl(); + if (url.isValid()) { + auto typeData = typeLoader()->getType(url); + ref.typeData = typeData; + addDependency(typeData.data()); + } + } + } ref.majorVersion = majorVersion; ref.minorVersion = minorVersion; @@ -716,7 +893,6 @@ void QQmlTypeData::resolveTypes() ref.location.column = unresolvedRef->location.column; ref.needsCreation = unresolvedRef->needsCreation; - m_resolvedTypes.insert(unresolvedRef.key(), ref); } @@ -725,7 +901,7 @@ void QQmlTypeData::resolveTypes() loadImplicitImport(); } -QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( +QQmlError QQmlTypeData::buildTypeResolutionCaches( QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, QV4::ResolvedTypeReferenceMap *resolvedTypeCache ) const @@ -750,8 +926,40 @@ QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( 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->setCompilationUnit(resolvedType->typeData->compilationUnit()); + if (resolvedType->type.isInlineComponentType()) { + // Inline component which is part of an already resolved type + int objectId = -1; + if (qmlType.containingType().isValid()) { + objectId = qmlType.containingType().lookupInlineComponentIdByName(QString::fromUtf8(qmlType.typeName())); + qmlType.setInlineComponentObjectId(objectId); + } else { + objectId = resolvedType->type.inlineComponendId(); + } + Q_ASSERT(objectId != -1); + ref->typePropertyCache = resolvedType->typeData->compilationUnit()->propertyCaches.at(objectId); + ref->type = qmlType; + Q_ASSERT(ref->type.isInlineComponentType()); + } + } else if (resolvedType->type.isInlineComponentType()) { + // Inline component, defined in the file we are currently compiling + if (!m_inlineComponentToCompiledData.contains(resolvedType.key())) { + ref->type = qmlType; + if (qmlType.isValid()) { + // this is required for inline components in singletons + auto typeID = qmlType.lookupInlineComponentById(qmlType.inlineComponendId()).typeId(); + auto exUnit = engine->obtainExecutableCompilationUnit(typeID); + if (exUnit) { + ref->setCompilationUnit(exUnit); + ref->typePropertyCache = engine->propertyCacheForType(typeID); + } + } + } else { + ref->setCompilationUnit(m_inlineComponentToCompiledData[resolvedType.key()]); + ref->typePropertyCache = m_inlineComponentToCompiledData[resolvedType.key()]->rootPropertyCache(); + } + + } else if (qmlType.isValid() && !resolvedType->selfReference) { ref->type = qmlType; Q_ASSERT(ref->type.isValid()); @@ -772,26 +980,29 @@ QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( ref->doDynamicTypeCheck(); resolvedTypeCache->insert(resolvedType.key(), ref.take()); } - QQmlJS::DiagnosticMessage noError; + QQmlError noError; return noError; } bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, 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); + &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); + &typeNamespace, &errors, registrationType, + typeRecursionDetected); } else { return false; //loadImplicitImport() hit an error, and called setError already } |