Store int/bool/double/string and sizef as QV4::Value in a Javascript array
authorFrank Meerkoetter <frank.meerkoetter@basyskom.com>
Tue, 14 Jul 2015 20:38:51 +0000 (22:38 +0200)
committerSimon Hausmann <simon.hausmann@theqtcompany.com>
Tue, 18 Aug 2015 20:25:19 +0000 (20:25 +0000)
This is the first patch in a series of patches removing the QQmlVMEVariant
used for the storage of non-var properties. The overall goal is to reduce the
memory usage of QML. The QQmlVMEVariant has a size of 8*sizeof(void*) +
sizeof(int) which is quite an overhead for types such as int/bool or double.

Change-Id: I301661d134724300942911a3d75258fe45356a7a
Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
src/qml/qml/qqmlvmemetaobject.cpp
src/qml/qml/qqmlvmemetaobject_p.h

index 9f02c905fc308e8d8c00a00747795536ef241a22..5d8d5d0d54660be5bc6c7bba675c3dc32574c4db 100644 (file)
@@ -516,7 +516,7 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj,
 : object(obj),
   ctxt(QQmlData::get(obj, true)->outerContext), cache(cache), metaData(meta),
   hasAssignedMetaObjectData(false), data(0), aliasEndpoints(0), firstVarPropertyIndex(-1),
-  varPropertiesInitialized(false), interceptors(0), v8methods(0)
+  varPropertiesInitialized(false), propertiesInitialized(false), interceptors(0), v8methods(0)
 {
     QObjectPrivate *op = QObjectPrivate::get(obj);
 
@@ -583,6 +583,134 @@ QQmlVMEMetaObject::~QQmlVMEMetaObject()
     qDeleteAll(varObjectGuards);
 }
 
