aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/qml/qqmlengine.cpp28
-rw-r--r--src/qml/qml/qqmlnotifier.cpp56
-rw-r--r--src/qml/qml/qqmlnotifier_p.h7
-rw-r--r--tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp34
4 files changed, 96 insertions, 29 deletions
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 1f27e4ecb3..392cbe9371 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -1540,16 +1540,28 @@ QQmlDataExtended::~QQmlDataExtended()
void QQmlData::NotifyList::layout(QQmlNotifierEndpoint *endpoint)
{
- if (endpoint->next)
- layout(endpoint->next);
+ // Add a temporary sentinel at beginning of list. This will be overwritten
+ // when the end point is inserted into the notifies further down.
+ endpoint->prev = 0;
- int index = endpoint->sourceSignal;
- index = qMin(index, 0xFFFF - 1);
+ while (endpoint->next) {
+ Q_ASSERT(reinterpret_cast<QQmlNotifierEndpoint *>(endpoint->next->prev) == endpoint);
+ endpoint = endpoint->next;
+ }
+
+ while (endpoint) {
+ QQmlNotifierEndpoint *ep = (QQmlNotifierEndpoint *) endpoint->prev;
+
+ int index = endpoint->sourceSignal;
+ index = qMin(index, 0xFFFF - 1);
- endpoint->next = notifies[index];
- if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifies[index];
- notifies[index] = endpoint;
+ endpoint->next = notifies[index];
+ if (endpoint->next) endpoint->next->prev = &endpoint->next;
+ endpoint->prev = &notifies[index];
+ notifies[index] = endpoint;
+
+ endpoint = ep;
+ }
}
void QQmlData::NotifyList::layout()
diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp
index 4ce5be4d1a..ea3f7a1530 100644
--- a/src/qml/qml/qqmlnotifier.cpp
+++ b/src/qml/qml/qqmlnotifier.cpp
@@ -51,30 +51,52 @@ static Callback QQmlNotifier_callbacks[] = {
QQmlVMEMetaObjectEndpoint_callback
};
+namespace {
+ struct NotifyListTraversalData {
+ NotifyListTraversalData(QQmlNotifierEndpoint *ep = 0)
+ : originalSenderPtr(0)
+ , disconnectWatch(0)
+ , endpoint(ep)
+ {}
+
+ qintptr originalSenderPtr;
+ qintptr *disconnectWatch;
+ QQmlNotifierEndpoint *endpoint;
+ };
+}
+
void QQmlNotifier::emitNotify(QQmlNotifierEndpoint *endpoint, void **a)
{
- qintptr originalSenderPtr;
- qintptr *disconnectWatch;
-
- if (!endpoint->isNotifying()) {
- originalSenderPtr = endpoint->senderPtr;
- disconnectWatch = &originalSenderPtr;
- endpoint->senderPtr = qintptr(disconnectWatch) | 0x1;
- } else {
- disconnectWatch = (qintptr *)(endpoint->senderPtr & ~0x1);
+ QVarLengthArray<NotifyListTraversalData> stack;
+ while (endpoint) {
+ stack.append(NotifyListTraversalData(endpoint));
+ endpoint = endpoint->next;
}
- if (endpoint->next)
- emitNotify(endpoint->next, a);
+ int i = 0;
+ for (; i < stack.size(); ++i) {
+ NotifyListTraversalData &data = stack[i];
+
+ if (!data.endpoint->isNotifying()) {
+ data.originalSenderPtr = data.endpoint->senderPtr;
+ data.disconnectWatch = &data.originalSenderPtr;
+ data.endpoint->senderPtr = qintptr(data.disconnectWatch) | 0x1;
+ } else {
+ data.disconnectWatch = (qintptr *)(data.endpoint->senderPtr & ~0x1);
+ }
+ }
- if (*disconnectWatch) {
+ while (--i >= 0) {
+ const NotifyListTraversalData &data = stack.at(i);
+ if (*data.disconnectWatch) {
- Q_ASSERT(QQmlNotifier_callbacks[endpoint->callback]);
- QQmlNotifier_callbacks[endpoint->callback](endpoint, a);
+ Q_ASSERT(QQmlNotifier_callbacks[data.endpoint->callback]);
+ QQmlNotifier_callbacks[data.endpoint->callback](data.endpoint, a);
- if (disconnectWatch == &originalSenderPtr && originalSenderPtr) {
- // End of notifying, restore values
- endpoint->senderPtr = originalSenderPtr;
+ if (data.disconnectWatch == &data.originalSenderPtr && data.originalSenderPtr) {
+ // End of notifying, restore values
+ data.endpoint->senderPtr = data.originalSenderPtr;
+ }
}
}
}
diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h
index 2a35dcda12..2742bfc84b 100644
--- a/src/qml/qml/qqmlnotifier_p.h
+++ b/src/qml/qml/qqmlnotifier_p.h
@@ -60,6 +60,8 @@ private:
class QQmlEngine;
class QQmlNotifierEndpoint
{
+ QQmlNotifierEndpoint *next;
+ QQmlNotifierEndpoint **prev;
public:
inline QQmlNotifierEndpoint();
inline ~QQmlNotifierEndpoint();
@@ -103,9 +105,6 @@ private:
// The index is in the range returned by QObjectPrivate::signalIndex().
// This is different from QMetaMethod::methodIndex().
signed int sourceSignal:28;
-
- QQmlNotifierEndpoint *next;
- QQmlNotifierEndpoint **prev;
};
QQmlNotifier::QQmlNotifier()
@@ -137,7 +136,7 @@ void QQmlNotifier::notify()
}
QQmlNotifierEndpoint::QQmlNotifierEndpoint()
-: senderPtr(0), callback(None), sourceSignal(-1), next(0), prev(0)
+: next(0), prev(0), senderPtr(0), callback(None), sourceSignal(-1)
{
}
diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
index 81215f7a18..4f1c9ae53e 100644
--- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
+++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
@@ -165,6 +165,7 @@ private slots:
void readProperty();
void propertyChange();
void disconnectOnDestroy();
+ void lotsOfBindings();
private:
void createObjects();
@@ -312,6 +313,39 @@ void tst_qqmlnotifier::disconnectOnDestroy()
exportedObject->verifyReceiverCount();
}
+class TestObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int a READ a NOTIFY aChanged)
+
+public:
+ int a() const { return 0; }
+
+signals:
+ void aChanged();
+};
+
+void tst_qqmlnotifier::lotsOfBindings()
+{
+ TestObject o;
+ QQmlEngine *e = new QQmlEngine;
+
+ e->rootContext()->setContextProperty(QStringLiteral("test"), &o);
+
+ QList<QQmlComponent *> components;
+ for (int i = 0; i < 20000; ++i) {
+ QQmlComponent *component = new QQmlComponent(e);
+ component->setData("import QtQuick 2.0; Item { width: test.a; }", QUrl());
+ component->create(e->rootContext());
+ components.append(component);
+ }
+
+ o.aChanged();
+
+ qDeleteAll(components);
+ delete e;
+}
+
QTEST_MAIN(tst_qqmlnotifier)
#include "tst_qqmlnotifier.moc"