From c407d79f79c67f2f2bb84efc93061fd57fe4cf54 Mon Sep 17 00:00:00 2001
From: Aaron Kennedy <aaron.kennedy@nokia.com>
Date: Thu, 14 Oct 2010 18:22:57 +1000
Subject: Correctly splice properties from derived metaobjects together

Task-number: QTBUG-14449
---
 src/declarative/qml/qdeclarativepropertycache.cpp  | 77 ++++++----------------
 src/declarative/qml/qdeclarativepropertycache_p.h  |  9 ++-
 .../data/propertySplicing.qml                      | 10 +++
 .../qdeclarativeecmascript/testtypes.cpp           |  1 +
 .../declarative/qdeclarativeecmascript/testtypes.h |  9 +++
 .../tst_qdeclarativeecmascript.cpp                 | 13 ++++
 6 files changed, 61 insertions(+), 58 deletions(-)
 create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/propertySplicing.qml

diff --git a/src/declarative/qml/qdeclarativepropertycache.cpp b/src/declarative/qml/qdeclarativepropertycache.cpp
index 9e1ceb8..82fa98f 100644
--- a/src/declarative/qml/qdeclarativepropertycache.cpp
+++ b/src/declarative/qml/qdeclarativepropertycache.cpp
@@ -252,7 +252,8 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
     }
 
     int methodCount = metaObject->methodCount();
-    int methodOffset = qMax(2, metaObject->methodOffset()); // 2 to block the destroyed signal
+    // 3 to block the destroyed signal and the deleteLater() slot
+    int methodOffset = qMax(3, metaObject->methodOffset()); 
 
     methodIndexCache.resize(methodCount);
     for (int ii = methodOffset; ii < methodCount; ++ii) {
@@ -268,17 +269,17 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
         RData *data = new RData;
         data->identifier = enginePriv->objectClass->createPersistentIdentifier(methodName);
 
-        if (stringCache.contains(methodName)) {
-            stringCache[methodName]->release();
-            identifierCache[data->identifier.identifier]->release();
-        }
-
         data->load(m);
         if (m.methodType() == QMetaMethod::Slot || m.methodType() == QMetaMethod::Method) 
             data->flags |= methodFlags;
         else if (m.methodType() == QMetaMethod::Signal)
             data->flags |= signalFlags;
 
+        if (stringCache.contains(methodName)) {
+            stringCache[methodName]->release();
+            identifierCache[data->identifier.identifier]->release();
+        }
+
         methodIndexCache[ii] = data;
 
         stringCache.insert(methodName, data);
@@ -287,6 +288,16 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
     }
 }
 
+void QDeclarativePropertyCache::updateRecur(QDeclarativeEngine *engine, const QMetaObject *metaObject)
+{
+    if (!metaObject)
+        return;
+
+    updateRecur(engine, metaObject->superClass());
+
+    append(engine, metaObject);
+}
+
 void QDeclarativePropertyCache::update(QDeclarativeEngine *engine, const QMetaObject *metaObject)
 {
     Q_ASSERT(engine);
@@ -295,57 +306,11 @@ void QDeclarativePropertyCache::update(QDeclarativeEngine *engine, const QMetaOb
 
     clear();
 
-    // ### The properties/methods should probably be spliced on a per-metaobject basis
-    int propCount = metaObject->propertyCount();
-
-    indexCache.resize(propCount);
-    for (int ii = propCount - 1; ii >= 0; --ii) {
-        QMetaProperty p = metaObject->property(ii);
-        if (!p.isScriptable()) {
-            indexCache[ii] = 0;
-            continue;
-        }
-        QString propName = QString::fromUtf8(p.name());
+    // Optimization to prevent unnecessary reallocation of lists
+    indexCache.reserve(metaObject->propertyCount());
+    methodIndexCache.reserve(metaObject->methodCount());
 
-        RData *data = new RData;
-        data->identifier = enginePriv->objectClass->createPersistentIdentifier(propName);
-
-        data->load(p, engine);
-
-        indexCache[ii] = data;
-
-        if (stringCache.contains(propName))
-            continue;
-
-        stringCache.insert(propName, data);
-        identifierCache.insert(data->identifier.identifier, data);
-        data->addref();
-        data->addref();
-    }
-
-    int methodCount = metaObject->methodCount();
-    for (int ii = methodCount - 1; ii >= 3; --ii) { // >=3 to block the destroyed signal and deleteLater() slot
-        QMetaMethod m = metaObject->method(ii);
-        if (m.access() == QMetaMethod::Private)
-            continue;
-        QString methodName = QString::fromUtf8(m.signature());
-
-        int parenIdx = methodName.indexOf(QLatin1Char('('));
-        Q_ASSERT(parenIdx != -1);
-        methodName = methodName.left(parenIdx);
-
-        if (stringCache.contains(methodName))
-            continue;
-
-        RData *data = new RData;
-        data->identifier = enginePriv->objectClass->createPersistentIdentifier(methodName);
-
-        data->load(m);
-
-        stringCache.insert(methodName, data);
-        identifierCache.insert(data->identifier.identifier, data);
-        data->addref();
-    }
+    updateRecur(engine,metaObject);
 }
 
 QDeclarativePropertyCache::Data *
diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h
index 79b126d..922010d 100644
--- a/src/declarative/qml/qdeclarativepropertycache_p.h
+++ b/src/declarative/qml/qdeclarativepropertycache_p.h
@@ -96,7 +96,7 @@ public:
                     IsVMEFunction     = 0x00000400,
                     HasArguments      = 0x00000800,
                     IsSignal          = 0x00001000,
-                    IsVMESignal       = 0x00002000,
+                    IsVMESignal       = 0x00002000
         };
         Q_DECLARE_FLAGS(Flags, Flag)
 
