diff options
75 files changed, 1812 insertions, 1432 deletions
diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 51b2501a7b..c70a824fb6 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -69,8 +69,7 @@ bool SignalTransition::eventTest(QEvent *event) QQmlContext *outerContext = QQmlEngine::contextForObject(this); QQmlContext context(outerContext); - QQmlContextData::get(outerContext)->imports->addref(); - QQmlContextData::get(&context)->imports = QQmlContextData::get(outerContext)->imports; + QQmlContextData::get(&context)->setImports(QQmlContextData::get(outerContext)->imports()); QStateMachine::SignalEvent *e = static_cast<QStateMachine::SignalEvent*>(event); @@ -163,7 +162,7 @@ void SignalTransition::connectTriggered() QObject *target = senderObject(); QQmlData *ddata = QQmlData::get(this); - QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr; + QQmlRefPointer<QQmlContextData> ctxtdata = ddata ? ddata->outerContext : nullptr; Q_ASSERT(m_bindings.count() == 1); const QV4::CompiledData::Binding *binding = m_bindings.at(0); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index d435e82390..1aa24ea3a4 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -389,7 +389,7 @@ void QQmlEngineDebugServiceImpl::buildObjectList(QDataStream &message, if (!ctxt->isValid()) return; - QQmlContextData *p = QQmlContextData::get(ctxt); + QQmlRefPointer<QQmlContextData> p = QQmlContextData::get(ctxt); QString ctxtName = ctxt->objectName(); int ctxtId = QQmlDebugService::idForObject(ctxt); @@ -400,31 +400,31 @@ void QQmlEngineDebugServiceImpl::buildObjectList(QDataStream &message, int count = 0; - QQmlContextData *child = p->childContexts; + QQmlRefPointer<QQmlContextData> child = p->childContexts(); while (child) { ++count; - child = child->nextChild; + child = child->nextChild(); } message << count; - child = p->childContexts; + child = p->childContexts(); while (child) { buildObjectList(message, child->asQQmlContext(), instances); - child = child->nextChild; + child = child->nextChild(); } count = 0; for (int ii = 0; ii < instances.count(); ++ii) { QQmlData *data = QQmlData::get(instances.at(ii)); - if (data->context == p) + if (data->context == p.data()) count ++; } message << count; for (int ii = 0; ii < instances.count(); ++ii) { QQmlData *data = QQmlData::get(instances.at(ii)); - if (data->context == p) + if (data->context == p.data()) message << objectData(instances.at(ii)); } } @@ -522,16 +522,11 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message) if (engine) { QQmlContext *rootContext = engine->rootContext(); - // Clean deleted objects QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(rootContext); - for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { - if (!ctxtPriv->instances.at(ii)) { - ctxtPriv->instances.removeAt(ii); - --ii; - } - } - buildObjectList(rs, rootContext, ctxtPriv->instances); - buildStatesList(true, ctxtPriv->instances); + ctxtPriv->cleanInstances(); // Clean deleted objects + const QList<QPointer<QObject>> instances = ctxtPriv->instances(); + buildObjectList(rs, rootContext, instances); + buildStatesList(true, instances); } } else if (type == "FETCH_OBJECT") { @@ -783,7 +778,7 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth QQmlContext *context = qmlContext(object); if (!object || !context || !context->isValid()) return false; - QQmlContextData *contextData = QQmlContextData::get(context); + QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context); QQmlPropertyData dummy; QQmlPropertyData *prop = diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp index 333ce4b26f..a70941682b 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp @@ -88,10 +88,10 @@ void JavaScriptJob::run() QV4::ScopedObject withContext(scope, engine->newObject()); QV4::ScopedString k(scope); QV4::ScopedValue v(scope); - for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { - QObject *object = ctxtPriv->instances.at(ii); - if (QQmlContext *context = qmlContext(object)) { - if (QQmlContextData *cdata = QQmlContextData::get(context)) { + const QList<QPointer<QObject>> instances = ctxtPriv->instances(); + for (const QPointer<QObject> &object : instances) { + if (QQmlContext *context = qmlContext(object.data())) { + if (QQmlRefPointer<QQmlContextData> cdata = QQmlContextData::get(context)) { v = QV4::QObjectWrapper::wrap(engine, object); k = engine->newString(cdata->findObjectId(object)); withContext->put(k, v); diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp index 012730902b..301db59952 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp @@ -298,7 +298,7 @@ QString GlobalInspector::idStringForObject(QObject *obj) const { QQmlContext *context = qmlContext(obj); if (context) { - QQmlContextData *cdata = QQmlContextData::get(context); + QQmlRefPointer<QQmlContextData> cdata = QQmlContextData::get(context); if (cdata) return cdata->findObjectId(obj); } diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp index 1561777202..5da26e9610 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp @@ -58,7 +58,9 @@ QString QQmlDebugTranslationServiceImpl::foundElidedText(QObject *textObject, co return elideText; } -void QQmlDebugTranslationServiceImpl::foundTranslationBinding(QQmlTranslationBinding *binding, QObject *scopeObject, QQmlContextData *contextData) +void QQmlDebugTranslationServiceImpl::foundTranslationBinding( + QQmlTranslationBinding *binding, QObject *scopeObject, + const QQmlRefPointer<QQmlContextData> &contextData) { Q_UNUSED(binding) Q_UNUSED(scopeObject) diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.h index a337a937a5..802f21ecce 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.h @@ -78,7 +78,8 @@ public: QQmlDebugTranslationServiceImpl(QObject *parent = 0); QString foundElidedText(QObject *textObject, const QString &layoutText, const QString &elideText) override; - void foundTranslationBinding(QQmlTranslationBinding *binding, QObject *scopeObject, QQmlContextData *contextData) override; + void foundTranslationBinding(QQmlTranslationBinding *binding, QObject *scopeObject, + const QQmlRefPointer<QQmlContextData> &contextData) override; void messageReceived(const QByteArray &message) override; }; diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index 3240e48092..3a6f75a58b 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -191,6 +191,7 @@ qt_add_module(Qml qml/qqmlcomponent.cpp qml/qqmlcomponent.h qml/qqmlcomponent_p.h qml/qqmlcomponentattached_p.h qml/qqmlcontext.cpp qml/qqmlcontext.h qml/qqmlcontext_p.h + qml/qqmlcontextdata.cpp qml/qqmlcontextdata_p.h qml/qqmlcustomparser.cpp qml/qqmlcustomparser_p.h qml/qqmldata_p.h qml/qqmldatablob.cpp qml/qqmldatablob_p.h @@ -207,6 +208,7 @@ qt_add_module(Qml qml/qqmlfileselector.cpp qml/qqmlfileselector.h qml/qqmlfileselector_p.h qml/qqmlglobal.cpp qml/qqmlglobal_p.h qml/qqmlguard_p.h + qml/qqmlguardedcontextdata_p.h qml/qqmlimport.cpp qml/qqmlimport_p.h qml/qqmlincubator.cpp qml/qqmlincubator.h qml/qqmlincubator_p.h qml/qqmlinfo.cpp qml/qqmlinfo.h diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h index b259e047a1..df1ed68c7b 100644 --- a/src/qml/debugger/qqmldebugserviceinterfaces_p.h +++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h @@ -107,7 +107,8 @@ class QQmlNativeDebugService {}; class QQmlDebugTranslationService { public: virtual QString foundElidedText(QObject *, const QString &, const QString &) {return {};} - virtual void foundTranslationBinding(QQmlTranslationBinding *, QObject *, QQmlContextData *) {} + virtual void foundTranslationBinding(QQmlTranslationBinding *, QObject *, + const QQmlRefPointer<QQmlContextData> &) {} }; #else @@ -175,7 +176,8 @@ public: static const QString s_key; virtual QString foundElidedText(QObject *qQuickTextObject, const QString &layoutText, const QString &elideText) = 0; - virtual void foundTranslationBinding(QQmlTranslationBinding *binding, QObject *scopeObject, QQmlContextData *contextData) = 0; + virtual void foundTranslationBinding(QQmlTranslationBinding *binding, QObject *scopeObject, + const QQmlRefPointer<QQmlContextData> &contextData) = 0; protected: friend class QQmlDebugConnector; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index d4725ce7c2..5edf2fdea7 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1198,13 +1198,13 @@ QObject *ExecutionEngine::qmlScopeObject() const return ctx->qml()->scopeObject; } -QQmlContextData *ExecutionEngine::callingQmlContext() const +QQmlRefPointer<QQmlContextData> ExecutionEngine::callingQmlContext() const { Heap::QmlContext *ctx = qmlContext(); if (!ctx) return nullptr; - return ctx->qml()->context->contextData(); + return ctx->qml()->context; } StackTrace ExecutionEngine::stackTrace(int frameLimit) const diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 21971c1c23..9d734a16a0 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -609,7 +609,7 @@ public: Heap::QmlContext *qmlContext() const; QObject *qmlScopeObject() const; - QQmlContextData *callingQmlContext() const; + QQmlRefPointer<QQmlContextData> callingQmlContext() const; StackTrace stackTrace(int frameLimit = -1) const; diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 745ffab0cd..17512cf4ff 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -208,9 +208,9 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons if (!argc) RETURN_UNDEFINED(); - QQmlContextData *context = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> context = scope.engine->callingQmlContext(); - if ((!context || !context->isJSContext) && scope.engine->qmlEngine()) + if ((!context || !context->isJSContext()) && scope.engine->qmlEngine()) RETURN_RESULT(scope.engine->throwError(QString::fromUtf8("Qt.include(): Can only be called from JavaScript files"))); QV4::ScopedValue callbackFunction(scope, QV4::Value::undefinedValue()); diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 70ccfbf223..9d0a17a5cc 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -53,11 +53,11 @@ #include <QtCore/qobject.h> #include <QtCore/qurl.h> - -#include <private/qqmlcontext_p.h> +#include <QtCore/qpointer.h> #include <private/qv4value_p.h> #include <private/qv4context_p.h> +#include <private/qv4persistent_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 4538900d21..9863edead0 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -64,23 +64,25 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QQmlContextWrapper); DEFINE_MANAGED_VTABLE(QmlContext); -void Heap::QQmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject) +void Heap::QQmlContextWrapper::init(QQmlRefPointer<QQmlContextData> context, QObject *scopeObject) { Object::init(); - this->context = new QQmlContextDataRef(context); + this->context = context.take(); this->scopeObject.init(scopeObject); } void Heap::QQmlContextWrapper::destroy() { - delete context; + context->release(); + context = nullptr; scopeObject.destroy(); Object::destroy(); } -static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, QQmlContextData *context, String *name, - bool *hasProperty, Value *base, QV4::Lookup *lookup, - QV4::Lookup *originalLookup, QQmlEnginePrivate *ep) +static OptionalReturnedValue searchContextProperties( + QV4::ExecutionEngine *v4, const QQmlRefPointer<QQmlContextData> &context, String *name, + bool *hasProperty, Value *base, QV4::Lookup *lookup, QV4::Lookup *originalLookup, + QQmlEnginePrivate *ep) { const QV4::IdentifierHash &properties = context->propertyNames(); if (properties.count() == 0) @@ -91,7 +93,7 @@ static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, Q if (propertyIdx == -1) return OptionalReturnedValue(); - if (propertyIdx < context->idValueCount) { + if (propertyIdx < context->numIdValues()) { if (hasProperty) *hasProperty = true; @@ -104,16 +106,16 @@ static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, Q } if (ep->propertyCapture) - ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings); - return OptionalReturnedValue(QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx])); + ep->propertyCapture->captureProperty(context->idValueBindings(propertyIdx)); + return OptionalReturnedValue(QV4::QObjectWrapper::wrap(v4, context->idValue(propertyIdx))); } QQmlContextPrivate *cp = context->asQQmlContextPrivate(); if (ep->propertyCapture) - ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex); + ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex()); - const QVariant &value = cp->propertyValues.at(propertyIdx); + const QVariant &value = cp->propertyValue(propertyIdx); if (hasProperty) *hasProperty = true; if (value.userType() == qMetaTypeId<QList<QObject*> >()) { @@ -122,7 +124,7 @@ static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, Q QQmlContextPrivate::context_at); return OptionalReturnedValue(QmlListWrapper::create(v4, prop, qMetaTypeId<QQmlListProperty<QObject> >())); } - return OptionalReturnedValue(v4->fromVariant(cp->propertyValues.at(propertyIdx))); + return OptionalReturnedValue(v4->fromVariant(cp->propertyValue(propertyIdx))); } ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup) @@ -133,7 +135,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); - if (v4->callingQmlContext() != *resource->d()->context) { + if (v4->callingQmlContext().data() != resource->d()->context) { if (resource->d()->module) { Scoped<Module> module(scope, resource->d()->module); bool hasProp = false; @@ -158,8 +160,8 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // It's possible we could delay the calculation of the "actual" context (in the case // of sub contexts) until it is definitely needed. - QQmlContextData *context = resource->getContext(); - QQmlContextData *expressionContext = context; + QQmlRefPointer<QQmlContextData> context = resource->getContext(); + QQmlRefPointer<QQmlContextData> expressionContext = context; if (!context) { if (hasProperty) @@ -211,9 +213,9 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r return result->asReturnedValue(); } - if (context->imports && name->startsWithUpper()) { + if (context->imports() && name->startsWithUpper()) { // Search for attached properties, enums and imported scripts - QQmlTypeNameCache::Result r = context->imports->query(name, QQmlImport::AllowRecursion); + QQmlTypeNameCache::Result r = context->imports()->query(name, QQmlImport::AllowRecursion); if (r.isValid()) { if (hasProperty) @@ -224,7 +226,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript; return lookup->qmlContextPropertyGetter(lookup, v4, base); } - QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); + QV4::ScopedObject scripts(scope, context->importedScripts().valueRef()); if (scripts) return scripts->get(r.scriptIndex); return QV4::Encode::null(); @@ -250,7 +252,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } result = QQmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { - result = QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); + result = QQmlTypeWrapper::create(v4, scopeObject, context->imports(), r.importNamespace); } if (lookup) { lookup->qmlTypeLookup.qmlTypeWrapper = static_cast<Heap::Object*>(result->heapObject()); @@ -268,7 +270,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty; // minor optimization so we don't potentially try two property lookups on the same object - if (scopeObject == context->contextObject) { + if (scopeObject == context->contextObject()) { scopeObject = nullptr; contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty; } @@ -310,22 +312,24 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // Search context object - if (context->contextObject) { + if (QObject *contextObject = context->contextObject()) { bool hasProp = false; QQmlPropertyData *propertyData = nullptr; - result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData); + result = QV4::QObjectWrapper::getQmlProperty(v4, context, contextObject, + name, QV4::QObjectWrapper::CheckRevision, + &hasProp, &propertyData); if (hasProp) { if (hasProperty) *hasProperty = true; if (base) - *base = QV4::QObjectWrapper::wrap(v4, context->contextObject); + *base = QV4::QObjectWrapper::wrap(v4, contextObject); if (propertyData) { if (lookup) { - QQmlData *ddata = QQmlData::get(context->contextObject, false); + QQmlData *ddata = QQmlData::get(contextObject, false); if (ddata && ddata->propertyCache) { - ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject))); + ScopedValue val(scope, base ? *base + : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, contextObject))); const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue()); lookup->qobjectLookup.ic = That->internalClass(); lookup->qobjectLookup.propertyCache = ddata->propertyCache; @@ -342,7 +346,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } } - context = context->parent; + context = context->parent(); // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond // the immediate QML context (of the .qml file). @@ -370,7 +374,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r return result->asReturnedValue(); } - expressionContext->unresolvedNames = true; + expressionContext->setUnresolvedNames(true); return Encode::undefined(); } @@ -402,8 +406,8 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val // It's possible we could delay the calculation of the "actual" context (in the case // of sub contexts) until it is definitely needed. - QQmlContextData *context = wrapper->getContext(); - QQmlContextData *expressionContext = context; + QQmlRefPointer<QQmlContextData> context = wrapper->getContext(); + QQmlRefPointer<QQmlContextData> expressionContext = context; if (!context) return false; @@ -419,7 +423,7 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val if (properties.count()) { const int propertyIndex = properties.value(name); if (propertyIndex != -1) { - if (propertyIndex < context->idValueCount) { + if (propertyIndex < context->numIdValues()) { v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue")); return false; } @@ -434,14 +438,15 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val scopeObject = nullptr; // Search context object - if (context->contextObject && - QV4::QObjectWrapper::setQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, value)) + if (context->contextObject() && + QV4::QObjectWrapper::setQmlProperty(v4, context, context->contextObject(), name, + QV4::QObjectWrapper::CheckRevision, value)) return true; - context = context->parent; + context = context->parent(); } - expressionContext->unresolvedNames = true; + expressionContext->setUnresolvedNames(true); QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + QLatin1Char('"'); @@ -502,11 +507,11 @@ ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engin if (!qmlContext) return QV4::Encode::null(); - QQmlContextData *context = qmlContext->qmlContext(); + QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext(); if (!context) return QV4::Encode::null(); - QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); + QV4::ScopedObject scripts(scope, context->importedScripts().valueRef()); if (!scripts) return QV4::Encode::null(); return scripts->get(l->qmlContextScriptLookup.scriptIndex); @@ -527,7 +532,7 @@ ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *eng if (!qmlContext) return QV4::Encode::null(); - QQmlContextData *context = qmlContext->qmlContext(); + QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext(); if (!context) return QV4::Encode::null(); @@ -535,9 +540,9 @@ ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *eng const int objectId = l->qmlContextIdObjectLookup.objectId; if (qmlEngine->propertyCapture) - qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings); + qmlEngine->propertyCapture->captureProperty(context->idValueBindings(objectId)); - return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]); + return QV4::QObjectWrapper::wrap(engine, context->idValue(objectId)); } ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) @@ -576,11 +581,11 @@ ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, Executi if (!qmlContext) return QV4::Encode::undefined(); - QQmlContextData *context = qmlContext->qmlContext(); + QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext(); if (!context) return QV4::Encode::undefined(); - QObject *contextObject = context->contextObject; + QObject *contextObject = context->contextObject(); if (!contextObject) return QV4::Encode::undefined(); @@ -621,11 +626,11 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec if (!qmlContext) return QV4::Encode::undefined(); - QQmlContextData *context = qmlContext->qmlContext(); + QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext(); if (!context) return QV4::Encode::undefined(); - QQmlContextData *expressionContext = context; + QQmlRefPointer<QQmlContextData> expressionContext = context; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine()); @@ -635,18 +640,19 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec ScopedValue result(scope); - for (context = context->parent; context; context = context->parent) { + for (context = context->parent(); context; context = context->parent()) { if (auto property = searchContextProperties(engine, context, name, nullptr, base, nullptr, nullptr, ep)) return *property; // Search context object - if (context->contextObject) { + if (QObject *contextObject = context->contextObject()) { bool hasProp = false; - result = QV4::QObjectWrapper::getQmlProperty(engine, context, context->contextObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp); + result = QV4::QObjectWrapper::getQmlProperty( + engine, context, contextObject, name, + QV4::QObjectWrapper::CheckRevision, &hasProp); if (hasProp) { if (base) - *base = QV4::QObjectWrapper::wrap(engine, context->contextObject); + *base = QV4::QObjectWrapper::wrap(engine, contextObject); return result->asReturnedValue(); } @@ -658,7 +664,7 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec if (hasProp) return result->asReturnedValue(); - expressionContext->unresolvedNames = true; + expressionContext->setUnresolvedNames(true); return Encode::undefined(); } @@ -692,11 +698,15 @@ void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContex this->activation.set(internalClass->engine, qml->d()); } -Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject) +Heap::QmlContext *QmlContext::create( + ExecutionContext *parent, QQmlRefPointer<QQmlContextData> context, + QObject *scopeObject) { Scope scope(parent); - Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, scopeObject)); + Scoped<QQmlContextWrapper> qml( + scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>( + std::move(context), scopeObject)); Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml); Q_ASSERT(c->vtable() == staticVTable()); return c; diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index e3e7239fe5..ba7c5da146 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -56,7 +56,7 @@ #include <private/qv4object_p.h> #include <private/qv4context_p.h> -#include <private/qqmlcontext_p.h> +#include <private/qqmlcontextdata_p.h> QT_BEGIN_NAMESPACE @@ -72,10 +72,11 @@ namespace Heap { DECLARE_HEAP_OBJECT(QQmlContextWrapper, Object) { DECLARE_MARKOBJECTS(QQmlContextWrapper); - void init(QQmlContextData *context, QObject *scopeObject); + void init(QQmlRefPointer<QQmlContextData> context, QObject *scopeObject); void destroy(); - QQmlContextDataRef *context; + // This has to be a plain pointer because object needs to be a POD type. + QQmlContextData *context; QQmlQPointer<QObject> scopeObject; }; @@ -97,7 +98,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object V4_INTERNALCLASS(QmlContextWrapper) inline QObject *getScopeObject() const { return d()->scopeObject; } - inline QQmlContextData *getContext() const { return *d()->context; } + inline QQmlRefPointer<QQmlContextData> getContext() const { return d()->context; } static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup = nullptr); @@ -120,13 +121,16 @@ struct Q_QML_EXPORT QmlContext : public ExecutionContext V4_MANAGED(QmlContext, ExecutionContext) V4_INTERNALCLASS(QmlContext) - static Heap::QmlContext *create(QV4::ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject); + static Heap::QmlContext *create( + QV4::ExecutionContext *parent, QQmlRefPointer<QQmlContextData> context, + QObject *scopeObject); QObject *qmlScope() const { return d()->qml()->scopeObject; } - QQmlContextData *qmlContext() const { - return *d()->qml()->context; + + QQmlRefPointer<QQmlContextData> qmlContext() const { + return d()->qml()->context; } }; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index a924fa2975..4e387d0380 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -212,13 +212,17 @@ void QObjectWrapper::initializeBindings(ExecutionEngine *engine) engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), method_disconnect); } -QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const +QQmlPropertyData *QObjectWrapper::findProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, + RevisionMode revisionMode, QQmlPropertyData *local) const { QObject *o = d()->object(); return findProperty(engine, o, qmlContext, name, revisionMode, local); } -QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) +QQmlPropertyData *QObjectWrapper::findProperty( + ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext, + String *name, RevisionMode revisionMode, QQmlPropertyData *local) { Q_UNUSED(revisionMode); @@ -285,13 +289,14 @@ static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, Str return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index)); } -static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj, - bool *hasProperty = nullptr) +static OptionalReturnedValue getPropertyFromImports( + ExecutionEngine *v4, String *name, const QQmlRefPointer<QQmlContextData> &qmlContext, + QObject *qobj, bool *hasProperty = nullptr) { - if (!qmlContext || !qmlContext->imports) + if (!qmlContext || !qmlContext->imports()) return OptionalReturnedValue(); - QQmlTypeNameCache::Result r = qmlContext->imports->query(name); + QQmlTypeNameCache::Result r = qmlContext->imports()->query(name); if (hasProperty) *hasProperty = true; @@ -304,15 +309,17 @@ static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String } else if (r.type.isValid()) { return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums)); } else if (r.importNamespace) { - return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace, - Heap::QQmlTypeWrapper::ExcludeEnums)); + return OptionalReturnedValue(QQmlTypeWrapper::create( + v4, qobj, qmlContext->imports(), r.importNamespace, + Heap::QQmlTypeWrapper::ExcludeEnums)); } Q_UNREACHABLE(); return OptionalReturnedValue(); } -ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, - bool *hasProperty, bool includeImports) const +ReturnedValue QObjectWrapper::getQmlProperty( + const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, + QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) const { // Keep this code in sync with ::virtualResolveLookupGetter @@ -355,7 +362,10 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String return getProperty(v4, d()->object(), result); } -ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property) +ReturnedValue QObjectWrapper::getQmlProperty( + QV4::ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, + QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, + QQmlPropertyData **property) { if (QQmlData::wasDeleted(object)) { if (hasProperty) @@ -415,8 +425,9 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC } -bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, - QObjectWrapper::RevisionMode revisionMode, const Value &value) +bool QObjectWrapper::setQmlProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, QObject *object, + String *name, QObjectWrapper::RevisionMode revisionMode, const Value &value) { if (QQmlData::wasDeleted(object)) return false; @@ -462,7 +473,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP } } else { // binding assignment. - QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext(); QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); @@ -561,7 +572,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP else v = scope.engine->toVariant(value, property->propType()); - QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext(); if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) { const char *valueType = (v.userType() == QMetaType::UnknownType) ? "an unknown type" @@ -702,7 +713,7 @@ QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); Scope scope(that); ScopedString n(scope, id.asStringOrSymbol()); - QQmlContextData *qmlContext = that->engine()->callingQmlContext(); + QQmlRefPointer<QQmlContextData> qmlContext = that->engine()->callingQmlContext(); return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true); } @@ -718,7 +729,7 @@ bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, if (scope.engine->hasException || QQmlData::wasDeleted(that->d()->object())) return false; - QQmlContextData *qmlContext = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext(); if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { QQmlData *ddata = QQmlData::get(that->d()->object()); // Types created by QML are not extensible at run-time, but for other QObjects we can store them @@ -744,7 +755,7 @@ PropertyAttributes QObjectWrapper::virtualGetOwnProperty(const Managed *m, Prope if (!QQmlData::wasDeleted(thatObject)) { Scope scope(m); ScopedString n(scope, id.asStringOrSymbol()); - QQmlContextData *qmlContext = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext(); QQmlPropertyData local; if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local) || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) { @@ -848,7 +859,7 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object); ScopedString name(scope, id.asStringOrSymbol()); - QQmlContextData *qmlContext = engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> qmlContext = engine->callingQmlContext(); QObject * const qobj = This->d()->object(); @@ -1168,7 +1179,7 @@ void QObjectWrapper::destroyObject(bool lastCall) if (ddata) { if (!h->object()->parent() && !ddata->indestructible) { if (ddata && ddata->ownContext) { - Q_ASSERT(ddata->ownContext == ddata->context); + Q_ASSERT(ddata->ownContext.data() == ddata->context); ddata->ownContext->emitDestruction(); ddata->ownContext = nullptr; ddata->context = nullptr; diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index ac9cad2bdb..6d12271eb8 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -165,10 +165,19 @@ struct Q_QML_EXPORT QObjectWrapper : public Object QObject *object() const { return d()->object(); } - ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, bool includeImports = false) const; - static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr); - - static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); + ReturnedValue getQmlProperty( + const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, + RevisionMode revisionMode, bool *hasProperty = nullptr, + bool includeImports = false) const; + \ + static ReturnedValue getQmlProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, + QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, + QQmlPropertyData **property = nullptr); + + static bool setQmlProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, + QObject *object, String *name, RevisionMode revisionMode, const Value &value); static ReturnedValue wrap(ExecutionEngine *engine, QObject *object); static void markWrapper(QObject *object, MarkStack *markStack); @@ -193,8 +202,13 @@ protected: static bool virtualIsEqualTo(Managed *that, Managed *o); static ReturnedValue create(ExecutionEngine *engine, QObject *object); - static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local); - QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; + static QQmlPropertyData *findProperty( + ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext, + String *name, RevisionMode revisionMode, QQmlPropertyData *local); + + QQmlPropertyData *findProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, + String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index a16f3d4167..f937d4d0b3 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -17,6 +17,7 @@ SOURCES += \ $$PWD/qqmlcomponent.cpp \ $$PWD/qqmlincubator.cpp \ $$PWD/qqmlcontext.cpp \ + $$PWD/qqmlcontextdata.cpp \ $$PWD/qqmlcustomparser.cpp \ $$PWD/qqmlpropertyvaluesource.cpp \ $$PWD/qqmlpropertyvalueinterceptor.cpp \ @@ -112,6 +113,8 @@ HEADERS += \ $$PWD/qqmlinfo.h \ $$PWD/qqmlproperty_p.h \ $$PWD/qqmlcontext_p.h \ + $$PWD/qqmlcontextdata_p.h \ + $$PWD/qqmlguardedcontextdata_p.h \ $$PWD/qqmltypeloader_p.h \ $$PWD/qqmllist.h \ $$PWD/qqmllist_p.h \ diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 59f2759c81..e8e57eb725 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -81,19 +81,19 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr QString url; QV4::Function *runtimeFunction = nullptr; - QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context); + QQmlRefPointer<QQmlContextData> ctxtdata = QQmlContextData::get(scriptPrivate->context); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine()); - if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit) { + if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit()) { url = ctxtdata->urlString(); if (scriptPrivate->bindingId != QQmlBinding::Invalid) - runtimeFunction = ctxtdata->typeCompilationUnit->runtimeFunctions.at(scriptPrivate->bindingId); + runtimeFunction = ctxtdata->typeCompilationUnit()->runtimeFunctions.at(scriptPrivate->bindingId); } b->setNotifyOnValueChanged(true); b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); b->setScopeObject(obj ? obj : scriptPrivate->scope); - QV4::ExecutionEngine *v4 = b->context()->engine->handle(); + QV4::ExecutionEngine *v4 = b->engine()->handle(); if (runtimeFunction) { QV4::Scope scope(v4); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxtdata, b->scopeObject())); @@ -121,8 +121,9 @@ void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location) } -QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, - QQmlContextData *ctxt, const QString &url, quint16 lineNumber) +QQmlBinding *QQmlBinding::create( + const QQmlPropertyData *property, const QString &str, QObject *obj, + const QQmlRefPointer<QQmlContextData> &ctxt, const QString &url, quint16 lineNumber) { QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); @@ -135,8 +136,9 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString return b; } -QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function *function, - QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope) +QQmlBinding *QQmlBinding::create( + const QQmlPropertyData *property, QV4::Function *function, QObject *obj, + const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *scope) { QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property); @@ -162,7 +164,7 @@ void QQmlBinding::setNotifyOnValueChanged(bool v) void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) { - if (!enabledFlag() || !context() || !context()->isValid()) + if (!enabledFlag() || !hasValidContext()) return; // Check that the target has not been deleted @@ -183,15 +185,15 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) DeleteWatcher watcher(this); - QQmlEngine *engine = context()->engine; - QV4::Scope scope(engine->handle()); + QQmlEngine *qmlEngine = engine(); + QV4::Scope scope(qmlEngine->handle()); if (canUseAccessor()) flags.setFlag(QQmlPropertyData::BypassInterceptor); Q_TRACE_SCOPE(QQmlBinding, engine, function() ? function()->name()->toQString() : QString(), sourceLocation().sourceFile, sourceLocation().line, sourceLocation().column); - QQmlBindingProfiler prof(QQmlEnginePrivate::get(engine)->profiler, function()); + QQmlBindingProfiler prof(QQmlEnginePrivate::get(qmlEngine)->profiler, function()); doUpdate(watcher, flags, scope); if (!watcher.wasDeleted()) @@ -200,7 +202,7 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined) { - QV4::ExecutionEngine *v4 = context()->engine->handle(); + QV4::ExecutionEngine *v4 = engine()->handle(); int argc = 0; const QV4::Value *argv = nullptr; const QV4::Value *thisObject = nullptr; @@ -266,7 +268,7 @@ protected: } if (hasError()) { - if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine)); + if (!delayedError()->addError(ep)) ep->warning(this->error(engine())); } else { clearError(); } @@ -389,7 +391,10 @@ private: const QV4::CompiledData::Binding *m_binding; }; -QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt) +QQmlBinding *QQmlBinding::createTranslationBinding( + const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, + const QV4::CompiledData::Binding *binding, QObject *obj, + const QQmlRefPointer<QQmlContextData> &ctxt) { QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding); @@ -410,8 +415,8 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) { - QQmlEngine *engine = context()->engine; - QV4::ExecutionEngine *v4engine = engine->handle(); + QQmlEngine *qmlEngine = engine(); + QV4::ExecutionEngine *v4engine = qmlEngine->handle(); int type = valueTypeData.isValid() ? valueTypeData.propType() : core.propType(); @@ -486,7 +491,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, if (QObject *o = *(QObject *const *)value.constData()) { valueType = o->metaObject()->className(); - QQmlMetaObject propertyMetaObject = QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate::get(engine), type); + QQmlMetaObject propertyMetaObject = QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate::get(qmlEngine), type); if (!propertyMetaObject.isNull()) propertyType = propertyMetaObject.className(); } @@ -516,13 +521,13 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, QVariant QQmlBinding::evaluate() { - QQmlEngine *engine = context()->engine; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + QQmlEngine *qmlEngine = engine(); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine); ep->referenceScarceResources(); bool isUndefined = false; - QV4::Scope scope(engine->handle()); + QV4::Scope scope(qmlEngine->handle()); QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); ep->dereferenceScarceResources(); @@ -620,7 +625,7 @@ bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlData *data = QQmlData::get(*m_target, true); if (!data->propertyCache) { - data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); + data->propertyCache = QQmlEnginePrivate::get(engine())->cache(m_target->metaObject()); data->propertyCache->addref(); } @@ -635,7 +640,7 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD Q_ASSERT(data); if (Q_UNLIKELY(!data->propertyCache)) { - data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); + data->propertyCache = QQmlEnginePrivate::get(engine())->cache(m_target->metaObject()); data->propertyCache->addref(); } @@ -722,8 +727,9 @@ protected: } } else if (auto variant = result.as<QV4::VariantObject>()) { QVariant value = variant->d()->data(); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()); - resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType()); + QQmlEngine *qmlEngine = engine(); + resultMo = QQmlPropertyPrivate::rawMetaObjectForType( + qmlEngine ? QQmlEnginePrivate::get(qmlEngine) : nullptr, value.userType()); if (resultMo.isNull()) return slowWrite(*pd, vtpd, result, isUndefined, flags); resultObject = *static_cast<QObject *const *>(value.constData()); diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 7f96b4df9f..5c9a15bec3 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -76,12 +76,21 @@ 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); - static QQmlBinding *create(const QQmlPropertyData *property, QV4::Function *function, - QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope); - static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, const QV4::CompiledData::Binding *binding, - QObject *obj, QQmlContextData *ctxt); + + static QQmlBinding *create( + const QQmlPropertyData *, const QString &, QObject *, + const QQmlRefPointer<QQmlContextData> &, const QString &url = QString(), + quint16 lineNumber = 0); + + static QQmlBinding *create( + const QQmlPropertyData *property, QV4::Function *function, QObject *obj, + const QQmlRefPointer<QQmlContextData> &ctxt, QV4::ExecutionContext *scope); + + static QQmlBinding *createTranslationBinding( + const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, + const QV4::CompiledData::Binding *binding, QObject *obj, + const QQmlRefPointer<QQmlContextData> &ctxt); + ~QQmlBinding() override; void setTarget(const QQmlProperty &); diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 6de4ef153e..6272979e92 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -64,11 +64,10 @@ QT_BEGIN_NAMESPACE -QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, - QQmlContextData *ctxt, QObject *scope, const QString &expression, - const QString &fileName, quint16 line, quint16 column, - const QString &handlerName, - const QString ¶meterString) +QQmlBoundSignalExpression::QQmlBoundSignalExpression( + QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope, + const QString &expression, const QString &fileName, quint16 line, quint16 column, + const QString &handlerName, const QString ¶meterString) : QQmlJavaScriptExpression(), m_index(index), m_target(target) @@ -105,8 +104,9 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, setupFunction(context, f->function()); } -QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scopeObject, - QV4::Function *function, QV4::ExecutionContext *scope) +QQmlBoundSignalExpression::QQmlBoundSignalExpression( + QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, + QObject *scopeObject, QV4::Function *function, QV4::ExecutionContext *scope) : QQmlJavaScriptExpression(), m_index(index), m_target(target) @@ -114,7 +114,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // It's important to call init first, because m_index gets remapped in case of cloned signals. init(ctxt, scopeObject); - QV4::ExecutionEngine *engine = ctxt->engine->handle(); + QV4::ExecutionEngine *engine = ctxt->engine()->handle(); // If the function is marked as having a nested function, then the user wrote: // onSomeSignal: function() { /*....*/ } @@ -141,7 +141,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, setupFunction(qmlContext, function); } -void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope) +void QQmlBoundSignalExpression::init(const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope) { setNotifyOnValueChanged(false); setContext(ctxt); @@ -177,7 +177,7 @@ QString QQmlBoundSignalExpression::expression() const // Changes made here may need to be made there and vice versa. void QQmlBoundSignalExpression::evaluate(void **a) { - Q_ASSERT (context() && engine()); + Q_ASSERT (engine()); if (!expressionFunctionValid()) return; @@ -225,7 +225,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) void QQmlBoundSignalExpression::evaluate(const QList<QVariant> &args) { - Q_ASSERT (context() && engine()); + Q_ASSERT (engine()); if (!expressionFunctionValid()) return; diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h index d1ec67210e..4a0fa6d4e7 100644 --- a/src/qml/qml/qqmlboundsignal_p.h +++ b/src/qml/qml/qqmlboundsignal_p.h @@ -66,15 +66,14 @@ QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlJavaScriptExpression, public QQmlRefCount { public: - QQmlBoundSignalExpression(QObject *target, int index, - QQmlContextData *ctxt, QObject *scope, const QString &expression, - const QString &fileName, quint16 line, quint16 column, - const QString &handlerName = QString(), - const QString ¶meterString = QString()); + QQmlBoundSignalExpression( + QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope, + const QString &expression, const QString &fileName, quint16 line, quint16 column, + const QString &handlerName = QString(), const QString ¶meterString = QString()); - QQmlBoundSignalExpression(QObject *target, int index, - QQmlContextData *ctxt, QObject *scopeObject, QV4::Function *function, - QV4::ExecutionContext *scope = nullptr); + QQmlBoundSignalExpression( + QObject *target, int index, const QQmlRefPointer<QQmlContextData> &ctxt, + QObject *scopeObject, QV4::Function *function, QV4::ExecutionContext *scope = nullptr); // inherited from QQmlJavaScriptExpression. QString expressionIdentifier() const override; @@ -87,12 +86,10 @@ public: QString expression() const; QObject *target() const { return m_target; } - QQmlEngine *engine() const { return context() ? context()->engine : nullptr; } - private: ~QQmlBoundSignalExpression() override; - void init(QQmlContextData *ctxt, QObject *scope); + void init(const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope); bool expressionFunctionValid() const { return function() != nullptr; } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 9ee4fdbe41..64e2c9a90e 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -521,6 +521,10 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent) { Q_D(QQmlComponent); d->engine = engine; + QObject::connect(engine, &QObject::destroyed, this, [d]() { + d->state.creator.reset(); + d->engine = nullptr; + }); } /*! @@ -635,7 +639,7 @@ void QQmlComponent::setData(const QByteArray &data, const QUrl &url) QQmlContext *QQmlComponent::creationContext() const { Q_D(const QQmlComponent); - if(d->creationContext) + if (!d->creationContext.isNull()) return d->creationContext->asQQmlContext(); return qmlContext(this); @@ -879,15 +883,11 @@ QObject *QQmlComponent::createWithInitialProperties(const QVariantMap& initialPr QObject *QQmlComponent::beginCreate(QQmlContext *publicContext) { Q_D(QQmlComponent); - Q_ASSERT(publicContext); - QQmlContextData *context = QQmlContextData::get(publicContext); - - return d->beginCreate(context); + return d->beginCreate(QQmlContextData::get(publicContext)); } -QObject * -QQmlComponentPrivate::beginCreate(QQmlContextData *context) +QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> context) { Q_Q(QQmlComponent); if (!context) { @@ -900,7 +900,7 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) return nullptr; } - if (context->engine != engine) { + if (context->engine() != engine) { qWarning("QQmlComponent: Must create component in context from the same QQmlEngine"); return nullptr; } @@ -930,7 +930,7 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) enginePriv->referenceScarceResources(); QObject *rv = nullptr; - state.creator.reset(new QQmlObjectCreator(context, compilationUnit, creationContext)); + state.creator.reset(new QQmlObjectCreator(std::move(context), compilationUnit, creationContext)); rv = state.creator->create(start); if (!rv) state.errors = state.creator->errors; @@ -963,8 +963,9 @@ void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv, ConstructionState *state = new ConstructionState; state->completePending = true; - QQmlContextData *creationContext = nullptr; - state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext)); + state->creator.reset(new QQmlObjectCreator( + deferredData->context->parent(), deferredData->compilationUnit, + QQmlRefPointer<QQmlContextData>())); if (!state->creator->populateDeferredProperties(object, deferredData)) state->errors << state->creator->errors; @@ -1082,16 +1083,16 @@ void QQmlComponentPrivate::completeCreate() } QQmlComponentAttached::QQmlComponentAttached(QObject *parent) -: QObject(parent), prev(nullptr), next(nullptr) +: QObject(parent), m_prev(nullptr), m_next(nullptr) { } QQmlComponentAttached::~QQmlComponentAttached() { - if (prev) *prev = next; - if (next) next->prev = prev; - prev = nullptr; - next = nullptr; + if (m_prev) *m_prev = m_next; + if (m_next) m_next->m_prev = m_prev; + m_prev = nullptr; + m_next = nullptr; } /*! @@ -1107,12 +1108,12 @@ QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj) QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine); if (p->activeObjectCreator) { // XXX should only be allowed during begin - a->add(p->activeObjectCreator->componentAttachment()); + a->insertIntoList(p->activeObjectCreator->componentAttachment()); } else { QQmlData *d = QQmlData::get(obj); Q_ASSERT(d); Q_ASSERT(d->context); - a->add(&d->context->componentAttached); + d->context->addComponentAttached(a); } return a; @@ -1138,24 +1139,23 @@ QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj) \sa QQmlIncubator */ -void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, - QQmlContext *forContext) +void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context, QQmlContext *forContext) { Q_D(QQmlComponent); if (!context) context = d->engine->rootContext(); - QQmlContextData *contextData = QQmlContextData::get(context); - QQmlContextData *forContextData = contextData; - if (forContext) forContextData = QQmlContextData::get(forContext); + QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context); + QQmlRefPointer<QQmlContextData> forContextData = + forContext ? QQmlContextData::get(forContext) : contextData; if (!contextData->isValid()) { qWarning("QQmlComponent: Cannot create a component in an invalid context"); return; } - if (contextData->engine != d->engine) { + if (contextData->engine() != d->engine) { qWarning("QQmlComponent: Must create component in context from the same QQmlEngine"); return; } @@ -1209,8 +1209,8 @@ void QQmlComponentPrivate::incubateObject( QQmlIncubator *incubationTask, QQmlComponent *component, QQmlEngine *engine, - QQmlContextData *context, - QQmlContextData *forContext) + const QQmlRefPointer<QQmlContextData> &context, + const QQmlRefPointer<QQmlContextData> &forContext) { QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask); QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index a919eb45c0..cc938371a1 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -61,6 +61,7 @@ #include "qqml.h" #include <private/qqmlobjectcreator_p.h> #include <private/qqmltypedata_p.h> +#include <private/qqmlguardedcontextdata_p.h> #include <QtCore/QString> #include <QtCore/QStringList> @@ -84,7 +85,7 @@ public: void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous); - QObject *beginCreate(QQmlContextData *); + QObject *beginCreate(QQmlRefPointer<QQmlContextData>); void completeCreate(); void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate, RequiredProperties &requiredProperties); static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v, RequiredProperties &requiredProperties, QObject *createdComponent); @@ -94,8 +95,8 @@ public: QQmlIncubator *incubationTask, QQmlComponent *component, QQmlEngine *engine, - QQmlContextData *context, - QQmlContextData *forContext); + const QQmlRefPointer<QQmlContextData> &context, + const QQmlRefPointer<QQmlContextData> &forContext); QQmlRefPointer<QQmlTypeData> typeData; void typeDataReady(QQmlTypeData *) override; diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h index eb6d35652b..b94d474d1f 100644 --- a/src/qml/qml/qqmlcomponentattached_p.h +++ b/src/qml/qml/qqmlcomponentattached_p.h @@ -71,24 +71,33 @@ public: QQmlComponentAttached(QObject *parent = nullptr); ~QQmlComponentAttached(); - void add(QQmlComponentAttached **a) { - prev = a; next = *a; *a = this; - if (next) next->prev = &next; + void insertIntoList(QQmlComponentAttached **listHead) + { + m_prev = listHead; + m_next = *listHead; + *listHead = this; + if (m_next) + m_next->m_prev = &m_next; } - void rem() { - if (next) next->prev = prev; - *prev = next; - next = nullptr; prev = nullptr; + + void removeFromList() + { + *m_prev = m_next; + if (m_next) + m_next->m_prev = m_prev; + m_next = nullptr; + m_prev = nullptr; } - QQmlComponentAttached **prev; - QQmlComponentAttached *next; + + QQmlComponentAttached *next() const { return m_next; } Q_SIGNALS: void completed(); void destruction(); private: - friend class QQmlContextData; + QQmlComponentAttached **m_prev; + QQmlComponentAttached *m_next; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index abd379a320..a67eeeecc9 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -55,11 +55,6 @@ QT_BEGIN_NAMESPACE -QQmlContextPrivate::QQmlContextPrivate() -: data(nullptr), notifyIndex(-1) -{ -} - /*! \class QQmlContext \brief The QQmlContext class defines a context within a QML engine. @@ -157,13 +152,8 @@ QQmlContextPrivate::QQmlContextPrivate() /*! \internal */ QQmlContext::QQmlContext(QQmlEngine *e, bool) -: QObject(*(new QQmlContextPrivate)) + : QObject(*(new QQmlContextPrivate(this, nullptr, e))) { - Q_D(QQmlContext); - d->data = new QQmlContextData(this); - ++d->data->refCount; - - d->data->engine = e; } /*! @@ -171,13 +161,10 @@ QQmlContext::QQmlContext(QQmlEngine *e, bool) QObject \a parent. */ QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent) -: QObject(*(new QQmlContextPrivate), parent) + : QObject(*(new QQmlContextPrivate( + this, engine ? QQmlContextData::get(engine->rootContext()).data() : nullptr)), + parent) { - Q_D(QQmlContext); - d->data = new QQmlContextData(this); - ++d->data->refCount; - - d->data->setParent(engine?QQmlContextData::get(engine->rootContext()):nullptr); } /*! @@ -185,24 +172,18 @@ QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent) QObject \a parent. */ QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent) -: QObject(*(new QQmlContextPrivate), parent) + : QObject(*(new QQmlContextPrivate( + this, parentContext ? QQmlContextData::get(parentContext).data() : nullptr)), + parent) { - Q_D(QQmlContext); - d->data = new QQmlContextData(this); - ++d->data->refCount; - - d->data->setParent(parentContext?QQmlContextData::get(parentContext):nullptr); } /*! \internal */ -QQmlContext::QQmlContext(QQmlContextData *data) -: QObject(*(new QQmlContextPrivate), nullptr) +QQmlContext::QQmlContext(QQmlContextPrivate &dd, QObject *parent) + : QObject(dd, parent) { - Q_D(QQmlContext); - d->data = data; - // don't add a refcount here, as the data owns this context } /*! @@ -215,10 +196,7 @@ QQmlContext::QQmlContext(QQmlContextData *data) QQmlContext::~QQmlContext() { Q_D(QQmlContext); - - d->data->publicContext = nullptr; - if (!--d->data->refCount) - d->data->destroy(); + d->m_data->clearPublicContext(); } /*! @@ -230,7 +208,7 @@ QQmlContext::~QQmlContext() bool QQmlContext::isValid() const { Q_D(const QQmlContext); - return d->data && d->data->isValid(); + return d->m_data->isValid(); } /*! @@ -240,7 +218,7 @@ bool QQmlContext::isValid() const QQmlEngine *QQmlContext::engine() const { Q_D(const QQmlContext); - return d->data->engine; + return d->m_data->engine(); } /*! @@ -250,7 +228,10 @@ QQmlEngine *QQmlContext::engine() const QQmlContext *QQmlContext::parentContext() const { Q_D(const QQmlContext); - return d->data->parent?d->data->parent->asQQmlContext():nullptr; + + if (QQmlRefPointer<QQmlContextData> parent = d->m_data->parent()) + return parent->asQQmlContext(); + return nullptr; } /*! @@ -259,7 +240,7 @@ QQmlContext *QQmlContext::parentContext() const QObject *QQmlContext::contextObject() const { Q_D(const QQmlContext); - return d->data->contextObject; + return d->m_data->contextObject(); } /*! @@ -269,19 +250,19 @@ void QQmlContext::setContextObject(QObject *object) { Q_D(QQmlContext); - QQmlContextData *data = d->data; + QQmlRefPointer<QQmlContextData> data = d->m_data; - if (data->isInternal) { + if (data->isInternal()) { qWarning("QQmlContext: Cannot set context object for internal context."); return; } - if (!isValid()) { + if (!data->isValid()) { qWarning("QQmlContext: Cannot set context object on invalid context."); return; } - data->contextObject = object; + data->setContextObject(object); data->refreshExpressions(); } @@ -291,31 +272,30 @@ void QQmlContext::setContextObject(QObject *object) void QQmlContext::setContextProperty(const QString &name, const QVariant &value) { Q_D(QQmlContext); - if (d->notifyIndex == -1) - d->notifyIndex = QMetaObjectPrivate::absoluteSignalCount(&QQmlContext::staticMetaObject); + if (d->notifyIndex() == -1) + d->setNotifyIndex(QMetaObjectPrivate::absoluteSignalCount(&QQmlContext::staticMetaObject)); - QQmlContextData *data = d->data; + QQmlRefPointer<QQmlContextData> data = d->m_data; - if (data->isInternal) { + if (data->isInternal()) { qWarning("QQmlContext: Cannot set property on internal context."); return; } - if (!isValid()) { + if (!data->isValid()) { qWarning("QQmlContext: Cannot set property on invalid context."); return; } - QV4::IdentifierHash &properties = data->detachedPropertyNames(); - int idx = properties.value(name); + QV4::IdentifierHash *properties = data->detachedPropertyNames(); + int idx = properties->value(name); if (idx == -1) { - properties.add(name, data->idValueCount + d->propertyValues.count()); - d->propertyValues.append(value); - + properties->add(name, data->numIdValues() + d->numPropertyValues()); + d->appendPropertyValue(value); data->refreshExpressions(); } else { - d->propertyValues[idx] = value; - QMetaObject::activate(this, d->notifyIndex, idx, nullptr); + d->setPropertyValue(idx, value); + QMetaObject::activate(this, d->notifyIndex(), idx, nullptr); } if (auto *obj = qvariant_cast<QObject *>(value)) { @@ -350,20 +330,15 @@ void QQmlContext::setContextProperties(const QVector<PropertyPair> &properties) { Q_D(const QQmlContext); - QQmlContextData *data = d->data; - - QQmlJavaScriptExpression *expressions = data->expressions; - QQmlContextData *childContexts = data->childContexts; - - data->expressions = nullptr; - data->childContexts = nullptr; + QQmlRefPointer<QQmlContextData> data = d->m_data; + QQmlJavaScriptExpression *expressions = data->takeExpressions(); + QQmlRefPointer<QQmlContextData> childContexts = data->takeChildContexts(); for (const auto &property : properties) setContextProperty(property.name, property.value); - data->expressions = expressions; - data->childContexts = childContexts; - + data->setExpressions(expressions); + data->setChildContexts(childContexts); data->refreshExpressions(); } @@ -389,28 +364,27 @@ QVariant QQmlContext::contextProperty(const QString &name) const QVariant value; int idx = -1; - QQmlContextData *data = d->data; + QQmlRefPointer<QQmlContextData> data = d->m_data; - const QV4::IdentifierHash &properties = data->propertyNames(); + const QV4::IdentifierHash properties = data->propertyNames(); if (properties.count()) idx = properties.value(name); if (idx == -1) { - if (data->contextObject) { - QObject *obj = data->contextObject; + if (QObject *obj = data->contextObject()) { QQmlPropertyData local; QQmlPropertyData *property = - QQmlPropertyCache::property(data->engine, obj, name, data, local); + QQmlPropertyCache::property(data->engine(), obj, name, data, local); if (property) value = obj->metaObject()->property(property->coreIndex()).read(obj); } if (!value.isValid() && parentContext()) value = parentContext()->contextProperty(name); } else { - if (idx >= d->propertyValues.count()) - value = QVariant::fromValue(data->idValues[idx - d->propertyValues.count()].data()); + if (idx >= d->numPropertyValues()) + value = QVariant::fromValue(data->idValue(idx - d->numPropertyValues())); else - value = d->propertyValues[idx]; + value = d->propertyValue(idx); } return value; @@ -427,7 +401,7 @@ QString QQmlContext::nameForObject(QObject *object) const { Q_D(const QQmlContext); - return d->data->findObjectId(object); + return d->m_data->findObjectId(object); } /*! @@ -439,37 +413,9 @@ QString QQmlContext::nameForObject(QObject *object) const QUrl QQmlContext::resolvedUrl(const QUrl &src) { Q_D(QQmlContext); - return d->data->resolvedUrl(src); -} - -QUrl QQmlContextData::resolvedUrl(const QUrl &src) -{ - QUrl resolved; - if (src.isRelative() && !src.isEmpty()) { - QQmlContextData *ctxt = this; - do { - if (ctxt->url().isValid()) - break; - else - ctxt = ctxt->parent; - } while (ctxt); - - if (ctxt) - resolved = ctxt->url().resolved(src); - else if (engine) - resolved = engine->baseUrl().resolved(src); - } else { - resolved = src; - } - - if (resolved.isEmpty()) //relative but no ctxt - return resolved; - - return engine ? engine->interceptUrl(resolved, QQmlAbstractUrlInterceptor::UrlString) - : resolved; + return d->m_data->resolvedUrl(src); } - /*! Explicitly sets the url resolvedUrl() will use for relative references to \a baseUrl. @@ -481,9 +427,8 @@ QUrl QQmlContextData::resolvedUrl(const QUrl &src) void QQmlContext::setBaseUrl(const QUrl &baseUrl) { Q_D(QQmlContext); - - d->data->baseUrl = baseUrl; - d->data->baseUrlString = baseUrl.toString(); + d->m_data->setBaseUrl(baseUrl); + d->m_data->setBaseUrlString(baseUrl.toString()); } /*! @@ -493,14 +438,7 @@ void QQmlContext::setBaseUrl(const QUrl &baseUrl) QUrl QQmlContext::baseUrl() const { Q_D(const QQmlContext); - const QQmlContextData* data = d->data; - while (data && data->url().isEmpty()) - data = data->parent; - - if (data) - return data->url(); - else - return QUrl(); + return d->m_data->baseUrl(); } int QQmlContextPrivate::context_count(QQmlListProperty<QObject> *prop) @@ -509,11 +447,10 @@ int QQmlContextPrivate::context_count(QQmlListProperty<QObject> *prop) QQmlContextPrivate *d = QQmlContextPrivate::get(context); int contextProperty = (int)(quintptr)prop->data; - if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) { + if (d->propertyValue(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) return 0; - } else { - return ((const QList<QObject> *)d->propertyValues.at(contextProperty).constData())->count(); - } + else + return ((const QList<QObject> *)d->propertyValue(contextProperty).constData())->count(); } QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int index) @@ -522,384 +459,40 @@ QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int ind QQmlContextPrivate *d = QQmlContextPrivate::get(context); int contextProperty = (int)(quintptr)prop->data; - if (d->propertyValues.at(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) { + if (d->propertyValue(contextProperty).userType() != qMetaTypeId<QList<QObject*> >()) return nullptr; - } else { - return ((const QList<QObject*> *)d->propertyValues.at(contextProperty).constData())->at(index); - } + else + return ((const QList<QObject*> *)d->propertyValue(contextProperty).constData())->at(index); } void QQmlContextPrivate::dropDestroyedQObject(const QString &name, QObject *destroyed) { - if (!data->isValid()) + if (!m_data->isValid()) return; - const int idx = data->propertyNames().value(name); + const int idx = m_data->propertyNames().value(name); Q_ASSERT(idx >= 0); - if (qvariant_cast<QObject *>(propertyValues[idx]) != destroyed) + if (qvariant_cast<QObject *>(propertyValue(idx)) != destroyed) return; - propertyValues[idx] = QVariant::fromValue<QObject *>(nullptr); - QMetaObject::activate(q_func(), notifyIndex, idx, nullptr); -} - - -QQmlContextData::QQmlContextData() - : QQmlContextData(nullptr) -{ -} - -QQmlContextData::QQmlContextData(QQmlContext *ctxt) - : engine(nullptr), isInternal(false), isJSContext(false), - isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), - stronglyReferencedByParent(false), hasExtraObject(false), publicContext(ctxt), incubator(nullptr), componentObjectIndex(-1), - contextObject(nullptr), nextChild(nullptr), prevChild(nullptr), - expressions(nullptr), contextObjects(nullptr), idValues(nullptr), idValueCount(0), - componentAttached(nullptr) -{ -} - -void QQmlContextData::emitDestruction() -{ - if (!hasEmittedDestruction) { - hasEmittedDestruction = true; - - // Emit the destruction signal - must be emitted before invalidate so that the - // context is still valid if bindings or resultant expression evaluation requires it - if (engine) { - while (componentAttached) { - QQmlComponentAttached *a = componentAttached; - componentAttached = a->next; - if (componentAttached) componentAttached->prev = &componentAttached; - - a->next = nullptr; - a->prev = nullptr; - - emit a->destruction(); - } - - QQmlContextData * child = childContexts; - while (child) { - child->emitDestruction(); - child = child->nextChild; - } - } - } -} - -void QQmlContextData::invalidate() -{ - emitDestruction(); - - while (childContexts) { - Q_ASSERT(childContexts != this); - if (childContexts->stronglyReferencedByParent && !--childContexts->refCount) - childContexts->destroy(); - else - childContexts->invalidate(); - } - - if (prevChild) { - *prevChild = nextChild; - if (nextChild) nextChild->prevChild = prevChild; - nextChild = nullptr; - prevChild = nullptr; - } - - importedScripts.clear(); - - engine = nullptr; - parent = nullptr; -} - -void QQmlContextData::clearContextRecursively() -{ - clearContext(); - - for (auto ctxIt = childContexts; ctxIt; ctxIt = ctxIt->nextChild) - ctxIt->clearContextRecursively(); -} - -void QQmlContextData::clearContext() -{ - emitDestruction(); - - QQmlJavaScriptExpression *expression = expressions; - while (expression) { - QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression; - - expression->m_prevExpression = nullptr; - expression->m_nextExpression = nullptr; - - expression->setContext(nullptr); - - expression = nextExpression; - } - expressions = nullptr; -} - -void QQmlContextData::destroy() -{ - Q_ASSERT(refCount == 0); - linkedContext = nullptr; - - // avoid recursion - ++refCount; - if (engine) - invalidate(); - - Q_ASSERT(refCount == 1); - clearContext(); - Q_ASSERT(refCount == 1); - - while (contextObjects) { - QQmlData *co = contextObjects; - contextObjects = contextObjects->nextContextObject; - - if (co->context == this) - co->context = nullptr; - co->outerContext = nullptr; - co->nextContextObject = nullptr; - co->prevContextObject = nullptr; - } - Q_ASSERT(refCount == 1); - - QQmlGuardedContextData *contextGuard = contextGuards; - while (contextGuard) { - QQmlGuardedContextData *next = contextGuard->m_next; - contextGuard->m_next = nullptr; - contextGuard->m_prev = nullptr; - contextGuard->m_contextData = nullptr; - contextGuard = next; - } - contextGuards = nullptr; - Q_ASSERT(refCount == 1); - - delete [] idValues; - idValues = nullptr; - - 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 stronglyReferencedByParent) -{ - if (p == parent) - return; - if (p) { - Q_ASSERT(!parent); - parent = p; - this->stronglyReferencedByParent = stronglyReferencedByParent; - if (stronglyReferencedByParent) - ++refCount; // balanced in QQmlContextData::invalidate() - engine = p->engine; - nextChild = p->childContexts; - if (nextChild) nextChild->prevChild = &nextChild; - prevChild = &p->childContexts; - p->childContexts = this; - } -} - -void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression) -{ - QQmlJavaScriptExpression::DeleteWatcher w(expression); - - if (expression->m_nextExpression) - refreshExpressionsRecursive(expression->m_nextExpression); - - if (!w.wasDeleted()) - expression->refresh(); -} - -QQmlContextData::~QQmlContextData() -{ -} - -static inline bool expressions_to_run(QQmlContextData *ctxt, bool isGlobalRefresh) -{ - return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames); -} - -void QQmlContextData::refreshExpressionsRecursive(bool isGlobal) -{ - // For efficiency, we try and minimize the number of guards we have to create - if (expressions_to_run(this, isGlobal) && (nextChild || childContexts)) { - QQmlGuardedContextData guard(this); - - if (childContexts) - childContexts->refreshExpressionsRecursive(isGlobal); - - if (guard.isNull()) return; - - if (nextChild) - nextChild->refreshExpressionsRecursive(isGlobal); - - if (guard.isNull()) return; - - if (expressions_to_run(this, isGlobal)) - refreshExpressionsRecursive(expressions); - - } else if (expressions_to_run(this, isGlobal)) { - - refreshExpressionsRecursive(expressions); - - } else if (nextChild && childContexts) { - - QQmlGuardedContextData guard(this); - - childContexts->refreshExpressionsRecursive(isGlobal); - - if (!guard.isNull() && nextChild) - nextChild->refreshExpressionsRecursive(isGlobal); - - } else if (nextChild) { - - nextChild->refreshExpressionsRecursive(isGlobal); - - } else if (childContexts) { - - childContexts->refreshExpressionsRecursive(isGlobal); - - } -} - -// Refreshes all expressions that could possibly depend on this context. Refreshing flushes all -// context-tree dependent caches in the expressions, and should occur every time the context tree -// *structure* (not values) changes. -void QQmlContextData::refreshExpressions() -{ - bool isGlobal = (parent == nullptr); - - // For efficiency, we try and minimize the number of guards we have to create - if (expressions_to_run(this, isGlobal) && childContexts) { - QQmlGuardedContextData guard(this); - - childContexts->refreshExpressionsRecursive(isGlobal); - - if (!guard.isNull() && expressions_to_run(this, isGlobal)) - refreshExpressionsRecursive(expressions); - - } else if (expressions_to_run(this, isGlobal)) { - - refreshExpressionsRecursive(expressions); - - } else if (childContexts) { - - childContexts->refreshExpressionsRecursive(isGlobal); - - } -} - -void QQmlContextData::addObject(QQmlData *data) -{ - if (data->outerContext) { - if (data->nextContextObject) - data->nextContextObject->prevContextObject = data->prevContextObject; - if (data->prevContextObject) - *data->prevContextObject = data->nextContextObject; - else if (data->outerContext->contextObjects == data) - data->outerContext->contextObjects = data->nextContextObject; - } - - data->outerContext = this; - - data->nextContextObject = contextObjects; - if (data->nextContextObject) - data->nextContextObject->prevContextObject = &data->nextContextObject; - data->prevContextObject = &contextObjects; - contextObjects = data; -} - -void QQmlContextData::setIdProperty(int idx, QObject *obj) -{ - idValues[idx] = obj; - idValues[idx].context = this; -} - -QString QQmlContextData::findObjectId(const QObject *obj) const -{ - const QV4::IdentifierHash &properties = propertyNames(); - if (propertyNameCache.isEmpty()) - return QString(); - - for (int ii = 0; ii < idValueCount; ii++) { - if (idValues[ii] == obj) - return properties.findId(ii); - } - - if (publicContext) { - QQmlContextPrivate *p = QQmlContextPrivate::get(publicContext); - for (int ii = 0; ii < p->propertyValues.count(); ++ii) - if (p->propertyValues.at(ii) == QVariant::fromValue(const_cast<QObject *>(obj))) - return properties.findId(ii); - } - - if (linkedContext) - return linkedContext->findObjectId(obj); - return QString(); -} - -QQmlContext *QQmlContextData::asQQmlContext() -{ - if (!publicContext) - publicContext = new QQmlContext(this); - return publicContext; -} - -QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() -{ - return QQmlContextPrivate::get(asQQmlContext()); -} - -void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex) -{ - typeCompilationUnit = unit; - componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex; - Q_ASSERT(!idValues); - idValueCount = typeCompilationUnit->objectAt(componentObjectIndex)->nNamedObjectsInComponent; - idValues = new ContextGuard[idValueCount]; -} - -const QV4::IdentifierHash &QQmlContextData::propertyNames() const -{ - if (propertyNameCache.isEmpty()) { - if (typeCompilationUnit) - propertyNameCache = typeCompilationUnit->namedObjectsPerComponent(componentObjectIndex); - else - propertyNameCache = QV4::IdentifierHash(engine->handle()); - } - return propertyNameCache; -} - -QV4::IdentifierHash &QQmlContextData::detachedPropertyNames() -{ - propertyNames(); - propertyNameCache.detach(); - return propertyNameCache; + setPropertyValue(idx, QVariant::fromValue<QObject *>(nullptr)); + QMetaObject::activate(q_func(), notifyIndex(), idx, nullptr); } -QUrl QQmlContextData::url() const +void QQmlContextPrivate::emitDestruction() { - if (typeCompilationUnit) - return typeCompilationUnit->finalUrl(); - return baseUrl; + m_data->emitDestruction(); } -QString QQmlContextData::urlString() const +// m_data is owned by the public context. When the public context is reset to nullptr, it will be +// deref'd. It's OK to pass a half-created publicContext here. We will not dereference it during +// construction. +QQmlContextPrivate::QQmlContextPrivate( + QQmlContext *publicContext, QQmlContextData *parent, QQmlEngine *engine) : + m_data(new QQmlContextData(QQmlContextData::OwnedByPublicContext, publicContext, + parent, engine)) { - if (typeCompilationUnit) - return typeCompilationUnit->finalUrlString(); - return baseUrlString; + Q_ASSERT(publicContext != nullptr); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h index 7ed70c7619..f12a94ef9d 100644 --- a/src/qml/qml/qqmlcontext.h +++ b/src/qml/qml/qqmlcontext.h @@ -100,7 +100,7 @@ private: friend class QQmlComponentPrivate; friend class QQmlScriptPrivate; friend class QQmlContextData; - QQmlContext(QQmlContextData *); + QQmlContext(QQmlContextPrivate &dd, QObject *parent = nullptr); QQmlContext(QQmlEngine *, bool); Q_DISABLE_COPY(QQmlContext) }; diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index f93393a11b..ca49cbf963 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -51,342 +51,73 @@ // We mean it. // -#include "qqmlcontext.h" +#include <QtCore/qlist.h> +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> +#include <QtCore/qpointer.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmllist.h> -#include "qqmldata_p.h" -#include "qqmltypenamecache_p.h" -#include "qqmlnotifier_p.h" -#include "qqmllist.h" - -#include <QtCore/qhash.h> -#include <QtQml/qjsvalue.h> -#include <QtCore/qset.h> - -#include <private/qobject_p.h> -#include <private/qflagpointer_p.h> -#include <private/qqmlguard_p.h> - -#include <private/qv4executablecompilationunit_p.h> -#include <private/qv4identifier_p.h> +#include <QtCore/private/qobject_p.h> +#include <QtQml/private/qqmlrefcount_p.h> QT_BEGIN_NAMESPACE -class QQmlContext; -class QQmlExpression; -class QQmlEngine; -class QQmlExpression; -class QQmlExpressionPrivate; -class QQmlJavaScriptExpression; class QQmlContextData; -class QQmlGuardedContextData; -class QQmlIncubatorPrivate; class QQmlContextPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QQmlContext) public: - QQmlContextPrivate(); - - QQmlContextData *data; - - QList<QVariant> propertyValues; - int notifyIndex; - static QQmlContextPrivate *get(QQmlContext *context) { return static_cast<QQmlContextPrivate *>(QObjectPrivate::get(context)); } + static QQmlContext *get(QQmlContextPrivate *context) { return static_cast<QQmlContext *>(context->q_func()); } - // Only used for debugging - QList<QPointer<QObject> > instances; - static int context_count(QQmlListProperty<QObject> *); static QObject *context_at(QQmlListProperty<QObject> *, int); void dropDestroyedQObject(const QString &name, QObject *destroyed); -}; - -class QQmlComponentAttached; - -class Q_QML_PRIVATE_EXPORT QQmlContextData -{ -public: - QQmlContextData(); - QQmlContextData(QQmlContext *); - void emitDestruction(); - void clearContext(); - void clearContextRecursively(); - void invalidate(); - - inline bool isValid() const { - return engine && (!isInternal || !contextObject || !QObjectPrivate::get(contextObject)->wasDeleted); - } - - // My parent context and engine - QQmlContextData *parent = nullptr; - QQmlEngine *engine; - - void setParent(QQmlContextData *, bool stronglyReferencedByParent = false); - void refreshExpressions(); - - void addObject(QQmlData *data); - - QUrl resolvedUrl(const QUrl &); - - // My containing QQmlContext. If isInternal is true this owns publicContext. - // If internal is false publicContext owns this. - QQmlContext *asQQmlContext(); - QQmlContextPrivate *asQQmlContextPrivate(); - quint32 refCount = 0; - quint32 isInternal:1; - 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 stronglyReferencedByParent:1; - quint32 hasExtraObject:1; // used in QQmlDelegateModelItem::dataForObject to find the corresponding QQmlDelegateModelItem of an object - quint32 dummy:24; - QQmlContext *publicContext; - union { - // The incubator that is constructing this context if any - QQmlIncubatorPrivate *incubator; - // a pointer to extra data, currently only used in QQmlDelegateModel - QObject *extraObject; - }; + int notifyIndex() const { return m_notifyIndex; } + void setNotifyIndex(int notifyIndex) { m_notifyIndex = notifyIndex; } - // Compilation unit for contexts that belong to a compiled type. - QQmlRefPointer<QV4::ExecutableCompilationUnit> typeCompilationUnit; + int numPropertyValues() const { return m_propertyValues.count(); } + void appendPropertyValue(const QVariant &value) { m_propertyValues.append(value); } + void setPropertyValue(int index, const QVariant &value) { m_propertyValues[index] = value; } + QVariant propertyValue(int index) const { return m_propertyValues[index]; } - // object index in CompiledData::Unit to component that created this context - int componentObjectIndex; - - void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex); - - // flag indicates whether the context owns the cache (after mutation) or not. - mutable QV4::IdentifierHash propertyNameCache; - const QV4::IdentifierHash &propertyNames() const; - QV4::IdentifierHash &detachedPropertyNames(); - - // Context object - QObject *contextObject; - - // Any script blocks that exist on this context - QV4::PersistentValue importedScripts; // This is a JS Array - - QUrl baseUrl; - QString baseUrlString; - - QUrl url() const; - QString urlString() const; - - // List of imports that apply to this context - QQmlRefPointer<QQmlTypeNameCache> imports; - - // My children - QQmlContextData *childContexts = nullptr; - - // My peers in parent's childContexts list - QQmlContextData *nextChild; - QQmlContextData **prevChild; - - // Expressions that use this context - QQmlJavaScriptExpression *expressions; - - // Doubly-linked list of objects that are owned by this context - QQmlData *contextObjects; - - // Doubly-linked list of context guards (XXX merge with contextObjects) - QQmlGuardedContextData *contextGuards = nullptr; - - // id guards - struct ContextGuard : public QQmlGuard<QObject> + QList<QPointer<QObject>> instances() const { return m_instances; } + void appendInstance(QObject *instance) { m_instances.append(instance); } + void cleanInstances() { - inline ContextGuard(); - inline ContextGuard &operator=(QObject *obj); - inline void objectDestroyed(QObject *) override; - - inline bool wasSet() const; - - QFlagPointer<QQmlContextData> context; - QQmlNotifier bindings; - }; - ContextGuard *idValues; - int idValueCount; - void setIdProperty(int, QObject *); - - // Linked contexts. this owns linkedContext. - QQmlContextDataRef linkedContext; - - // Linked list of uses of the Component attached property in this - // context - QQmlComponentAttached *componentAttached; - - // Return the outermost id for obj, if any. - QString findObjectId(const QObject *obj) const; - - static QQmlContextData *get(QQmlContext *context) { - return QQmlContextPrivate::get(context)->data; + for (auto it = m_instances.begin(); it != m_instances.end(); + it->isNull() ? (it = m_instances.erase(it)) : ++it) {} } -private: - friend class QQmlContextDataRef; - friend class QQmlContext; // needs to do manual refcounting :/ - void refreshExpressionsRecursive(bool isGlobal); - void refreshExpressionsRecursive(QQmlJavaScriptExpression *); - ~QQmlContextData(); - void destroy(); -}; - - -class QQmlGuardedContextData -{ -public: - 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) { - setContextData(d); return *this; - } + void emitDestruction(); private: - QQmlGuardedContextData &operator=(const QQmlGuardedContextData &) = delete; - QQmlGuardedContextData(const QQmlGuardedContextData &) = delete; friend class QQmlContextData; - inline void clear(); - - QQmlContextData *m_contextData = nullptr; - QQmlGuardedContextData *m_next = nullptr; - QQmlGuardedContextData **m_prev = nullptr; -}; - - -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 = nullptr; - m_next = nullptr; - m_prev = nullptr; - } -} - -QQmlContextDataRef::QQmlContextDataRef() - : m_contextData(nullptr) -{ -} - -QQmlContextDataRef::QQmlContextDataRef(const QQmlContextDataRef &other) - : m_contextData(other.m_contextData) -{ - if (m_contextData) - ++m_contextData->refCount; -} - -QQmlContextDataRef::QQmlContextDataRef(QQmlContextData *data) - : m_contextData(data) -{ - if (m_contextData) - ++m_contextData->refCount; -} - -QQmlContextDataRef::~QQmlContextDataRef() -{ - clear(); -} - -void QQmlContextDataRef::setContextData(QQmlContextData *contextData) -{ - if (m_contextData == contextData) - return; - clear(); - - if (contextData) { - m_contextData = contextData; - ++m_contextData->refCount; - } -} - -QQmlContextData *QQmlContextDataRef::contextData() const -{ - return m_contextData; -} - -void QQmlContextDataRef::clear() -{ - if (m_contextData && !--m_contextData->refCount) - m_contextData->destroy(); - m_contextData = nullptr; -} + QQmlContextPrivate(QQmlContextData *data) : m_data(data) {} + QQmlContextPrivate(QQmlContext *publicContext, QQmlContextData *parent, + QQmlEngine *engine = nullptr); -QQmlContextDataRef & -QQmlContextDataRef::operator=(QQmlContextData *d) -{ - setContextData(d); - return *this; -} - -QQmlContextDataRef & -QQmlContextDataRef::operator=(const QQmlContextDataRef &other) -{ - setContextData(other.m_contextData); - return *this; -} + // Intentionally a bare pointer. QQmlContextData knows whether it owns QQmlContext or vice + // versa. If QQmlContext is the owner, QQmlContextData keeps an extra ref for its publicContext. + // The publicContext ref is released when doing QQmlContextData::setPublicContext(nullptr). + QQmlContextData *m_data; -QQmlContextData::ContextGuard::ContextGuard() -: context(nullptr) -{ -} - -QQmlContextData::ContextGuard &QQmlContextData::ContextGuard::operator=(QObject *obj) -{ - QQmlGuard<QObject>::operator=(obj); - context.setFlag(); - bindings.notify(); // For alias connections - return *this; -} + QList<QVariant> m_propertyValues; + int m_notifyIndex = -1; -void QQmlContextData::ContextGuard::objectDestroyed(QObject *) -{ - if (context->contextObject && !QObjectPrivate::get(context->contextObject)->wasDeleted) - bindings.notify(); -} - -bool QQmlContextData::ContextGuard::wasSet() const -{ - return context.flag(); -} + // Only used for debugging + QList<QPointer<QObject>> m_instances; +}; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontextdata.cpp b/src/qml/qml/qqmlcontextdata.cpp new file mode 100644 index 0000000000..33be23b14d --- /dev/null +++ b/src/qml/qml/qqmlcontextdata.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcontextdata_p.h" + +#include <QtQml/qqmlengine.h> +#include <QtQml/private/qqmlcomponentattached_p.h> +#include <QtQml/private/qqmljavascriptexpression_p.h> +#include <QtQml/private/qqmlguardedcontextdata_p.h> + +QT_BEGIN_NAMESPACE + +QUrl QQmlContextData::resolvedUrl(const QUrl &src) +{ + QUrl resolved; + if (src.isRelative() && !src.isEmpty()) { + QQmlRefPointer<QQmlContextData> ctxt = this; + do { + if (ctxt->url().isValid()) + break; + else + ctxt = ctxt->parent(); + } while (ctxt); + + if (ctxt) + resolved = ctxt->url().resolved(src); + else if (m_engine) + resolved = m_engine->baseUrl().resolved(src); + } else { + resolved = src; + } + + if (resolved.isEmpty()) //relative but no ctxt + return resolved; + + return m_engine ? m_engine->interceptUrl(resolved, QQmlAbstractUrlInterceptor::UrlString) + : resolved; +} + +void QQmlContextData::emitDestruction() +{ + if (!m_hasEmittedDestruction) { + m_hasEmittedDestruction = true; + + // Emit the destruction signal - must be emitted before invalidate so that the + // context is still valid if bindings or resultant expression evaluation requires it + if (m_engine) { + while (m_componentAttacheds) { + QQmlComponentAttached *attached = m_componentAttacheds; + attached->removeFromList(); + emit attached->destruction(); + } + + for (QQmlContextData *child = m_childContexts; child; child = child->m_nextChild) + child->emitDestruction(); + } + } +} + +void QQmlContextData::invalidate() +{ + emitDestruction(); + + while (m_childContexts) { + Q_ASSERT(m_childContexts != this); + m_childContexts->invalidate(); + } + + if (m_prevChild) { + *m_prevChild = m_nextChild; + if (m_nextChild) m_nextChild->m_prevChild = m_prevChild; + m_nextChild = nullptr; + m_prevChild = nullptr; + } + + m_importedScripts.clear(); + + m_engine = nullptr; + clearParent(); +} + +void QQmlContextData::clearContextRecursively() +{ + clearContext(); + + for (auto ctxIt = m_childContexts; ctxIt; ctxIt = ctxIt->m_nextChild) + ctxIt->clearContextRecursively(); +} + +void QQmlContextData::clearContext() +{ + emitDestruction(); + + QQmlJavaScriptExpression *expression = m_expressions; + while (expression) { + QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression; + + expression->m_prevExpression = nullptr; + expression->m_nextExpression = nullptr; + + expression->setContext(nullptr); + + expression = nextExpression; + } + m_expressions = nullptr; +} + +QQmlContextData::~QQmlContextData() +{ + Q_ASSERT(refCount() == 0); + m_linkedContext = nullptr; + + // avoid recursion + addref(); + if (m_engine) + invalidate(); + + Q_ASSERT(refCount() == 1); + clearContext(); + Q_ASSERT(refCount() == 1); + + while (m_ownedObjects) { + QQmlData *co = m_ownedObjects; + m_ownedObjects = m_ownedObjects->nextContextObject; + + if (co->context == this) + co->context = nullptr; + co->outerContext = nullptr; + co->nextContextObject = nullptr; + co->prevContextObject = nullptr; + } + Q_ASSERT(refCount() == 1); + + QQmlGuardedContextData *contextGuard = m_contextGuards; + while (contextGuard) { + QQmlGuardedContextData *next = contextGuard->next(); + next->reset(); + contextGuard = next; + } + m_contextGuards = nullptr; + Q_ASSERT(refCount() == 1); + + delete [] m_idValues; + m_idValues = nullptr; + + Q_ASSERT(refCount() == 1); + if (m_publicContext) + delete m_publicContext; + + Q_ASSERT(refCount() == 1); +} + +void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression) +{ + QQmlJavaScriptExpression::DeleteWatcher w(expression); + + if (expression->m_nextExpression) + refreshExpressionsRecursive(expression->m_nextExpression); + + if (!w.wasDeleted()) + expression->refresh(); +} + +void QQmlContextData::refreshExpressionsRecursive(bool isGlobal) +{ + // For efficiency, we try and minimize the number of guards we have to create + if (hasExpressionsToRun(isGlobal) && (m_nextChild || m_childContexts)) { + QQmlGuardedContextData guard(this); + + if (m_childContexts) + m_childContexts->refreshExpressionsRecursive(isGlobal); + + if (guard.isNull()) return; + + if (m_nextChild) + m_nextChild->refreshExpressionsRecursive(isGlobal); + + if (guard.isNull()) return; + + if (hasExpressionsToRun(isGlobal)) + refreshExpressionsRecursive(m_expressions); + + } else if (hasExpressionsToRun(isGlobal)) { + refreshExpressionsRecursive(m_expressions); + } else if (m_nextChild && m_childContexts) { + QQmlGuardedContextData guard(this); + m_childContexts->refreshExpressionsRecursive(isGlobal); + if (!guard.isNull() && m_nextChild) + m_nextChild->refreshExpressionsRecursive(isGlobal); + } else if (m_nextChild) { + m_nextChild->refreshExpressionsRecursive(isGlobal); + } else if (m_childContexts) { + m_childContexts->refreshExpressionsRecursive(isGlobal); + } +} + +// Refreshes all expressions that could possibly depend on this context. Refreshing flushes all +// context-tree dependent caches in the expressions, and should occur every time the context tree +// *structure* (not values) changes. +void QQmlContextData::refreshExpressions() +{ + bool isGlobal = (m_parent == nullptr); + + // For efficiency, we try and minimize the number of guards we have to create + if (hasExpressionsToRun(isGlobal) && m_childContexts) { + QQmlGuardedContextData guard(this); + m_childContexts->refreshExpressionsRecursive(isGlobal); + if (!guard.isNull() && hasExpressionsToRun(isGlobal)) + refreshExpressionsRecursive(m_expressions); + } else if (hasExpressionsToRun(isGlobal)) { + refreshExpressionsRecursive(m_expressions); + } else if (m_childContexts) { + m_childContexts->refreshExpressionsRecursive(isGlobal); + } +} + +void QQmlContextData::addOwnedObject(QQmlData *data) +{ + if (data->outerContext) { + if (data->nextContextObject) + data->nextContextObject->prevContextObject = data->prevContextObject; + if (data->prevContextObject) + *data->prevContextObject = data->nextContextObject; + else if (data->outerContext->m_ownedObjects == data) + data->outerContext->m_ownedObjects = data->nextContextObject; + } + + data->outerContext = this; + + data->nextContextObject = m_ownedObjects; + if (data->nextContextObject) + data->nextContextObject->prevContextObject = &data->nextContextObject; + data->prevContextObject = &m_ownedObjects; + m_ownedObjects = data; +} + +void QQmlContextData::setIdValue(int idx, QObject *obj) +{ + m_idValues[idx] = obj; + m_idValues[idx].setContext(this); +} + +QString QQmlContextData::findObjectId(const QObject *obj) const +{ + const QV4::IdentifierHash &properties = propertyNames(); + if (m_propertyNameCache.isEmpty()) + return QString(); + + for (int ii = 0; ii < m_idValueCount; ii++) { + if (m_idValues[ii] == obj) + return properties.findId(ii); + } + + if (m_publicContext) { + QQmlContextPrivate *p = QQmlContextPrivate::get(m_publicContext); + for (int ii = 0; ii < p->numPropertyValues(); ++ii) + if (p->propertyValue(ii) == QVariant::fromValue(const_cast<QObject *>(obj))) + return properties.findId(ii); + } + + if (m_linkedContext) + return m_linkedContext->findObjectId(obj); + return QString(); +} + +QQmlContext *QQmlContextData::asQQmlContext() +{ + if (!m_publicContext) + m_publicContext = new QQmlContext(*new QQmlContextPrivate(this)); + return m_publicContext; +} + +QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() +{ + return QQmlContextPrivate::get(asQQmlContext()); +} + +void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit, int subComponentIndex) +{ + m_typeCompilationUnit = unit; + m_componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex; + Q_ASSERT(!m_idValues); + m_idValueCount = m_typeCompilationUnit->objectAt(m_componentObjectIndex) + ->nNamedObjectsInComponent; + m_idValues = new ContextGuard[m_idValueCount]; +} + +void QQmlContextData::addComponentAttached(QQmlComponentAttached *attached) +{ + attached->insertIntoList(&m_componentAttacheds); +} + +void QQmlContextData::addExpression(QQmlJavaScriptExpression *expression) +{ + expression->insertIntoList(&m_expressions); +} + +QV4::IdentifierHash QQmlContextData::propertyNames() const +{ + if (m_propertyNameCache.isEmpty()) { + if (m_typeCompilationUnit) + m_propertyNameCache = m_typeCompilationUnit->namedObjectsPerComponent(m_componentObjectIndex); + else + m_propertyNameCache = QV4::IdentifierHash(m_engine->handle()); + } + return m_propertyNameCache; +} + +QV4::IdentifierHash *QQmlContextData::detachedPropertyNames() +{ + propertyNames(); + m_propertyNameCache.detach(); + return &m_propertyNameCache; +} + +QUrl QQmlContextData::url() const +{ + if (m_typeCompilationUnit) + return m_typeCompilationUnit->finalUrl(); + return m_baseUrl; +} + +QString QQmlContextData::urlString() const +{ + if (m_typeCompilationUnit) + return m_typeCompilationUnit->finalUrlString(); + return m_baseUrlString; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h new file mode 100644 index 0000000000..e31a45dea4 --- /dev/null +++ b/src/qml/qml/qqmlcontextdata_p.h @@ -0,0 +1,427 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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/qqmlglobal_p.h> +#include <QtQml/private/qqmlcontext_p.h> +#include <QtQml/private/qqmlguard_p.h> +#include <QtQml/private/qqmltypenamecache_p.h> +#include <QtQml/private/qv4identifier_p.h> +#include <QtQml/private/qv4executablecompilationunit_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlComponentAttached; +class QQmlGuardedContextData; +class QQmlJavaScriptExpression; +class QQmlIncubatorPrivate; + +class Q_QML_PRIVATE_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; } + + QUrl resolvedUrl(const QUrl &); + + // My containing QQmlContext. If isInternal is true this owns publicContext. + // If internal is false publicContext owns this. + QQmlContext *asQQmlContext(); + QQmlContextPrivate *asQQmlContextPrivate(); + + QObject *contextObject() const { return m_contextObject; } + void setContextObject(QObject *contextObject) { m_contextObject = contextObject; } + + 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(); + } + } + + QV4::IdentifierHash propertyNames() const; + QV4::IdentifierHash *detachedPropertyNames(); + + 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); + +private: + friend class QQmlGuardedContextData; + friend class QQmlContextPrivate; + + enum Ownership { + RefCounted, + OwnedByParent, + OwnedByPublicContext + }; + + // id guards + struct ContextGuard : public QQmlGuard<QObject> + { + inline ContextGuard() : m_context(nullptr) {} + inline ContextGuard &operator=(QObject *obj); + inline void objectDestroyed(QObject *) override; + + inline bool wasSet() const; + + QQmlNotifier *bindings() { return &m_bindings; } + void setContext(const QQmlRefPointer<QQmlContextData> &context) + { + m_context = context.data(); + } + + private: + // Not refcounted, as it always belongs to the QQmlContextData. + QFlagPointer<QQmlContextData> 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 *); + + // 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 + 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.setFlag(); + m_bindings.notify(); // For alias connections + return *this; +} + +void QQmlContextData::ContextGuard::objectDestroyed(QObject *) +{ + if (QObject *contextObject = m_context->contextObject()) { + if (!QObjectPrivate::get(contextObject)->wasDeleted) + m_bindings.notify(); + } +} + +bool QQmlContextData::ContextGuard::wasSet() const +{ + return m_context.flag(); +} + +QT_END_NAMESPACE + +#endif // QQMLCONTEXTDATA_P_H diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index ee31cb38d9..1c238767d8 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -82,34 +82,6 @@ struct Binding; } } -// 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. @@ -195,11 +167,11 @@ public: bool signalHasEndpoint(int index) const; void disconnectNotifiers(); - // The context that created the C++ object + // The context that created the C++ object; not refcounted to prevent cycles QQmlContextData *context = nullptr; - // The outermost context in which this object lives + // The outermost context in which this object lives; not refcounted to prevent cycles QQmlContextData *outerContext = nullptr; - QQmlContextDataRef ownContext; + QQmlRefPointer<QQmlContextData> ownContext; QQmlAbstractBinding *bindings; QQmlBoundSignal *signalHandlers; @@ -226,14 +198,19 @@ public: ~DeferredData(); unsigned int deferredIdx; QMultiHash<int, const QV4::CompiledData::Binding *> bindings; - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;//Not always the same as the other compilation unit - QQmlContextData *context;//Could be either context or outerContext + + // Not always the same as the other compilation unit + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; + + // Could be either context or outerContext + QQmlRefPointer<QQmlContextData> context; Q_DISABLE_COPY(DeferredData); }; QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; QVector<DeferredData *> deferredData; - void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, QQmlContextData *); + void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &, + const QQmlRefPointer<QQmlContextData> &); void releaseDeferredData(); QV4::WeakValue jsWrapper; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 0d9326b9a9..eb23b6e256 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -665,20 +665,21 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) { if (QQmlData *d = QQmlData::get(o)) { if (d->ownContext) { - for (QQmlContextData *lc = d->ownContext->linkedContext; lc; lc = lc->linkedContext) { + for (QQmlRefPointer<QQmlContextData> lc = d->ownContext->linkedContext().data(); lc; + lc = lc->linkedContext()) { lc->invalidate(); - if (lc->contextObject == o) - lc->contextObject = nullptr; + if (lc->contextObject() == o) + lc->setContextObject(nullptr); } d->ownContext->invalidate(); - if (d->ownContext->contextObject == o) - d->ownContext->contextObject = nullptr; + if (d->ownContext->contextObject() == o) + d->ownContext->setContextObject(nullptr); d->ownContext = nullptr; d->context = nullptr; } - if (d->outerContext && d->outerContext->contextObject == o) - d->outerContext->contextObject = nullptr; + if (d->outerContext && d->outerContext->contextObject() == o) + d->outerContext->setContextObject(nullptr); // Mark this object as in the process of deletion to // prevent it resolving in bindings @@ -849,10 +850,10 @@ void QQmlData::setQueuedForDeletion(QObject *object) if (object) { if (QQmlData *ddata = QQmlData::get(object)) { if (ddata->ownContext) { - Q_ASSERT(ddata->ownContext == ddata->context); + Q_ASSERT(ddata->ownContext.data() == ddata->context); ddata->context->emitDestruction(); - if (ddata->ownContext->contextObject == object) - ddata->ownContext->contextObject = nullptr; + if (ddata->ownContext->contextObject() == object) + ddata->ownContext->setContextObject(nullptr); ddata->ownContext = nullptr; ddata->context = nullptr; } @@ -982,7 +983,7 @@ QQmlEngine::~QQmlEngine() // Emit onDestruction signals for the root context before // we destroy the contexts, engine, Singleton Types etc. that // may be required to handle the destruction signal. - QQmlContextData::get(rootContext())->emitDestruction(); + QQmlContextPrivate::get(rootContext())->emitDestruction(); // clean up all singleton type instances which we own. // we do this here and not in the private dtor since otherwise a crash can @@ -1422,10 +1423,10 @@ QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId) void QQmlEngine::retranslate() { Q_D(QQmlEngine); - QQmlContextData *context = QQmlContextData::get(d->rootContext)->childContexts; - while (context) { + for (QQmlRefPointer<QQmlContextData> context + = QQmlContextData::get(d->rootContext)->childContexts(); + context; context = context->nextChild()) { context->refreshExpressions(); - context = context->nextChild; } } @@ -1469,10 +1470,10 @@ void QQmlEngine::setContextForObject(QObject *object, QQmlContext *context) return; } - QQmlContextData *contextData = QQmlContextData::get(context); + QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context); Q_ASSERT(data->context == nullptr); - data->context = contextData; - contextData->addObject(data); + data->context = contextData.data(); + contextData->addOwnedObject(data); } /*! @@ -1576,7 +1577,7 @@ void qmlExecuteDeferred(QObject *object) QQmlData *data = QQmlData::get(object); if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine()); QQmlComponentPrivate::DeferredState state; QQmlComponentPrivate::beginDeferred(ep, object, &state); @@ -1598,7 +1599,7 @@ QQmlEngine *qmlEngine(const QObject *obj) QQmlData *data = QQmlData::get(obj, false); if (!data || !data->context) return nullptr; - return data->context->engine; + return data->context->engine(); } static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlData *data, @@ -1786,7 +1787,9 @@ void QQmlData::NotifyList::layout() todo = nullptr; } -void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *context) +void QQmlData::deferData( + int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, + const QQmlRefPointer<QQmlContextData> &context) { QQmlData::DeferredData *deferData = new QQmlData::DeferredData; deferData->deferredIdx = objectIndex; @@ -1880,8 +1883,8 @@ void QQmlData::destroyed(QObject *object) nextContextObject->prevContextObject = prevContextObject; if (prevContextObject) *prevContextObject = nextContextObject; - else if (outerContext && outerContext->contextObjects == this) - outerContext->contextObjects = nextContextObject; + else if (outerContext && outerContext->ownedObjects() == this) + outerContext->setOwnedObjects(nextContextObject); QQmlAbstractBinding *binding = bindings; while (binding) { diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 23c69651ed..f8b8b187a5 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -59,7 +59,7 @@ #include "qqml.h" #include "qqmlvaluetype_p.h" #include "qqmlcontext.h" -#include "qqmlcontext_p.h" +#include "qqmlcontextdata_p.h" #include "qqmlexpression.h" #include "qqmlproperty_p.h" #include "qqmlmetatype_p.h" @@ -205,7 +205,7 @@ public: QIntrusiveList<Incubator, &Incubator::next> incubatorList; unsigned int incubatorCount; QQmlIncubationController *incubationController; - void incubate(QQmlIncubator &, QQmlContextData *); + void incubate(QQmlIncubator &, const QQmlRefPointer<QQmlContextData> &); // These methods may be called from any thread inline bool isEngineThread() const; @@ -255,7 +255,7 @@ public: inline static QQmlEnginePrivate *get(QQmlEngine *e); inline static const QQmlEnginePrivate *get(const QQmlEngine *e); inline static QQmlEnginePrivate *get(QQmlContext *c); - inline static QQmlEnginePrivate *get(QQmlContextData *c); + inline static QQmlEnginePrivate *get(const QQmlRefPointer<QQmlContextData> &c); inline static QQmlEngine *get(QQmlEnginePrivate *p); inline static QQmlEnginePrivate *get(QV4::ExecutionEngine *e); @@ -442,14 +442,24 @@ const QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlEngine *e) return e ? e->d_func() : nullptr; } +template<typename Context> +QQmlEnginePrivate *contextEngine(const Context &context) +{ + if (!context) + return nullptr; + if (QQmlEngine *engine = context->engine()) + return QQmlEnginePrivate::get(engine); + return nullptr; +} + QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlContext *c) { - return (c && c->engine()) ? QQmlEnginePrivate::get(c->engine()) : nullptr; + return contextEngine(c); } -QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlContextData *c) +QQmlEnginePrivate *QQmlEnginePrivate::get(const QQmlRefPointer<QQmlContextData> &c) { - return (c && c->engine) ? QQmlEnginePrivate::get(c->engine) : nullptr; + return contextEngine(c); } QQmlEngine *QQmlEnginePrivate::get(QQmlEnginePrivate *p) diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index f6a5afb891..5864c3245c 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -63,7 +63,8 @@ QQmlExpressionPrivate::~QQmlExpressionPrivate() { } -void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QObject *me) +void QQmlExpressionPrivate::init(const QQmlRefPointer<QQmlContextData> &ctxt, const QString &expr, + QObject *me) { expression = expr; @@ -72,10 +73,11 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QOb expressionFunctionValid = false; } -void QQmlExpressionPrivate::init(QQmlContextData *ctxt, QV4::Function *runtimeFunction, QObject *me) +void QQmlExpressionPrivate::init(const QQmlRefPointer<QQmlContextData> &ctxt, + QV4::Function *runtimeFunction, QObject *me) { expressionFunctionValid = true; - QV4::ExecutionEngine *engine = ctxt->engine->handle(); + QV4::ExecutionEngine *engine = ctxt->engine()->handle(); QV4::Scope scope(engine); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(engine->rootContext(), ctxt, me)); setupFunction(qmlContext, runtimeFunction); @@ -144,20 +146,24 @@ QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid())) return; - QQmlContextData *evalCtxtData = QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context); + QQmlRefPointer<QQmlContextData> evalCtxtData + = QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context); QObject *scopeObject = scope ? scope : scriptPrivate->scope; QV4::Function *runtimeFunction = nullptr; if (scriptPrivate->context) { - QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context); + QQmlRefPointer<QQmlContextData> ctxtdata = QQmlContextData::get(scriptPrivate->context); QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine()); - if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit) { + if (engine + && ctxtdata + && !ctxtdata->urlString().isEmpty() + && ctxtdata->typeCompilationUnit()) { d->url = ctxtdata->urlString(); d->line = scriptPrivate->lineNumber; d->column = scriptPrivate->columnNumber; if (scriptPrivate->bindingId != QQmlBinding::Invalid) - runtimeFunction = ctxtdata->typeCompilationUnit->runtimeFunctions.at(scriptPrivate->bindingId); + runtimeFunction = ctxtdata->typeCompilationUnit()->runtimeFunctions.at(scriptPrivate->bindingId); } } @@ -175,10 +181,8 @@ QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt If specified, the \a scope object's properties will also be in scope during the expression's execution. */ -QQmlExpression::QQmlExpression(QQmlContext *ctxt, - QObject *scope, - const QString &expression, - QObject *parent) +QQmlExpression::QQmlExpression(QQmlContext *ctxt, QObject *scope, const QString &expression, + QObject *parent) : QObject(*new QQmlExpressionPrivate, parent) { Q_D(QQmlExpression); @@ -188,12 +192,10 @@ QQmlExpression::QQmlExpression(QQmlContext *ctxt, /*! \internal */ -QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope, - const QString &expression) -: QObject(*new QQmlExpressionPrivate, nullptr) +QQmlExpression::QQmlExpression(QQmlExpressionPrivate &dd, QObject *parent) : QObject(dd, parent) { - Q_D(QQmlExpression); - d->init(ctxt, expression, scope); +// Q_D(QQmlExpression); +// d->init(QQmlContextData::get(ctxt), expression, scope); } /*! @@ -210,7 +212,7 @@ QQmlExpression::~QQmlExpression() QQmlEngine *QQmlExpression::engine() const { Q_D(const QQmlExpression); - return d->context()?d->context()->engine:nullptr; + return d->engine(); } /*! @@ -220,8 +222,7 @@ QQmlEngine *QQmlExpression::engine() const QQmlContext *QQmlExpression::context() const { Q_D(const QQmlExpression); - QQmlContextData *data = d->context(); - return data?data->asQQmlContext():nullptr; + return d->publicContext(); } /*! @@ -265,7 +266,7 @@ QVariant QQmlExpressionPrivate::value(bool *isUndefined) { Q_Q(QQmlExpression); - if (!context() || !context()->isValid()) { + if (!hasValidContext()) { qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context"); return QVariant(); } diff --git a/src/qml/qml/qqmlexpression.h b/src/qml/qml/qqmlexpression.h index 0eceeb12e1..c9719aec52 100644 --- a/src/qml/qml/qqmlexpression.h +++ b/src/qml/qml/qqmlexpression.h @@ -90,7 +90,7 @@ Q_SIGNALS: void valueChanged(); private: - QQmlExpression(QQmlContextData *, QObject *, const QString &); + QQmlExpression(QQmlExpressionPrivate &dd, QObject *parent); Q_DISABLE_COPY(QQmlExpression) Q_DECLARE_PRIVATE(QQmlExpression) diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index da10b31b2c..3df839a6a2 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -70,8 +70,8 @@ public: QQmlExpressionPrivate(); ~QQmlExpressionPrivate() override; - void init(QQmlContextData *, const QString &, QObject *); - void init(QQmlContextData *, QV4::Function *runtimeFunction, QObject *); + void init(const QQmlRefPointer<QQmlContextData> &, const QString &, QObject *); + void init(const QQmlRefPointer<QQmlContextData> &, QV4::Function *runtimeFunction, QObject *); QVariant value(bool *isUndefined = nullptr); diff --git a/src/qml/qml/qqmlguardedcontextdata_p.h b/src/qml/qml/qqmlguardedcontextdata_p.h new file mode 100644 index 0000000000..0b24c04305 --- /dev/null +++ b/src/qml/qml/qqmlguardedcontextdata_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLGUARDEDCONTEXTDATA_P_H +#define QQMLGUARDEDCONTEXTDATA_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/qqmlglobal_p.h> +#include <QtQml/private/qqmlcontextdata_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlGuardedContextData +{ + Q_DISABLE_COPY(QQmlGuardedContextData); +public: + QQmlGuardedContextData() = default; + ~QQmlGuardedContextData() { unlink(); } + + QQmlGuardedContextData(QQmlGuardedContextData &&) = default; + QQmlGuardedContextData &operator=(QQmlGuardedContextData &&) = default; + + QQmlGuardedContextData(QQmlRefPointer<QQmlContextData> data) + { + setContextData(std::move(data)); + } + + QQmlGuardedContextData &operator=(QQmlRefPointer<QQmlContextData> d) + { + setContextData(std::move(d)); + return *this; + } + + QQmlRefPointer<QQmlContextData> contextData() const { return m_contextData; } + void setContextData(QQmlRefPointer<QQmlContextData> contextData) + { + if (m_contextData.data() == contextData.data()) + return; + unlink(); + + if (contextData) { + m_contextData = std::move(contextData); + m_next = m_contextData->m_contextGuards; + if (m_next) + m_next->m_prev = &m_next; + + m_contextData->m_contextGuards = this; + m_prev = &m_contextData->m_contextGuards; + } + } + + bool isNull() const { return !m_contextData; } + + operator const QQmlRefPointer<QQmlContextData> &() const { return m_contextData; } + QQmlContextData &operator*() const { return m_contextData.operator*(); } + QQmlContextData *operator->() const { return m_contextData.operator->(); } + + QQmlGuardedContextData *next() const { return m_next; } + + void reset() + { + m_contextData = nullptr; + m_next = nullptr; + m_prev = nullptr; + } + +private: + void unlink() + { + if (m_prev) { + *m_prev = m_next; + if (m_next) + m_next->m_prev = m_prev; + reset(); + } + } + + QQmlRefPointer<QQmlContextData> m_contextData; + QQmlGuardedContextData *m_next = nullptr; + QQmlGuardedContextData **m_prev = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QQMLGUARDEDCONTEXTDATA_P_H diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 0ad013e90b..1f41934c3c 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -45,7 +45,8 @@ #include "qqmlobjectcreator_p.h" #include <private/qqmlcomponent_p.h> -void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext) +void QQmlEnginePrivate::incubate( + QQmlIncubator &i, const QQmlRefPointer<QQmlContextData> &forContext) { QExplicitlySharedDataPointer<QQmlIncubatorPrivate> p(i.d); @@ -59,13 +60,13 @@ void QQmlEnginePrivate::incubate(QQmlIncubator &i, QQmlContextData *forContext) // Need to find the first constructing context and see if it is asynchronous QExplicitlySharedDataPointer<QQmlIncubatorPrivate> parentIncubator; - QQmlContextData *cctxt = forContext; + QQmlRefPointer<QQmlContextData> cctxt = forContext; while (cctxt) { - if (!cctxt->hasExtraObject && cctxt->incubator) { - parentIncubator = cctxt->incubator; + if (QQmlIncubatorPrivate *incubator = cctxt->incubator()) { + parentIncubator = incubator; break; } - cctxt = cctxt->parent; + cctxt = cctxt->parent(); } if (parentIncubator && parentIncubator->isAsynchronous) { @@ -149,8 +150,8 @@ void QQmlIncubatorPrivate::clear() } enginePriv = nullptr; if (!rootContext.isNull()) { - if (!rootContext->hasExtraObject) - rootContext->incubator = nullptr; + if (rootContext->incubator()) + rootContext->setIncubator(nullptr); rootContext = nullptr; } @@ -360,10 +361,8 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) if (watcher.hasRecursed()) return; - QQmlContextData *ctxt = nullptr; - ctxt = creator->finalize(i); - if (ctxt) { - rootContext = ctxt; + if (creator->finalize(i)) { + rootContext = creator->rootContext(); progress = QQmlIncubatorPrivate::Completed; goto finishIncubate; } diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index a674ff274f..b178c6aa29 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -46,7 +46,7 @@ #include <private/qqmlvme_p.h> #include <private/qrecursionwatcher_p.h> #include <private/qqmlengine_p.h> -#include <private/qqmlcontext_p.h> +#include <private/qqmlguardedcontextdata_p.h> // // W A R N I N G diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 6a9ef06159..93af2d7c52 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -134,7 +134,7 @@ QQmlSourceLocation QQmlJavaScriptExpression::sourceLocation() const return QQmlSourceLocation(); } -void QQmlJavaScriptExpression::setContext(QQmlContextData *context) +void QQmlJavaScriptExpression::setContext(const QQmlRefPointer<QQmlContextData> &context) { if (m_prevExpression) { *m_prevExpression = m_nextExpression; @@ -144,15 +144,10 @@ void QQmlJavaScriptExpression::setContext(QQmlContextData *context) m_nextExpression = nullptr; } - m_context = context; + m_context = context.data(); - if (context) { - m_nextExpression = context->expressions; - if (m_nextExpression) - m_nextExpression->m_prevExpression = &m_nextExpression; - m_prevExpression = &context->expressions; - context->expressions = this; - } + if (context) + context->addExpression(this); } QV4::Function *QQmlJavaScriptExpression::function() const @@ -166,7 +161,7 @@ void QQmlJavaScriptExpression::refresh() QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined) { - QV4::ExecutionEngine *v4 = m_context->engine->handle(); + QV4::ExecutionEngine *v4 = m_context->engine()->handle(); QV4::Scope scope(v4); QV4::JSCallData jsCall(scope); @@ -175,7 +170,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined) QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined) { - Q_ASSERT(m_context && m_context->engine); + Q_ASSERT(m_context && m_context->engine()); QV4::Function *v4Function = function(); if (!v4Function) { @@ -184,14 +179,14 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b return QV4::Encode::undefined(); } - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine()); // All code that follows must check with watcher before it accesses data members // incase we have been deleted. DeleteWatcher watcher(this); Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty()); - QQmlPropertyCapture capture(m_context->engine, this, &watcher); + QQmlPropertyCapture capture(m_context->engine(), this, &watcher); QQmlPropertyCapture *lastPropertyCapture = ep->propertyCapture; ep->propertyCapture = notifyOnValueChanged() ? &capture : nullptr; @@ -200,7 +195,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b if (notifyOnValueChanged()) capture.guards.copyAndClearPrepend(activeGuards); - QV4::ExecutionEngine *v4 = m_context->engine->handle(); + QV4::ExecutionEngine *v4 = m_context->engine()->handle(); callData->thisObject = v4->globalObject; if (scopeObject()) { QV4::ReturnedValue scope = QV4::QObjectWrapper::wrap(v4, scopeObject()); @@ -337,10 +332,11 @@ QQmlDelayedError *QQmlJavaScriptExpression::delayedError() } QV4::ReturnedValue -QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObject, - const QString &code, const QString &filename, quint16 line) +QQmlJavaScriptExpression::evalFunction( + const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scopeObject, + const QString &code, const QString &filename, quint16 line) { - QQmlEngine *engine = ctxt->engine; + QQmlEngine *engine = ctxt->engine(); QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); QV4::ExecutionEngine *v4 = engine->handle(); @@ -367,10 +363,11 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObje return result->asReturnedValue(); } -void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject *qmlScope, - const QString &code, const QString &filename, quint16 line) +void QQmlJavaScriptExpression::createQmlBinding( + const QQmlRefPointer<QQmlContextData> &ctxt, QObject *qmlScope, const QString &code, + const QString &filename, quint16 line) { - QQmlEngine *engine = ctxt->engine; + QQmlEngine *engine = ctxt->engine(); QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); QV4::ExecutionEngine *v4 = engine->handle(); diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index eecee08062..6e904f15f5 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -118,10 +118,21 @@ public: virtual QQmlSourceLocation sourceLocation() const; - bool isValid() const { return context() != nullptr; } - - QQmlContextData *context() const { return m_context; } - void setContext(QQmlContextData *context); + bool hasContext() const { return m_context != nullptr; } + bool hasValidContext() const { return m_context && m_context->isValid(); } + QQmlContext *publicContext() const { return m_context ? m_context->asQQmlContext() : nullptr; } + + QQmlRefPointer<QQmlContextData> context() const { return m_context; } + void setContext(const QQmlRefPointer<QQmlContextData> &context); + + void insertIntoList(QQmlJavaScriptExpression **listHead) + { + m_nextExpression = *listHead; + if (m_nextExpression) + m_nextExpression->m_prevExpression = &m_nextExpression; + m_prevExpression = listHead; + *listHead = this; + } QV4::Function *function() const; @@ -146,11 +157,16 @@ public: void clearActiveGuards(); QQmlDelayedError *delayedError(); - static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope, - const QString &code, const QString &filename, - quint16 line); + static QV4::ReturnedValue evalFunction( + const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope, const QString &code, + const QString &filename, quint16 line); + + QQmlEngine *engine() const { return m_context ? m_context->engine() : nullptr; } + bool hasUnresolvedNames() const { return m_context && m_context->hasUnresolvedNames(); } + protected: - void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line); + void createQmlBinding(const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope, + const QString &code, const QString &filename, quint16 line); void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f); void setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit); @@ -173,7 +189,9 @@ private: // m_error:flag1 translationsCapturedDuringEvaluation QFlagPointer<QQmlDelayedError> m_error; + // Not refcounted as the context will clear the expressions when destructed. QQmlContextData *m_context; + QQmlJavaScriptExpression **m_prevExpression; QQmlJavaScriptExpression *m_nextExpression; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index e0187eb75a..8f209311fa 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -67,8 +67,11 @@ QT_USE_NAMESPACE -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, - QQmlIncubatorPrivate *incubator) +QQmlObjectCreator::QQmlObjectCreator( + QQmlRefPointer<QQmlContextData> parentContext, + const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, + const QQmlRefPointer<QQmlContextData> &creationContext, + QQmlIncubatorPrivate *incubator) : phase(Startup) , compilationUnit(compilationUnit) , propertyCaches(&compilationUnit->propertyCaches) @@ -76,7 +79,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR , topLevelCreator(true) , incubator(incubator) { - init(parentContext); + init(std::move(parentContext)); sharedState->componentAttached = nullptr; sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount()); @@ -95,7 +98,10 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR } } -QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState) +QQmlObjectCreator::QQmlObjectCreator( + QQmlRefPointer<QQmlContextData> parentContext, + const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, + QQmlObjectCreatorSharedState *inheritedSharedState) : phase(Startup) , compilationUnit(compilationUnit) , propertyCaches(&compilationUnit->propertyCaches) @@ -103,13 +109,13 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlR , topLevelCreator(false) , incubator(nullptr) { - init(parentContext); + init(std::move(parentContext)); } -void QQmlObjectCreator::init(QQmlContextData *providedParentContext) +void QQmlObjectCreator::init(QQmlRefPointer<QQmlContextData> providedParentContext) { - parentContext = providedParentContext; - engine = parentContext->engine; + parentContext = std::move(providedParentContext); + engine = parentContext->engine(); v4 = engine->handle(); if (compilationUnit && !compilationUnit->engine) @@ -142,7 +148,7 @@ QQmlObjectCreator::~QQmlObjectCreator() } while (sharedState->componentAttached) { QQmlComponentAttached *a = sharedState->componentAttached; - a->rem(); + a->removeFromList(); } } } @@ -151,7 +157,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI { if (phase == CreatingObjectsPhase2) { phase = ObjectsCreated; - return context->contextObject; + return context->contextObject(); } Q_ASSERT(phase == Startup); phase = CreatingObjects; @@ -171,16 +177,15 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI } } - context = new QQmlContextData; - context->isInternal = true; - context->imports = compilationUnit->typeNameCache; + context = QQmlContextData::createRefCounted(parentContext); + context->setInternal(true); + context->setImports(compilationUnit->typeNameCache); context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex); - context->setParent(parentContext); if (!sharedState->rootContext) { sharedState->rootContext = context; - sharedState->rootContext->incubator = incubator; - sharedState->rootContext->isRootObjectInCreation = true; + sharedState->rootContext->setIncubator(incubator); + sharedState->rootContext->setRootObjectInCreation(true); } QV4::Scope scope(v4); @@ -191,14 +196,14 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) { QV4::ScopedObject scripts(scope, v4->newArrayObject(compilationUnit->dependentScripts.count())); - context->importedScripts.set(v4, scripts); + context->setImportedScripts(QV4::PersistentValue(v4, scripts.asReturnedValue())); QV4::ScopedValue v(scope); for (int i = 0; i < compilationUnit->dependentScripts.count(); ++i) { QQmlRefPointer<QQmlScriptData> s = compilationUnit->dependentScripts.at(i); scripts->put(i, (v = s->scriptValueForContext(context))); } } else if (sharedState->creationContext) { - context->importedScripts = sharedState->creationContext->importedScripts; + context->setImportedScripts(sharedState->creationContext->importedScripts()); } QObject *instance = createInstance(objectToCreate, parent, /*isContextObject*/true); @@ -221,18 +226,18 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI if (instance) { if (QQmlEngineDebugService *service = QQmlDebugConnector::service<QQmlEngineDebugService>()) { - if (!parentContext->isInternal) - parentContext->asQQmlContextPrivate()->instances.append(instance); + if (!parentContext->isInternal()) + parentContext->asQQmlContextPrivate()->appendInstance(instance); service->objectCreated(engine, instance); - } else if (!parentContext->isInternal && QQmlDebugConnector::service<QV4DebugService>()) { - parentContext->asQQmlContextPrivate()->instances.append(instance); + } else if (!parentContext->isInternal() && QQmlDebugConnector::service<QV4DebugService>()) { + parentContext->asQQmlContextPrivate()->appendInstance(instance); } } return instance; } -void QQmlObjectCreator::beginPopulateDeferred(QQmlContextData *newContext) +void QQmlObjectCreator::beginPopulateDeferred(const QQmlRefPointer<QQmlContextData> &newContext) { context = newContext; sharedState->rootContext = newContext; @@ -741,7 +746,10 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { - QQmlPropertyData *property = binding->propertyNameIndex != 0 ? _propertyCache->property(stringAt(binding->propertyNameIndex), _qobject, context) : defaultProperty; + QQmlPropertyData *property = binding->propertyNameIndex != 0 + ? _propertyCache->property(stringAt(binding->propertyNameIndex), + _qobject, context) + : defaultProperty; if (property) bindingSkipList |= (1 << property->coreIndex()); } @@ -809,7 +817,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper Q_ASSERT(tr); QQmlType attachedType = tr->type; if (!attachedType.isValid()) { - QQmlTypeNameCache::Result res = context->imports->query(stringAt(binding->propertyNameIndex)); + QQmlTypeNameCache::Result res = context->imports()->query( + stringAt(binding->propertyNameIndex)); if (res.isValid()) attachedType = res.type; else @@ -902,8 +911,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); - QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, - context, _scopeObject, runtimeFunction, currentQmlContext()); + QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression( + _bindingTarget, signalIndex, context, + _scopeObject, runtimeFunction, currentQmlContext()); bs->takeExpression(expr); } else { @@ -920,10 +930,12 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper subprop = bindingProperty; } if (binding->isTranslationBinding()) { - qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context); + qmlBinding = QQmlBinding::createTranslationBinding( + compilationUnit, binding, _scopeObject, context); } else { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - qmlBinding = QQmlBinding::create(targetProperty, runtimeFunction, _scopeObject, context, currentQmlContext()); + qmlBinding = QQmlBinding::create(targetProperty, runtimeFunction, _scopeObject, + context, currentQmlContext()); } auto bindingTarget = _bindingTarget; @@ -965,10 +977,12 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(createdSubObject) + valueSourceCast); QObject *target = createdSubObject->parent(); QQmlProperty prop; - if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); - else + if (_valueTypeProperty) { + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, + bindingProperty, context); + } else { prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); + } vs->setTarget(prop); return true; } @@ -990,15 +1004,19 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper // we can't have aliasses on subproperties of value types, so: QQmlPropertyData targetPropertyData = *data->propertyCache->property(propIndex.coreIndex()); - auto prop = QQmlPropertyPrivate::restore(target, targetPropertyData, nullptr, context); + auto prop = QQmlPropertyPrivate::restore( + target, targetPropertyData, nullptr, context); vi->setTarget(prop); propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); } else { QQmlProperty prop; - if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); - else - prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); + if (_valueTypeProperty) { + prop = QQmlPropertyPrivate::restore( + target, *_valueTypeProperty, bindingProperty, context); + } else { + prop = QQmlPropertyPrivate::restore( + target, *bindingProperty, nullptr, context); + } vi->setTarget(prop); propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); } @@ -1144,7 +1162,7 @@ void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const { if (object->id >= 0) - context->setIdProperty(object->id, instance); + context->setIdValue(object->id, instance); } QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject) @@ -1200,10 +1218,10 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo customParser = type.customParser(); - if (sharedState->rootContext && sharedState->rootContext->isRootObjectInCreation) { + if (sharedState->rootContext && sharedState->rootContext->isRootObjectInCreation()) { QQmlData *ddata = QQmlData::get(instance, /*create*/true); ddata->rootObjectInCreation = true; - sharedState->rootContext->isRootObjectInCreation = false; + sharedState->rootContext->setRootObjectInCreation(false); } sharedState->allCreatedObjects.push(instance); @@ -1264,21 +1282,22 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo const bool isInlineComponent = obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot; if (static_cast<quint32>(index) == /*root object*/0 || ddata->rootObjectInCreation || isInlineComponent) { if (ddata->context) { - Q_ASSERT(ddata->context != context); + Q_ASSERT(ddata->context != context.data()); Q_ASSERT(ddata->outerContext); - Q_ASSERT(ddata->outerContext != context); - QQmlContextData *c = ddata->context; - while (c->linkedContext) c = c->linkedContext; - c->linkedContext = context; + Q_ASSERT(ddata->outerContext != context.data()); + QQmlRefPointer<QQmlContextData> c = ddata->context; + while (QQmlRefPointer<QQmlContextData> linked = c->linkedContext()) + c = linked; + c->setLinkedContext(context); } else { - ddata->context = context; + ddata->context = context.data(); } ddata->ownContext = ddata->context; } else if (!ddata->context) { - ddata->context = context; + ddata->context = context.data(); } - context->addObject(ddata); + context->addOwnedObject(ddata); if (parserStatus) { parserStatus->classBegin(); @@ -1292,7 +1311,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo // Register the context object in the context early on in order for pending binding // initialization to find it available. if (isContextObject) - context->contextObject = instance; + context->setContextObject(instance); if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { customParser->engine = QQmlEnginePrivate::get(engine); @@ -1367,7 +1386,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo return ok ? instance : nullptr; } -QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) +bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) { Q_ASSERT(phase == ObjectsCreated || phase == Finalizing); phase = Finalizing; @@ -1389,12 +1408,13 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru if (!b->isValueTypeProxy()) { QQmlBinding *binding = static_cast<QQmlBinding*>(b.data()); if (!binding->hasError() && !binding->hasDependencies() - && binding->context() && !binding->context()->unresolvedNames) + && binding->hasContext() && !binding->hasUnresolvedNames()) { b->removeFromObject(); + } } if (watcher.hasRecursed() || interrupt.shouldInterrupt()) - return nullptr; + return false; } if (QQmlVME::componentCompleteEnabled()) { // the qml designer does the component complete later @@ -1408,7 +1428,7 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru } if (watcher.hasRecursed() || interrupt.shouldInterrupt()) - return nullptr; + return false; } } @@ -1420,27 +1440,27 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args); } if (watcher.hasRecursed()) - return nullptr; + return false; } sharedState->finalizeCallbacks.clear(); while (sharedState->componentAttached) { QQmlComponentAttached *a = sharedState->componentAttached; - a->rem(); + a->removeFromList(); QQmlData *d = QQmlData::get(a->parent()); Q_ASSERT(d); Q_ASSERT(d->context); - a->add(&d->context->componentAttached); + d->context->addComponentAttached(a); if (QQmlVME::componentCompleteEnabled()) emit a->completed(); if (watcher.hasRecursed() || interrupt.shouldInterrupt()) - return nullptr; + return false; } phase = Done; - return sharedState->rootContext; + return true; } void QQmlObjectCreator::clear() @@ -1458,7 +1478,7 @@ void QQmlObjectCreator::clear() while (sharedState->componentAttached) { QQmlComponentAttached *a = sharedState->componentAttached; - a->rem(); + a->removeFromList(); } phase = Done; @@ -1553,9 +1573,9 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * while (alias->aliasToLocalAlias) alias = _compiledObject->aliasesBegin() + alias->localAliasIndex; Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); - if (!context->idValues->wasSet()) + if (!context->isIdValueSet(0)) // TODO: Do we really want 0 here? continue; - QObject *target = context->idValues[alias->targetObjectId].data(); + QObject *target = context->idValue(alias->targetObjectId); if (!target) continue; QQmlData *targetDData = QQmlData::get(target, /*create*/false); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 50ce8d5909..0b14ec6603 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -57,6 +57,7 @@ #include <private/qrecursionwatcher_p.h> #include <private/qqmlprofiler_p.h> #include <private/qv4qmlcontext_p.h> +#include <private/qqmlguardedcontextdata_p.h> #include <qpointer.h> @@ -90,8 +91,8 @@ class RequiredProperties : public QHash<QQmlPropertyData*, RequiredPropertyInfo> struct QQmlObjectCreatorSharedState : public QSharedData { - QQmlContextData *rootContext; - QQmlContextData *creationContext; + QQmlRefPointer<QQmlContextData> rootContext; + QQmlRefPointer<QQmlContextData> creationContext; QFiniteStack<QQmlAbstractBinding::Ptr> allCreatedBindings; QFiniteStack<QQmlParserStatus*> allParserStatusCallbacks; QFiniteStack<QPointer<QObject> > allCreatedObjects; @@ -108,38 +109,48 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator) public: - QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr); + QQmlObjectCreator(QQmlRefPointer<QQmlContextData> parentContext, + const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, + const QQmlRefPointer<QQmlContextData> &creationContext, + QQmlIncubatorPrivate *incubator = nullptr); ~QQmlObjectCreator(); enum CreationFlags { NormalObject = 1, InlineComponent = 2 }; - QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr, int flags = NormalObject); + QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, + QQmlInstantiationInterrupt *interrupt = nullptr, int flags = NormalObject); bool populateDeferredProperties(QObject *instance, const QQmlData::DeferredData *deferredData); - void beginPopulateDeferred(QQmlContextData *context); + void beginPopulateDeferred(const QQmlRefPointer<QQmlContextData> &context); void populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex, const QV4::CompiledData::Binding *binding); void finalizePopulateDeferred(); - QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt); + bool finalize(QQmlInstantiationInterrupt &interrupt); void clear(); + QQmlRefPointer<QQmlContextData> rootContext() const { return sharedState->rootContext; } QQmlComponentAttached **componentAttachment() { return &sharedState->componentAttached; } QList<QQmlEnginePrivate::FinalizeCallback> *finalizeCallbacks() { return &sharedState->finalizeCallbacks; } QList<QQmlError> errors; - QQmlContextData *parentContextData() const { return parentContext.contextData(); } + QQmlRefPointer<QQmlContextData> parentContextData() const + { + return parentContext.contextData(); + } QFiniteStack<QPointer<QObject> > &allCreatedObjects() { return sharedState->allCreatedObjects; } RequiredProperties &requiredProperties() {return sharedState->requiredProperties;} bool componentHadRequiredProperties() const {return sharedState->hadRequiredProperties;} private: - QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState); + QQmlObjectCreator(QQmlRefPointer<QQmlContextData> contextData, + const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, + QQmlObjectCreatorSharedState *inheritedSharedState); - void init(QQmlContextData *parentContext); + void init(QQmlRefPointer<QQmlContextData> parentContext); QObject *createInstance(int index, QObject *parent = nullptr, bool isContextObject = false); @@ -181,7 +192,7 @@ private: QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; const QV4::CompiledData::Unit *qmlUnit; QQmlGuardedContextData parentContext; - QQmlContextData *context; + QQmlRefPointer<QQmlContextData> context; const QQmlPropertyCacheVector *propertyCaches; QExplicitlySharedDataPointer<QQmlObjectCreatorSharedState> sharedState; bool topLevelCreator; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 8521de6ab3..3824a7fb6d 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -163,7 +163,6 @@ QQmlProperty::QQmlProperty(QObject *obj, QQmlContext *ctxt) QQmlProperty::QQmlProperty(QObject *obj, QQmlEngine *engine) : d(new QQmlPropertyPrivate) { - d->context = nullptr; d->engine = engine; d->initDefault(obj); } @@ -205,7 +204,11 @@ QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlContext *ctxt) d->context = ctxt?QQmlContextData::get(ctxt):nullptr; d->engine = ctxt?ctxt->engine():nullptr; d->initProperty(obj, name); - if (!isValid()) { d->object = nullptr; d->context = nullptr; d->engine = nullptr; } + if (!isValid()) { + d->object = nullptr; + d->context = nullptr; + d->engine = nullptr; + } } /*! @@ -216,19 +219,23 @@ QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlContext *ctxt) QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlEngine *engine) : d(new QQmlPropertyPrivate) { - d->context = nullptr; d->engine = engine; d->initProperty(obj, name); - if (!isValid()) { d->object = nullptr; d->context = nullptr; d->engine = nullptr; } + if (!isValid()) { + d->object = nullptr; + d->context = nullptr; + d->engine = nullptr; + } } -QQmlProperty QQmlPropertyPrivate::create(QObject *target, const QString &propertyName, QQmlContextData *context) +QQmlProperty QQmlPropertyPrivate::create(QObject *target, const QString &propertyName, + const QQmlRefPointer<QQmlContextData> &context) { QQmlProperty result; auto d = new QQmlPropertyPrivate; result.d = d; d->context = context; - d->engine = context ? context->engine : nullptr; + d->engine = context ? context->engine() : nullptr; d->initProperty(target, propertyName); if (!result.isValid()) { d->object = nullptr; @@ -238,23 +245,21 @@ QQmlProperty QQmlPropertyPrivate::create(QObject *target, const QString &propert return result; } -QQmlPropertyPrivate::QQmlPropertyPrivate() -: context(nullptr), engine(nullptr), object(nullptr), isNameCached(false) +QQmlRefPointer<QQmlContextData> QQmlPropertyPrivate::effectiveContext() const { -} - -QQmlContextData *QQmlPropertyPrivate::effectiveContext() const -{ - if (context) return context; - else if (engine) return QQmlContextData::get(engine->rootContext()); - else return nullptr; + if (context) + return context; + else if (engine) + return QQmlContextData::get(engine->rootContext()); + else + return nullptr; } void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) { if (!obj) return; - QQmlRefPointer<QQmlTypeNameCache> typeNameCache = context?context->imports:nullptr; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache = context ? context->imports() : nullptr; QObject *currentObject = obj; QVector<QStringRef> path; @@ -974,7 +979,7 @@ void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that, if (expr) { int signalIndex = QQmlPropertyPrivate::get(that)->signalIndex(); QQmlBoundSignal *signal = new QQmlBoundSignal(that.d->object, signalIndex, that.d->object, - expr->context()->engine); + expr->engine()); signal->takeExpression(expr); } } @@ -1097,7 +1102,8 @@ QVariant QQmlPropertyPrivate::readValueProperty() } // helper function to allow assignment / binding to QList<QUrl> properties. -QVariant QQmlPropertyPrivate::resolvedUrlSequence(const QVariant &value, QQmlContextData *context) +QVariant QQmlPropertyPrivate::resolvedUrlSequence( + const QVariant &value, const QQmlRefPointer<QQmlContextData> &context) { QList<QUrl> urls; if (value.userType() == qMetaTypeId<QUrl>()) { @@ -1186,7 +1192,8 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, const QVariant &value, - QQmlContextData *context,QQmlPropertyData::WriteFlags flags) + const QQmlRefPointer<QQmlContextData> &context, + QQmlPropertyData::WriteFlags flags) { // Remove any existing bindings on this property if (!(flags & QQmlPropertyData::DontRemoveBinding) && object) @@ -1201,7 +1208,7 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, }; QQmlGadgetPtrWrapper *wrapper = context - ? QQmlGadgetPtrWrapper::instance(context->engine, core.propType()) + ? QQmlGadgetPtrWrapper::instance(context->engine(), core.propType()) : nullptr; if (wrapper) { doWrite(wrapper); @@ -1217,10 +1224,9 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, return rv; } -bool QQmlPropertyPrivate::write(QObject *object, - const QQmlPropertyData &property, - const QVariant &value, QQmlContextData *context, - QQmlPropertyData::WriteFlags flags) +bool QQmlPropertyPrivate::write( + QObject *object, const QQmlPropertyData &property, const QVariant &value, + const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData::WriteFlags flags) { const int propertyType = property.propType(); const int variantType = value.userType(); @@ -1647,14 +1653,15 @@ QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that) QQmlProperty QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data, - const QQmlPropertyData *valueTypeData, QQmlContextData *ctxt) + const QQmlPropertyData *valueTypeData, + const QQmlRefPointer<QQmlContextData> &ctxt) { QQmlProperty prop; prop.d = new QQmlPropertyPrivate; prop.d->object = object; prop.d->context = ctxt; - prop.d->engine = ctxt ? ctxt->engine : nullptr; + prop.d->engine = ctxt ? ctxt->engine() : nullptr; prop.d->core = data; if (valueTypeData) diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 8abd83d7b4..5e357d77c0 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -57,9 +57,10 @@ #include <private/qobject_p.h> #include <private/qtqmlglobal_p.h> #include <private/qqmlrefcount_p.h> -#include <private/qqmlcontext_p.h> +#include <private/qqmlcontextdata_p.h> #include <private/qqmlboundsignalexpressionpointer_p.h> #include <private/qqmlpropertydata_p.h> +#include <private/qqmlpropertyindex_p.h> QT_BEGIN_NAMESPACE @@ -67,11 +68,12 @@ class QQmlContext; class QQmlEnginePrivate; class QQmlJavaScriptExpression; class QQmlMetaObject; +class QQmlAbstractBinding; class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount { public: - QQmlContextData *context; + QQmlRefPointer<QQmlContextData> context; QPointer<QQmlEngine> engine; QPointer<QObject> object; @@ -81,14 +83,14 @@ public: bool isNameCached:1; QString nameCache; - QQmlPropertyPrivate(); + QQmlPropertyPrivate() : isNameCached(false) {} QQmlPropertyIndex encodedIndex() const { return encodedIndex(core, valueTypeData); } static QQmlPropertyIndex encodedIndex(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData) { return QQmlPropertyIndex(core.coreIndex(), valueTypeData.coreIndex()); } - inline QQmlContextData *effectiveContext() const; + QQmlRefPointer<QQmlContextData> effectiveContext() const; void initProperty(QObject *obj, const QString &name); void initDefault(QObject *obj); @@ -106,10 +108,11 @@ public: const QVariant &value, int flags); static bool writeValueProperty(QObject *, const QQmlPropertyData &, const QQmlPropertyData &valueTypeData, - const QVariant &, QQmlContextData *, + const QVariant &, const QQmlRefPointer<QQmlContextData> &, QQmlPropertyData::WriteFlags flags = {}); static bool write(QObject *, const QQmlPropertyData &, const QVariant &, - QQmlContextData *, QQmlPropertyData::WriteFlags flags = {}); + const QQmlRefPointer<QQmlContextData> &, + QQmlPropertyData::WriteFlags flags = {}); static void findAliasTarget(QObject *, QQmlPropertyIndex, QObject **, QQmlPropertyIndex *); enum BindingFlag { @@ -126,7 +129,8 @@ public: static void removeBinding(QQmlAbstractBinding *b); static QQmlAbstractBinding *binding(QObject *, QQmlPropertyIndex index); - static QQmlProperty restore(QObject *, const QQmlPropertyData &, const QQmlPropertyData *, QQmlContextData *); + static QQmlProperty restore(QObject *, const QQmlPropertyData &, const QQmlPropertyData *, + const QQmlRefPointer<QQmlContextData> &); int signalIndex() const; @@ -146,8 +150,11 @@ public: int type = 0, int *types = nullptr); static void flushSignal(const QObject *sender, int signal_index); - static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context); - static QQmlProperty create(QObject *target, const QString &propertyName, QQmlContextData *context); + static QVariant resolvedUrlSequence( + const QVariant &value, const QQmlRefPointer<QQmlContextData> &context); + static QQmlProperty create( + QObject *target, const QString &propertyName, + const QQmlRefPointer<QQmlContextData> &context); }; diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index aa171c1c29..4b17760f8e 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -753,7 +753,9 @@ void QQmlPropertyCache::invalidate(const QMetaObject *metaObject) } } -QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, QObject *object, QQmlContextData *context) const +QQmlPropertyData *QQmlPropertyCache::findProperty( + StringCache::ConstIterator it, QObject *object, + const QQmlRefPointer<QQmlContextData> &context) const { QQmlData *data = (object ? QQmlData::get(object) : nullptr); const QQmlVMEMetaObject *vmemo = nullptr; @@ -766,11 +768,11 @@ QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, namespace { -inline bool contextHasNoExtensions(QQmlContextData *context) +inline bool contextHasNoExtensions(const QQmlRefPointer<QQmlContextData> &context) { // This context has no extension if its parent is the engine's rootContext, // which has children but no imports - return (!context->parent || !context->parent->imports); + return (!context->parent() || !context->parent()->imports()); } inline int maximumIndexForProperty(QQmlPropertyData *prop, const int methodCount, const int signalCount, const int propertyCount) @@ -782,7 +784,9 @@ inline int maximumIndexForProperty(QQmlPropertyData *prop, const int methodCount } -QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *vmemo, QQmlContextData *context) const +QQmlPropertyData *QQmlPropertyCache::findProperty( + StringCache::ConstIterator it, const QQmlVMEMetaObject *vmemo, + const QQmlRefPointer<QQmlContextData> &context) const { StringCache::ConstIterator end = stringCache.end(); @@ -796,7 +800,7 @@ QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, if (vmemo && context && !contextHasNoExtensions(context)) { // Find the meta-object that corresponds to the supplied context do { - if (vmemo->ctxt == context) + if (vmemo->ctxt.contextData().data() == context.data()) break; vmemo = vmemo->parentVMEMetaObject(); @@ -1007,7 +1011,7 @@ static inline QByteArray qQmlPropertyCacheToString(const QV4::String *string) template<typename T> QQmlPropertyData * qQmlPropertyCacheProperty(QJSEngine *engine, QObject *obj, T name, - QQmlContextData *context, QQmlPropertyData &local) + const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData &local) { QQmlPropertyCache *cache = nullptr; @@ -1040,21 +1044,21 @@ qQmlPropertyCacheProperty(QJSEngine *engine, QObject *obj, T name, QQmlPropertyData * QQmlPropertyCache::property(QJSEngine *engine, QObject *obj, const QV4::String *name, - QQmlContextData *context, QQmlPropertyData &local) + const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData &local) { return qQmlPropertyCacheProperty<const QV4::String *>(engine, obj, name, context, local); } QQmlPropertyData * QQmlPropertyCache::property(QJSEngine *engine, QObject *obj, const QStringRef &name, - QQmlContextData *context, QQmlPropertyData &local) + const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData &local) { return qQmlPropertyCacheProperty<const QStringRef &>(engine, obj, name, context, local); } QQmlPropertyData * QQmlPropertyCache::property(QJSEngine *engine, QObject *obj, const QLatin1String &name, - QQmlContextData *context, QQmlPropertyData &local) + const QQmlRefPointer<QQmlContextData> &context, QQmlPropertyData &local) { return qQmlPropertyCacheProperty<const QLatin1String &>(engine, obj, name, context, local); } diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 6038056991..43ffb0fb36 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -114,7 +114,8 @@ public: const QMetaObject *firstCppMetaObject() const; template<typename K> - QQmlPropertyData *property(const K &key, QObject *object, QQmlContextData *context) const + QQmlPropertyData *property(const K &key, QObject *object, + const QQmlRefPointer<QQmlContextData> &context) const { return findProperty(stringCache.find(key), object, context); } @@ -136,14 +137,15 @@ public: inline bool isAllowedInRevision(QQmlPropertyData *) const; static QQmlPropertyData *property(QJSEngine *, QObject *, const QStringRef &, - QQmlContextData *, QQmlPropertyData &); + const QQmlRefPointer<QQmlContextData> &, QQmlPropertyData &); static QQmlPropertyData *property(QJSEngine *, QObject *, const QLatin1String &, - QQmlContextData *, QQmlPropertyData &); + const QQmlRefPointer<QQmlContextData> &, QQmlPropertyData &); static QQmlPropertyData *property(QJSEngine *, QObject *, const QV4::String *, - QQmlContextData *, QQmlPropertyData &); + const QQmlRefPointer<QQmlContextData> &, QQmlPropertyData &); static QQmlPropertyData *property(QJSEngine *engine, QObject *obj, const QString &name, - QQmlContextData *context, QQmlPropertyData &local) + const QQmlRefPointer<QQmlContextData> &context, + QQmlPropertyData &local) { return property(engine, obj, QStringRef(&name), context, local); } @@ -200,8 +202,10 @@ private: typedef QLinkedStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache; typedef QVector<QTypeRevision> AllowedRevisionCache; - QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *, QQmlContextData *) const; - QQmlPropertyData *findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *, QQmlContextData *) const; + QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *, + const QQmlRefPointer<QQmlContextData> &) const; + QQmlPropertyData *findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *, + const QQmlRefPointer<QQmlContextData> &) const; QQmlPropertyData *ensureResolved(QQmlPropertyData*) const; diff --git a/src/qml/qml/qqmlpropertyresolver.cpp b/src/qml/qml/qqmlpropertyresolver.cpp index 90eaca0b90..396f2876b9 100644 --- a/src/qml/qml/qqmlpropertyresolver.cpp +++ b/src/qml/qml/qqmlpropertyresolver.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qqmlpropertyresolver_p.h" +#include <private/qqmlcontextdata_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp index dac21f7ee7..14ed501131 100644 --- a/src/qml/qml/qqmlscriptblob.cpp +++ b/src/qml/qml/qqmlscriptblob.cpp @@ -42,6 +42,7 @@ #include <private/qqmlscriptblob_p.h> #include <private/qqmlscriptdata_p.h> #include <private/qqmlsourcecoordinate_p.h> +#include <private/qqmlcontextdata_p.h> #include <private/qv4runtimecodegen_p.h> #include <private/qv4script_p.h> diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp index ae268ca904..50ec0e20cd 100644 --- a/src/qml/qml/qqmlscriptdata.cpp +++ b/src/qml/qml/qqmlscriptdata.cpp @@ -55,78 +55,82 @@ QQmlScriptData::QQmlScriptData() { } -QQmlContextData *QQmlScriptData::qmlContextDataForContext(QQmlContextData *parentQmlContextData) +QQmlRefPointer<QQmlContextData> QQmlScriptData::qmlContextDataForContext( + const QQmlRefPointer<QQmlContextData> &parentQmlContextData) { - Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); + Q_ASSERT(parentQmlContextData && parentQmlContextData->engine()); if (m_precompiledScript->isESModule()) return nullptr; - auto qmlContextData = new QQmlContextData(); + QQmlRefPointer<QQmlContextData> qmlContextData = m_precompiledScript->isSharedLibrary() + ? QQmlContextData::createRefCounted(QQmlRefPointer<QQmlContextData>()) + : QQmlContextData::createRefCounted(parentQmlContextData); - qmlContextData->isInternal = true; - qmlContextData->isJSContext = true; + qmlContextData->setInternal(true); + qmlContextData->setJSContext(true); if (m_precompiledScript->isSharedLibrary()) - qmlContextData->isPragmaLibraryContext = true; + qmlContextData->setPragmaLibraryContext(true); else - qmlContextData->isPragmaLibraryContext = parentQmlContextData->isPragmaLibraryContext; - qmlContextData->baseUrl = url; - qmlContextData->baseUrlString = urlString; + qmlContextData->setPragmaLibraryContext(parentQmlContextData->isPragmaLibraryContext()); + qmlContextData->setBaseUrl(url); + qmlContextData->setBaseUrlString(urlString); // For backward compatibility, if there are no imports, we need to use the // imports from the parent context. See QTBUG-17518. if (!typeNameCache->isEmpty()) { - qmlContextData->imports = typeNameCache; + qmlContextData->setImports(typeNameCache); } else if (!m_precompiledScript->isSharedLibrary()) { - qmlContextData->imports = parentQmlContextData->imports; - qmlContextData->importedScripts = parentQmlContextData->importedScripts; + qmlContextData->setImports(parentQmlContextData->imports()); + qmlContextData->setImportedScripts(parentQmlContextData->importedScripts()); } - if (!m_precompiledScript->isSharedLibrary()) { - qmlContextData->setParent(parentQmlContextData); - } else { - qmlContextData->engine = parentQmlContextData->engine; // Fix for QTBUG-21620 - } + if (m_precompiledScript->isSharedLibrary()) + qmlContextData->setEngine(parentQmlContextData->engine()); // Fix for QTBUG-21620 - QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); + QV4::ExecutionEngine *v4 = parentQmlContextData->engine()->handle(); QV4::Scope scope(v4); QV4::ScopedObject scriptsArray(scope); - if (qmlContextData->importedScripts.isNullOrUndefined()) { + if (qmlContextData->importedScripts().isNullOrUndefined()) { scriptsArray = v4->newArrayObject(scripts.count()); - qmlContextData->importedScripts.set(v4, scriptsArray); + qmlContextData->setImportedScripts( + QV4::PersistentValue(v4, scriptsArray.asReturnedValue())); } else { - scriptsArray = qmlContextData->importedScripts.valueRef(); + scriptsArray = qmlContextData->importedScripts().valueRef(); } QV4::ScopedValue v(scope); - for (int ii = 0; ii < scripts.count(); ++ii) - scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData))); + for (int ii = 0; ii < scripts.count(); ++ii) { + v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData.data()); + scriptsArray->put(ii, v); + } return qmlContextData; } -QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentQmlContextData) +QV4::ReturnedValue QQmlScriptData::scriptValueForContext( + const QQmlRefPointer<QQmlContextData> &parentQmlContextData) { if (m_loaded) return m_value.value(); - Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); - QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); + Q_ASSERT(parentQmlContextData && parentQmlContextData->engine()); + QV4::ExecutionEngine *v4 = parentQmlContextData->engine()->handle(); QV4::Scope scope(v4); if (!hasEngine()) { - addToEngine(parentQmlContextData->engine); + addToEngine(parentQmlContextData->engine()); addref(); } - QQmlContextDataRef qmlContextData = qmlContextDataForContext(parentQmlContextData); QV4::Scoped<QV4::QmlContext> qmlExecutionContext(scope); - if (qmlContextData) - qmlExecutionContext = - QV4::QmlContext::create(v4->rootContext(), qmlContextData, /* scopeObject: */ nullptr); + if (auto qmlContextData = qmlContextDataForContext(parentQmlContextData)) { + qmlExecutionContext = QV4::QmlContext::create(v4->rootContext(), std::move(qmlContextData), + /* scopeObject: */ nullptr); + } QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4)); if (module) { - if (qmlContextData) { + if (qmlExecutionContext) { module->d()->scope->outer.set(v4, qmlExecutionContext->d()); qmlExecutionContext->d()->qml()->module.set(v4, module->d()); } @@ -141,7 +145,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent } QV4::ScopedValue value(scope); - if (qmlContextData) + if (qmlExecutionContext) value = qmlExecutionContext->d()->qml(); else if (module) value = module->d(); diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h index 80b65b699c..abbafc33c2 100644 --- a/src/qml/qml/qqmlscriptdata_p.h +++ b/src/qml/qml/qqmlscriptdata_p.h @@ -85,7 +85,7 @@ public: QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QVector<QQmlRefPointer<QQmlScriptBlob>> scripts; - QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); + QV4::ReturnedValue scriptValueForContext(const QQmlRefPointer<QQmlContextData> &parentCtxt); QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; } @@ -96,7 +96,8 @@ private: friend class QQmlScriptBlob; void initialize(QQmlEngine *); - QQmlContextData *qmlContextDataForContext(QQmlContextData *parentQmlContextData); + QQmlRefPointer<QQmlContextData> qmlContextDataForContext( + const QQmlRefPointer<QQmlContextData> &parentQmlContextData); bool m_loaded; QQmlRefPointer<QV4::ExecutableCompilationUnit> m_precompiledScript; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index c787d4092e..da76659ef4 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -183,7 +183,7 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons if (hasProperty) *hasProperty = true; - QQmlContextData *context = v4->callingQmlContext(); + QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext(); QObject *object = w->d()->object; QQmlType type = w->d()->type(); @@ -283,10 +283,10 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons if (r.type.isValid()) { return create(scope.engine, object, r.type, w->d()->mode); } else if (r.scriptIndex != -1) { - QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); + QV4::ScopedObject scripts(scope, context->importedScripts().valueRef()); return scripts->get(r.scriptIndex); } else if (r.importNamespace) { - return create(scope.engine, object, context->imports, r.importNamespace); + return create(scope.engine, object, context->imports(), r.importNamespace); } return QV4::Encode::undefined(); @@ -329,7 +329,7 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, return false; ScopedString name(scope, id.asStringOrSymbol()); - QQmlContextData *context = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> context = scope.engine->callingQmlContext(); QQmlType type = w->d()->type(); if (type.isValid() && !type.isSingleton() && w->d()->object) { @@ -440,7 +440,7 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object); ScopedString name(scope, id.asStringOrSymbol()); - QQmlContextData *qmlContext = engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> qmlContext = engine->callingQmlContext(); Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This)); QQmlType type = w->d()->type(); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index aa7e28add0..d367edad1b 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -569,7 +569,7 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v return false; } - QQmlContextData *context = v4->callingQmlContext(); + QQmlRefPointer<QQmlContextData> context = v4->callingQmlContext(); QQmlPropertyData cacheData; cacheData.setWritable(true); diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 018769948d..38e9fad2b6 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -138,7 +138,7 @@ bool QQmlVMEGuard::isOK() const return false; for (int ii = 0; ii < m_contextCount; ++ii) - if (m_contexts[ii].isNull() || !m_contexts[ii]->engine) + if (m_contexts[ii].isNull() || !m_contexts[ii]->engine()) return false; return true; diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h index 05a78a17fe..784df5d7ef 100644 --- a/src/qml/qml/qqmlvme_p.h +++ b/src/qml/qml/qqmlvme_p.h @@ -87,7 +87,7 @@ namespace QQmlVMETypes { State() : flags(0), context(nullptr), instructionStream(nullptr) {} quint32 flags; - QQmlContextData *context; + QQmlRefPointer<QQmlContextData> context; const char *instructionStream; QBitField bindingSkipList; }; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index aa9f4bc1bd..c107f22df5 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -230,8 +230,8 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() } else { const QV4::CompiledData::Alias *aliasData = &metaObject->compiledObject->aliasTable()[aliasId]; if (!aliasData->isObjectAlias()) { - QQmlContextData *ctxt = metaObject->ctxt; - QObject *target = ctxt->idValues[aliasData->targetObjectId].data(); + QQmlRefPointer<QQmlContextData> ctxt = metaObject->ctxt; + QObject *target = ctxt->idValue(aliasData->targetObjectId); if (!target) return; @@ -255,7 +255,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() return; if (pd->notifyIndex() != -1) - connect(target, pd->notifyIndex(), ctxt->engine); + connect(target, pd->notifyIndex(), ctxt->engine()); } metaObject.setFlag(); @@ -321,7 +321,7 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a) if (type != QMetaType::UnknownType) { if (valueIndex != -1) { QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance( - data->context->engine, type); + data->context->engine(), type); Q_ASSERT(valueType); // @@ -694,7 +694,9 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * const QV4::CompiledData::BuiltinType t = property.builtinType(); // the context can be null if accessing var properties from cpp after re-parenting an item. - QQmlEnginePrivate *ep = (ctxt == nullptr || ctxt->engine == nullptr) ? nullptr : QQmlEnginePrivate::get(ctxt->engine); + QQmlEnginePrivate *ep = (ctxt.isNull() || ctxt->engine() == nullptr) + ? nullptr + : QQmlEnginePrivate::get(ctxt->engine()); const int fallbackMetaType = QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(t); @@ -891,15 +893,13 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if ((aliasData->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject) && c == QMetaObject::ReadProperty) *reinterpret_cast<void **>(a[0]) = nullptr; - if (!ctxt) return -1; + if (ctxt.isNull()) + return -1; while (aliasData->aliasToLocalAlias) aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex]; - QQmlContext *context = ctxt->asQQmlContext(); - QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context); - - QObject *target = ctxtPriv->data->idValues[aliasData->targetObjectId].data(); + QObject *target = ctxt->idValue(aliasData->targetObjectId); if (!target) return -1; @@ -934,7 +934,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); // Value type property or deep alias QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance( - ctxt->engine, pd->propType()); + ctxt->engine(), pd->propType()); if (valueType) { valueType->read(target, coreIndex); int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); @@ -973,7 +973,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * id -= plainSignals; if (id < methodCount) { - QQmlEngine *engine = ctxt->engine; + QQmlEngine *engine = ctxt->engine(); if (!engine) return -1; // We can't run the method @@ -1050,7 +1050,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * QV4::ReturnedValue QQmlVMEMetaObject::method(int index) const { - if (!ctxt || !ctxt->isValid() || !compiledObject) { + if (ctxt.isNull() || !ctxt->isValid() || !compiledObject) { qWarning("QQmlVMEMetaObject: Internal error - attempted to evaluate a function in an invalid context"); return QV4::Encode::undefined(); } @@ -1255,14 +1255,14 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, *coreIndex = -1; *valueTypeIndex = -1; - if (!ctxt) + if (ctxt.isNull()) return false; const int aliasId = index - propOffset() - compiledObject->nProperties; const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId]; while (aliasData->aliasToLocalAlias) aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex]; - *target = ctxt->idValues[aliasData->targetObjectId].data(); + *target = ctxt->idValue(aliasData->targetObjectId); if (!*target) return false; @@ -1290,7 +1290,7 @@ void QQmlVMEMetaObject::connectAlias(int aliasId) } endpoint->metaObject = this; - endpoint->connect(&ctxt->idValues[aliasData->targetObjectId].bindings); + endpoint->connect(ctxt->idValueBindings(aliasData->targetObjectId)); endpoint->tryConnect(); } diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index e17701a968..10fa0d89a2 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -64,8 +64,8 @@ #include <private/qobject_p.h> #include "qqmlguard_p.h" -#include "qqmlcontext_p.h" +#include <private/qqmlguardedcontextdata_p.h> #include <private/qflagpointer_p.h> #include <private/qv4object_p.h> @@ -144,7 +144,10 @@ class QQmlVMEMetaObjectEndpoint; class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject { public: - QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, int qmlObjectId); + QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, + const QQmlRefPointer<QQmlPropertyCache> &cache, + const QQmlRefPointer<QV4::ExecutableCompilationUnit> &qmlCompilationUnit, + int qmlObjectId); ~QQmlVMEMetaObject() override; bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 61070113cc..179d5314a8 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1020,7 +1020,8 @@ public: QString replyStatusText() const; ReturnedValue open(Object *thisObject, const QString &, const QUrl &, LoadType); - ReturnedValue send(Object *thisObject, QQmlContextData *context, const QByteArray &); + ReturnedValue send(Object *thisObject, const QQmlRefPointer<QQmlContextData> &context, + const QByteArray &); ReturnedValue abort(Object *thisObject); void addHeader(const QString &, const QString &); @@ -1068,7 +1069,7 @@ private: void readEncoding(); PersistentValue m_thisObject; - QQmlContextDataRef m_qmlContext; + QQmlRefPointer<QQmlContextData> m_qmlContext; bool m_wasConstructedWithQmlContext = true; void dispatchCallbackNow(Object *thisObj); @@ -1095,7 +1096,7 @@ QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::Exec , m_responseType() , m_parsedDocument() { - m_wasConstructedWithQmlContext = v4->callingQmlContext() != nullptr; + m_wasConstructedWithQmlContext = !v4->callingQmlContext().isNull(); } QQmlXMLHttpRequest::~QQmlXMLHttpRequest() @@ -1297,7 +1298,8 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) } } -ReturnedValue QQmlXMLHttpRequest::send(Object *thisObject, QQmlContextData *context, const QByteArray &data) +ReturnedValue QQmlXMLHttpRequest::send( + Object *thisObject, const QQmlRefPointer<QQmlContextData> &context, const QByteArray &data) { m_errorFlag = false; m_sendFlag = true; @@ -1462,7 +1464,7 @@ void QQmlXMLHttpRequest::finished() dispatchCallbackSafely(); m_thisObject.clear(); - m_qmlContext.setContextData(nullptr); + m_qmlContext = nullptr; } @@ -1615,12 +1617,13 @@ void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj, bool done, bool er void QQmlXMLHttpRequest::dispatchCallbackSafely() { - if (m_wasConstructedWithQmlContext && !m_qmlContext.contextData()) + if (m_wasConstructedWithQmlContext && m_qmlContext.isNull()) { // if the calling context object is no longer valid, then it has been // deleted explicitly (e.g., by a Loader deleting the itemContext when // the source is changed). We do nothing in this case, as the evaluation // cannot succeed. return; + } dispatchCallbackNow(m_thisObject.as<Object>()); } @@ -1791,8 +1794,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(const FunctionObject *b, const QUrl url = QUrl(argv[1].toQStringNoThrow()); if (url.isRelative()) { - QQmlContextData *qmlContextData = scope.engine->callingQmlContext(); - if (qmlContextData) + if (QQmlRefPointer<QQmlContextData> qmlContextData = scope.engine->callingQmlContext()) url = qmlContextData->resolvedUrl(url); else url = scope.engine->resolvedUrl(url.url()); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 4e5ab9b899..10932a0091 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -988,8 +988,7 @@ ReturnedValue QtObject::method_resolvedUrl(const FunctionObject *b, const Value QQmlEnginePrivate *p = nullptr; if (e) p = QQmlEnginePrivate::get(e); if (p) { - QQmlContextData *ctxt = scope.engine->callingQmlContext(); - if (ctxt) + if (QQmlRefPointer<QQmlContextData> ctxt = scope.engine->callingQmlContext()) return Encode(scope.engine->newString(ctxt->resolvedUrl(url).toString())); else return Encode(scope.engine->newString(url.toString())); @@ -1156,7 +1155,7 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va QQmlEngine *engine = scope.engine->qmlEngine(); - QQmlContextData *context = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> context = scope.engine->callingQmlContext(); if (!context) { QQmlEngine *qmlEngine = scope.engine->qmlEngine(); if (qmlEngine) @@ -1164,7 +1163,7 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va } Q_ASSERT(context); QQmlContext *effectiveContext = nullptr; - if (context->isPragmaLibraryContext) + if (context->isPragmaLibraryContext()) effectiveContext = engine->rootContext(); else effectiveContext = context->asQQmlContext(); @@ -1288,16 +1287,15 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va QQmlEngine *engine = scope.engine->qmlEngine(); - QQmlContextData *context = scope.engine->callingQmlContext(); + QQmlRefPointer<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) - effectiveContext = nullptr; + QQmlRefPointer<QQmlContextData> effectiveContext + = context->isPragmaLibraryContext() ? nullptr : context; QString arg = argv[0].toQStringNoThrow(); if (arg.isEmpty()) @@ -2044,7 +2042,7 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value } if (context.isEmpty()) { - if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { + if (QQmlRefPointer<QQmlContextData> ctxt = scope.engine->callingQmlContext()) { QString path = ctxt->urlString(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); int lastDot = path.lastIndexOf(QLatin1Char('.')); diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 4c44bba43e..29946b1da4 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -292,9 +292,9 @@ void QQmlConnections::connectSignalsToMethods() if (!ddata) return; - QV4::ExecutionEngine *engine = ddata->context->engine->handle(); + QV4::ExecutionEngine *engine = ddata->context->engine()->handle(); - QQmlContextData *ctxtdata = ddata->outerContext; + QQmlRefPointer<QQmlContextData> ctxtdata = ddata->outerContext; for (int i = ddata->propertyCache->methodOffset(), end = ddata->propertyCache->methodOffset() + ddata->propertyCache->methodCount(); i < end; @@ -344,7 +344,7 @@ void QQmlConnections::connectSignalsToBindings() Q_D(QQmlConnections); QObject *target = this->target(); QQmlData *ddata = QQmlData::get(this); - QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr; + QQmlRefPointer<QQmlContextData> ctxtdata = ddata ? ddata->outerContext : nullptr; for (const QV4::CompiledData::Binding *binding : qAsConst(d->bindings)) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index 08c91d172e..710cc359b4 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -267,7 +267,6 @@ QQmlDelegateModel::~QQmlDelegateModel() cacheItem->object = nullptr; cacheItem->contextData->invalidate(); - Q_ASSERT(cacheItem->contextData->refCount == 1); cacheItem->contextData = nullptr; cacheItem->scriptRef -= 1; } @@ -957,10 +956,8 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod if (incubatorPriv->hadRequiredProperties()) { QQmlData *d = QQmlData::get(object); auto contextData = d ? d->context : nullptr; - if (contextData) { - contextData->hasExtraObject = true; - contextData->extraObject = modelItemToIncubate; - } + if (contextData) + contextData->setExtraObject(modelItemToIncubate); if (incubatorPriv->requiredProperties().empty()) return; @@ -1022,9 +1019,9 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod } } } else { - modelItemToIncubate->contextData->contextObject = modelItemToIncubate; + modelItemToIncubate->contextData->setContextObject(modelItemToIncubate); if (proxiedObject) - proxyContext->contextObject = proxiedObject; + proxyContext->setContextObject(proxiedObject); } } @@ -1039,7 +1036,7 @@ void QQDMIncubationTask::statusChanged(Status status) incubating->object = nullptr; if (incubating->contextData) { incubating->contextData->invalidate(); - Q_ASSERT(incubating->contextData->refCount == 1); + Q_ASSERT(incubating->contextData->refCount() == 1); incubating->contextData = nullptr; } incubating->scriptRef = 0; @@ -1174,7 +1171,7 @@ void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incuba cacheItem->scriptRef -= 1; if (cacheItem->contextData) { cacheItem->contextData->invalidate(); - Q_ASSERT(cacheItem->contextData->refCount == 1); + Q_ASSERT(cacheItem->contextData->refCount() == 1); } cacheItem->contextData = nullptr; @@ -1269,15 +1266,14 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ for (int i = 1; i < m_groupCount; ++i) cacheItem->incubationTask->index[i] = it.index[i]; - QQmlContextData *ctxt = new QQmlContextData; - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context.data())); + QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted( + QQmlContextData::get(creationContext ? creationContext : m_context.data())); cacheItem->contextData = ctxt; if (m_adaptorModel.hasProxyObject()) { if (QQmlAdaptorModelProxyInterface *proxy = qobject_cast<QQmlAdaptorModelProxyInterface *>(cacheItem)) { - ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, /*stronglyReferencedByParent*/true); + ctxt = QQmlContextData::createChild(cacheItem->contextData); QObject *proxied = proxy->proxiedObject(); cacheItem->incubationTask->proxiedObject = proxied; cacheItem->incubationTask->proxyContext = ctxt; @@ -2252,9 +2248,10 @@ void QQmlDelegateModelItem::childContextObjectDestroyed(QObject *childContextObj if (!contextData) return; - for (QQmlContextData *ctxt = contextData->childContexts; ctxt; ctxt = ctxt->nextChild) { - if (ctxt->contextObject == childContextObject) - ctxt->contextObject = nullptr; + for (QQmlRefPointer<QQmlContextData> ctxt = contextData->childContexts(); ctxt; + ctxt = ctxt->nextChild()) { + if (ctxt->contextObject() == childContextObject) + ctxt->setContextObject(nullptr); } } @@ -2359,8 +2356,8 @@ void QQmlDelegateModelItem::destroyObject() Q_ASSERT(data); if (data->ownContext) { data->ownContext->clearContext(); - if (data->ownContext->contextObject == object) - data->ownContext->contextObject = nullptr; + if (data->ownContext->contextObject() == object) + data->ownContext->setContextObject(nullptr); data->ownContext = nullptr; data->context = nullptr; } @@ -2379,12 +2376,12 @@ void QQmlDelegateModelItem::destroyObject() QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) { QQmlData *d = QQmlData::get(object); - QQmlContextData *context = d ? d->context : nullptr; - if (context && context->hasExtraObject) - return qobject_cast<QQmlDelegateModelItem *>(context->extraObject); - for (context = context ? context->parent : nullptr; context; context = context->parent) { + QQmlRefPointer<QQmlContextData> context = d ? d->context : nullptr; + if (QObject *extraObject = context ? context->extraObject() : nullptr) + return qobject_cast<QQmlDelegateModelItem *>(extraObject); + for (context = context ? context->parent() : nullptr; context; context = context->parent()) { if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>( - context->contextObject)) { + context->contextObject())) { return cacheItem; } } diff --git a/src/qmlmodels/qqmldelegatemodel_p_p.h b/src/qmlmodels/qqmldelegatemodel_p_p.h index 8684439508..20fb47f021 100644 --- a/src/qmlmodels/qqmldelegatemodel_p_p.h +++ b/src/qmlmodels/qqmldelegatemodel_p_p.h @@ -149,7 +149,7 @@ public: QV4::ExecutionEngine *v4; QQmlRefPointer<QQmlDelegateModelItemMetaType> const metaType; - QQmlContextDataRef contextData; + QQmlRefPointer<QQmlContextData> contextData; QPointer<QObject> object; QPointer<QQmlDelegateModelAttached> attached; QQDMIncubationTask *incubationTask; @@ -222,7 +222,7 @@ public: QQmlDelegateModelItem *incubating = nullptr; QQmlDelegateModelPrivate *vdm = nullptr; - QQmlContextData *proxyContext = nullptr; + QQmlRefPointer<QQmlContextData> proxyContext; QPointer<QObject> proxiedObject = nullptr; // the proxied object might disapear, so we use a QPointer instead of a raw one int index[QQmlListCompositor::MaximumGroupCount]; }; diff --git a/src/qmlmodels/qqmltableinstancemodel.cpp b/src/qmlmodels/qqmltableinstancemodel.cpp index 8c034356f3..4152cbe3ea 100644 --- a/src/qmlmodels/qqmltableinstancemodel.cpp +++ b/src/qmlmodels/qqmltableinstancemodel.cpp @@ -68,7 +68,7 @@ void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelIt if (modelItem->contextData) { modelItem->contextData->invalidate(); - Q_ASSERT(modelItem->contextData->refCount == 1); + Q_ASSERT(modelItem->contextData->refCount() == 1); modelItem->contextData = nullptr; } @@ -325,10 +325,10 @@ void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, } else { modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode); - QQmlContextData *ctxt = new QQmlContextData; QQmlContext *creationContext = modelItem->delegate->creationContext(); - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); - ctxt->contextObject = modelItem; + QQmlRefPointer<QQmlContextData> ctxt = QQmlContextData::createRefCounted( + QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); + ctxt->setContextObject(modelItem); modelItem->contextData = ctxt; QQmlComponentPrivate::get(modelItem->delegate)->incubateObject( diff --git a/src/quick/designer/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp index 70b568800d..48b6b9733b 100644 --- a/src/quick/designer/qquickdesignersupport.cpp +++ b/src/quick/designer/qquickdesignersupport.cpp @@ -390,13 +390,13 @@ void QQuickDesignerSupport::emitComponentCompleteSignalForAttachedProperty(QObje QQmlData *data = QQmlData::get(object); if (data && data->context) { - QQmlComponentAttached *componentAttached = data->context->componentAttached; + QQmlComponentAttached *componentAttached = data->context->componentAttacheds(); while (componentAttached) { if (componentAttached->parent()) if (componentAttached->parent() == object) emit componentAttached->completed(); - componentAttached = componentAttached->next; + componentAttached = componentAttached->next(); } } } @@ -429,7 +429,7 @@ int QQuickDesignerSupport::borderWidth(QQuickItem *item) void QQuickDesignerSupport::refreshExpressions(QQmlContext *context) { - QQmlContextPrivate::get(context)->data->refreshExpressions(); + QQmlContextData::get(context)->refreshExpressions(); } void QQuickDesignerSupport::setRootItem(QQuickView *view, QQuickItem *item) diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index e1d533f092..bf868fd449 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -962,12 +962,7 @@ QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV4Function *args) QV4::Scope scope(args->v4engine()); QV4::ScopedValue v(scope, (*args)[0]); QString arg = v->toQString(); - if (arg.isEmpty()) - return QUrl(); - - QQmlContextData *context = scope.engine->callingQmlContext(); - Q_ASSERT(context); - return context->resolvedUrl(QUrl(arg)); + return arg.isEmpty() ? QUrl() : scope.engine->callingQmlContext()->resolvedUrl(QUrl(arg)); } QV4::ReturnedValue QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV4Function *args, QObject *loader, bool *error) diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index 593181ee61..6100d1466a 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -154,9 +154,9 @@ void QQuickWindowQmlImpl::setWindowVisibility() QQmlError error; error.setObject(this); - const QQmlContextData* urlContext = data->context; + QQmlRefPointer<QQmlContextData> urlContext = data->context; while (urlContext && urlContext->url().isEmpty()) - urlContext = urlContext->parent; + urlContext = urlContext->parent(); error.setUrl(urlContext ? urlContext->url() : QUrl()); QString objectId = data->context->findObjectId(this); @@ -167,7 +167,7 @@ void QQuickWindowQmlImpl::setWindowVisibility() error.setDescription(QCoreApplication::translate("QQuickWindowQmlImpl", "Conflicting properties 'visible' and 'visibility'")); - QQmlEnginePrivate::get(data->context->engine)->warning(error); + QQmlEnginePrivate::get(data->context->engine())->warning(error); } if (d->visibility == AutomaticVisibility) { diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index 1cb30f5a8d..74e8d056c5 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -411,10 +411,9 @@ QQmlProperty QQuickPropertyChangesPrivate::property(const QString &property) { Q_Q(QQuickPropertyChanges); - QQmlContextData *context = nullptr; - if (QQmlData *ddata = QQmlData::get(q)) - context = ddata->outerContext; - QQmlProperty prop = QQmlPropertyPrivate::create(object, property, context); + QQmlData *ddata = QQmlData::get(q); + QQmlProperty prop = QQmlPropertyPrivate::create( + object, property, ddata ? ddata->outerContext : QQmlRefPointer<QQmlContextData>()); if (!prop.isValid()) { qmlWarning(q) << QQuickPropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(property); return QQmlProperty(); @@ -469,7 +468,7 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() a.specifiedObject = d->object; a.specifiedProperty = property; - QQmlContextData *context = QQmlContextData::get(qmlContext(this)); + QQmlRefPointer<QQmlContextData> context = QQmlContextData::get(qmlContext(this)); QQmlBinding *newBinding = nullptr; if (e.binding && e.binding->isTranslationBinding()) { diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index 12befeb1ec..3ae40bf687 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -1395,17 +1395,14 @@ void tst_QQmlEngineDebugService::invalidContexts() QQmlContext context(m_engine); getContexts(); QCOMPARE(m_dbg->rootContext().contexts.count(), base + 1); - QQmlContextData *contextData = QQmlContextData::get(&context); + QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(&context); contextData->invalidate(); getContexts(); QCOMPARE(m_dbg->rootContext().contexts.count(), base); - QQmlContextData *rootData = QQmlContextData::get(m_engine->rootContext()); + QQmlRefPointer<QQmlContextData> rootData = QQmlContextData::get(m_engine->rootContext()); rootData->invalidate(); getContexts(); QCOMPARE(m_dbg->rootContext().contexts.count(), 0); - contextData->setParent(rootData); // makes context valid again, but not root. - getContexts(); - QCOMPARE(m_dbg->rootContext().contexts.count(), 0); } void tst_QQmlEngineDebugService::createObjectOnDestruction() diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index 43cbd93396..79014a4450 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -35,7 +35,7 @@ #include <QtQuick> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickmousearea_p.h> -#include <private/qqmlcontext_p.h> +#include <private/qqmlguardedcontextdata_p.h> #include <private/qv4qmlcontext_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4qmlcontext_p.h> diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index 6754f22049..7b62469003 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -34,6 +34,7 @@ #include <QQmlComponent> #include <QQmlExpression> #include <private/qqmlcontext_p.h> +#include <private/qqmlguardedcontextdata_p.h> #include <private/qv4qmlcontext_p.h> #include <private/qv4object_p.h> #include "../../shared/util.h" @@ -737,7 +738,7 @@ void tst_qqmlcontext::qobjectDerived() // the engine QQmlContext context(engine.rootContext()); - QObject *o1 = component.create(&context); + QScopedPointer<QObject> o1(component.create(&context)); Q_UNUSED(o1); QCOMPARE(command.count, 2); @@ -800,32 +801,33 @@ void tst_qqmlcontext::contextLeak() QQmlData *ddata = QQmlData::get(obj.data()); QVERIFY(ddata); - QQmlContextData *context = ddata->context; + QQmlRefPointer<QQmlContextData> context = ddata->context; QVERIFY(context); - QVERIFY(!context->importedScripts.isNullOrUndefined()); - QCOMPARE(int(context->importedScripts.valueRef()->as<QV4::Object>()->getLength()), 1); - - { - QV4::Scope scope(ddata->jsWrapper.engine()); - QV4::ScopedValue scriptContextWrapper(scope); - scriptContextWrapper = context->importedScripts.valueRef()->as<QV4::Object>()->get(uint(0)); - scriptContext = scriptContextWrapper->as<QV4::QQmlContextWrapper>()->getContext(); - } + QVERIFY(!context->importedScripts().isNullOrUndefined()); + QCOMPARE(int(context->importedScripts().valueRef()->as<QV4::Object>()->getLength()), 1); + + QV4::Scope scope(ddata->jsWrapper.engine()); + QV4::ScopedValue scriptContextWrapper(scope); + scriptContextWrapper = context->importedScripts().valueRef() + ->as<QV4::Object>()->get(uint(0)); + scriptContext = scriptContextWrapper->as<QV4::QQmlContextWrapper>()->getContext(); } engine.collectGarbage(); // Each time a JS file (non-pragma-shared) is imported, we create a QQmlContext(Data) for it. // Make sure that context does not leak. - QVERIFY(scriptContext.isNull()); + // The QQmlGuardedContextData also holds a reference. Therefore, the refCount is still 1. + // All other references should be gone by now. + QCOMPARE(scriptContext->refCount(), 1); } static bool buildObjectList(QQmlContext *ctxt) { static QHash<QObject *, QString> deletedObjects; - QQmlContextData *p = QQmlContextData::get(ctxt); - QObject *object = p->contextObject; + QQmlRefPointer<QQmlContextData> p = QQmlContextData::get(ctxt); + QObject *object = p->contextObject(); if (object) { // If the object was actually deleted this is likely to crash in one way or another. // Either the memory is still intact, then we will probably find the objectName, or it is @@ -838,11 +840,11 @@ static bool buildObjectList(QQmlContext *ctxt) }); } - QQmlContextData *child = p->childContexts; + QQmlRefPointer<QQmlContextData> child = p->childContexts(); while (child) { if (!buildObjectList(child->asQQmlContext())) return false; - child = child->nextChild; + child = child->nextChild(); } return true; @@ -878,10 +880,14 @@ void tst_qqmlcontext::outerContextObject() void tst_qqmlcontext::contextObjectHierarchy() { QQmlEngine engine; - QQmlComponent component(&engine); - component.loadUrl(testFileUrl("contextObjectHierarchy.qml")); - QVERIFY(component.isReady()); - QScopedPointer<QObject> root(component.create()); + QScopedPointer<QObject> root; + { + // Drop the component after create(), to release the root context. + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("contextObjectHierarchy.qml")); + QVERIFY(component.isReady()); + root.reset(component.create()); + } QVERIFY(!root.isNull()); for (const QObject *child : root->children()) diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index cfbc329198..b3e4296a13 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -386,7 +386,7 @@ private slots: private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); - static void verifyContextLifetime(QQmlContextData *ctxt); + static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt); // When calling into JavaScript, the specific type of the return value can differ if that return // value is a number. This is not only the case for non-integral numbers, or numbers that do not @@ -4289,16 +4289,16 @@ void tst_qqmlecmascript::singletonTypeResolution() delete object; } -void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { - QQmlContextData *childCtxt = ctxt->childContexts; +void tst_qqmlecmascript::verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt) { + QQmlRefPointer<QQmlContextData> childCtxt = ctxt->childContexts(); - if (!ctxt->importedScripts.isNullOrUndefined()) { - QV4::ExecutionEngine *v4 = ctxt->engine->handle(); + if (!ctxt->importedScripts().isNullOrUndefined()) { + QV4::ExecutionEngine *v4 = ctxt->engine()->handle(); QV4::Scope scope(v4); - QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts.value()); + QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts().value()); QV4::Scoped<QV4::QQmlContextWrapper> qml(scope); for (quint32 i = 0; i < scripts->getLength(); ++i) { - QQmlContextData *scriptContext, *newContext; + QQmlRefPointer<QQmlContextData> scriptContext, newContext; qml = scripts->get(i); scriptContext = qml ? qml->getContext() : nullptr; @@ -4310,17 +4310,16 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { Q_UNUSED(temporaryScope) } - ctxt->engine->collectGarbage(); + ctxt->engine()->collectGarbage(); qml = scripts->get(i); newContext = qml ? qml->getContext() : nullptr; - QCOMPARE(scriptContext, newContext); + QCOMPARE(scriptContext.data(), newContext.data()); } } while (childCtxt) { verifyContextLifetime(childCtxt); - - childCtxt = childCtxt->nextChild; + childCtxt = childCtxt->nextChild(); } } @@ -4592,9 +4591,7 @@ void tst_qqmlecmascript::importScripts() QVERIFY(!object); } else { QVERIFY(object != nullptr); - - QQmlContextData *ctxt = QQmlContextData::get(engine.rootContext()); - tst_qqmlecmascript::verifyContextLifetime(ctxt); + tst_qqmlecmascript::verifyContextLifetime(QQmlContextData::get(engine.rootContext())); for (int i = 0; i < propertyNames.size(); ++i) QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i)); @@ -6138,24 +6135,22 @@ void tst_qqmlecmascript::nullObjectInitializer() // Test that bindings don't evaluate once the engine has been destroyed void tst_qqmlecmascript::deletedEngine() { - QQmlEngine *engine = new QQmlEngine; - QQmlComponent component(engine, testFileUrl("deletedEngine.qml")); + QScopedPointer<QQmlEngine> engine(new QQmlEngine); + QQmlComponent component(engine.data(), testFileUrl("deletedEngine.qml")); + QScopedPointer<QObject> object(component.create()); - QObject *object = component.create(); QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 39); object->setProperty("b", QVariant(9)); QCOMPARE(object->property("a").toInt(), 117); - delete engine; + engine.reset(); // This drops the engine's context hierarchy and the object gets notified. QCOMPARE(object->property("a").toInt(), 0); object->setProperty("b", QVariant(10)); object->setProperty("b", QVariant()); QCOMPARE(object->property("a").toInt(), 0); - - delete object; } // Test the crashing part of QTBUG-9705 @@ -6217,25 +6212,30 @@ void tst_qqmlecmascript::variants() void tst_qqmlecmascript::qtbug_9792() { QQmlEngine engine; - QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml")); - QQmlContext *context = new QQmlContext(engine.rootContext()); + QScopedPointer<QQmlContext> context(new QQmlContext(engine.rootContext())); + QScopedPointer<QObject> object; - MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context)); - QVERIFY(object != nullptr); + MyQmlObject *myQmlObject = nullptr; + { + // Drop the component after creation as that holds a strong reference + // to the root QQmlContextData. We want the context data to be destroyed. + QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml")); + object.reset(component.create(context.data())); + myQmlObject = qobject_cast<MyQmlObject*>(object.data()); + } + QVERIFY(myQmlObject != nullptr); QTest::ignoreMessage(QtDebugMsg, "Hello world!"); - object->basicSignal(); + myQmlObject->basicSignal(); - delete context; + context.reset(); QQmlTestMessageHandler messageHandler; - object->basicSignal(); + myQmlObject->basicSignal(); QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString())); - - delete object; } // Verifies that QPointer<>s used in the vmemetaobject are cleaned correctly diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 31a4135d89..2c0e5f1d8c 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -149,7 +149,7 @@ void CustomBinding::componentComplete() int bindingId = binding->value.compiledScriptIndex; - QQmlContextData *context = QQmlContextData::get(qmlContext(this)); + QQmlRefPointer<QQmlContextData> context = QQmlContextData::get(qmlContext(this)); QQmlProperty property(m_target, name, qmlContext(this)); QV4::Scope scope(qmlEngine(this)->handle()); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 6d932c2665..f2fa301565 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -4871,8 +4871,9 @@ static void beginDeferredOnce(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::ConstructionState *state = new QQmlComponentPrivate::ConstructionState; state->completePending = true; - QQmlContextData *creationContext = nullptr; - state->creator.reset(new QQmlObjectCreator(deferData->context->parent, deferData->compilationUnit, creationContext)); + state->creator.reset(new QQmlObjectCreator( + deferData->context->parent(), deferData->compilationUnit, + QQmlRefPointer<QQmlContextData>())); enginePriv->inProgressCreations++; @@ -4904,7 +4905,7 @@ static void testExecuteDeferredOnce(const QQmlProperty &property) QObject *object = property.object(); QQmlData *data = QQmlData::get(object); if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine()); QQmlComponentPrivate::DeferredState state; beginDeferredOnce(ep, property, &state); diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp index 2e040eec18..956759668e 100644 --- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp +++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp @@ -32,6 +32,7 @@ #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlcomponent.h> #include <private/qmetaobjectbuilder_p.h> +#include <private/qqmlcontextdata_p.h> #include <QCryptographicHash> #include "../../shared/util.h" diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp index 33b8742170..1ede8968a4 100644 --- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp +++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp @@ -1148,8 +1148,7 @@ void tst_QQuickRepeater::contextProperties() while (!items.isEmpty()) { QQuickItem *item = items.dequeue(); - QQmlContextData *data = QQmlContextData::get(qmlContext(item)); - QVERIFY(!data->hasExtraObject); + QVERIFY(!QQmlContextData::get(qmlContext(item))->extraObject()); for (QQuickItem *child : item->childItems()) items.enqueue(child); } diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index 66069c48cb..d5ac20cafc 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -4219,11 +4219,15 @@ void tst_qquickvisualdatamodel::invalidContext() engine.rootContext()->setContextProperty("myModel", &model); QScopedPointer<QQmlContext> context(new QQmlContext(engine.rootContext())); + QScopedPointer<QObject> obj; + { + // The component keeps a reference to the root context as long as the engine lives. + // In order to drop the root context we need to drop the component first. + QQmlComponent c(&engine, testFileUrl("visualdatamodel.qml")); + obj.reset(c.create(context.data())); + } - QQmlComponent c(&engine, testFileUrl("visualdatamodel.qml")); - - - QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create(context.data())); + QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel *>(obj.get()); QVERIFY(visualModel); QQuickItem *item = qobject_cast<QQuickItem*>(visualModel->object(4)); |