diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-03-12 21:02:14 +0100 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-03-12 21:02:14 +0100 |
commit | 3f4496ecea2cb1cb773c899675a6ecd0ef6183d5 (patch) | |
tree | a55813fb4fbe24fe0b1e1446abe29fd0818df576 /src/qml | |
parent | 31e64606f253663f8ee2032b94521afd1804ab11 (diff) | |
parent | 4c689ad93d06bcfff5dfc4fdc41c69767aa04bee (diff) |
Merge remote-tracking branch 'origin/5.11' into dev
Change-Id: I6b2568337c09ff98850b4ecccccc9a8fc25064cd
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/compiler/qqmlirbuilder_p.h | 11 | ||||
-rw-r--r-- | src/qml/compiler/qqmlpropertycachecreator.cpp | 56 | ||||
-rw-r--r-- | src/qml/compiler/qqmlpropertycachecreator_p.h | 51 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler.cpp | 9 | ||||
-rw-r--r-- | src/qml/compiler/qqmltypecompiler_p.h | 12 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodegenerator_p.h | 19 | ||||
-rw-r--r-- | src/qml/doc/src/javascript/dynamicobjectcreation.qdoc | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 5 | ||||
-rw-r--r-- | src/qml/memory/qv4mm.cpp | 25 | ||||
-rw-r--r-- | src/qml/memory/qv4mm_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlbinding.cpp | 14 | ||||
-rw-r--r-- | src/qml/qml/qqmlbinding_p.h | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent.cpp | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 135 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator_p.h | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlproperty.cpp | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache_p.h | 19 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 8 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 10 |
19 files changed, 276 insertions, 117 deletions
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 7f2cf40611..c2cf18e3c4 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -665,6 +665,17 @@ private: } // namespace QmlIR +struct QQmlCompileError +{ + QQmlCompileError() {} + QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) + : location(location), description(description) {} + QV4::CompiledData::Location location; + QString description; + + bool isSet() const { return !description.isEmpty(); } +}; + QT_END_NAMESPACE #endif // QQMLIRBUILDER_P_H diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index 4f8b48f52e..fd22cd58f1 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -45,26 +45,54 @@ QT_BEGIN_NAMESPACE QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0); -QQmlBindingInstantiationContext::QQmlBindingInstantiationContext() - : referencingObjectIndex(-1) - , instantiatingBinding(nullptr) - , instantiatingProperty(nullptr) +QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, + const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache) + : referencingObjectIndex(referencingObjectIndex) + , instantiatingBinding(instantiatingBinding) + , instantiatingPropertyName(instantiatingPropertyName) + , referencingObjectPropertyCache(referencingObjectPropertyCache) { +} +bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() +{ + if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty) + return true; + + Q_ASSERT(referencingObjectIndex >= 0); + Q_ASSERT(referencingObjectPropertyCache); + Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); + + bool notInRevision = false; + instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision, QmlIR::PropertyResolver::IgnoreRevision); + return instantiatingProperty != nullptr; } -QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache) - : referencingObjectIndex(referencingObjectIndex) - , instantiatingBinding(instantiatingBinding) - , instantiatingProperty(nullptr) +QQmlRefPointer<QQmlPropertyCache> QQmlBindingInstantiationContext::instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const { - if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) { - Q_ASSERT(referencingObjectIndex >= 0); - Q_ASSERT(referencingObjectPropertyCache); - Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); + if (instantiatingProperty) { + if (instantiatingProperty->isQObject()) { + return enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType(), instantiatingProperty->typeMinorVersion()); + } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType())) { + return enginePrivate->cache(vtmo); + } + } + return QQmlRefPointer<QQmlPropertyCache>(); +} + +void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const +{ + for (QQmlBindingInstantiationContext pendingBinding: *this) { + const int groupPropertyObjectIndex = pendingBinding.instantiatingBinding->value.objectIndex; + + if (propertyCaches->at(groupPropertyObjectIndex)) + continue; + + if (!pendingBinding.resolveInstantiatingProperty()) + continue; - bool notInRevision = false; - instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision, QmlIR::PropertyResolver::IgnoreRevision); + auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate); + propertyCaches->set(groupPropertyObjectIndex, cache); } } diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 2af0fe0036..8bbc8291b4 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -50,18 +50,31 @@ // We mean it. // -#include "qqmltypecompiler_p.h" #include <private/qqmlvaluetype_p.h> #include <private/qqmlengine_p.h> QT_BEGIN_NAMESPACE struct QQmlBindingInstantiationContext { - QQmlBindingInstantiationContext(); - QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache); - int referencingObjectIndex; - const QV4::CompiledData::Binding *instantiatingBinding; - QQmlPropertyData *instantiatingProperty; + QQmlBindingInstantiationContext() {} + QQmlBindingInstantiationContext(int referencingObjectIndex, + const QV4::CompiledData::Binding *instantiatingBinding, + const QString &instantiatingPropertyName, + QQmlPropertyCache *referencingObjectPropertyCache); + + bool resolveInstantiatingProperty(); + QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const; + + int referencingObjectIndex = -1; + const QV4::CompiledData::Binding *instantiatingBinding = nullptr; + QString instantiatingPropertyName; + QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache; + QQmlPropertyData *instantiatingProperty = nullptr; +}; + +struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext> +{ + void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const; }; struct QQmlPropertyCacheCreatorBase @@ -77,7 +90,10 @@ class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase public: typedef typename ObjectContainer::CompiledObject CompiledObject; - QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports); + QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, + QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, + QQmlEnginePrivate *enginePrivate, + const ObjectContainer *objectContainer, const QQmlImports *imports); QQmlCompileError buildMetaObjects(); @@ -92,14 +108,19 @@ protected: const ObjectContainer * const objectContainer; const QQmlImports * const imports; QQmlPropertyCacheVector *propertyCaches; + QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings; }; template <typename ObjectContainer> -inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports) +inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, + QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, + QQmlEnginePrivate *enginePrivate, + const ObjectContainer *objectContainer, const QQmlImports *imports) : enginePrivate(enginePrivate) , objectContainer(objectContainer) , imports(imports) , propertyCaches(propertyCaches) + , pendingGroupPropertyBindings(pendingGroupPropertyBindings) { propertyCaches->resize(objectContainer->objectCount()); } @@ -169,6 +190,14 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje for ( ; binding != end; ++binding) if (binding->type >= QV4::CompiledData::Binding::Type_Object) { QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); + + // Binding to group property where we failed to look up the type of the + // property? Possibly a group property that is an alias that's not resolved yet. + // Let's attempt to resolve it after we're done with the aliases and fill in the + // propertyCaches entry then. + if (!context.resolveInstantiatingProperty()) + pendingGroupPropertyBindings->append(context); + QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); if (error.isSet()) return error; @@ -183,11 +212,7 @@ template <typename ObjectContainer> inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const { if (context.instantiatingProperty) { - if (context.instantiatingProperty->isQObject()) { - return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType(), context.instantiatingProperty->typeMinorVersion()); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType())) { - return enginePrivate->cache(vtmo); - } + return context.instantiatingPropertyCache(enginePrivate); } else if (obj->inheritedTypeNameIndex != 0) { auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 3415e1c073..a896745b3f 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -45,7 +45,6 @@ #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlcomponent_p.h> -#include "qqmlpropertycachecreator_p.h" //#include "qv4jssimplifier_p.h" #define COMPILE_EXCEPTION(token, desc) \ @@ -79,8 +78,12 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() customParsers.insert(it.key(), customParser); } + QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; + + { - QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, engine, this, imports()); + QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings, + engine, this, imports()); QQmlCompileError error = propertyCacheBuilder.buildMetaObjects(); if (error.isSet()) { recordError(error); @@ -122,6 +125,8 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() QQmlComponentAndAliasResolver resolver(this); if (!resolver.resolve()) return nullptr; + + pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_propertyCaches); } { diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index d905b956c7..b8eddcb9b2 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -55,6 +55,7 @@ #include <qhash.h> #include <private/qqmltypeloader_p.h> #include <private/qqmlirbuilder_p.h> +#include <private/qqmlpropertycachecreator_p.h> QT_BEGIN_NAMESPACE @@ -74,17 +75,6 @@ struct Location; } } -struct QQmlCompileError -{ - QQmlCompileError() {} - QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description) - : location(location), description(description) {} - QV4::CompiledData::Location location; - QString description; - - bool isSet() const { return !description.isEmpty(); } -}; - struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 3b3c766bfe..e69f2cd310 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -102,14 +102,19 @@ public: Jump(BytecodeGenerator *generator, int instruction) : generator(generator), index(instruction) - {} + { Q_ASSERT(generator && index != -1); } + ~Jump() { - Q_ASSERT(generator->instructions[index].linkedLabel != -1); + Q_ASSERT(index == -1 || generator->instructions[index].linkedLabel != -1); // make sure link() got called } + Jump(Jump &&j) { + std::swap(generator, j.generator); + std::swap(index, j.index); + } - BytecodeGenerator *generator; - int index; + BytecodeGenerator *generator = nullptr; + int index = -1; void link() { link(generator->label()); @@ -119,6 +124,12 @@ public: Q_ASSERT(generator->instructions[index].linkedLabel == -1); generator->instructions[index].linkedLabel = l.index; } + + private: + // make this type move-only: + Q_DISABLE_COPY(Jump) + // we never move-assign this type anywhere, so disable it: + Jump &operator=(Jump &&) = delete; }; struct ExceptionHandler : public Label { diff --git a/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc b/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc index 0471d7db9b..be4db4c917 100644 --- a/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc +++ b/src/qml/doc/src/javascript/dynamicobjectcreation.qdoc @@ -157,7 +157,7 @@ to inside the string literals. When managing dynamically created objects, you must ensure the creation context outlives the created object. Otherwise, if the creation context is destroyed -first, the bindings in the dynamic object will no longer work. +first, the bindings and signal handlers in the dynamic object will no longer work. The actual creation context depends on how an object is created: diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index f555740455..36448638f3 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -341,6 +341,11 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje if (!ddata) return QV4::Encode::undefined(); + if (Q_UNLIKELY(!ddata->propertyCache)) { + ddata->propertyCache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); + ddata->propertyCache->addref(); + } + QQmlPropertyCache *cache = ddata->propertyCache; Q_ASSERT(cache); QQmlPropertyData *property = cache->property(propertyIndex); diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index cdda0bf7ef..4e83abebdf 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -664,8 +664,21 @@ void BlockAllocator::collectGrayItems(MarkStack *markStack) } HeapItem *HugeItemAllocator::allocate(size_t size) { - Chunk *c = chunkAllocator->allocate(size); - chunks.push_back(HugeChunk{c, size}); + MemorySegment *m = nullptr; + Chunk *c = nullptr; + if (size >= MemorySegment::SegmentSize/2) { + // too large to handle through the ChunkAllocator, let's get our own memory segement + size_t segmentSize = size + Chunk::HeaderSize; // space required for the Chunk header + size_t pageSize = WTF::pageSize(); + segmentSize = (segmentSize + pageSize - 1) & ~(pageSize - 1); // align to page sizes + m = new MemorySegment(segmentSize); + size = (size + pageSize - 1) & ~(pageSize - 1); // align to page sizes + c = m->allocate(size); + } else { + c = chunkAllocator->allocate(size); + } + Q_ASSERT(c); + chunks.push_back(HugeChunk{m, c, size}); Chunk::setBit(c->objectBitmap, c->first() - c->realBase()); Q_V4_PROFILE_ALLOC(engine, size, Profiling::LargeItem); #ifdef V4_USE_HEAPTRACK @@ -686,7 +699,13 @@ static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocato v->destroy(b); b->_checkIsDestroyed(); } - chunkAllocator->free(c.chunk, c.size); + if (c.segment) { + // own memory segment + c.segment->free(c.chunk, c.size); + delete c.segment; + } else { + chunkAllocator->free(c.chunk, c.size); + } #ifdef V4_USE_HEAPTRACK heaptrack_report_free(c.chunk); #endif diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 1ef54ffcc6..40670bcdc7 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -69,6 +69,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { struct ChunkAllocator; +struct MemorySegment; struct BlockAllocator { BlockAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine) @@ -136,6 +137,7 @@ struct HugeItemAllocator { ChunkAllocator *chunkAllocator; ExecutionEngine *engine; struct HugeChunk { + MemorySegment *segment; Chunk *chunk; size_t size; }; diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index ca3bff43a4..c314833304 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -490,6 +490,7 @@ void QQmlBinding::refresh() void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { + const bool wasEnabled = enabledFlag(); setEnabledFlag(e); setNotifyOnValueChanged(e); @@ -499,7 +500,7 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) m_nextBinding.clearFlag2(); } - if (e) + if (e && !wasEnabled) update(flags); } @@ -514,13 +515,13 @@ void QQmlBinding::setTarget(const QQmlProperty &prop) setTarget(prop.object(), pd->core, &pd->valueTypeData); } -void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) +bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) { m_target = object; if (!object) { m_targetIndex = QQmlPropertyIndex(); - return; + return false; } int coreIndex = core.coreIndex(); @@ -530,9 +531,10 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const int aValueTypeIndex; if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { + // can't resolve id (yet) m_target = nullptr; m_targetIndex = QQmlPropertyIndex(); - return; + return false; } if (valueTypeIndex == -1) valueTypeIndex = aValueTypeIndex; @@ -541,7 +543,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const if (!data || !data->propertyCache) { m_target = nullptr; m_targetIndex = QQmlPropertyIndex(); - return; + return false; } QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); Q_ASSERT(propertyData); @@ -557,6 +559,8 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); data->propertyCache->addref(); } + + return true; } void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 19ec3f5d4f..b67b02975f 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -72,6 +72,8 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression, { friend class QQmlAbstractBinding; public: + typedef QExplicitlySharedDataPointer<QQmlBinding> Ptr; + static QQmlBinding *create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *); static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *, const QString &url = QString(), quint16 lineNumber = 0); @@ -82,7 +84,7 @@ public: ~QQmlBinding() override; void setTarget(const QQmlProperty &); - void setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); + bool setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); void setNotifyOnValueChanged(bool); diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 061f3b54a5..3174bbecd3 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -241,6 +241,9 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension); \li main.qml \li \snippet qml/component/main.qml 0 \endtable + + It is important that the lifetime of the creation context outlive any created objects. See + \l{Maintaining Dynamically Created Objects} for more details. */ /*! diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 36e56a01f8..90f3beb40b 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -780,7 +780,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) qSwap(_currentList, savedList); } -bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) +bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); @@ -802,7 +802,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } // ### resolve this at compile time - if (property && property->propType() == qMetaTypeId<QQmlScriptString>()) { + if (bindingProperty && bindingProperty->propType() == qMetaTypeId<QQmlScriptString>()) { QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; @@ -815,7 +815,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { &ss, nullptr, &propertyWriteStatus, &propertyWriteFlags }; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); return true; } @@ -826,7 +826,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - if (!property) // ### error + if (!bindingProperty) // ### error return true; if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) { @@ -838,20 +838,20 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con const QQmlPropertyData *valueTypeProperty = nullptr; QObject *bindingTarget = _bindingTarget; - if (QQmlValueTypeFactory::isValueType(property->propType())) { - valueType = QQmlValueTypeFactory::valueType(property->propType()); + if (QQmlValueTypeFactory::isValueType(bindingProperty->propType())) { + valueType = QQmlValueTypeFactory::valueType(bindingProperty->propType()); if (!valueType) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; } - valueType->read(_qobject, property->coreIndex()); + valueType->read(_qobject, bindingProperty->coreIndex()); groupObject = valueType; - valueTypeProperty = property; + valueTypeProperty = bindingProperty; } else { void *argv[1] = { &groupObject }; - QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, bindingProperty->coreIndex(), argv); if (!groupObject) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; @@ -864,21 +864,21 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; if (valueType) - valueType->write(_qobject, property->coreIndex(), QQmlPropertyData::BypassInterceptor); + valueType->write(_qobject, bindingProperty->coreIndex(), QQmlPropertyData::BypassInterceptor); return true; } } - if (_ddata->hasBindingBit(property->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex())); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex())); if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex()); + int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, context, _scopeObject, runtimeFunction, currentQmlContext()); @@ -890,34 +890,44 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // the point property (_qobjectForBindings) and after evaluating the expression, // the result is written to a value type virtual property, that contains the sub-index // of the "x" property. - QQmlBinding *qmlBinding; - const QQmlPropertyData *prop = property; + QQmlBinding::Ptr qmlBinding; + const QQmlPropertyData *targetProperty = bindingProperty; const QQmlPropertyData *subprop = nullptr; if (_valueTypeProperty) { - prop = _valueTypeProperty; - subprop = property; + targetProperty = _valueTypeProperty; + subprop = bindingProperty; } if (binding->containsTranslations()) { qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context); } else { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - qmlBinding = QQmlBinding::create(prop, runtimeFunction, _scopeObject, context, currentQmlContext()); + qmlBinding = QQmlBinding::create(targetProperty, runtimeFunction, _scopeObject, context, currentQmlContext()); } - qmlBinding->setTarget(_bindingTarget, *prop, subprop); - sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); + auto bindingTarget = _bindingTarget; + auto valueTypeProperty = _valueTypeProperty; + auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) -> bool { + if (!qmlBinding->setTarget(bindingTarget, *targetProperty, subprop) && targetProperty->isAlias()) + return false; + + sharedState->allCreatedBindings.push(qmlBinding); - if (property->isAlias()) { - QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable); - } else { - qmlBinding->addToObject(); + if (bindingProperty->isAlias()) { + QQmlPropertyPrivate::setBinding(qmlBinding.data(), QQmlPropertyPrivate::DontEnable); + } else { + qmlBinding->addToObject(); - if (!_valueTypeProperty) { - QQmlData *targetDeclarativeData = QQmlData::get(_bindingTarget); - Q_ASSERT(targetDeclarativeData); - targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex()); + if (!valueTypeProperty) { + QQmlData *targetDeclarativeData = QQmlData::get(bindingTarget); + Q_ASSERT(targetDeclarativeData); + targetDeclarativeData->setPendingBindingBit(bindingTarget, bindingProperty->coreIndex()); + } } - } + + return true; + }; + if (!assignBinding(sharedState.data())) + pendingAliasBindings.push_back(assignBinding); } return true; } @@ -934,9 +944,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); QQmlProperty prop; if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); else - prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); vs->setTarget(prop); return true; } @@ -946,8 +956,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); QQmlPropertyIndex propertyIndex; - if (property->isAlias()) { - QQmlPropertyIndex originalIndex(property->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); + if (bindingProperty->isAlias()) { + QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); QQmlPropertyIndex propIndex; QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); QQmlData *data = QQmlData::get(target); @@ -964,9 +974,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { QQmlProperty prop; if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); else - prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); vi->setTarget(prop); propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); } @@ -982,8 +992,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // Assigning object to signal property? if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { - if (!property->isFunction()) { - recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(property->name(_qobject))); + if (!bindingProperty->isFunction()) { + recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(bindingProperty->name(_qobject))); return false; } QMetaMethod method = QQmlMetaType::defaultMethod(createdSubObject); @@ -992,7 +1002,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex()); + QMetaMethod signalMethod = _qobject->metaObject()->method(bindingProperty->coreIndex()); if (!QMetaObject::checkConnectArgs(signalMethod, method)) { recordError(binding->valueLocation, tr("Cannot connect mismatched signal/slot %1 %vs. %2") @@ -1001,7 +1011,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QQmlPropertyPrivate::connect(_qobject, property->coreIndex(), createdSubObject, method.methodIndex()); + QQmlPropertyPrivate::connect(_qobject, bindingProperty->coreIndex(), createdSubObject, method.methodIndex()); return true; } @@ -1010,32 +1020,32 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con int propertyWriteStatus = -1; void *argv[] = { nullptr, nullptr, &propertyWriteStatus, &propertyWriteFlags }; - if (const char *iid = QQmlMetaType::interfaceIId(property->propType())) { + if (const char *iid = QQmlMetaType::interfaceIId(bindingProperty->propType())) { void *ptr = createdSubObject->qt_metacast(iid); if (ptr) { argv[0] = &ptr; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } else { recordError(binding->location, tr("Cannot assign object to interface property")); return false; } - } else if (property->propType() == QMetaType::QVariant) { - if (property->isVarProperty()) { + } else if (bindingProperty->propType() == QMetaType::QVariant) { + if (bindingProperty->isVarProperty()) { QV4::Scope scope(v4); QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject)); - _vmeMetaObject->setVMEProperty(property->coreIndex(), wrappedObject); + _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject); } else { QVariant value = QVariant::fromValue(createdSubObject); argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } - } else if (property->isQList()) { + } else if (bindingProperty->isQList()) { Q_ASSERT(_currentList.object); void *itemToAdd = createdSubObject; const char *iid = nullptr; - int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType()); + int listItemType = QQmlEnginePrivate::get(engine)->listType(bindingProperty->propType()); if (listItemType != -1) iid = QQmlMetaType::interfaceIId(listItemType); if (iid) @@ -1051,17 +1061,17 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { // pointer compatibility was tested in QQmlPropertyValidator at type compile time argv[0] = &createdSubObject; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } return true; } - if (property->isQList()) { + if (bindingProperty->isQList()) { recordError(binding->location, tr("Cannot assign primitives to lists")); return false; } - setPropertyValue(property, binding); + setPropertyValue(bindingProperty, binding); return true; } @@ -1274,12 +1284,33 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo qSwap(_qmlContext, qmlContext); - bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/nullptr); + bool ok = populateInstance(index, instance, /*binding target*/instance, /*value type property*/nullptr); + if (ok) { + if (isContextObject && !pendingAliasBindings.empty()) { + bool processedAtLeastOneBinding = false; + do { + processedAtLeastOneBinding = false; + for (std::vector<PendingAliasBinding>::iterator it = pendingAliasBindings.begin(); + it != pendingAliasBindings.end(); ) { + if ((*it)(sharedState.data())) { + it = pendingAliasBindings.erase(it); + processedAtLeastOneBinding = true; + } else { + ++it; + } + } + } while (processedAtLeastOneBinding && pendingAliasBindings.empty()); + Q_ASSERT(pendingAliasBindings.empty()); + } + } else { + // an error occurred, so we can't setup the pending alias bindings + pendingAliasBindings.clear(); + } qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); - return result ? instance : nullptr; + return ok ? instance : nullptr; } QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 399a5f6d4a..67a5bdd827 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -161,6 +161,9 @@ private: QV4::QmlContext *_qmlContext; friend struct QQmlObjectCreatorRecursionWatcher; + + typedef std::function<bool(QQmlObjectCreatorSharedState *sharedState)> PendingAliasBinding; + std::vector<PendingAliasBinding> pendingAliasBindings; }; struct QQmlObjectCreatorRecursionWatcher diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 8ecd597a39..c4487f91a3 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -269,7 +269,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) for (int ii = 0; ii < path.count() - 1; ++ii) { const QStringRef &pathName = path.at(ii); - if (typeNameCache) { + // Types must begin with an uppercase letter (see checkRegistration() + // in qqmlmetatype.cpp for the enforcement of this). + if (typeNameCache && !pathName.isEmpty() && pathName.at(0).isUpper()) { QQmlTypeNameCache::Result r = typeNameCache->query(pathName); if (r.isValid()) { if (r.type.isValid()) { @@ -875,6 +877,7 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bin void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags) { Q_ASSERT(binding); + Q_ASSERT(binding->targetObject()); QObject *object = binding->targetObject(); const QQmlPropertyIndex index = binding->targetPropertyIndex(); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 51a191a41f..b78a2ddd20 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -81,6 +81,7 @@ template <typename T> class QQmlPropertyCacheAliasCreator; // We have this somewhat awful split between RawData and Data so that RawData can be // used in unions. In normal code, you should always use Data which initializes RawData // to an invalid state on construction. +// ### We should be able to remove this split nowadays class QQmlPropertyRawData { public: @@ -273,20 +274,20 @@ public: private: Flags _flags; - qint16 _coreIndex; - quint16 _propType; + qint16 _coreIndex = 0; + quint16 _propType = 0; // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). - qint16 _notifyIndex; - qint16 _overrideIndex; + qint16 _notifyIndex = 0; + qint16 _overrideIndex = 0; - quint8 _revision; - quint8 _typeMinorVersion; - qint16 _metaObjectOffset; + quint8 _revision = 0; + quint8 _typeMinorVersion = 0; + qint16 _metaObjectOffset = 0; - QQmlPropertyCacheMethodArguments *_arguments; - StaticMetaCallFunction _staticMetaCallFunction; + QQmlPropertyCacheMethodArguments *_arguments = nullptr; + StaticMetaCallFunction _staticMetaCallFunction = nullptr; friend class QQmlPropertyData; friend class QQmlPropertyCache; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index a107bb42ce..61208d1c4a 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2187,8 +2187,12 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; + { - QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches, engine, m_compiledData, &m_importCache); + QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches, + &pendingGroupPropertyBindings, + engine, m_compiledData, &m_importCache); QQmlCompileError error = propertyCacheCreator.buildMetaObjects(); if (error.isSet()) { setError(error); @@ -2198,6 +2202,8 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData); aliasCreator.appendAliasPropertiesToMetaObjects(); + + pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches); } static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine) diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 0e1d9e6fad..14ba6561d4 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1123,6 +1123,11 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va QQmlEngine *engine = scope.engine->qmlEngine(); QQmlContextData *context = scope.engine->callingQmlContext(); + if (!context) { + QQmlEngine *qmlEngine = scope.engine->qmlEngine(); + if (qmlEngine) + context = QQmlContextData::get(QQmlEnginePrivate::get(qmlEngine)->rootContext); + } Q_ASSERT(context); QQmlContext *effectiveContext = nullptr; if (context->isPragmaLibraryContext) @@ -1250,6 +1255,11 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va QQmlEngine *engine = scope.engine->qmlEngine(); QQmlContextData *context = scope.engine->callingQmlContext(); + if (!context) { + QQmlEngine *qmlEngine = scope.engine->qmlEngine(); + if (qmlEngine) + context = QQmlContextData::get(QQmlEnginePrivate::get(qmlEngine)->rootContext); + } Q_ASSERT(context); QQmlContextData *effectiveContext = context; if (context->isPragmaLibraryContext) |