+void QQmlVMEMetaObject::writeProperty(int id, int v)
+{
+    if (!ensurePropertiesAllocated())
+        return;
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    vp->putIndexed(id, QV4::Primitive::fromInt32(v));
+}
+
+void QQmlVMEMetaObject::writeProperty(int id, bool v)
+{
+    if (!ensurePropertiesAllocated())
+        return;
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    vp->putIndexed(id, QV4::Primitive::fromBoolean(v));
+}
+
+void QQmlVMEMetaObject::writeProperty(int id, double v)
+{
+    if (!ensurePropertiesAllocated())
+        return;
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    vp->putIndexed(id, QV4::Primitive::fromDouble(v));
+}
+
+void QQmlVMEMetaObject::writeProperty(int id, const QString& v)
+{
+    if (!ensurePropertiesAllocated())
+        return;
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    QV4::ScopedValue s(scope, properties.engine()->newString(v));
+    vp->putIndexed(id, s);
+}
+
+void QQmlVMEMetaObject::writeProperty(int id, const QSizeF& v)
+{
+    if (!ensurePropertiesAllocated())
+        return;
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    QV4::ScopedValue sv(scope, properties.engine()->newVariantObject(QVariant::fromValue(v)));
+    vp->putIndexed(id, sv);
+}
+
+int QQmlVMEMetaObject::readPropertyAsInt(int id)
+{
+    if (!ensurePropertiesAllocated())
+        return 0;
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    QV4::ScopedValue sv(scope, vp->getIndexed(id));
+    if (!sv->isInt32()) {
+        writeProperty(id, int(0));
+        return 0;
+    }
+    return sv->integerValue();
+}
+
+bool QQmlVMEMetaObject::readPropertyAsBool(int id)
+{
+    if (!ensurePropertiesAllocated())
+        return false;
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    QV4::ScopedValue sv(scope, vp->getIndexed(id));
+    if (!sv->isBoolean()) {
+        writeProperty(id, false);
+        return false;
+    }
+    return sv->booleanValue();
+}
+
+double QQmlVMEMetaObject::readPropertyAsDouble(int id)
+{
+    if (!ensurePropertiesAllocated())
+        return 0.0;
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    QV4::ScopedValue sv(scope, vp->getIndexed(id));
+    if (!sv->isDouble()) {
+        writeProperty(id, 0.0);
+        return 0.0;
+    }
+    return sv->doubleValue();
+}
+
+QString QQmlVMEMetaObject::readPropertyAsString(int id)
+{
+    if (!ensurePropertiesAllocated())
+        return QString();
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    QV4::ScopedValue sv(scope, vp->getIndexed(id));
+    if (!sv->isString()) {
+        writeProperty(id, QString());
+        return QString();
+    }
+    return sv->stringValue()->toQString();
+}
+
+QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id)
+{
+    if (!ensurePropertiesAllocated())
+        return QSizeF();
+
+    QV4::Scope scope(properties.engine());
+    QV4::ScopedObject vp(scope, properties.value());
+    QV4::ScopedValue sv(scope, vp->getIndexed(id));
+    const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
+    if (!v || v->d()->data.type() != QVariant::SizeF) {
+        writeProperty(id, QSizeF());
+        return QSizeF();
+    }
+    return v->d()->data.value<QSizeF>();
+}
+
 int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
 {
     int id = _id;
@@ -689,16 +817,16 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
                     if (c == QMetaObject::ReadProperty) {
                         switch(t) {
                         case QVariant::Int:
-                            *reinterpret_cast<int *>(a[0]) = data[id].asInt();
+                            *reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
                             break;
                         case QVariant::Bool:
-                            *reinterpret_cast<bool *>(a[0]) = data[id].asBool();
+                            *reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
                             break;
                         case QVariant::Double:
-                            *reinterpret_cast<double *>(a[0]) = data[id].asDouble();
+                            *reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
                             break;
                         case QVariant::String:
-                            *reinterpret_cast<QString *>(a[0]) = data[id].asQString();
+                            *reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
                             break;
                         case QVariant::Url:
                             *reinterpret_cast<QUrl *>(a[0]) = data[id].asQUrl();
@@ -713,7 +841,7 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
                             *reinterpret_cast<QRectF *>(a[0]) = data[id].asQRectF();
                             break;
                         case QVariant::SizeF:
-                            *reinterpret_cast<QSizeF *>(a[0]) = data[id].asQSizeF();
+                            *reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
                             break;
                         case QVariant::PointF:
                             *reinterpret_cast<QPointF *>(a[0]) = data[id].asQPointF();
@@ -741,20 +869,20 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
 
                         switch(t) {
                         case QVariant::Int:
-                            needActivate = *reinterpret_cast<int *>(a[0]) != data[id].asInt();
-                            data[id].setValue(*reinterpret_cast<int *>(a[0]));
+                            needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
+                            writeProperty(id, *reinterpret_cast<int *>(a[0]));
                             break;
                         case QVariant::Bool:
-                            needActivate = *reinterpret_cast<bool *>(a[0]) != data[id].asBool();
-                            data[id].setValue(*reinterpret_cast<bool *>(a[0]));
+                            needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
+                            writeProperty(id, *reinterpret_cast<bool *>(a[0]));
                             break;
                         case QVariant::Double:
-                            needActivate = *reinterpret_cast<double *>(a[0]) != data[id].asDouble();
-                            data[id].setValue(*reinterpret_cast<double *>(a[0]));
+                            needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
+                            writeProperty(id, *reinterpret_cast<double *>(a[0]));
                             break;
                         case QVariant::String:
-                            needActivate = *reinterpret_cast<QString *>(a[0]) != data[id].asQString();
-                            data[id].setValue(*reinterpret_cast<QString *>(a[0]));
+                            needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
+                            writeProperty(id, *reinterpret_cast<QString *>(a[0]));
                             break;
                         case QVariant::Url:
                             needActivate = *reinterpret_cast<QUrl *>(a[0]) != data[id].asQUrl();
@@ -773,8 +901,8 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
                             data[id].setValue(*reinterpret_cast<QRectF *>(a[0]));
                             break;
                         case QVariant::SizeF:
-                            needActivate = *reinterpret_cast<QSizeF *>(a[0]) != data[id].asQSizeF();
-                            data[id].setValue(*reinterpret_cast<QSizeF *>(a[0]));
+                            needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
+                            writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
                             break;
                         case QVariant::PointF:
                             needActivate = *reinterpret_cast<QPointF *>(a[0]) != data[id].asQPointF();
@@ -1176,6 +1304,19 @@ bool QQmlVMEMetaObject::ensureVarPropertiesAllocated()
     return !varProperties.isUndefined();
 }
 
+bool QQmlVMEMetaObject::ensurePropertiesAllocated()
+{
+    if (!propertiesInitialized)
+        allocatePropertiesArray();
+
+    // in some situations, the QObject's v8object (and associated v8 data,
+    // such as the varProperties array) will have been cleaned up, but the
+    // QObject ptr will not yet have been deleted (eg, waiting on deleteLater).
+    // In this situation, the varProperties handle will be (and should remain)
+    // empty.
+    return !properties.isUndefined();
+}
+
 void QQmlVMEMetaObject::ensureQObjectWrapper()
 {
     Q_ASSERT(ctxt && ctxt->engine);
@@ -1192,6 +1333,7 @@ void QQmlVMEMetaObject::mark(QV4::ExecutionEngine *e)
         return;
 
     varProperties.markOnce(e);
+    properties.markOnce(e);
 
     // add references created by VMEVariant properties
     int maxDataIdx = metaData->propertyCount - metaData->varPropertyCount;
@@ -1217,6 +1359,17 @@ void QQmlVMEMetaObject::allocateVarPropertiesArray()
     varPropertiesInitialized = true;
 }
 
+void QQmlVMEMetaObject::allocatePropertiesArray()
+{
+    QQmlEngine *qml = qmlEngine(object);
+    Q_ASSERT(qml);
+    QV4::ExecutionEngine *v4 = QV8Engine::getV4(qml->handle());
+    QV4::Scope scope(v4);
+    properties.set(scope.engine, v4->newArrayObject(metaData->propertyCount - metaData->varPropertyCount));
+    propertiesInitialized = true;
+}
+
+
 bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const
 {
     Q_ASSERT(index >= propOffset() + metaData->propertyCount);
index a320163f231f9901f3c35ddf2fa53654e4ed54d0..a87feec04376bb9cfd63989421b746da0d7b1d83 100644 (file)
@@ -204,6 +204,26 @@ public:
     inline void allocateVarPropertiesArray();
     inline bool ensureVarPropertiesAllocated();
 
+    // temporary solution so I can experiment with storing
+    // properties in a JS array. Should be switched over to also
+    // use the 'varProperties' in the end.
+    QV4::WeakValue properties;
+    bool propertiesInitialized;
+    inline void allocatePropertiesArray();
+    inline bool ensurePropertiesAllocated();
+
+    int readPropertyAsInt(int id);
+    bool readPropertyAsBool(int id);
+    double readPropertyAsDouble(int id);
+    QString readPropertyAsString(int id);
+    QSizeF readPropertyAsSizeF(int id);
+
+    void writeProperty(int id, int v);
+    void writeProperty(int id, bool v);
+    void writeProperty(int id, double v);
+    void writeProperty(int id, const QString& v);
+    void writeProperty(int id, const QSizeF& v);
+
     void ensureQObjectWrapper();
 
     void mark(QV4::ExecutionEngine *e);