diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/qml/jsruntime/qv4dateobject_p.h | 3 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4referenceobject.cpp | 70 | ||||
| -rw-r--r-- | src/qml/jsruntime/qv4referenceobject_p.h | 64 |
3 files changed, 90 insertions, 47 deletions
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 6e6546bacc..844fe11457 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -224,6 +224,9 @@ DECLARE_HEAP_OBJECT(DateObject, ReferenceObject) { QV4::Scope scope(internalClass->engine); QV4::ScopedObject o(scope, object()); + if (!isConnected() && QV4::ReferenceObject::shouldConnect(this)) + QV4::ReferenceObject::connect(this); + bool wasRead = false; if (isVariant()) { QVariant variant; diff --git a/src/qml/jsruntime/qv4referenceobject.cpp b/src/qml/jsruntime/qv4referenceobject.cpp index 336b715ca0..93be59b212 100644 --- a/src/qml/jsruntime/qv4referenceobject.cpp +++ b/src/qml/jsruntime/qv4referenceobject.cpp @@ -550,6 +550,76 @@ void Heap::ReferenceObject::connectToBindable(QObject *obj, int property, QQmlEn QObject::connect(obj, &QObject::destroyed, [this]() { setDirty(true); })); } +bool ReferenceObject::shouldConnect(Heap::ReferenceObject *ref) +{ + if (ref->isAlwaysDirty()) + return false; + + // We want to connect when we are reading the reference object from a statement that is not + // its creation statement. In other words, when first storing it in a variable and then + // reusing it, potentially multiple times. This ensures that the cost of the connection itself + // is only paid when the user separated the declaration from the usage in their code, in the + // hope that this catches more complex and frequent uses, like accesses in a loop, where + // unnecessary copies would impact performance more significantly, and not one time accesses. + if (CppStackFrame *frame = ref->internalClass->engine->currentStackFrame) { + const auto *refObject = static_cast<Heap::ReferenceObject *>(ref); + if (frame->v4Function && frame->v4Function == refObject->function() + && frame->statementNumber() == refObject->statementIndex()) { + return false; + } + } + return true; +} + +void ReferenceObject::connect(Heap::ReferenceObject *ref) +{ + int property = ref->property(); + Heap::Object *object = ref->object(); + + while (object + && object->internalClass->vtable->type != Managed::Type_V4QObjectWrapper + && object->internalClass->vtable->type != Managed::Type_QMLTypeWrapper) + { + const auto type = object->internalClass->vtable->type; + if (type != Managed::Type_V4ReferenceObject + && type != Managed::Type_V4Sequence + && type != Managed::Type_DateObject + && type != Managed::Type_QMLValueTypeWrapper) { + break; + } + + property = static_cast<QV4::Heap::ReferenceObject *>(object)->property(); + object = static_cast<QV4::Heap::ReferenceObject *>(object)->object(); + } + + const auto doConnect = [&](auto wrapper) { + // The object may be valid on the first read but not on the second, check for that. + if (QObject *obj = wrapper->object()) { + auto *refObject = static_cast<Heap::ReferenceObject *>(ref); + + auto *qmlEngine = object->internalClass->engine->qmlEngine(); + if (qmlEngine && obj->metaObject()->property(property).isBindable()) + refObject->connectToBindable(obj, property, qmlEngine); + else if (qmlEngine && obj->metaObject()->property(property).hasNotifySignal()) + refObject->connectToNotifySignal(obj, property, qmlEngine); + } + }; + + if (object && object->internalClass->vtable->type == Managed::Type_V4QObjectWrapper) { + doConnect(static_cast<QV4::Heap::QObjectWrapper *>(object)); + } else if (object && object->internalClass->vtable->type == Managed::Type_QMLTypeWrapper) { + auto wrapper = static_cast<QV4::Heap::QQmlTypeWrapper *>(object); + Scope scope(object->internalClass->engine); + Scoped<QV4::QQmlTypeWrapper> scopedWrapper(scope, wrapper); + doConnect(scopedWrapper); + } + + if (!ref->isConnected()) { + // Mark AlwaysDirty to prevent further connection failures on every read + ref->setAlwaysDirty(true); + } +} + } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4referenceobject_p.h b/src/qml/jsruntime/qv4referenceobject_p.h index c179e83363..c3a80db2c1 100644 --- a/src/qml/jsruntime/qv4referenceobject_p.h +++ b/src/qml/jsruntime/qv4referenceobject_p.h @@ -47,7 +47,8 @@ DECLARE_HEAP_OBJECT(ReferenceObject, Object) { CanWriteBack = 1 << 0, IsVariant = 1 << 1, EnforcesLocation = 1 << 2, - IsDirty = 1 << 3, + IsDirty = 1 << 3, + IsAlwaysDirty = 1 << 4, }; Q_DECLARE_FLAGS(Flags, Flag); @@ -57,52 +58,11 @@ DECLARE_HEAP_OBJECT(ReferenceObject, Object) { m_property = property; m_flags = flags; - while (object && - object->internalClass->vtable->type != Managed::Type_V4QObjectWrapper && - object->internalClass->vtable->type != Managed::Type_QMLTypeWrapper) - { - if (!(object->internalClass->vtable->type == Managed::Type_V4ReferenceObject) && - !(object->internalClass->vtable->type == Managed::Type_V4Sequence) && - !(object->internalClass->vtable->type == Managed::Type_DateObject) && - !(object->internalClass->vtable->type == Managed::Type_QMLValueTypeWrapper)) - { - break; - } - - property = static_cast<QV4::Heap::ReferenceObject*>(object)->property(); - object = static_cast<QV4::Heap::ReferenceObject*>(object)->object(); - } - - if (object && object->internalClass->vtable->type == Managed::Type_V4QObjectWrapper) - { - auto wrapper = static_cast<QV4::Heap::QObjectWrapper*>(object); - QObject* obj = wrapper->object(); - - if (obj->metaObject()->property(property).isBindable() && internalClass->engine->qmlEngine()) - connectToBindable(obj, property, internalClass->engine->qmlEngine()); - else if (obj->metaObject()->property(property).hasNotifySignal() && internalClass->engine->qmlEngine()) - connectToNotifySignal(obj, property, internalClass->engine->qmlEngine()); - } - - if (object && object->internalClass->vtable->type == Managed::Type_QMLTypeWrapper) { - auto wrapper = static_cast<QV4::Heap::QQmlTypeWrapper*>(object); - - Scope scope(internalClass->engine); - Scoped<QV4::QQmlTypeWrapper> scopedWrapper(scope, wrapper); - QObject* obj = scopedWrapper->object(); - - if (obj->metaObject()->property(property).isBindable() && internalClass->engine->qmlEngine()) - connectToBindable(obj, property, internalClass->engine->qmlEngine()); - else if (obj->metaObject()->property(property).hasNotifySignal() && internalClass->engine->qmlEngine()) - connectToNotifySignal(obj, property, internalClass->engine->qmlEngine()); - } - - // If we could not connect to anything we don't have a way to - // dirty on-demand and thus should be in an always dirty state - // to ensure that reads go through. - if (!isConnected()) - setDirty(true); - + setDirty(true); + if (CppStackFrame *frame = internalClass->engine->currentStackFrame) + setLocation(frame->v4Function, frame->statementNumber()); + else + setLocation(nullptr, -1); Object::init(); } @@ -144,6 +104,10 @@ DECLARE_HEAP_OBJECT(ReferenceObject, Object) { bool isDirty() const { return hasFlag(IsDirty); } void setDirty(bool dirty) { setFlag(IsDirty, dirty); } + + bool isAlwaysDirty() const { return hasFlag(IsAlwaysDirty); } + void setAlwaysDirty(bool alwaysDirty) { setFlag(IsAlwaysDirty, alwaysDirty); } + bool isConnected() { return (referenceEndpoint && referenceEndpoint->isConnected()) || bindableNotifier; } @@ -207,6 +171,9 @@ struct ReferenceObject : public Object public: static constexpr const int AllProperties = -1; + static bool shouldConnect(Heap::ReferenceObject *ref); + static void connect(Heap::ReferenceObject *ref); + template<typename HeapObject> static bool readReference(HeapObject *ref) { @@ -219,6 +186,9 @@ public: QV4::Scope scope(ref->internalClass->engine); QV4::ScopedObject object(scope, ref->object()); + if (!ref->isConnected() && shouldConnect(ref)) + connect(ref); + bool wasRead = false; if (ref->isVariant()) { QVariant variant; |
