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.cpp283
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
}