diff options
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext.cpp | 9 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qmlcontext_p.h | 13 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 8 | ||||
-rw-r--r-- | src/qml/qml/qqmlcontext.cpp | 59 | ||||
-rw-r--r-- | src/qml/qml/qqmlcontext_p.h | 132 | ||||
-rw-r--r-- | src/qml/qml/qqmldata_p.h | 36 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 20 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 6 | ||||
-rw-r--r-- | src/qml/qml/qqmlxmlhttprequest.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 4 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel.cpp | 23 | ||||
-rw-r--r-- | src/qml/types/qqmldelegatemodel_p_p.h | 2 | ||||
-rw-r--r-- | tests/auto/qml/qqmlcontext/data/Singleton.qml | 5 | ||||
-rw-r--r-- | tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml | 14 | ||||
-rw-r--r-- | tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp | 28 |
16 files changed, 257 insertions, 106 deletions
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 144ab1b1a5..d97d44379d 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -62,20 +62,17 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QmlContextWrapper); DEFINE_MANAGED_VTABLE(QmlContext); -void Heap::QmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject, bool ownsContext) +void Heap::QmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject) { Object::init(); readOnly = true; - this->ownsContext = ownsContext; isNullWrapper = false; - this->context = new QQmlGuardedContextData(context); + this->context = new QQmlContextDataRef(context); this->scopeObject.init(scopeObject); } void Heap::QmlContextWrapper::destroy() { - if (*context && ownsContext) - (*context)->destroy(); delete context; scopeObject.destroy(); Object::destroy(); @@ -321,7 +318,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons context->isInternal = true; context->isJSContext = true; - Scoped<QmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QmlContextWrapper>(context, (QObject*)0, true)); + Scoped<QmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QmlContextWrapper>(context, (QObject*)0)); qml->d()->isNullWrapper = true; qml->setReadOnly(false); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 9aec7467da..9caeb75b8c 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -67,13 +67,12 @@ struct QmlContextWrapper; namespace Heap { struct QmlContextWrapper : Object { - void init(QQmlContextData *context, QObject *scopeObject, bool ownsContext = false); + void init(QQmlContextData *context, QObject *scopeObject); void destroy(); bool readOnly; - bool ownsContext; bool isNullWrapper; - QQmlGuardedContextData *context; + QQmlContextDataRef *context; QQmlQPointer<QObject> scopeObject; }; @@ -90,10 +89,6 @@ struct Q_QML_EXPORT QmlContextWrapper : Object V4_OBJECT2(QmlContextWrapper, Object) V4_NEEDS_DESTROY - void takeContextOwnership() { - d()->ownsContext = true; - } - inline QObject *getScopeObject() const { return d()->scopeObject; } inline QQmlContextData *getContext() const { return *d()->context; } @@ -116,10 +111,6 @@ struct Q_QML_EXPORT QmlContext : public ExecutionContext QQmlContextData *qmlContext() const { return *d()->qml->context; } - - void takeContextOwnership() { - d()->qml->ownsContext = true; - } }; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 5dc5f5d568..8d96f5b480 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -977,8 +977,12 @@ void QObjectWrapper::destroyObject(bool lastCall) QQmlData *ddata = QQmlData::get(h->object(), false); if (ddata) { if (!h->object()->parent() && !ddata->indestructible) { - if (ddata && ddata->ownContext && ddata->context) - ddata->context->emitDestruction(); + if (ddata && ddata->ownContext) { + Q_ASSERT(ddata->ownContext == ddata->context); + ddata->ownContext->emitDestruction(); + ddata->ownContext = 0; + ddata->context = 0; + } // This object is notionally destroyed now ddata->isQueuedForDeletion = true; if (lastCall) diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 531d9ae457..e06491a814 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -161,6 +161,7 @@ QQmlContext::QQmlContext(QQmlEngine *e, bool) { Q_D(QQmlContext); d->data = new QQmlContextData(this); + ++d->data->refCount; d->data->engine = e; } @@ -174,6 +175,7 @@ QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent) { Q_D(QQmlContext); d->data = new QQmlContextData(this); + ++d->data->refCount; d->data->setParent(engine?QQmlContextData::get(engine->rootContext()):0); } @@ -187,6 +189,7 @@ QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent) { Q_D(QQmlContext); d->data = new QQmlContextData(this); + ++d->data->refCount; d->data->setParent(parentContext?QQmlContextData::get(parentContext):0); } @@ -199,6 +202,7 @@ QQmlContext::QQmlContext(QQmlContextData *data) { Q_D(QQmlContext); d->data = data; + // don't add a refcount here, as the data owns this context } /*! @@ -212,7 +216,8 @@ QQmlContext::~QQmlContext() { Q_D(QQmlContext); - if (!d->data->isInternal) + d->data->publicContext = 0; + if (!--d->data->refCount) d->data->destroy(); } @@ -521,12 +526,12 @@ QQmlContextData::QQmlContextData() } QQmlContextData::QQmlContextData(QQmlContext *ctxt) -: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), - isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), - publicContext(ctxt), incubator(0), componentObjectIndex(-1), - contextObject(0), childContexts(0), nextChild(0), prevChild(0), - expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), - componentAttached(0) + : engine(0), isInternal(false), isJSContext(false), + isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), + publicContext(ctxt), incubator(0), componentObjectIndex(-1), + contextObject(0), nextChild(0), prevChild(0), + expressions(0), contextObjects(0), idValues(0), idValueCount(0), + componentAttached(0) { } @@ -563,11 +568,8 @@ void QQmlContextData::invalidate() emitDestruction(); while (childContexts) { - if (childContexts->ownedByParent) { - childContexts->destroy(); - } else { - childContexts->invalidate(); - } + Q_ASSERT(childContexts != this); + childContexts->invalidate(); } if (prevChild) { @@ -601,12 +603,17 @@ void QQmlContextData::clearContext() void QQmlContextData::destroy() { - if (linkedContext) - linkedContext->destroy(); + Q_ASSERT(refCount == 0); + linkedContext = 0; - if (engine) invalidate(); + // avoid recursion + ++refCount; + if (engine) + invalidate(); + Q_ASSERT(refCount == 1); clearContext(); + Q_ASSERT(refCount == 1); while (contextObjects) { QQmlData *co = contextObjects; @@ -617,6 +624,7 @@ void QQmlContextData::destroy() co->nextContextObject = 0; co->prevContextObject = 0; } + Q_ASSERT(refCount == 1); QQmlGuardedContextData *contextGuard = contextGuards; while (contextGuard) { @@ -627,17 +635,29 @@ void QQmlContextData::destroy() contextGuard = next; } contextGuards = 0; + Q_ASSERT(refCount == 1); delete [] idValues; + idValues = 0; - if (isInternal) + Q_ASSERT(refCount == 1); + if (publicContext) { + // the QQmlContext destructor will remove one ref again + ++refCount; delete publicContext; + } + + Q_ASSERT(refCount == 1); + --refCount; + Q_ASSERT(refCount == 0); delete this; } -void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership) +void QQmlContextData::setParent(QQmlContextData *p) { + if (p == parent) + return; if (p) { parent = p; engine = p->engine; @@ -645,7 +665,6 @@ void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership) if (nextChild) nextChild->prevChild = &nextChild; prevChild = &p->childContexts; p->childContexts = this; - ownedByParent = parentTakesOwnership; } } @@ -660,6 +679,10 @@ void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expr expression->refresh(); } +QQmlContextData::~QQmlContextData() +{ +} + static inline bool expressions_to_run(QQmlContextData *ctxt, bool isGlobalRefresh) { return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames); diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index a259fd62d8..d01820a430 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -78,6 +78,7 @@ class QQmlExpression; class QQmlExpressionPrivate; class QQmlJavaScriptExpression; class QQmlContextData; +class QQmlGuardedContextData; class QQmlIncubatorPrivate; class QQmlContextPrivate : public QObjectPrivate @@ -106,7 +107,7 @@ public: }; class QQmlComponentAttached; -class QQmlGuardedContextData; + class Q_QML_PRIVATE_EXPORT QQmlContextData { public: @@ -114,7 +115,6 @@ public: QQmlContextData(QQmlContext *); void emitDestruction(); void clearContext(); - void destroy(); void invalidate(); inline bool isValid() const { @@ -122,10 +122,10 @@ public: } // My parent context and engine - QQmlContextData *parent; + QQmlContextData *parent = nullptr; QQmlEngine *engine; - void setParent(QQmlContextData *, bool parentTakesOwnership = false); + void setParent(QQmlContextData *); void refreshExpressions(); void addObject(QObject *); @@ -136,14 +136,14 @@ public: // If internal is false publicContext owns this. QQmlContext *asQQmlContext(); QQmlContextPrivate *asQQmlContextPrivate(); + quint32 refCount = 0; quint32 isInternal:1; - quint32 ownedByParent:1; // unrelated to isInternal; parent context deletes children if true. quint32 isJSContext:1; quint32 isPragmaLibraryContext:1; quint32 unresolvedNames:1; // True if expressions in this context failed to resolve a toplevel name quint32 hasEmittedDestruction:1; quint32 isRootObjectInCreation:1; - quint32 dummy:25; + quint32 dummy:26; QQmlContext *publicContext; // The incubator that is constructing this context if any @@ -178,7 +178,7 @@ public: QQmlRefPointer<QQmlTypeNameCache> imports; // My children - QQmlContextData *childContexts; + QQmlContextData *childContexts = 0; // My peers in parent's childContexts list QQmlContextData *nextChild; @@ -191,7 +191,7 @@ public: QQmlData *contextObjects; // Doubly-linked list of context guards (XXX merge with contextObjects) - QQmlGuardedContextData *contextGuards; + QQmlGuardedContextData *contextGuards = 0; // id guards struct ContextGuard : public QQmlGuard<QObject> @@ -210,7 +210,7 @@ public: void setIdProperty(int, QObject *); // Linked contexts. this owns linkedContext. - QQmlContextData *linkedContext; + QQmlContextDataRef linkedContext; // Linked list of uses of the Component attached property in this // context @@ -224,91 +224,137 @@ public: } private: + friend class QQmlContextDataRef; + friend class QQmlContext; // needs to do manual refcounting :/ void refreshExpressionsRecursive(bool isGlobal); void refreshExpressionsRecursive(QQmlJavaScriptExpression *); - ~QQmlContextData() {} + ~QQmlContextData(); + void destroy(); }; + class QQmlGuardedContextData { public: - inline QQmlGuardedContextData(); - inline QQmlGuardedContextData(QQmlContextData *); - inline ~QQmlGuardedContextData(); - - inline QQmlContextData *contextData() const; + inline QQmlGuardedContextData() = default; + inline QQmlGuardedContextData(QQmlContextData *data) + { setContextData(data); } + inline ~QQmlGuardedContextData() + { clear(); } + + inline QQmlContextData *contextData() const + { return m_contextData; } inline void setContextData(QQmlContextData *); inline bool isNull() const { return !m_contextData; } inline operator QQmlContextData*() const { return m_contextData; } inline QQmlContextData* operator->() const { return m_contextData; } - inline QQmlGuardedContextData &operator=(QQmlContextData *d); + inline QQmlGuardedContextData &operator=(QQmlContextData *d) { + setContextData(d); return *this; + } private: - QQmlGuardedContextData &operator=(const QQmlGuardedContextData &); - QQmlGuardedContextData(const QQmlGuardedContextData &); + QQmlGuardedContextData &operator=(const QQmlGuardedContextData &) = delete; + QQmlGuardedContextData(const QQmlGuardedContextData &) = delete; friend class QQmlContextData; inline void clear(); - QQmlContextData *m_contextData; - QQmlGuardedContextData *m_next; - QQmlGuardedContextData **m_prev; + QQmlContextData *m_contextData = 0; + QQmlGuardedContextData *m_next = 0; + QQmlGuardedContextData **m_prev = 0; }; -QQmlGuardedContextData::QQmlGuardedContextData() -: m_contextData(0), m_next(0), m_prev(0) + +void QQmlGuardedContextData::setContextData(QQmlContextData *contextData) + { + if (m_contextData == contextData) + return; + clear(); + + if (contextData) { + m_contextData = contextData; + m_next = contextData->contextGuards; + if (m_next) m_next->m_prev = &m_next; + m_prev = &contextData->contextGuards; + contextData->contextGuards = this; + } +} + +void QQmlGuardedContextData::clear() +{ + if (m_prev) { + *m_prev = m_next; + if (m_next) m_next->m_prev = m_prev; + m_contextData = 0; + m_next = 0; + m_prev = 0; + } +} + +QQmlContextDataRef::QQmlContextDataRef() + : m_contextData(0) { } -QQmlGuardedContextData::QQmlGuardedContextData(QQmlContextData *data) -: m_contextData(0), m_next(0), m_prev(0) +QQmlContextDataRef::QQmlContextDataRef(const QQmlContextDataRef &other) + : m_contextData(other.m_contextData) { - setContextData(data); + if (m_contextData) + ++m_contextData->refCount; } -QQmlGuardedContextData::~QQmlGuardedContextData() +QQmlContextDataRef::QQmlContextDataRef(QQmlContextData *data) + : m_contextData(data) +{ + if (m_contextData) + ++m_contextData->refCount; +} + +QQmlContextDataRef::~QQmlContextDataRef() { clear(); } -void QQmlGuardedContextData::setContextData(QQmlContextData *contextData) +void QQmlContextDataRef::setContextData(QQmlContextData *contextData) { + if (m_contextData == contextData) + return; clear(); if (contextData) { m_contextData = contextData; - m_next = contextData->contextGuards; - if (m_next) m_next->m_prev = &m_next; - m_prev = &contextData->contextGuards; - contextData->contextGuards = this; + ++m_contextData->refCount; } } -QQmlContextData *QQmlGuardedContextData::contextData() const +QQmlContextData *QQmlContextDataRef::contextData() const { return m_contextData; } -void QQmlGuardedContextData::clear() +void QQmlContextDataRef::clear() { - if (m_prev) { - *m_prev = m_next; - if (m_next) m_next->m_prev = m_prev; - m_contextData = 0; - m_next = 0; - m_prev = 0; - } + if (m_contextData && !--m_contextData->refCount) + m_contextData->destroy(); + m_contextData = 0; } -QQmlGuardedContextData & -QQmlGuardedContextData::operator=(QQmlContextData *d) +QQmlContextDataRef & +QQmlContextDataRef::operator=(QQmlContextData *d) { setContextData(d); return *this; } +QQmlContextDataRef & +QQmlContextDataRef::operator=(const QQmlContextDataRef &other) +{ + setContextData(other.m_contextData); + return *this; +} + QQmlContextData::ContextGuard::ContextGuard() : context(0) { diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 2083326cd5..75ea720358 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -78,6 +78,34 @@ struct CompilationUnit; } } +// This is declared here because QQmlData below needs it and this file +// in turn is included from qqmlcontext_p.h. +class QQmlContextData; +class Q_QML_PRIVATE_EXPORT QQmlContextDataRef +{ +public: + inline QQmlContextDataRef(); + inline QQmlContextDataRef(QQmlContextData *); + inline QQmlContextDataRef(const QQmlContextDataRef &); + inline ~QQmlContextDataRef(); + + inline QQmlContextData *contextData() const; + inline void setContextData(QQmlContextData *); + + inline bool isNull() const { return !m_contextData; } + + inline operator QQmlContextData*() const { return m_contextData; } + inline QQmlContextData* operator->() const { return m_contextData; } + inline QQmlContextDataRef &operator=(QQmlContextData *d); + inline QQmlContextDataRef &operator=(const QQmlContextDataRef &other); + +private: + + inline void clear(); + + QQmlContextData *m_contextData; +}; + // This class is structured in such a way, that simply zero'ing it is the // default state for elemental object allocations. This is crucial in the // workings of the QQmlInstruction::CreateSimpleObject instruction. @@ -114,7 +142,6 @@ public: quint32 ownedByQml1:1; // This bit is shared with QML1's QDeclarativeData. quint32 ownMemory:1; - quint32 ownContext:1; quint32 indestructible:1; quint32 explicitIndestructibleSet:1; quint32 hasTaintedV4Object:1; @@ -127,7 +154,7 @@ public: quint32 hasInterceptorMetaObject:1; quint32 hasVMEMetaObject:1; quint32 parentFrozen:1; - quint32 dummy:21; + quint32 dummy:22; // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated @@ -161,9 +188,10 @@ public: void disconnectNotifiers(); // The context that created the C++ object - QQmlContextData *context; + QQmlContextData *context = 0; // The outermost context in which this object lives - QQmlContextData *outerContext; + QQmlContextData *outerContext = 0; + QQmlContextDataRef ownContext; QQmlAbstractBinding *bindings; QQmlBoundSignal *signalHandlers; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 9676feb279..f31db457d3 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -712,8 +712,11 @@ QQmlEnginePrivate::~QQmlEnginePrivate() void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) { if (QQmlData *d = QQmlData::get(o)) { - if (d->ownContext && d->context) { - d->context->destroy(); + if (d->ownContext) { + for (QQmlContextData *lc = d->ownContext->linkedContext; lc; lc = lc->linkedContext) + lc->invalidate(); + d->ownContext->invalidate(); + d->ownContext = 0; d->context = 0; } @@ -729,10 +732,10 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) } QQmlData::QQmlData() - : ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), + : ownedByQml1(false), ownMemory(true), indestructible(true), explicitIndestructibleSet(false), hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), - bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0), context(0), outerContext(0), + bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0), bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), deferredData(0), propertyCache(0), guards(0), extendedData(0) @@ -882,8 +885,12 @@ void QQmlData::setQueuedForDeletion(QObject *object) { if (object) { if (QQmlData *ddata = QQmlData::get(object)) { - if (ddata->ownContext && ddata->context) + if (ddata->ownContext) { + Q_ASSERT(ddata->ownContext == ddata->context); ddata->context->emitDestruction(); + ddata->ownContext = 0; + ddata->context = 0; + } ddata->isQueuedForDeletion = true; } } @@ -1753,8 +1760,7 @@ void QQmlData::destroyed(QObject *object) if (propertyCache) propertyCache->release(); - if (ownContext && context) - context->destroy(); + ownContext = 0; while (guards) { QQmlGuard<QObject> *guard = static_cast<QQmlGuard<QObject> *>(guards); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index b34c2ea5e6..890d1ca7fe 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1131,7 +1131,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo c->linkedContext = context; } else context->addObject(instance); - ddata->ownContext = true; + ddata->ownContext = ddata->context; } else if (!ddata->context) context->addObject(instance); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index ce795be757..342e6c0725 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2817,7 +2817,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent effectiveCtxt = 0; // Create the script context if required - QQmlContextData *ctxt = new QQmlContextData; + QQmlContextDataRef ctxt(new QQmlContextData); ctxt->isInternal = true; ctxt->isJSContext = true; if (shared) @@ -2837,7 +2837,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent } if (effectiveCtxt) { - ctxt->setParent(effectiveCtxt, true); + ctxt->setParent(effectiveCtxt); } else { ctxt->engine = parentCtxt->engine; // Fix for QTBUG-21620 } @@ -2859,12 +2859,10 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent if (!m_program) { if (shared) m_loaded = true; - ctxt->destroy(); return QV4::Encode::undefined(); } QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxt, 0)); - qmlContext->takeContextOwnership(); m_program->qmlContext.set(scope.engine, qmlContext); m_program->run(); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index f3a39313c1..58d8322675 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1058,7 +1058,7 @@ private: void readEncoding(); PersistentValue m_thisObject; - QQmlGuardedContextData m_qmlContext; + QQmlContextDataRef m_qmlContext; static void dispatchCallback(Object *thisObj, QQmlContextData *context); void dispatchCallback(); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 8cc0b32168..96d83b9870 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1150,6 +1150,10 @@ void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, Cal if (!component.isReady()) THROW_GENERIC_ERROR("Qt.createQmlObject(): Component is not ready"); + if (!effectiveContext->isValid()) { + THROW_GENERIC_ERROR("Qt.createQmlObject(): Cannot create a component in an invalid context"); + } + QObject *obj = component.beginCreate(effectiveContext); if (obj) { QQmlData::get(obj, true)->explicitIndestructibleSet = false; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 6584ecfb84..9b1417eccd 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -269,7 +269,8 @@ QQmlDelegateModel::~QQmlDelegateModel() delete cacheItem->object; cacheItem->object = 0; - cacheItem->contextData->destroy(); + cacheItem->contextData->invalidate(); + Q_ASSERT(cacheItem->contextData->refCount == 1); cacheItem->contextData = 0; cacheItem->scriptRef -= 1; } @@ -840,7 +841,8 @@ void QQDMIncubationTask::statusChanged(Status status) delete incubating->object; incubating->object = 0; if (incubating->contextData) { - incubating->contextData->destroy(); + incubating->contextData->invalidate(); + Q_ASSERT(incubating->contextData->refCount == 1); incubating->contextData = 0; } incubating->scriptRef = 0; @@ -900,8 +902,10 @@ void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incuba delete cacheItem->object; cacheItem->object = 0; cacheItem->scriptRef -= 1; - if (cacheItem->contextData) - cacheItem->contextData->destroy(); + if (cacheItem->contextData) { + cacheItem->contextData->invalidate(); + Q_ASSERT(cacheItem->contextData->refCount == 1); + } cacheItem->contextData = 0; if (!cacheItem->isReferenced()) { @@ -983,7 +987,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bo if (QQmlAdaptorModelProxyInterface *proxy = qobject_cast<QQmlAdaptorModelProxyInterface *>(cacheItem)) { ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, true); + ctxt->setParent(cacheItem->contextData); ctxt->contextObject = proxy->proxiedObject(); } } @@ -1966,8 +1970,11 @@ void QQmlDelegateModelItem::destroyObject() QQmlData *data = QQmlData::get(object); Q_ASSERT(data); - if (data->ownContext && data->context) - data->context->clearContext(); + if (data->ownContext) { + data->ownContext->clearContext(); + data->ownContext = 0; + data->context = 0; + } object->deleteLater(); if (attached) { @@ -1975,7 +1982,7 @@ void QQmlDelegateModelItem::destroyObject() attached = 0; } - contextData->destroy(); + contextData->invalidate(); contextData = 0; object = 0; } diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index cb4a1f79ba..0f8438870a 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -140,7 +140,7 @@ public: QV4::ExecutionEngine *v4; QQmlDelegateModelItemMetaType * const metaType; - QQmlContextData *contextData; + QQmlContextDataRef contextData; QPointer<QObject> object; QPointer<QQmlDelegateModelAttached> attached; QQDMIncubationTask *incubationTask; diff --git a/tests/auto/qml/qqmlcontext/data/Singleton.qml b/tests/auto/qml/qqmlcontext/data/Singleton.qml new file mode 100644 index 0000000000..68ef5850e3 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/Singleton.qml @@ -0,0 +1,5 @@ +pragma Singleton +import QtQml 2.0 +QtObject { + readonly property string song: "Highway to Hell" +} diff --git a/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml b/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml new file mode 100644 index 0000000000..2e0e6f20e2 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 + +import constants 1.0 + +QtObject { + function createClosure() { + return function() { return Sing.song; } + } + function createComponentFactory() { + return function(parentObj) { + return Qt.createQmlObject('import QtQml 2.0; QtObject { property string test: "ok"; }', parentObj); + } + } +} diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index f49fd391ac..5f57b9ebb0 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -62,6 +62,7 @@ private slots: void evalAfterInvalidate(); void qobjectDerived(); void qtbug_49232(); + void contextViaClosureAfterDestruction(); private: QQmlEngine engine; @@ -723,6 +724,33 @@ void tst_qqmlcontext::qtbug_49232() QCOMPARE(obj->property("valueTwo"), QVariant(97)); } +void tst_qqmlcontext::contextViaClosureAfterDestruction() +{ + qmlRegisterSingletonType(testFileUrl("Singleton.qml"), "constants", 1, 0, "Sing"); + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("contextViaClosureAfterDestruction.qml")); + QJSValue valueClosure; + QJSValue componentFactoryClosure; + { + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + // meta-calls don't support QJSValue return types, so do the call "by hand" + valueClosure = engine.newQObject(obj.data()).property(QStringLiteral("createClosure")).call(); + QVERIFY(valueClosure.isCallable()); + componentFactoryClosure = engine.newQObject(obj.data()).property(QStringLiteral("createComponentFactory")).call(); + QVERIFY(componentFactoryClosure.isCallable()); + } + QCOMPARE(valueClosure.call().toString(), QLatin1String("Highway to Hell")); + + QScopedPointer<QObject> parent(new QObject); + QJSValue parentWrapper = engine.newQObject(parent.data()); + QQmlEngine::setObjectOwnership(parent.data(), QQmlEngine::CppOwnership); + + QJSValue subObject = componentFactoryClosure.callWithInstance(componentFactoryClosure, QJSValueList() << parentWrapper); + QVERIFY(subObject.isError()); + QCOMPARE(subObject.toString(), QLatin1String("Error: Qt.createQmlObject(): Cannot create a component in an invalid context")); +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" |