Fix hasOwnProperty on various types wrapped in QML
authorSimon Hausmann <simon.hausmann@digia.com>
Thu, 8 Aug 2013 10:52:56 +0000 (12:52 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Thu, 8 Aug 2013 13:35:18 +0000 (15:35 +0200)
* Change semantics of Object::query to not walk the prototype chain but let the
  caller do that where needed (__hasProperty__)
* Re-implement query in various places
* Implement method_hasOwnProperty to fall back to query() if getOwnProperty failed
* Fix missing prototype initialization in some qml wrappers, as well as missing base
  class calls to ::get()

Change-Id: Ic2a702fd5ff3be2ff3c8317a8a24f99940a9594f
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/jsruntime/qv4object.cpp
src/qml/jsruntime/qv4object_p.h
src/qml/jsruntime/qv4objectproto.cpp
src/qml/qml/qqmllistwrapper.cpp
src/qml/qml/qqmltypewrapper.cpp
src/qml/qml/qqmltypewrapper_p.h
src/qml/qml/qqmlvaluetypewrapper.cpp
src/qml/qml/qqmlvaluetypewrapper_p.h
tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp

index edfb535..ad18edd 100644 (file)
@@ -411,7 +411,30 @@ bool Object::__hasProperty__(String *name) const
 {
     if (__getPropertyDescriptor__(name))
         return true;
-    return !query(name).isEmpty();
+
+    const Object *o = this;
+    while (o) {
+        if (!o->query(name).isEmpty())
+            return true;
+        o = o->prototype;
+    }
+
+    return false;
+}
+
+bool Object::__hasProperty__(uint index) const
+{
+    if (__getPropertyDescriptor__(index))
+        return true;
+
+    const Object *o = this;
+    while (o) {
+        if (!o->queryIndexed(index).isEmpty())
+            return true;
+        o = o->prototype;
+    }
+
+    return false;
 }
 
 Value Object::get(Managed *m, String *name, bool *hasProperty)
@@ -441,32 +464,26 @@ PropertyAttributes Object::query(const Managed *m, String *name)
         return queryIndexed(m, idx);
 
     const Object *o = static_cast<const Object *>(m);
-    while (o) {
-        uint idx = o->internalClass->find(name);
-        if (idx < UINT_MAX)
-            return o->internalClass->propertyData[idx];
+    idx = o->internalClass->find(name);
+    if (idx < UINT_MAX)
+        return o->internalClass->propertyData[idx];
 
-        o = o->prototype;
-    }
     return Attr_Invalid;
 }
 
 PropertyAttributes Object::queryIndexed(const Managed *m, uint index)
 {
     const Object *o = static_cast<const Object *>(m);
-    while (o) {
-        uint pidx = o->propertyIndexFromArrayIndex(index);
-        if (pidx < UINT_MAX) {
-            if (o->arrayAttributes)
-                return o->arrayAttributes[pidx];
+    uint pidx = o->propertyIndexFromArrayIndex(index);
+    if (pidx < UINT_MAX) {
+        if (o->arrayAttributes)
+            return o->arrayAttributes[pidx];
+        return Attr_Data;
+    }
+    if (o->isStringObject()) {
+        Property *p = static_cast<const StringObject *>(o)->getIndex(index);
+        if (p)
             return Attr_Data;
-        }
-        if (o->isStringObject()) {
-            Property *p = static_cast<const StringObject *>(o)->getIndex(index);
-            if (p)
-                return Attr_Data;
-        }
-        o = o->prototype;
     }
     return Attr_Invalid;
 }
index a9f947b..ffcca13 100644 (file)
@@ -131,9 +131,7 @@ struct Q_QML_EXPORT Object: Managed {
     Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const;
 
     bool __hasProperty__(String *name) const;
-    bool __hasProperty__(uint index) const {
-        return __getPropertyDescriptor__(index);
-    }
+    bool __hasProperty__(uint index) const;
 
     bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs);
     bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs);
index 462c9ca..fd4afa3 100644 (file)
@@ -399,6 +399,8 @@ Value ObjectPrototype::method_hasOwnProperty(SimpleCallContext *ctx)
     String *P = ctx->argument(0).toString(ctx);
     Object *O = ctx->thisObject.toObject(ctx);
     bool r = O->__getOwnProperty__(P) != 0;
+    if (!r)
+        r = !O->query(P).isEmpty();
     return Value::fromBoolean(r);
 }
 
index e396f70..7afbe3f 100644 (file)
@@ -42,6 +42,7 @@
 #include "qqmllistwrapper_p.h"
 #include <private/qv8engine_p.h>
 #include <private/qqmllist_p.h>
+#include <private/qv4objectproto_p.h>
 
 #include <private/qv4functionobject_p.h>
 
@@ -56,6 +57,7 @@ QmlListWrapper::QmlListWrapper(QV8Engine *engine)
       v8(engine)
 {
     vtbl = &static_vtbl;
+    prototype = QV8Engine::getV4(engine)->objectPrototype;
 }
 
 QmlListWrapper::~QmlListWrapper()