@@ -105,7 +105,10 @@ public:
         Flags flags;
         int propType;
         int coreIndex;
-        int notifyIndex;
+        union {
+            int notifyIndex; // When !IsFunction
+            int relatedIndex; // When IsFunction
+        };
 
         static Flags flagsForProperty(const QMetaProperty &, QDeclarativeEngine *engine = 0);
         void load(const QMetaProperty &, QDeclarativeEngine *engine = 0);
@@ -152,6 +155,8 @@ private:
     typedef QHash<QString, RData *> StringCache;
     typedef QHash<QScriptDeclarativeClass::Identifier, RData *> IdentifierCache;
 
+    void updateRecur(QDeclarativeEngine *, const QMetaObject *);
+
     QDeclarativeEngine *engine;
     IndexCache indexCache;
     IndexCache methodIndexCache;
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertySplicing.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertySplicing.qml
new file mode 100644
index 0000000..7deb84a
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertySplicing.qml
@@ -0,0 +1,10 @@
+import Qt.test 1.0
+import QtQuick 1.0
+
+MyDerivedObject {
+    property bool test: false
+        
+    Component.onCompleted: {
+        test = intProperty()
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
index 810a0f7..94135f9 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
@@ -109,6 +109,7 @@ void registerTypes()
     qmlRegisterExtendedType<MyBaseExtendedObject, BaseExtensionObject>("Qt.test", 1,0, "MyBaseExtendedObject");
     qmlRegisterExtendedType<MyExtendedObject, ExtensionObject>("Qt.test", 1,0, "MyExtendedObject");
     qmlRegisterType<MyTypeObject>("Qt.test", 1,0, "MyTypeObject");
+    qmlRegisterType<MyDerivedObject>("Qt.test", 1,0, "MyDerivedObject");
     qmlRegisterType<NumberAssignment>("Qt.test", 1,0, "NumberAssignment");
     qmlRegisterExtendedType<DefaultPropertyExtendedObject, DefaultPropertyExtensionObject>("Qt.test", 1,0, "DefaultPropertyExtendedObject");
     qmlRegisterType<OverrideDefaultPropertyObject>("Qt.test", 1,0, "OverrideDefaultPropertyObject");
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
index 220318d..182c4fa 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
@@ -562,6 +562,15 @@ signals:
 };
 Q_DECLARE_OPERATORS_FOR_FLAGS(MyTypeObject::MyFlags)
 
+class MyDerivedObject : public MyTypeObject
+{
+    Q_OBJECT
+public:
+    Q_INVOKABLE bool intProperty() const {
+        return true;
+    }
+};
+
 Q_DECLARE_METATYPE(QScriptValue);
 class MyInvokableObject : public QObject
 {
diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
index 02832f3..66dc160 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
@@ -139,6 +139,7 @@ private slots:
     void strictlyEquals();
     void compiled();
     void numberAssignment();
+    void propertySplicing();
 
     void bug1();
     void bug2();
@@ -2175,6 +2176,18 @@ void tst_qdeclarativeecmascript::numberAssignment()
     delete object;
 }
 
+void tst_qdeclarativeecmascript::propertySplicing()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("propertySplicing.qml"));
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    delete object;
+}
+
 // Test that assigning a null object works 
 // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
 void tst_qdeclarativeecmascript::nullObjectBinding()
-- 
cgit v0.12