diff options
Diffstat (limited to 'src/qml/qml/qqmlcontextdata_p.h')
-rw-r--r-- | src/qml/qml/qqmlcontextdata_p.h | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h new file mode 100644 index 0000000000..3aeabf72fa --- /dev/null +++ b/src/qml/qml/qqmlcontextdata_p.h @@ -0,0 +1,472 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQMLCONTEXTDATA_P_H +#define QQMLCONTEXTDATA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQml/private/qtqmlglobal_p.h> +#include <QtQml/private/qqmlcontext_p.h> +#include <QtQml/private/qqmlguard_p.h> +#include <QtQml/private/qqmltypenamecache_p.h> +#include <QtQml/private/qqmlnotifier_p.h> +#include <QtQml/private/qv4identifierhash_p.h> +#include <QtQml/private/qv4executablecompilationunit_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlComponentAttached; +class QQmlGuardedContextData; +class QQmlJavaScriptExpression; +class QQmlIncubatorPrivate; + +class Q_QML_EXPORT QQmlContextData +{ +public: + static QQmlRefPointer<QQmlContextData> createRefCounted( + const QQmlRefPointer<QQmlContextData> &parent) + { + return QQmlRefPointer<QQmlContextData>(new QQmlContextData(RefCounted, nullptr, parent), + QQmlRefPointer<QQmlContextData>::Adopt); + } + + // Owned by the parent. When the parent is reset to nullptr, it will be deref'd. + static QQmlRefPointer<QQmlContextData> createChild( + const QQmlRefPointer<QQmlContextData> &parent) + { + Q_ASSERT(!parent.isNull()); + return QQmlRefPointer<QQmlContextData>(new QQmlContextData(OwnedByParent, nullptr, parent)); + } + + void addref() const { ++m_refCount; } + void release() const { if (--m_refCount == 0) delete this; } + int count() const { return m_refCount; } + int refCount() const { return m_refCount; } + + QQmlRefPointer<QV4::ExecutableCompilationUnit> typeCompilationUnit() const + { + return m_typeCompilationUnit; + } + void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, + int subComponentIndex); + + static QQmlRefPointer<QQmlContextData> get(QQmlContext *context) { + return QQmlContextPrivate::get(context)->m_data; + } + + void emitDestruction(); + void clearContext(); + void clearContextRecursively(); + void invalidate(); + + bool isValid() const + { + return m_engine && (!m_isInternal || !m_contextObject + || !QObjectPrivate::get(m_contextObject)->wasDeleted); + } + + bool isInternal() const { return m_isInternal; } + void setInternal(bool isInternal) { m_isInternal = isInternal; } + + bool isJSContext() const { return m_isJSContext; } + void setJSContext(bool isJSContext) { m_isJSContext = isJSContext; } + + bool isPragmaLibraryContext() const { return m_isPragmaLibraryContext; } + void setPragmaLibraryContext(bool library) { m_isPragmaLibraryContext = library; } + + QQmlRefPointer<QQmlContextData> parent() const { return m_parent; } + void clearParent() + { + if (!m_parent) + return; + + m_parent = nullptr; + if (m_ownedByParent) { + m_ownedByParent = false; + release(); + } + } + + void refreshExpressions(); + + void addOwnedObject(QQmlData *ownedObject); + QQmlData *ownedObjects() const { return m_ownedObjects; } + void setOwnedObjects(QQmlData *ownedObjects) { m_ownedObjects = ownedObjects; } + + enum QmlObjectKind { + OrdinaryObject, + DocumentRoot, + }; + void installContext(QQmlData *ddata, QmlObjectKind kind); + + QUrl resolvedUrl(const QUrl &) const; + + // My containing QQmlContext. If isInternal is true this owns publicContext. + // If internal is false publicContext owns this. + QQmlContext *asQQmlContext() + { + if (!m_publicContext) + m_publicContext = new QQmlContext(*new QQmlContextPrivate(this)); + return m_publicContext; + } + + QQmlContextPrivate *asQQmlContextPrivate() + { + return QQmlContextPrivate::get(asQQmlContext()); + } + + QObject *contextObject() const { return m_contextObject; } + void setContextObject(QObject *contextObject) { m_contextObject = contextObject; } + + template<typename HandleSelf, typename HandleLinked> + void deepClearContextObject( + QObject *contextObject, HandleSelf &&handleSelf, HandleLinked &&handleLinked) { + for (QQmlContextData *lc = m_linkedContext.data(); lc; lc = lc->m_linkedContext.data()) { + handleLinked(lc); + if (lc->m_contextObject == contextObject) + lc->m_contextObject = nullptr; + } + + handleSelf(this); + if (m_contextObject == contextObject) + m_contextObject = nullptr; + } + + void deepClearContextObject(QObject *contextObject) + { + deepClearContextObject( + contextObject, + [](QQmlContextData *self) { self->emitDestruction(); }, + [](QQmlContextData *){}); + } + + QQmlEngine *engine() const { return m_engine; } + void setEngine(QQmlEngine *engine) { m_engine = engine; } + + QQmlContext *publicContext() const { return m_publicContext; } + void clearPublicContext() + { + if (!m_publicContext) + return; + + m_publicContext = nullptr; + if (m_ownedByPublicContext) { + m_ownedByPublicContext = false; + release(); + } + } + + int propertyIndex(const QString &name) const + { + ensurePropertyNames(); + return m_propertyNameCache.value(name); + } + + int propertyIndex(QV4::String *name) const + { + ensurePropertyNames(); + return m_propertyNameCache.value(name); + } + + QString propertyName(int index) const + { + ensurePropertyNames(); + return m_propertyNameCache.findId(index); + } + + void addPropertyNameAndIndex(const QString &name, int index) + { + Q_ASSERT(!m_propertyNameCache.isEmpty()); + m_propertyNameCache.add(name, index); + } + + void setExpressions(QQmlJavaScriptExpression *expressions) { m_expressions = expressions; } + QQmlJavaScriptExpression *takeExpressions() + { + QQmlJavaScriptExpression *expressions = m_expressions; + m_expressions = nullptr; + return expressions; + } + + void setChildContexts(const QQmlRefPointer<QQmlContextData> &childContexts) + { + m_childContexts = childContexts.data(); + } + QQmlRefPointer<QQmlContextData> childContexts() const { return m_childContexts; } + QQmlRefPointer<QQmlContextData> takeChildContexts() + { + QQmlRefPointer<QQmlContextData> childContexts = m_childContexts; + m_childContexts = nullptr; + return childContexts; + } + QQmlRefPointer<QQmlContextData> nextChild() const { return m_nextChild; } + + int numIdValues() const { return m_idValueCount; } + void setIdValue(int index, QObject *idValue); + bool isIdValueSet(int index) const { return m_idValues[index].wasSet(); } + QQmlNotifier *idValueBindings(int index) const { return m_idValues[index].bindings(); } + QObject *idValue(int index) const { return m_idValues[index].data(); } + + // Return the outermost id for obj, if any. + QString findObjectId(const QObject *obj) const; + + // url() and urlString() prefer the CU's URL over explicitly set baseUrls. They + // don't search the context hierarchy. + // baseUrl() and baseUrlString() search the context hierarchy and prefer explicit + // base URLs over CU Urls. + + QUrl url() const; + QString urlString() const; + + void setBaseUrlString(const QString &baseUrlString) { m_baseUrlString = baseUrlString; } + QString baseUrlString() const + { + for (const QQmlContextData *data = this; data; data = data->m_parent) { + if (!data->m_baseUrlString.isEmpty()) + return data->m_baseUrlString; + if (data->m_typeCompilationUnit) + return data->m_typeCompilationUnit->finalUrlString(); + } + return QString(); + } + + void setBaseUrl(const QUrl &baseUrl) { m_baseUrl = baseUrl; } + QUrl baseUrl() const + { + for (const QQmlContextData *data = this; data; data = data->m_parent) { + if (!data->m_baseUrl.isEmpty()) + return data->m_baseUrl; + if (data->m_typeCompilationUnit) + return data->m_typeCompilationUnit->finalUrl(); + } + return QUrl(); + } + + QQmlRefPointer<QQmlTypeNameCache> imports() const { return m_imports; } + void setImports(const QQmlRefPointer<QQmlTypeNameCache> &imports) { m_imports = imports; } + + QQmlIncubatorPrivate *incubator() const { return m_hasExtraObject ? nullptr : m_incubator; } + void setIncubator(QQmlIncubatorPrivate *incubator) + { + Q_ASSERT(!m_hasExtraObject || m_extraObject == nullptr); + m_hasExtraObject = false; + m_incubator = incubator; + } + + QObject *extraObject() const { return m_hasExtraObject ? m_extraObject : nullptr; } + void setExtraObject(QObject *extraObject) + { + Q_ASSERT(m_hasExtraObject || m_incubator == nullptr); + m_hasExtraObject = true; + m_extraObject = extraObject; + } + + bool isRootObjectInCreation() const { return m_isRootObjectInCreation; } + void setRootObjectInCreation(bool rootInCreation) { m_isRootObjectInCreation = rootInCreation; } + + QV4::PersistentValue importedScripts() const { return m_importedScripts; } + void setImportedScripts(const QV4::PersistentValue &scripts) { m_importedScripts = scripts; } + + QQmlRefPointer<QQmlContextData> linkedContext() const { return m_linkedContext; } + void setLinkedContext(const QQmlRefPointer<QQmlContextData> &context) { m_linkedContext = context; } + + bool hasUnresolvedNames() const { return m_unresolvedNames; } + void setUnresolvedNames(bool hasUnresolvedNames) { m_unresolvedNames = hasUnresolvedNames; } + + QQmlComponentAttached *componentAttacheds() const { return m_componentAttacheds; } + void addComponentAttached(QQmlComponentAttached *attached); + + void addExpression(QQmlJavaScriptExpression *expression); + + bool valueTypesAreAddressable() const { + return m_typeCompilationUnit && m_typeCompilationUnit->valueTypesAreAddressable(); + } + + bool valueTypesAreAssertable() const { + return m_typeCompilationUnit && m_typeCompilationUnit->valueTypesAreAssertable(); + } + +private: + friend class QQmlGuardedContextData; + friend class QQmlContextPrivate; + + enum Ownership { + RefCounted, + OwnedByParent, + OwnedByPublicContext + }; + + // id guards + struct ContextGuard : public QQmlGuard<QObject> + { + enum Tag { + NoTag, + ObjectWasSet + }; + + inline ContextGuard() : QQmlGuard<QObject>(&ContextGuard::objectDestroyedImpl, nullptr), m_context(nullptr) {} + inline ContextGuard &operator=(QObject *obj); + + inline bool wasSet() const; + + QQmlNotifier *bindings() { return &m_bindings; } + void setContext(const QQmlRefPointer<QQmlContextData> &context) + { + m_context = context.data(); + } + + private: + inline static void objectDestroyedImpl(QQmlGuardImpl *); + // Not refcounted, as it always belongs to the QQmlContextData. + QTaggedPointer<QQmlContextData, Tag> m_context; + QQmlNotifier m_bindings; + }; + + // It's OK to pass a half-created publicContext here. We will not dereference it during + // construction. + QQmlContextData( + Ownership ownership, QQmlContext *publicContext, + const QQmlRefPointer<QQmlContextData> &parent, QQmlEngine *engine = nullptr) + : m_parent(parent.data()), + m_engine(engine ? engine : (parent.isNull() ? nullptr : parent->engine())), + m_isInternal(false), m_isJSContext(false), m_isPragmaLibraryContext(false), + m_unresolvedNames(false), m_hasEmittedDestruction(false), m_isRootObjectInCreation(false), + m_ownedByParent(ownership == OwnedByParent), + m_ownedByPublicContext(ownership == OwnedByPublicContext), m_hasExtraObject(false), + m_dummy(0), m_publicContext(publicContext), m_incubator(nullptr) + { + Q_ASSERT(!m_ownedByParent || !m_ownedByPublicContext); + if (!m_parent) + return; + + m_nextChild = m_parent->m_childContexts; + if (m_nextChild) + m_nextChild->m_prevChild = &m_nextChild; + m_prevChild = &m_parent->m_childContexts; + m_parent->m_childContexts = this; + } + + ~QQmlContextData(); + + bool hasExpressionsToRun(bool isGlobalRefresh) const + { + return m_expressions && (!isGlobalRefresh || m_unresolvedNames); + } + + void refreshExpressionsRecursive(bool isGlobal); + void refreshExpressionsRecursive(QQmlJavaScriptExpression *); + void initPropertyNames() const; + + void ensurePropertyNames() const + { + if (m_propertyNameCache.isEmpty()) + initPropertyNames(); + Q_ASSERT(!m_propertyNameCache.isEmpty()); + } + + // My parent context and engine + QQmlContextData *m_parent = nullptr; + QQmlEngine *m_engine = nullptr; + + mutable quint32 m_refCount = 1; + quint32 m_isInternal:1; + quint32 m_isJSContext:1; + quint32 m_isPragmaLibraryContext:1; + quint32 m_unresolvedNames:1; // True if expressions in this context failed to resolve a toplevel name + quint32 m_hasEmittedDestruction:1; + quint32 m_isRootObjectInCreation:1; + quint32 m_ownedByParent:1; + quint32 m_ownedByPublicContext:1; + quint32 m_hasExtraObject:1; // used in QQmlDelegateModelItem::dataForObject to find the corresponding QQmlDelegateModelItem of an object + Q_DECL_UNUSED_MEMBER quint32 m_dummy:23; + QQmlContext *m_publicContext = nullptr; + + union { + // The incubator that is constructing this context if any + QQmlIncubatorPrivate *m_incubator; + // a pointer to extra data, currently only used in QQmlDelegateModel + QObject *m_extraObject; + }; + + // Compilation unit for contexts that belong to a compiled type. + QQmlRefPointer<QV4::ExecutableCompilationUnit> m_typeCompilationUnit; + + // object index in CompiledData::Unit to component that created this context + int m_componentObjectIndex = -1; + + // flag indicates whether the context owns the cache (after mutation) or not. + mutable QV4::IdentifierHash m_propertyNameCache; + + // Context object + QObject *m_contextObject = nullptr; + + // Any script blocks that exist on this context + QV4::PersistentValue m_importedScripts; // This is a JS Array + + QUrl m_baseUrl; + QString m_baseUrlString; + + // List of imports that apply to this context + QQmlRefPointer<QQmlTypeNameCache> m_imports; + + // My children, not refcounted as that would create cyclic references + QQmlContextData *m_childContexts = nullptr; + + // My peers in parent's childContexts list; not refcounted + QQmlContextData *m_nextChild = nullptr; + QQmlContextData **m_prevChild = nullptr; + + // Expressions that use this context + QQmlJavaScriptExpression *m_expressions = nullptr; + + // Doubly-linked list of objects that are owned by this context + QQmlData *m_ownedObjects = nullptr; + + // Doubly-linked list of context guards (XXX merge with contextObjects) + QQmlGuardedContextData *m_contextGuards = nullptr; + + ContextGuard *m_idValues = nullptr; + int m_idValueCount = 0; + + // Linked contexts. this owns linkedContext. + QQmlRefPointer<QQmlContextData> m_linkedContext; + + // Linked list of uses of the Component attached property in this context + QQmlComponentAttached *m_componentAttacheds = nullptr; +}; + +QQmlContextData::ContextGuard &QQmlContextData::ContextGuard::operator=(QObject *obj) +{ + QQmlGuard<QObject>::operator=(obj); + m_context.setTag(ObjectWasSet); + m_bindings.notify(); // For alias connections + return *this; +} + + void QQmlContextData::ContextGuard::objectDestroyedImpl(QQmlGuardImpl *impl) +{ + auto This = static_cast<QQmlContextData::ContextGuard *>(impl); + if (QObject *contextObject = This->m_context->contextObject()) { + if (!QObjectPrivate::get(contextObject)->wasDeleted) + This->m_bindings.notify(); + } +} + +bool QQmlContextData::ContextGuard::wasSet() const +{ + return m_context.tag() == ObjectWasSet; +} + +QT_END_NAMESPACE + +#endif // QQMLCONTEXTDATA_P_H |