@@ -113,7 +115,7 @@ Value QmlListWrapper::get(Managed *m, String *name, bool *hasProperty)
     if (idx != UINT_MAX)
         return getIndexed(m, idx, hasProperty);
 
-    return Value::undefinedValue();
+    return Object::get(m, name, hasProperty);
 }
 
 Value QmlListWrapper::getIndexed(Managed *m, uint index, bool *hasProperty)
index f4e9d9b..20c403c 100644 (file)
@@ -157,7 +157,7 @@ Value QmlTypeWrapper::get(Managed *m, String *name, bool *hasProperty)
                 }
 
                 // check for property.
-                return QV4::QObjectWrapper::getQmlProperty(v4->current, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision);
+                return QV4::QObjectWrapper::getQmlProperty(v4->current, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty);
             } else if (!siinfo->scriptApi(e).isUndefined()) {
                 QV4::ExecutionEngine *engine = QV8Engine::getV4(v8engine);
                 // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable.
@@ -181,7 +181,7 @@ Value QmlTypeWrapper::get(Managed *m, String *name, bool *hasProperty)
             } else if (w->object) {
                 QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object);
                 if (ao)
-                    return QV4::QObjectWrapper::getQmlProperty(v4->current, context, ao, name, QV4::QObjectWrapper::IgnoreRevision);
+                    return QV4::QObjectWrapper::getQmlProperty(v4->current, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty);
 
                 // Fall through to base implementation
             }
@@ -193,8 +193,7 @@ Value QmlTypeWrapper::get(Managed *m, String *name, bool *hasProperty)
 
     } else if (w->typeNamespace) {
         Q_ASSERT(w->importNamespace);
-        QQmlTypeNameCache::Result r = w->typeNamespace->query(name,
-                                                                             w->importNamespace);
+        QQmlTypeNameCache::Result r = w->typeNamespace->query(name, w->importNamespace);
 
         if (r.isValid()) {
             QQmlContextData *context = v8engine->callingContext();
@@ -260,6 +259,14 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value)
     }
 }
 
+PropertyAttributes QmlTypeWrapper::query(const Managed *m, String *name)
+{
+    // ### Implement more efficiently.
+    bool hasProperty = false;
+    const_cast<Managed*>(m)->get(name, &hasProperty);
+    return hasProperty ? Attr_Data : Attr_Invalid;
+}
+
 void QmlTypeWrapper::destroy(Managed *that)
 {
     static_cast<QmlTypeWrapper *>(that)->~QmlTypeWrapper();
index ae70367..944621b 100644 (file)
@@ -84,6 +84,7 @@ public:
 
     static Value get(Managed *m, String *name, bool *hasProperty);
     static void put(Managed *m, String *name, const Value &value);
+    static PropertyAttributes query(const Managed *, String *name);
     static void destroy(Managed *that);
 
 private:
index 05412e9..64baf8b 100644 (file)
@@ -201,6 +201,25 @@ bool QmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other)
     return false;
 }
 
+PropertyAttributes QmlValueTypeWrapper::query(const Managed *m, String *name)
+{
+    const QmlValueTypeWrapper *r = m->as<const QmlValueTypeWrapper>();
+    QV4::ExecutionEngine *v4 = m->engine();
+    if (!r)
+        v4->current->throwTypeError();
+
+    QQmlPropertyData local;
+    QQmlPropertyData *result = 0;
+    {
+        QQmlData *ddata = QQmlData::get(r->type, false);
+        if (ddata && ddata->propertyCache)
+            result = ddata->propertyCache->property(name, 0, 0);
+        else
+            result = QQmlPropertyCache::property(r->v8->engine(), r->type, name, 0, local);
+    }
+    return result ? Attr_Data : Attr_Invalid;
+}
+
 bool QmlValueTypeWrapper::isEqual(const QVariant& value)
 {
     if (objectType == QmlValueTypeWrapper::Reference) {
index 18dca0a..f5088a5 100644 (file)
@@ -87,6 +87,7 @@ public:
     static void put(Managed *m, String *name, const Value &value);
     static void destroy(Managed *that);
     static bool isEqualTo(Managed *m, Managed *other);
+    static PropertyAttributes query(const Managed *, String *name);
 
     static QV4::Value method_toString(SimpleCallContext *ctx);
 
index 321aa47..b06f006 100644 (file)
@@ -1710,7 +1710,6 @@ void tst_qqmlecmascript::objectHasOwnProperty()
     QVERIFY(object != 0);
 
     // test QObjects in QML
-    QEXPECT_FAIL("", "hasOwnProperty is currently not properly supported for dynamic objects that re-implement get()", Abort);
     QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess");
     QVERIFY(object->property("result").value<bool>() == true);
     QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure");