aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2020-01-15 14:47:35 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2020-01-23 15:58:10 +0100
commit684f9df7849bc79f1f02a60844fb43c7a3927d2f (patch)
tree531be4d102388395e4ddd2d14f38a679e15c283d /src/qml/qml
parent020a6e67766595351bcf911e965b26952a7c81b8 (diff)
Long live QML inline components
[ChangeLog][QtQml] It is now possible to declare new QML components in a QML file via the component keyword. They can be used just as if they were declared in another file, with the only difference that the type name needs to be prefixed with the name of the containing type outside of the file were the inline component has been declared. Notably, inline components are not closures: In the following example, the output would be 42 // MyItem.qml Item { property int i: 33 component IC: Item { Component.onCompleted: console.log(i) } } // user.qml Item { property int i: 42 MyItem.IC {} } Fixes: QTBUG-79382 Change-Id: I6a5ffc43f093a76323f435cfee9bab217781b8f5 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/qqmlengine.cpp30
-rw-r--r--src/qml/qml/qqmlengine_p.h1
-rw-r--r--src/qml/qml/qqmlimport.cpp173
-rw-r--r--src/qml/qml/qqmlimport_p.h4
-rw-r--r--src/qml/qml/qqmlirloader.cpp7
-rw-r--r--src/qml/qml/qqmlmetatype.cpp4
-rw-r--r--src/qml/qml/qqmlmetatype_p.h21
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp59
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h3
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h98
-rw-r--r--src/qml/qml/qqmlpropertyvalidator.cpp3
-rw-r--r--src/qml/qml/qqmltype.cpp113
-rw-r--r--src/qml/qml/qqmltype_p.h16
-rw-r--r--src/qml/qml/qqmltype_p_p.h19
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp10
-rw-r--r--src/qml/qml/qqmltypecompiler_p.h11
-rw-r--r--src/qml/qml/qqmltypedata.cpp201
-rw-r--r--src/qml/qml/qqmltypedata_p.h14
18 files changed, 683 insertions, 104 deletions
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 3345fcb19e..07d5429423 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -2383,12 +2383,23 @@ int QQmlEnginePrivate::listType(int t) const
return QQmlMetaType::listType(t);
}
+
+static QQmlPropertyCache *propertyCacheForPotentialInlineComponentType(int t, const QHash<int, QV4::ExecutableCompilationUnit *>::const_iterator &iter) {
+ if (t != (*iter)->metaTypeId) {
+ // this is an inline component, and what we have in the iterator is currently the parent compilation unit
+ for (auto &&icDatum: (*iter)->inlineComponentData)
+ if (icDatum.typeIds.id == t)
+ return (*iter)->propertyCaches.at(icDatum.objectIndex);
+ }
+ return (*iter)->rootPropertyCache().data();
+}
+
QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const
{
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache().data());
+ return propertyCacheForPotentialInlineComponentType(t, iter);
} else {
QQmlType type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type.baseMetaObject());
@@ -2400,7 +2411,7 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache().data());
+ return propertyCacheForPotentialInlineComponentType(t, iter);
} else {
QQmlType type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type.metaObject());
@@ -2412,7 +2423,7 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t)
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache().data();
+ return propertyCacheForPotentialInlineComponentType(t, iter);
} else {
QQmlType type = QQmlMetaType::qmlType(t);
locker.unlock();
@@ -2425,7 +2436,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache().data();
+ return propertyCacheForPotentialInlineComponentType(t, iter);
} else {
QQmlType type = QQmlMetaType::qmlType(t);
locker.unlock();
@@ -2445,6 +2456,9 @@ void QQmlEnginePrivate::registerInternalCompositeType(QV4::ExecutableCompilation
// The QQmlCompiledData is not referenced here, but it is removed from this
// hash in the QQmlCompiledData destructor
m_compositeTypes.insert(compilationUnit->metaTypeId, compilationUnit);
+ for (auto &&data: compilationUnit->inlineComponentData) {
+ m_compositeTypes.insert(data.typeIds.id, compilationUnit);
+ }
}
void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit)
@@ -2453,6 +2467,14 @@ void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::ExecutableCompilati
Locker locker(this);
m_compositeTypes.remove(compilationUnit->metaTypeId);
+ for (auto&& icDatum: compilationUnit->inlineComponentData)
+ m_compositeTypes.remove(icDatum.typeIds.id);
+}
+
+QV4::ExecutableCompilationUnit *QQmlEnginePrivate::obtainExecutableCompilationUnit(int typeId)
+{
+ Locker locker(this);
+ return m_compositeTypes.value(typeId, nullptr);
}
template<>
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 5b1b676c89..ed81e055e5 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -231,6 +231,7 @@ public:
QQmlPropertyCache *rawPropertyCacheForType(int, int minorVersion = -1);
void registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit);
+ QV4::ExecutableCompilationUnit *obtainExecutableCompilationUnit(int typeId);
bool isTypeLoaded(const QUrl &url) const;
bool isScriptLoaded(const QUrl &url) const;
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 7da0685872..80eafdf146 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -58,6 +58,7 @@
#include <private/qqmltypeloaderqmldircontent_p.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonarray.h>
+#include <QtQml/private/qqmltype_p_p.h>
#include <algorithm>
#include <functional>
@@ -613,6 +614,8 @@ bool QQmlImports::resolveType(const QHashedStringRef &type,
RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL-SINGLETON";
else if (type_return->isComposite())
RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE/URL";
+ else if (type_return->isInlineComponentType())
+ RESOLVE_TYPE_DEBUG << type_return->typeName() << ' ' << type_return->sourceUrl() << " TYPE(INLINECOMPONENT)";
else
RESOLVE_TYPE_DEBUG << type_return->typeName() << " TYPE";
}
@@ -709,6 +712,37 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
}
const QString typeStr = type.toString();
+ if (isInlineComponent) {
+ Q_ASSERT(type_return);
+ bool ret = uri == typeStr;
+ if (ret) {
+ Q_ASSERT(!type_return->isValid());
+ auto createICType = [&]() {
+ auto typePriv = new QQmlTypePrivate {QQmlType::RegistrationType::InlineComponentType};
+ bool ok = false;
+ typePriv->extraData.id->objectId = QUrl(this->url).fragment().toInt(&ok);
+ Q_ASSERT(ok);
+ typePriv->extraData.id->url = QUrl(this->url);
+ auto icType = QQmlType(typePriv);
+ return icType;
+ };
+ if (containingType.isValid()) {
+ // we currently cannot reference a Singleton inside itself
+ // in that case, containingType is still invalid
+ if (int icID = containingType.lookupInlineComponentIdByName(typeStr) != -1) {
+ *type_return = containingType.lookupInlineComponentById(icID);
+ } else {
+ auto icType = createICType();
+ int placeholderId = containingType.generatePlaceHolderICId();
+ const_cast<QQmlImportInstance*>(this)->containingType.associateInlineComponent(typeStr, placeholderId, CompositeMetaTypeIds {}, icType);
+ *type_return = QQmlType(icType);
+ }
+ } else {
+ *type_return = createICType();
+ }
+ }
+ return ret;
+ }
QQmlDirComponents::ConstIterator it = qmlDirComponents.find(typeStr), end = qmlDirComponents.end();
if (it != end) {
QString componentUrl;
@@ -826,47 +860,107 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor,
QQmlType::RegistrationType registrationType,
bool *typeRecursionDetected)
{
- QQmlImportNamespace *s = nullptr;
- int dot = type.indexOf(Dot);
- if (dot >= 0) {
- QHashedStringRef namespaceName(type.constData(), dot);
- s = findQualifiedNamespace(namespaceName);
- if (!s) {
- if (errors) {
- QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName.toString()));
- errors->prepend(error);
- }
- return false;
- }
- int ndot = type.indexOf(Dot,dot+1);
- if (ndot > 0) {
- if (errors) {
- QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
- errors->prepend(error);
- }
- return false;
- }
- } else {
- s = &unqualifiedset;
- }
- QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1);
- if (s) {
- if (s->resolveType(typeLoader, unqualifiedtype, vmajor, vminor, type_return, &base, errors,
+ const QVector<QHashedStringRef> splitName = type.split(Dot);
+ auto resolveTypeInNamespace = [&](QHashedStringRef unqualifiedtype, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) -> bool {
+ if (nameSpace->resolveType(typeLoader, unqualifiedtype, vmajor, vminor, type_return, &base, errors,
registrationType, typeRecursionDetected))
return true;
- if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) {
+ if (nameSpace->imports.count() == 1 && !nameSpace->imports.at(0)->isLibrary && type_return && nameSpace != &unqualifiedset) {
// qualified, and only 1 url
*type_return = QQmlMetaType::typeForUrl(
- resolveLocalUrl(s->imports.at(0)->url,
+ resolveLocalUrl(nameSpace->imports.at(0)->url,
unqualifiedtype.toString() + QLatin1String(".qml")),
type, false, errors);
return type_return->isValid();
}
+ return false;
+ };
+ switch (splitName.size()) {
+ case 1: {
+ // must be a simple type
+ return resolveTypeInNamespace(type, &unqualifiedset, errors);
}
-
- return false;
+ case 2: {
+ // either namespace + simple type OR simple type + inline component
+ QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
+ if (s) {
+ // namespace + simple type
+ return resolveTypeInNamespace(splitName.at(1), s, errors);
+ } else {
+ if (resolveTypeInNamespace(splitName.at(0), &unqualifiedset, nullptr)) {
+ // either simple type + inline component
+ auto const icName = splitName.at(1).toString();
+ auto objectIndex = type_return->lookupInlineComponentIdByName(icName);
+ if (objectIndex != -1) {
+ *type_return = type_return->lookupInlineComponentById(objectIndex);
+ } else {
+ auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
+ icTypePriv->setContainingType(type_return);
+ icTypePriv->extraData.id->url = type_return->sourceUrl();
+ int placeholderId = type_return->generatePlaceHolderICId();
+ icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId));
+ auto icType = QQmlType(icTypePriv);
+ type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType);
+ *type_return = icType;
+ }
+ Q_ASSERT(type_return->containingType().isValid());
+ type_return->setPendingResolutionName(icName);
+ return true;
+ } else {
+ // or a failure
+ if (errors) {
+ QQmlError error;
+ error.setDescription(QQmlImportDatabase::tr("- %1 is neither a type nor a namespace").arg(splitName.at(0).toString()));
+ errors->prepend(error);
+ }
+ return false;
+ }
+ }
+ }
+ case 3: {
+ // must be namespace + simple type + inline component
+ QQmlImportNamespace *s = findQualifiedNamespace(splitName.at(0));
+ QQmlError error;
+ if (!s) {
+ error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(splitName.at(0).toString()));
+ } else {
+ if (resolveTypeInNamespace(splitName.at(1), s, nullptr)) {
+ auto const icName = splitName.at(2).toString();
+ auto objectIndex = type_return->lookupInlineComponentIdByName(icName);
+ if (objectIndex != -1)
+ *type_return = type_return->lookupInlineComponentById(objectIndex);
+ else {
+ auto icTypePriv = new QQmlTypePrivate(QQmlType::RegistrationType::InlineComponentType);
+ icTypePriv->setContainingType(type_return);
+ icTypePriv->extraData.id->url = type_return->sourceUrl();
+ int placeholderId = type_return->generatePlaceHolderICId();
+ icTypePriv->extraData.id->url.setFragment(QString::number(placeholderId));
+ auto icType = QQmlType(icTypePriv);
+ type_return->associateInlineComponent(icName, placeholderId, CompositeMetaTypeIds {}, icType);
+ *type_return = icType;
+ }
+ type_return->setPendingResolutionName(icName);
+ return true;
+ } else {
+ error.setDescription(QQmlImportDatabase::tr("- %1 is not a type").arg(splitName.at(1).toString()));
+ }
+ }
+ if (errors) {
+ errors->prepend(error);
+ }
+ return false;
+ }
+ default: {
+ // all other numbers suggest a user error
+ if (errors) {
+ QQmlError error;
+ error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
+ errors->prepend(error);
+ }
+ return false;
+ }
+ }
+ Q_UNREACHABLE();
}
QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const
@@ -1652,6 +1746,21 @@ bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlErro
}
/*!
+ \internal
+ */
+bool QQmlImports::addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType)
+{
+ importInstance->url = importUrl.toString();
+ importInstance->uri = name;
+ importInstance->isInlineComponent = true;
+ importInstance->majversion = 0;
+ importInstance->minversion = 0;
+ importInstance->containingType = containingType;
+ d->unqualifiedset.imports.push_back(importInstance);
+ return true;
+}
+
+/*!
\internal
Adds information to \a imports such that subsequent calls to resolveType()
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 1f44b22deb..2e994fd27f 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -80,10 +80,12 @@ struct QQmlImportInstance
QString uri; // e.g. QtQuick
QString url; // the base path of the import
QString localDirectoryPath; // the base path of the import if it's a local file
+ QQmlType containingType; // points to the containing type for inline components
int majversion; // the major version imported
int minversion; // the minor version imported
bool isLibrary; // true means that this is not a file import
bool implicitlyImported = false;
+ bool isInlineComponent = false;
QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir
QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir
@@ -151,6 +153,8 @@ public:
bool addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors);
+ bool addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType);
+
bool addFileImport(QQmlImportDatabase *,
const QString& uri, const QString& prefix, int vmaj, int vmin, bool incomplete,
QList<QQmlError> *errors);
diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp
index 82cad8eba8..b284e44fdf 100644
--- a/src/qml/qml/qqmlirloader.cpp
+++ b/src/qml/qml/qqmlirloader.cpp
@@ -200,6 +200,13 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali
object->runtimeFunctionIndices.allocate(pool, functionIndices);
+ const QV4::CompiledData::InlineComponent *serializedInlineComponent = serializedObject->inlineComponentTable();
+ for (uint i = 0; i < serializedObject->nInlineComponents; ++i, ++serializedInlineComponent) {
+ QmlIR::InlineComponent *ic = pool->New<QmlIR::InlineComponent>();
+ *static_cast<QV4::CompiledData::InlineComponent*>(ic) = *serializedInlineComponent;
+ object->inlineComponents->append(ic);
+ }
+
return object;
}
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index f506cdece3..76816618ac 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -531,7 +531,7 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit
return QQmlType(priv);
}
-QQmlMetaType::CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className)
+CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className)
{
QByteArray ptr = className + '*';
QByteArray lst = "QQmlListProperty<" + className + '>';
@@ -555,7 +555,7 @@ QQmlMetaType::CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(c
return {ptr_type, lst_type};
}
-void QQmlMetaType::unregisterInternalCompositeType(const QQmlMetaType::CompositeMetaTypeIds &typeIds)
+void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds)
{
QQmlMetaTypeDataPtr data;
data->qmlLists.remove(typeIds.listId);
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 80126cbffb..037cf89beb 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -63,6 +63,15 @@ class QQmlError;
namespace QV4 { class ExecutableCompilationUnit; }
+struct CompositeMetaTypeIds
+{
+ int id = -1;
+ int listId = -1;
+ CompositeMetaTypeIds() = default;
+ CompositeMetaTypeIds(int id, int listId) : id(id), listId(listId) {}
+ bool isValid() const { return id != -1 && listId != -1; }
+};
+
class Q_QML_PRIVATE_EXPORT QQmlMetaType
{
public:
@@ -80,18 +89,8 @@ public:
static void unregisterType(int type);
- struct CompositeMetaTypeIds
- {
- int id = -1;
- int listId = -1;
- CompositeMetaTypeIds() = default;
- CompositeMetaTypeIds(int id, int listId) : id(id), listId(listId) {}
- bool isValid() const { return id != -1 && listId != -1; }
- };
-
static CompositeMetaTypeIds registerInternalCompositeType(const QByteArray &className);
- static void unregisterInternalCompositeType(const QQmlMetaType::CompositeMetaTypeIds &typeIds);
-
+ static void unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds);
static void registerModule(const char *uri, int versionMajor, int versionMinor);
static bool protectModule(const QString &uri, int majVersion);
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 78df02554c..198ce98f2d 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -62,6 +62,7 @@
#include <QScopedValueRollback>
#include <qtqml_tracepoints_p.h>
+#include <QScopedValueRollback>
QT_USE_NAMESPACE
@@ -77,9 +78,9 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR
init(parentContext);
sharedState->componentAttached = nullptr;
- sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount);
- sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount);
- sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount);
+ sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount());
+ sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount());
+ sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount());
sharedState->allJavaScriptObjects = nullptr;
sharedState->creationContext = creationContext;
sharedState->rootContext = nullptr;
@@ -87,7 +88,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR
if (auto profiler = QQmlEnginePrivate::get(engine)->profiler) {
Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler,
- sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount));
+ sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount()));
} else {
Q_UNUSED(profiler);
}
@@ -145,7 +146,7 @@ QQmlObjectCreator::~QQmlObjectCreator()
}
}
-QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlInstantiationInterrupt *interrupt)
+QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlInstantiationInterrupt *interrupt, int flags)
{
if (phase == CreatingObjectsPhase2) {
phase = ObjectsCreated;
@@ -159,14 +160,20 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
if (subComponentIndex == -1) {
objectToCreate = /*root object*/0;
} else {
- const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex);
- objectToCreate = compObj->bindingTable()->value.objectIndex;
+ Q_ASSERT(subComponentIndex >= 0);
+ if (flags & CreationFlags::InlineComponent) {
+ objectToCreate = subComponentIndex;
+ } else {
+ Q_ASSERT(flags & CreationFlags::NormalObject);
+ const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex);
+ objectToCreate = compObj->bindingTable()->value.objectIndex;
+ }
}
context = new QQmlContextData;
context->isInternal = true;
context->imports = compilationUnit->typeNameCache;
- context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex);
+ context->initFromTypeCompilationUnit(compilationUnit, flags & CreationFlags::NormalObject ? subComponentIndex : -1);
context->setParent(parentContext);
if (!sharedState->rootContext) {
@@ -179,7 +186,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator);
if (topLevelCreator)
- sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount);
+ sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount());
if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) {
QV4::ScopedObject scripts(scope, v4->newArrayObject(compilationUnit->dependentScripts.count()));
@@ -231,6 +238,9 @@ void QQmlObjectCreator::beginPopulateDeferred(QQmlContextData *newContext)
Q_ASSERT(topLevelCreator);
Q_ASSERT(!sharedState->allJavaScriptObjects);
+
+ QV4::Scope valueScope(v4);
+ sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount());
}
void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
@@ -248,7 +258,7 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
QV4::Scope valueScope(v4);
QScopedValueRollback<QV4::Value*> jsObjectGuard(sharedState->allJavaScriptObjects,
- valueScope.alloc(compilationUnit->totalObjectCount));
+ valueScope.alloc(compilationUnit->totalObjectCount()));
Q_ASSERT(topLevelCreator);
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
@@ -263,7 +273,6 @@ void QQmlObjectCreator::populateDeferred(QObject *instance, int deferredIndex,
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(_compiledObjectIndex);
qSwap(_compiledObject, obj);
-
qSwap(_ddata, declarativeData);
qSwap(_bindingTarget, bindingTarget);
qSwap(_vmeMetaObject, vmeMetaObject);
@@ -1172,7 +1181,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
Q_ASSERT(typeRef);
installPropertyCache = !typeRef->isFullyDynamicType;
QQmlType type = typeRef->type;
- if (type.isValid()) {
+ if (type.isValid() && !type.isInlineComponentType()) {
typeName = type.qmlTypeName();
void *ddataMemory = nullptr;
@@ -1206,17 +1215,31 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
} else {
Q_ASSERT(typeRef->compilationUnit);
typeName = typeRef->compilationUnit->fileName();
- if (typeRef->compilationUnit->unitData()->isSingleton())
+ // compilation unit is shared between root type and its inline component types
+ // so isSingleton errorneously returns true for inline components
+ if (typeRef->compilationUnit->unitData()->isSingleton() && !type.isInlineComponentType())
{
recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex)));
return nullptr;
}
- QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data());
- instance = subCreator.create();
- if (!instance) {
- errors += subCreator.errors;
- return nullptr;
+
+ if (!type.isInlineComponentType()) {
+ QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data());
+ instance = subCreator.create();
+ if (!instance) {
+ errors += subCreator.errors;
+ return nullptr;
+ }
+ } else {
+ int subObjectId = type.inlineComponendId();
+ QScopedValueRollback<int> rollback {typeRef->compilationUnit->icRoot, subObjectId};
+ QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data());
+ instance = subCreator.create(subObjectId, nullptr, nullptr, CreationFlags::InlineComponent);
+ if (!instance) {
+ errors += subCreator.errors;
+ return nullptr;
+ }
}
}
if (instance->isWidgetType()) {
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index 8b6cb67341..f8ad90be15 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -111,7 +111,8 @@ public:
QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr);
~QQmlObjectCreator();
- QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr);
+ enum CreationFlags { NormalObject = 1, InlineComponent = 2 };
+ QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr, int flags = NormalObject);
bool populateDeferredProperties(QObject *instance, const QQmlData::DeferredData *deferredData);
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index e8178603cf..a050a0bf0a 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -55,6 +55,10 @@
#include <private/qqmlmetaobject_p.h>
#include <private/qqmlpropertyresolver_p.h>
#include <private/qqmltypedata_p.h>
+#include <private/inlinecomponentutils_p.h>
+
+#include <QScopedValueRollback>
+#include <vector>
QT_BEGIN_NAMESPACE
@@ -115,8 +119,12 @@ public:
QQmlJS::DiagnosticMessage buildMetaObjects();
+ enum class VMEMetaObjectIsRequired {
+ Maybe,
+ Always
+ };
protected:
- QQmlJS::DiagnosticMessage buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
+ QQmlJS::DiagnosticMessage buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired);
QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const;
QQmlJS::DiagnosticMessage createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
@@ -129,7 +137,8 @@ protected:
const QQmlImports * const imports;
QQmlPropertyCacheVector *propertyCaches;
QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings;
- const QByteArray typeClassName;
+ QByteArray typeClassName; // not const as we temporarily chang it for inline components
+ unsigned int currentRoot; // set to objectID of inline component root when handling inline components
};
template <typename ObjectContainer>
@@ -151,12 +160,59 @@ inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlP
template <typename ObjectContainer>
inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
{
+ using namespace icutils;
QQmlBindingInstantiationContext context;
- return buildMetaObjectRecursively(/*root object*/0, context);
+
+ // get a list of all inline components
+ using InlineComponent = typename std::remove_reference<decltype (*(std::declval<CompiledObject>().inlineComponentsBegin()))>::type;
+ std::vector<InlineComponent> allICs {};
+ for (int i=0; i != objectContainer->objectCount(); ++i) {
+ const CompiledObject *obj = objectContainer->objectAt(i);
+ for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
+ allICs.push_back(*it);
+ }
+ }
+
+ // create a graph on inline components referencing inline components
+ std::vector<Node> nodes;
+ nodes.resize(allICs.size());
+ std::iota(nodes.begin(), nodes.end(), 0);
+ AdjacencyList adjacencyList;
+ adjacencyList.resize(nodes.size());
+ fillAdjacencyListForInlineComponents(objectContainer, adjacencyList, nodes, allICs);
+
+ bool hasCycle = false;
+ auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
+
+ if (hasCycle) {
+ QQmlJS::DiagnosticMessage diag;
+ diag.message = QLatin1String("Inline components form a cycle!");
+ return diag;
+ }
+
+ // create meta objects for inline components before compiling actual root component
+ for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
+ const auto &ic = allICs[nodeIt->index];
+ QV4::ResolvedTypeReference *typeRef = objectContainer->resolvedType(ic.nameIndex);
+ Q_ASSERT(propertyCaches->at(ic.objectIndex) == nullptr);
+ Q_ASSERT(typeRef->typePropertyCache.isNull()); // not set yet
+
+ QByteArray icTypeName { objectContainer->stringAt(ic.nameIndex).toUtf8() };
+ QScopedValueRollback<QByteArray> nameChange {typeClassName, icTypeName};
+ QScopedValueRollback<unsigned int> rootChange {currentRoot, ic.objectIndex};
+ QQmlJS::DiagnosticMessage diag = buildMetaObjectRecursively(ic.objectIndex, context, VMEMetaObjectIsRequired::Always);
+ if (diag.isValid()) {
+ return diag;
+ }
+ typeRef->typePropertyCache = propertyCaches->at(ic.objectIndex);
+ Q_ASSERT(!typeRef->typePropertyCache.isNull());
+ }
+
+ return buildMetaObjectRecursively(/*root object*/0, context, VMEMetaObjectIsRequired::Maybe);
}
template <typename ObjectContainer>
-inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context)
+inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context, VMEMetaObjectIsRequired isVMERequired)
{
auto isAddressable = [](const QUrl &url) {
const QString fileName = url.fileName();
@@ -164,7 +220,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil
};
const CompiledObject *obj = objectContainer->objectAt(objectIndex);
- bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0
+ bool needVMEMetaObject = isVMERequired == VMEMetaObjectIsRequired::Always || obj->propertyCount() != 0 || obj->aliasCount() != 0
|| obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0
|| (((obj->flags & QV4::CompiledData::Object::IsComponent)
|| (objectIndex == 0 && isAddressable(objectContainer->url())))
@@ -230,7 +286,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::buil
if (!context.resolveInstantiatingProperty())
pendingGroupPropertyBindings->append(context);
- QQmlJS::DiagnosticMessage error = buildMetaObjectRecursively(binding->value.objectIndex, context);
+ QQmlJS::DiagnosticMessage error = buildMetaObjectRecursively(binding->value.objectIndex, context, VMEMetaObjectIsRequired::Maybe);
if (error.isValid())
return error;
}
@@ -247,6 +303,7 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine
return context.instantiatingPropertyCache(enginePrivate);
} else if (obj->inheritedTypeNameIndex != 0) {
auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
+ QQmlType qmltype = typeRef->type;
Q_ASSERT(typeRef);
if (typeRef->isFullyDynamicType) {
@@ -298,7 +355,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea
QByteArray newClassName;
- if (objectIndex == /*root object*/0) {
+ if (objectIndex == /*root object*/0 || int(currentRoot) == objectIndex) {
newClassName = typeClassName;
}
if (newClassName.isEmpty()) {
@@ -504,18 +561,31 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea
return qQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
}
- Q_ASSERT(qmltype.isValid());
- if (qmltype.isComposite()) {
- QQmlMetaType::CompositeMetaTypeIds typeIds;
- if (selfReference) {
- typeIds = objectContainer->typeIds();
+ // inline components are not necessarily valid yet
+ Q_ASSERT(qmltype.isValid() || qmltype.isInlineComponentType());
+ if (qmltype.isComposite() || qmltype.isInlineComponentType()) {
+ CompositeMetaTypeIds typeIds;
+ if (qmltype.isInlineComponentType()) {
+ auto objectId = qmltype.inlineComponendId();
+ auto containingType = qmltype.containingType();
+ if (containingType.isValid()) {
+ auto icType = containingType.lookupInlineComponentById(objectId);
+ typeIds = {icType.typeId(), icType.qListTypeId()};
+ } else {
+ typeIds = {};
+ }
+ if (!typeIds.isValid()) // type has not been registered yet, we must be in containing type
+ typeIds = objectContainer->typeIdsForComponent(objectId);
+ Q_ASSERT(typeIds.isValid());
+ } else if (selfReference) {
+ typeIds = objectContainer->typeIdsForComponent();
} else {
QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
- typeIds = compilationUnit->typeIds();
+ typeIds = compilationUnit->typeIdsForComponent();
}
if (p->isList) {
@@ -578,7 +648,7 @@ inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const
return qmltype.typeId();
if (selfReference)
- return objectContainer->typeIds().id;
+ return objectContainer->typeIdsForComponent().id;
QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp
index 8f6e2737cc..8762dc328d 100644
--- a/src/qml/qml/qqmlpropertyvalidator.cpp
+++ b/src/qml/qml/qqmlpropertyvalidator.cpp
@@ -100,6 +100,9 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject(
int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
{
const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex);
+ for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
+ validateObject(it->objectIndex, /* instantiatingBinding*/ nullptr);
+ }
if (obj->flags & QV4::CompiledData::Object::IsComponent) {
Q_ASSERT(obj->nBindings == 1);
diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp
index 4a211ffa53..28fefca239 100644
--- a/src/qml/qml/qqmltype.cpp
+++ b/src/qml/qml/qqmltype.cpp
@@ -84,6 +84,9 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
case QQmlType::CompositeType:
extraData.fd = new QQmlCompositeTypeData;
break;
+ case QQmlType::InlineComponentType:
+ extraData.id = new QQmlInlineTypeData;
+ break;
default: qFatal("QQmlTypePrivate Internal Error.");
}
}
@@ -106,6 +109,9 @@ QQmlTypePrivate::~QQmlTypePrivate()
case QQmlType::CompositeType:
delete extraData.fd;
break;
+ case QQmlType::InlineComponentType:
+ delete extraData.id;
+ break;
default: //Also InterfaceType, because it has no extra data
break;
}
@@ -426,6 +432,12 @@ void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cach
insertEnums(cppMetaObject);
}
+void QQmlTypePrivate::setContainingType(QQmlType *containingType)
+{
+ Q_ASSERT(regType == QQmlType::InlineComponentType);
+ extraData.id->containingType = containingType->d.data();
+}
+
void QQmlTypePrivate::setName(const QString &uri, const QString &element)
{
module = uri;
@@ -440,6 +452,8 @@ QByteArray QQmlType::typeName() const
return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8();
else if (d->baseMetaObject)
return d->baseMetaObject->className();
+ else if (d->regType == InlineComponentType)
+ return d->extraData.id->inlineComponentName.toUtf8();
}
return QByteArray();
}
@@ -561,7 +575,11 @@ bool QQmlType::isComposite() const
bool QQmlType::isCompositeSingleton() const
{
- return d && d->regType == CompositeSingletonType;
+ // if the outer type is a composite singleton, d->regType will indicate that even for
+ // the inline component type
+ // however, inline components can -at least for now- never be singletons
+ // so we just do one additional check
+ return d && d->regType == CompositeSingletonType && !isInlineComponentType();
}
bool QQmlType::isQObjectSingleton() const
@@ -677,9 +695,28 @@ int QQmlType::index() const
return d ? d->index : -1;
}
+bool QQmlType::isInlineComponentType() const {
+ return d ? d->regType == QQmlType::InlineComponentType : false;
+}
+
+int QQmlType::inlineComponendId() const {
+ bool ok = false;
+ if (d->regType == QQmlType::RegistrationType::InlineComponentType) {
+ Q_ASSERT(d->extraData.id->objectId != -1);
+ return d->extraData.id->objectId;
+ }
+ int subObjectId = sourceUrl().fragment().toInt(&ok);
+ return ok ? subObjectId : -1;
+}
+
QUrl QQmlType::sourceUrl() const
{
- return d ? d->sourceUrl() : QUrl();
+ auto url = d ? d->sourceUrl() : QUrl();
+ if (url.isValid() && d->regType == QQmlType::RegistrationType::InlineComponentType && d->extraData.id->objectId) {
+ Q_ASSERT(url.hasFragment());
+ url.setFragment(QString::number(inlineComponendId()));
+ }
+ return url;
}
int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const
@@ -845,6 +882,19 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scope
return -1;
}
+int QQmlType::inlineComponentObjectId()
+{
+ if (!isInlineComponentType())
+ return -1;
+ return d->extraData.id->objectId;
+}
+
+void QQmlType::setInlineComponentObjectId(int id) const
+{
+ Q_ASSERT(d && d->regType == QQmlType::InlineComponentType);
+ d->extraData.id->objectId = id;
+}
+
void QQmlType::refHandle(const QQmlTypePrivate *priv)
{
if (priv)
@@ -864,4 +914,63 @@ int QQmlType::refCount(const QQmlTypePrivate *priv)
return -1;
}
+int QQmlType::lookupInlineComponentIdByName(const QString &name) const
+{
+ Q_ASSERT(d);
+ return d->namesToInlineComponentObjectIndex.value(name, -1);
+}
+
+QQmlType QQmlType::containingType() const
+{
+ Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
+ auto ret = QQmlType {d->extraData.id->containingType};
+ Q_ASSERT(!ret.isInlineComponentType());
+ return ret;
+}
+
+QQmlType QQmlType::lookupInlineComponentById(int objectid) const
+{
+ Q_ASSERT(d);
+ return d->objectIdToICType.value(objectid, QQmlType(nullptr));
+}
+
+int QQmlType::generatePlaceHolderICId() const
+{
+ Q_ASSERT(d);
+ int id = -2;
+ for (auto it = d->objectIdToICType.keyBegin(); it != d->objectIdToICType.keyEnd(); ++it)
+ if (*it < id)
+ id = *it;
+ return id;
+}
+
+void QQmlType::associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType)
+{
+ auto priv = existingType.isValid() ? const_cast<QQmlTypePrivate *>(existingType.d.data()) : new QQmlTypePrivate { RegistrationType::InlineComponentType } ;
+ priv->setName( QString::fromUtf8(typeName()), name);
+ auto icUrl = QUrl(sourceUrl());
+ icUrl.setFragment(QString::number(objectID));
+ priv->extraData.id->url = icUrl;
+ priv->extraData.id->containingType = d.data();
+ priv->extraData.id->objectId = objectID;
+ priv->typeId = metaTypeIds.id;
+ priv->listId = metaTypeIds.listId;
+ d->namesToInlineComponentObjectIndex.insert(name, objectID);
+ QQmlType icType(priv);
+ d->objectIdToICType.insert(objectID, icType);
+}
+
+void QQmlType::setPendingResolutionName(const QString &name)
+{
+ Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
+ Q_ASSERT(d->extraData.id->inlineComponentName == name|| d->extraData.id->inlineComponentName.isEmpty());
+ d->extraData.id->inlineComponentName = name;
+}
+
+QString QQmlType::pendingResolutionName() const
+{
+ Q_ASSERT(d && d->regType == QQmlType::RegistrationType::InlineComponentType);
+ return d->extraData.id->inlineComponentName;
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h
index af134b21f1..387baa74bb 100644
--- a/src/qml/qml/qqmltype_p.h
+++ b/src/qml/qml/qqmltype_p.h
@@ -74,6 +74,7 @@ class QQmlPropertyCache;
namespace QV4 {
struct String;
}
+struct CompositeMetaTypeIds;
class Q_QML_PRIVATE_EXPORT QQmlType
{
@@ -144,6 +145,9 @@ public:
int index() const;
+ bool isInlineComponentType() const;
+ int inlineComponendId() const;
+
struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo
{
QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *) = nullptr;
@@ -166,6 +170,8 @@ public:
int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const;
int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const;
int scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &, const QStringRef &, bool *ok) const;
+ int inlineComponentObjectId();
+ void setInlineComponentObjectId(int id) const; // TODO: const setters are BAD
const QQmlTypePrivate *priv() const { return d.data(); }
static void refHandle(const QQmlTypePrivate *priv);
@@ -178,9 +184,19 @@ public:
InterfaceType = 2,
CompositeType = 3,
CompositeSingletonType = 4,
+ InlineComponentType = 5,
AnyRegistrationType = 255
};
+ QQmlType containingType() const;
+ int lookupInlineComponentIdByName(const QString &name) const;
+ QQmlType lookupInlineComponentById(int objectid) const;
+ int generatePlaceHolderICId() const;
+
+ void associateInlineComponent(const QString &name, int objectID, const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType);
+ void setPendingResolutionName(const QString &name);
+ QString pendingResolutionName() const;
+
private:
friend class QQmlTypePrivate;
friend uint qHash(const QQmlType &t, uint seed);
diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h
index 51f776178c..43344827db 100644
--- a/src/qml/qml/qqmltype_p_p.h
+++ b/src/qml/qml/qqmltype_p_p.h
@@ -56,6 +56,7 @@
#include <private/qqmlproxymetaobject_p.h>
#include <private/qqmlrefcount_p.h>
#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlmetatype_p.h>
QT_BEGIN_NAMESPACE
@@ -69,6 +70,7 @@ public:
void initEnums(QQmlEnginePrivate *engine) const;
void insertEnums(const QMetaObject *metaObject) const;
void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const;
+ void setContainingType(QQmlType *containingType);
QUrl sourceUrl() const
{
@@ -77,6 +79,8 @@ public:
return extraData.fd->url;
case QQmlType::CompositeSingletonType:
return extraData.sd->singletonInstanceInfo->url;
+ case QQmlType::InlineComponentType:
+ return extraData.id->url;
default:
return QUrl();
}
@@ -130,10 +134,23 @@ public:
QUrl url;
};
+ struct QQmlInlineTypeData
+ {
+ QUrl url = QUrl();
+ // The containing type stores a pointer to the inline component type
+ // Using QQmlType here would create a reference cycle
+ // As the inline component type cannot outlive the containing type
+ // this should still be fine
+ QQmlTypePrivate const * containingType = nullptr;
+ QString inlineComponentName = QString();
+ int objectId = -1;
+ };
+
union extraData {
QQmlCppTypeData* cd;
QQmlSingletonTypeData* sd;
QQmlCompositeTypeData* fd;
+ QQmlInlineTypeData* id;
} extraData;
const char *iid;
@@ -160,6 +177,8 @@ public:
mutable QList<QStringHash<int>*> scopedEnums;
void setName(const QString &uri, const QString &element);
+ mutable QHash<QString, int> namesToInlineComponentObjectIndex;
+ mutable QHash<int, QQmlType> objectIdToICType;
private:
~QQmlTypePrivate() override;
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index 7b4cf1a580..8d499bfe6a 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -58,10 +58,10 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type
QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
: resolvedTypes(resolvedTypeCache)
, engine(engine)
- , typeData(typeData)
, dependencyHasher(dependencyHasher)
- , typeNameCache(typeNameCache)
, document(parsedQML)
+ , typeNameCache(typeNameCache)
+ , typeData(typeData)
{
}
@@ -279,9 +279,9 @@ void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier
document->imports.append(import);
}
-QQmlMetaType::CompositeMetaTypeIds QQmlTypeCompiler::typeIds() const
+CompositeMetaTypeIds QQmlTypeCompiler::typeIdsForComponent(int objectId) const
{
- return typeData->typeIds();
+ return typeData->typeIds(objectId);
}
QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
@@ -1221,7 +1221,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
if (obj->idNameIndex != 0)
_seenObjectWithId = true;
- if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) {
Q_ASSERT(obj->bindingCount() == 1);
const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h
index b43089dc06..319df8673b 100644
--- a/src/qml/qml/qqmltypecompiler_p.h
+++ b/src/qml/qml/qqmltypecompiler_p.h
@@ -79,7 +79,9 @@ struct QQmlTypeCompiler
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler)
public:
- QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document,
+ QQmlTypeCompiler(QQmlEnginePrivate *engine,
+ QQmlTypeData *typeData,
+ QmlIR::Document *document,
const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
QV4::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
@@ -129,14 +131,12 @@ public:
return resolvedTypes->value(id);
}
- QQmlMetaType::CompositeMetaTypeIds typeIds() const;
+ CompositeMetaTypeIds typeIdsForComponent(int objectId = 0) const;
private:
QList<QQmlError> errors;
QQmlEnginePrivate *engine;
- QQmlTypeData *typeData;
const QV4::CompiledData::DependentTypesHasher &dependencyHasher;
- QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
QmlIR::Document *document;
// index is string index of type name (use obj->inheritedTypeNameIndex)
QHash<int, QQmlCustomParser*> customParsers;
@@ -144,6 +144,9 @@ private:
// index in first hash is component index, vector inside contains object indices of objects with id property
QVector<quint32> m_componentRoots;
QQmlPropertyCacheVector m_propertyCaches;
+
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
+ QQmlTypeData *typeData;
};
struct QQmlCompilePass
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 22587c226a..f7abe67921 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,8 +124,10 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
Q_ASSERT(!m_callbacks.contains(callback));
}
-QQmlMetaType::CompositeMetaTypeIds QQmlTypeData::typeIds() const
+CompositeMetaTypeIds QQmlTypeData::typeIds(int objectId) const
{
+ if (objectId != 0)
+ return m_inlineComponentData[objectId].typeIds;
return m_typeIds;
}
@@ -135,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());
@@ -183,6 +211,20 @@ bool QQmlTypeData::tryLoadFromDiskCache()
}
}
+ 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;
}
@@ -233,6 +275,36 @@ 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);
+ typeRef->compilationUnit = m_compiledData; // share compilation unit
+ }
+ }
+}
+
void QQmlTypeData::done()
{
auto cleanup = qScopeGuard([this]{
@@ -263,10 +335,30 @@ 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(type.location.line);
+ error.setColumn(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());
@@ -304,13 +396,23 @@ void QQmlTypeData::done()
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())
+ if (isError() && m_typeIds.isValid()) {
QQmlMetaType::unregisterInternalCompositeType(m_typeIds);
+ for (auto&& icData: qAsConst(m_inlineComponentData)) {
+ QQmlMetaType::unregisterInternalCompositeType(icData.typeIds);
+ }
+ }
});
- QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
QV4::ResolvedTypeReferenceMap resolvedTypeCache;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
{
QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache);
if (error.isValid()) {
@@ -342,8 +444,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())
@@ -351,6 +457,7 @@ 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);
@@ -389,6 +496,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());
@@ -522,6 +649,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());
@@ -724,6 +867,17 @@ void QQmlTypeData::resolveTypes()
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;
@@ -731,7 +885,6 @@ void QQmlTypeData::resolveTypes()
ref.location.column = unresolvedRef->location.column;
ref.needsCreation = unresolvedRef->needsCreation;
-
m_resolvedTypes.insert(unresolvedRef.key(), ref);
}
@@ -766,6 +919,38 @@ QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches(
return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName()));
}
ref->compilationUnit = 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->compilationUnit = exUnit;
+ ref->typePropertyCache = engine->propertyCacheForType(typeID);
+ }
+ }
+ } else {
+ ref->compilationUnit = 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());
diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h
index 53e78e06d7..d894090b36 100644
--- a/src/qml/qml/qqmltypedata_p.h
+++ b/src/qml/qml/qqmltypedata_p.h
@@ -86,6 +86,8 @@ private:
friend class QQmlTypeLoader;
QQmlTypeData(const QUrl &, QQmlTypeLoader *);
+ template<typename Container>
+ void setCompileUnit(const Container &container);
public:
~QQmlTypeData() override;
@@ -93,6 +95,7 @@ public:
const QList<ScriptReference> &resolvedScripts() const;
QV4::ExecutableCompilationUnit *compilationUnit() const;
+ QV4::ExecutableCompilationUnit *compilationUnitForInlineComponent(unsigned int icObjectId) const;
// Used by QQmlComponent to get notifications
struct TypeDataCallback {
@@ -103,7 +106,7 @@ public:
void registerCallback(TypeDataCallback *);
void unregisterCallback(TypeDataCallback *);
- QQmlMetaType::CompositeMetaTypeIds typeIds() const;
+ CompositeMetaTypeIds typeIds(int objectId = 0) const;
QByteArray typeClassName() const { return m_typeClassName; }
protected:
@@ -155,10 +158,15 @@ private:
bool m_typesResolved:1;
// Used for self-referencing types, otherwise -1.
- QQmlMetaType::CompositeMetaTypeIds m_typeIds;
+ CompositeMetaTypeIds m_typeIds;
QByteArray m_typeClassName; // used for meta-object later
- QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compiledData;
+ using ExecutableCompilationUnitPtr = QQmlRefPointer<QV4::ExecutableCompilationUnit>;
+
+ QHash<int, InlineComponentData> m_inlineComponentData;
+
+ ExecutableCompilationUnitPtr m_compiledData;
+ QHash<int, ExecutableCompilationUnitPtr> m_inlineComponentToCompiledData;
QList<TypeDataCallback *> m_callbacks;