aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h3
-rw-r--r--src/qml/jsruntime/qv4referenceobject.cpp70
-rw-r--r--src/qml/jsruntime/qv4referenceobject_p.h64
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;