From 1325c549744346154a915115e9787b97427fad81 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Tue, 7 Jun 2011 16:54:09 +1000 Subject: [PATCH] Implement ListModel in V8 --- src/declarative/qml/qdeclarativeengine_p.h | 1 + src/declarative/qml/qdeclarativepropertycache.cpp | 29 +- src/declarative/qml/v8/qv8engine.cpp | 6 +- src/declarative/qml/v8/qv8engine_p.h | 11 +- src/declarative/qml/v8/qv8qobjectwrapper.cpp | 29 +- src/declarative/qml/v8/qv8variantwrapper.cpp | 11 +- src/declarative/qml/v8/qv8worker.cpp | 58 +++- src/declarative/util/qdeclarativelistmodel.cpp | 353 ++++++++++++++------ src/declarative/util/qdeclarativelistmodel_p.h | 14 +- src/declarative/util/qdeclarativelistmodel_p_p.h | 59 ++-- .../util/qdeclarativelistmodelworkeragent.cpp | 19 +- .../util/qdeclarativelistmodelworkeragent_p.h | 18 +- .../qdeclarativelistmodel/data/model.qml | 4 + .../tst_qdeclarativelistmodel.cpp | 241 +++++++------- 14 files changed, 526 insertions(+), 327 deletions(-) diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h index a12269f..4aa54be 100644 --- a/src/declarative/qml/qdeclarativeengine_p.h +++ b/src/declarative/qml/qdeclarativeengine_p.h @@ -278,6 +278,7 @@ public: static void warning(QDeclarativeEnginePrivate *, const QDeclarativeError &); static void warning(QDeclarativeEnginePrivate *, const QList &); + static QV8Engine *getV8Engine(QDeclarativeEngine *e) { return &e->d_func()->v8engine; } static QScriptEngine *getScriptEngine(QDeclarativeEngine *e) { return &e->d_func()->scriptEngine; } static QDeclarativeEngine *getEngine(QScriptEngine *e) { return static_cast(e)->p->q_func(); } static QDeclarativeEnginePrivate *get(QDeclarativeEngine *e) { return e->d_func(); } diff --git a/src/declarative/qml/qdeclarativepropertycache.cpp b/src/declarative/qml/qdeclarativepropertycache.cpp index e48a849..b2aad7b 100644 --- a/src/declarative/qml/qdeclarativepropertycache.cpp +++ b/src/declarative/qml/qdeclarativepropertycache.cpp @@ -168,8 +168,8 @@ void QDeclarativePropertyCache::clear() indexCache.clear(); methodIndexCache.clear(); stringCache.clear(); - constructor.Dispose(); - constructor = v8::Persistent(); + constructor.Dispose(); + constructor.Clear(); } QDeclarativePropertyCache::Data QDeclarativePropertyCache::create(const QMetaObject *metaObject, @@ -252,7 +252,7 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb Q_UNUSED(revision); constructor.Dispose(); // Now invalid - constructor = v8::Persistent(); + constructor.Clear(); allowedRevisionCache.append(0); @@ -416,16 +416,18 @@ QDeclarativePropertyCache::Data * QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, v8::Handle name, Data &local) { - Q_ASSERT(engine); - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - + // XXX Optimize for worker script case where engine isn't available QDeclarativePropertyCache *cache = 0; - QDeclarativeData *ddata = QDeclarativeData::get(obj); - if (ddata && ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine) // XXX aakenend - cache = ddata->propertyCache; - if (!cache) { - cache = ep->cache(obj); - if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; } + if (engine) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + + QDeclarativeData *ddata = QDeclarativeData::get(obj); + if (ddata && ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine) // XXX aakenend + cache = ddata->propertyCache; + if (!cache) { + cache = ep->cache(obj); + if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; } + } } QDeclarativePropertyCache::Data *rv = 0; @@ -433,7 +435,8 @@ QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, if (cache) { rv = cache->property(name); } else { - QString strname = ep->v8engine.toString(name); + QString strname = QV8Engine::toStringStatic(name); + // QString strname = ep->v8engine.toString(name); local = QDeclarativePropertyCache::create(obj->metaObject(), strname); if (local.isValid()) rv = &local; diff --git a/src/declarative/qml/v8/qv8engine.cpp b/src/declarative/qml/v8/qv8engine.cpp index ced07e1..87bb1fc 100644 --- a/src/declarative/qml/v8/qv8engine.cpp +++ b/src/declarative/qml/v8/qv8engine.cpp @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE QV8Engine::QV8Engine() -: m_xmlHttpRequestData(0), m_sqlDatabaseData(0) +: m_xmlHttpRequestData(0), m_sqlDatabaseData(0), m_listModelData(0) { } @@ -77,6 +77,8 @@ QV8Engine::~QV8Engine() m_sqlDatabaseData = 0; qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData); m_xmlHttpRequestData = 0; + delete m_listModelData; + m_listModelData = 0; m_getOwnPropertyNames.Dispose(); m_getOwnPropertyNames.Clear(); @@ -88,6 +90,7 @@ QV8Engine::~QV8Engine() m_contextWrapper.destroy(); m_stringWrapper.destroy(); m_context.Dispose(); + m_context.Clear(); } void QV8Engine::init(QDeclarativeEngine *engine) @@ -146,6 +149,7 @@ QVariant QV8Engine::toVariant(v8::Handle value, int typeHint) case QV8ObjectResource::XMLHttpRequestType: case QV8ObjectResource::DOMNodeType: case QV8ObjectResource::SQLDatabaseType: + case QV8ObjectResource::ListModelType: return QVariant(); case QV8ObjectResource::QObjectType: return qVariantFromValue(m_qobjectWrapper.toQObject(r)); diff --git a/src/declarative/qml/v8/qv8engine_p.h b/src/declarative/qml/v8/qv8engine_p.h index d4b8730..5aec866 100644 --- a/src/declarative/qml/v8/qv8engine_p.h +++ b/src/declarative/qml/v8/qv8engine_p.h @@ -90,7 +90,8 @@ class QV8ObjectResource : public v8::Object::ExternalResource public: QV8ObjectResource(QV8Engine *engine) : engine(engine) { Q_ASSERT(engine); } enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType, - ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType }; + ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType, + ListModelType }; virtual ResourceType resourceType() const = 0; QV8Engine *engine; @@ -170,6 +171,10 @@ public: QV8Engine(); ~QV8Engine(); + struct Deletable { + ~Deletable() {} + }; + void init(QDeclarativeEngine *); QDeclarativeEngine *engine() { return m_engine; } @@ -184,6 +189,9 @@ public: void *xmlHttpRequestData() { return m_xmlHttpRequestData; } void *sqlDatabaseData() { return m_sqlDatabaseData; } + Deletable *listModelData() { return m_listModelData; } + void setListModelData(Deletable *d) { if (m_listModelData) delete m_listModelData; m_listModelData = d; } + QDeclarativeContextData *callingContext(); v8::Local getOwnPropertyNames(v8::Handle); @@ -246,6 +254,7 @@ private: void *m_xmlHttpRequestData; void *m_sqlDatabaseData; + Deletable *m_listModelData; QSet m_illegalNames; diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp index 2647197..9ac482e 100644 --- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp +++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp @@ -70,6 +70,9 @@ Q_DECLARE_METATYPE(QDeclarativeV8Handle); #define QOBJECT_TOSTRING_INDEX -2 #define QOBJECT_DESTROY_INDEX -3 +// XXX Need to check all calls to QDeclarativeEngine *engine() to confirm this class works +// correctly in a worker thread + class QV8QObjectResource : public QV8ObjectResource { V8_RESOURCE_TYPE(QObjectType); @@ -153,11 +156,11 @@ void QV8QObjectWrapper::destroy() qDeleteAll(m_connections); m_connections.clear(); - m_hiddenObject.Dispose(); - m_destroySymbol.Dispose(); - m_toStringSymbol.Dispose(); - m_methodConstructor.Dispose(); - m_constructor.Dispose(); + m_hiddenObject.Dispose(); m_hiddenObject.Clear(); + m_destroySymbol.Dispose(); m_destroySymbol.Clear(); + m_toStringSymbol.Dispose(); m_toStringSymbol.Clear(); + m_methodConstructor.Dispose(); m_methodConstructor.Clear(); + m_constructor.Dispose(); m_constructor.Clear(); } #define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \ @@ -175,8 +178,8 @@ static v8::Handle name ## ValueGetter(v8::Local, const v8 int notify = (data & 0x7FFF0000) >> 16; \ if (notify == 0x7FFF) notify = -1; \ \ - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine()); \ - if (notify /* 0 means constant */ && ep->captureProperties) { \ + QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \ + if (ep && notify /* 0 means constant */ && ep->captureProperties) { \ typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \ ep->capturedProperties << CapturedProperty(object, index, notify); \ } \ @@ -331,7 +334,7 @@ v8::Handle QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject if (!result) return v8::Handle(); - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine()); + QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0; if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) { QDeclarativeData *ddata = QDeclarativeData::get(object); @@ -351,7 +354,7 @@ v8::Handle QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject } } - if (ep->captureProperties && !result->isConstant()) { + if (ep && ep->captureProperties && !result->isConstant()) { if (result->coreIndex == 0) ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier()); else @@ -661,6 +664,7 @@ static void WeakQObjectInstanceCallback(v8::Persistent handle, void * QV8QObjectInstance *instance = (QV8QObjectInstance *)data; instance->v8object.Clear(); handle.Dispose(); + handle.Clear(); } v8::Local QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine) @@ -808,7 +812,6 @@ v8::Handle QV8QObjectWrapper::newQObject(QObject *object) return rv; } else { - // If this object is tainted, we have to check to see if it is in our // tainted object list TaintedHash::Iterator iter = @@ -896,6 +899,8 @@ QV8QObjectConnectionList::~QV8QObjectConnectionList() for (int ii = 0; ii < connections.count(); ++ii) { connections[ii].thisObject.Dispose(); connections[ii].function.Dispose(); + connections[ii].thisObject.Clear(); + connections[ii].function.Clear(); } } slotHash.clear(); @@ -1081,6 +1086,8 @@ v8::Handle QV8QObjectWrapper::Disconnect(const v8::Arguments &args) // Match! connection.thisObject.Dispose(); connection.function.Dispose(); + connection.thisObject.Clear(); + connection.function.Clear(); connections.removeAt(ii); return v8::Undefined(); } @@ -1097,6 +1104,8 @@ v8::Handle QV8QObjectWrapper::Disconnect(const v8::Arguments &args) // Match! connection.thisObject.Dispose(); connection.function.Dispose(); + connection.thisObject.Clear(); + connection.function.Clear(); connections.removeAt(ii); return v8::Undefined(); } diff --git a/src/declarative/qml/v8/qv8variantwrapper.cpp b/src/declarative/qml/v8/qv8variantwrapper.cpp index 2d91a33..de3cb11 100644 --- a/src/declarative/qml/v8/qv8variantwrapper.cpp +++ b/src/declarative/qml/v8/qv8variantwrapper.cpp @@ -95,10 +95,10 @@ void QV8VariantWrapper::init(QV8Engine *engine) void QV8VariantWrapper::destroy() { - m_destroy.Dispose(); - m_preserve.Dispose(); - m_scarceConstructor.Dispose(); - m_constructor.Dispose(); + m_destroy.Dispose(); m_destroy.Clear(); + m_preserve.Dispose(); m_preserve.Clear(); + m_scarceConstructor.Dispose(); m_scarceConstructor.Clear(); + m_constructor.Dispose(); m_constructor.Clear(); } v8::Local QV8VariantWrapper::newVariant(const QVariant &value) @@ -106,13 +106,12 @@ v8::Local QV8VariantWrapper::newVariant(const QVariant &value) bool scarceResource = value.type() == QVariant::Pixmap || value.type() == QVariant::Image; - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_engine->engine()); - // XXX aakenned - NewInstance() is slow for our case v8::Local rv; QV8VariantResource *r = new QV8VariantResource(m_engine, value); if (scarceResource) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_engine->engine()); Q_ASSERT(ep->scarceResourcesRefCount); rv = m_scarceConstructor->NewInstance(); ep->scarceResources.insert(r); diff --git a/src/declarative/qml/v8/qv8worker.cpp b/src/declarative/qml/v8/qv8worker.cpp index 6b6ecd8..846fdb7 100644 --- a/src/declarative/qml/v8/qv8worker.cpp +++ b/src/declarative/qml/v8/qv8worker.cpp @@ -41,6 +41,9 @@ #include "qv8worker_p.h" +#include +#include + QT_BEGIN_NAMESPACE // We allow the following JavaScript types to be passed between the main and @@ -70,7 +73,8 @@ enum Type { WorkerUint32, WorkerNumber, WorkerDate, - WorkerRegexp + WorkerRegexp, + WorkerListModel }; static inline quint32 valueheader(Type type, quint32 size = 0) @@ -98,6 +102,11 @@ static inline void push(QByteArray &data, double value) data.append((const char *)&value, sizeof(double)); } +static inline void push(QByteArray &data, void *ptr) +{ + data.append((const char *)&ptr, sizeof(void *)); +} + static inline void reserve(QByteArray &data, int extra) { data.reserve(data.size() + extra); @@ -117,6 +126,13 @@ static inline double popDouble(const char *&data) return rv; } +static inline void *popPtr(const char *&data) +{ + void *rv = *((void **)data); + data += sizeof(void *); + return rv; +} + // XXX double check exception safety #include @@ -221,23 +237,21 @@ void QV8Worker::serialize(QByteArray &data, v8::Handle v, QV8Engine * serialize(data, val, engine); } } + } else if (engine->isQObject(v)) { + // XXX Can we generalize this? + QDeclarativeListModel *lm = qobject_cast(engine->toQObject(v)); + if (lm && lm->agent()) { + QDeclarativeListModelWorkerAgent *agent = lm->agent(); + agent->addref(); + push(data, valueheader(WorkerListModel)); + push(data, (void *)agent); + return; + } + // No other QObject's are allowed to be sent + push(data, valueheader(WorkerUndefined)); } else { push(data, valueheader(WorkerUndefined)); } - - // XXX Need to serialize QDeclarativeListModel - /* - QDeclarativeListModel *lm = qobject_cast(value.toQObject()); - if (lm) { - QDeclarativeListModelWorkerAgent *agent = lm->agent(); - if (agent) { - QDeclarativeListModelWorkerAgent::VariantRef v(agent); - return QVariant::fromValue(v); - } else { - return QVariant(); - } - } - */ } v8::Handle QV8Worker::deserialize(const char *&data, QV8Engine *engine) @@ -300,6 +314,20 @@ v8::Handle QV8Worker::deserialize(const char *&data, QV8Engine *engin data += ALIGN(length * sizeof(uint16_t)); return v8::RegExp::New(source, (v8::RegExp::Flags)flags); } + case WorkerListModel: + { + void *ptr = popPtr(data); + QDeclarativeListModelWorkerAgent *agent = (QDeclarativeListModelWorkerAgent *)ptr; + v8::Handle rv = engine->newQObject(agent); + if (rv->IsObject()) { + QDeclarativeListModelWorkerAgent::VariantRef ref(agent); + QVariant var = qVariantFromValue(ref); + rv->ToObject()->SetHiddenValue(v8::String::New("qml::ref"), engine->fromVariant(var)); + } + agent->release(); + agent->setV8Engine(engine); + return rv; + } } Q_ASSERT(!"Unreachable"); return v8::Undefined(); diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp index 4f66dba..1f26140 100644 --- a/src/declarative/util/qdeclarativelistmodel.cpp +++ b/src/declarative/util/qdeclarativelistmodel.cpp @@ -58,6 +58,122 @@ Q_DECLARE_METATYPE(QListModelInterface *) QT_BEGIN_NAMESPACE +QV8ListModelResource::QV8ListModelResource(FlatListModel *model, FlatNodeData *data, QV8Engine *engine) +: QV8ObjectResource(engine), model(model), nodeData(data) +{ + if (nodeData) nodeData->addData(this); +} + +QV8ListModelResource::~QV8ListModelResource() +{ + if (nodeData) nodeData->removeData(this); +} + +class QDeclarativeListModelV8Data : public QV8Engine::Deletable +{ +public: + QDeclarativeListModelV8Data(); + ~QDeclarativeListModelV8Data(); + + v8::Persistent constructor; + + static v8::Local create(QV8Engine *); + + static v8::Handle Getter(v8::Local property, + const v8::AccessorInfo &info); + static v8::Handle Setter(v8::Local property, + v8::Local value, + const v8::AccessorInfo &info); +}; + +v8::Local QDeclarativeListModelV8Data::create(QV8Engine *engine) +{ + if (!engine->listModelData()) { + QDeclarativeListModelV8Data *d = new QDeclarativeListModelV8Data; + engine->setListModelData(d); + } + + QDeclarativeListModelV8Data *d = (QDeclarativeListModelV8Data *)engine->listModelData(); + return d->constructor->NewInstance(); +} + +QDeclarativeListModelV8Data::QDeclarativeListModelV8Data() +{ + v8::Local ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetNamedPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + constructor = v8::Persistent::New(ft->GetFunction()); +} + +QDeclarativeListModelV8Data::~QDeclarativeListModelV8Data() +{ + constructor.Dispose(); constructor.Clear(); +} + +v8::Handle QDeclarativeListModelV8Data::Getter(v8::Local property, + const v8::AccessorInfo &info) +{ + QV8ListModelResource *r = v8_resource_cast(info.This()); + if (!r) + return v8::Undefined(); + + if (!r->nodeData) // Item at this index has been deleted + return v8::Undefined(); + + + int index = r->nodeData->index; + QString propName = r->engine->toString(property); + + int role = r->model->m_strings.value(propName, -1); + + if (role >= 0 && index >=0 ) { + const QHash &row = r->model->m_values[index]; + return r->engine->fromVariant(row[role]); + } + + return v8::Undefined(); +} + +v8::Handle QDeclarativeListModelV8Data::Setter(v8::Local property, + v8::Local value, + const v8::AccessorInfo &info) +{ + QV8ListModelResource *r = v8_resource_cast(info.This()); + if (!r) + return v8::Undefined(); + + if (!r->nodeData) // item at this index has been deleted + return value; + + + if (!value->IsRegExp() && !value->IsDate() && value->IsObject() && !r->engine->isVariant(value)) { + qmlInfo(r->model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; + return value; + } + + int index = r->nodeData->index; + QString propName = r->engine->toString(property); + + int role = r->model->m_strings.value(propName, -1); + if (role >= 0 && index >= 0) { + QHash &row = r->model->m_values[index]; + row[role] = r->engine->toVariant(value, -1); + + QList roles; + roles << role; + if (r->model->m_parentAgent) { + // This is the list in the worker thread, so tell the agent to + // emit itemsChanged() later + r->model->m_parentAgent->changedData(index, 1, roles); + } else { + // This is the list in the main thread, so emit itemsChanged() + emit r->model->m_listModel->itemsChanged(index, 1, roles); + } + } + + return value; +} + template void qdeclarativelistmodel_move(int from, int to, int n, T *items) { @@ -367,13 +483,14 @@ QDeclarativeListModel *ModelNode::model(const NestedListModel *model) ModelObject *ModelNode::object(const NestedListModel *model) { if (!objectCache) { - objectCache = new ModelObject(this, - const_cast(model), - QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(model->m_listModel))); + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(qmlEngine(model->m_listModel)); + objectCache = new ModelObject(this, const_cast(model), &ep->v8engine); + QHash::iterator it; for (it = properties.begin(); it != properties.end(); ++it) { objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it)); } + objectCache->setNodeUpdatesEnabled(true); } return objectCache; @@ -419,9 +536,11 @@ void QDeclarativeListModel::remove(int index) \sa set() append() */ -void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap) +void QDeclarativeListModel::insert(int index, const QDeclarativeV8Handle &handle) { - if (!valuemap.isObject() || valuemap.isArray()) { + v8::Handle valuemap = handle.toHandle(); + + if (!valuemap->IsObject() || valuemap->IsArray()) { qmlInfo(this) << tr("insert: value is not an object"); return; } @@ -432,6 +551,7 @@ void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap) } bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap); + if (ok && !inWorkerThread()) { emit itemsInserted(index, 1); emit countChanged(); @@ -494,14 +614,16 @@ void QDeclarativeListModel::move(int from, int to, int n) \sa set() remove() */ -void QDeclarativeListModel::append(const QScriptValue& valuemap) +void QDeclarativeListModel::append(const QDeclarativeV8Handle &handle) { - if (!valuemap.isObject() || valuemap.isArray()) { + v8::Handle valuemap = handle.toHandle(); + + if (!valuemap->IsObject() || valuemap->IsArray()) { qmlInfo(this) << tr("append: value is not an object"); return; } - insert(count(), valuemap); + insert(count(), handle); } /*! @@ -535,10 +657,10 @@ void QDeclarativeListModel::append(const QScriptValue& valuemap) \sa append() */ -QScriptValue QDeclarativeListModel::get(int index) const +QDeclarativeV8Handle QDeclarativeListModel::get(int index) const { // the internal flat/nested class checks for bad index - return m_flat ? m_flat->get(index) : m_nested->get(index); + return QDeclarativeV8Handle::fromHandle(m_flat ? m_flat->get(index) : m_nested->get(index)); } /*! @@ -557,7 +679,7 @@ QScriptValue QDeclarativeListModel::get(int index) const \sa append() */ -void QDeclarativeListModel::set(int index, const QScriptValue& valuemap) +void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &valuemap) { QList roles; set(index, valuemap, &roles); @@ -565,9 +687,11 @@ void QDeclarativeListModel::set(int index, const QScriptValue& valuemap) emit itemsChanged(index, 1, roles); } -void QDeclarativeListModel::set(int index, const QScriptValue& valuemap, QList *roles) +void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &handle, QList *roles) { - if (!valuemap.isObject() || valuemap.isArray()) { + v8::Handle valuemap = handle.toHandle(); + + if (!valuemap->IsObject() || valuemap->IsArray()) { qmlInfo(this) << tr("set: value is not an object"); return; } @@ -577,7 +701,7 @@ void QDeclarativeListModel::set(int index, const QScriptValue& valuemap, QListset(index, valuemap, roles); @@ -912,7 +1036,7 @@ bool QDeclarativeListModelParser::definesEmptyList(const QString &s) */ FlatListModel::FlatListModel(QDeclarativeListModel *base) - : m_scriptEngine(0), m_listModel(base), m_scriptClass(0), m_parentAgent(0) +: m_engine(0), m_listModel(base), m_scriptClass(0), m_parentAgent(0) { } @@ -960,7 +1084,7 @@ void FlatListModel::remove(int index) removedNode(index); } -bool FlatListModel::insert(int index, const QScriptValue &value) +bool FlatListModel::insert(int index, v8::Handle value) { Q_ASSERT(index >= 0 && index <= m_values.count()); @@ -974,19 +1098,27 @@ bool FlatListModel::insert(int index, const QScriptValue &value) return true; } -QScriptValue FlatListModel::get(int index) const +QV8Engine *FlatListModel::engine() const { - QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel)); + return m_engine?m_engine:QDeclarativeEnginePrivate::getV8Engine(qmlEngine(m_listModel)); +} - if (!scriptEngine) - return 0; +QV8Engine *NestedListModel::engine() const +{ + return QDeclarativeEnginePrivate::getV8Engine(qmlEngine(m_listModel)); +} + +v8::Handle FlatListModel::get(int index) const +{ + QV8Engine *v8engine = engine(); + + if (!v8engine) + return v8::Undefined(); if (index < 0 || index >= m_values.count()) - return scriptEngine->undefinedValue(); + return v8::Undefined(); FlatListModel *that = const_cast(this); - if (!m_scriptClass) - that->m_scriptClass = new FlatListScriptClass(that, scriptEngine); FlatNodeData *data = m_nodeData.value(index); if (!data) { @@ -994,10 +1126,13 @@ QScriptValue FlatListModel::get(int index) const that->m_nodeData.replace(index, data); } - return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data)); + v8::Local rv = QDeclarativeListModelV8Data::create(v8engine); + QV8ListModelResource *r = new QV8ListModelResource(that, data, v8engine); + rv->SetExternalResource(r); + return rv; } -void FlatListModel::set(int index, const QScriptValue &value, QList *roles) +void FlatListModel::set(int index, v8::Handle value, QList *roles) { Q_ASSERT(index >= 0 && index < m_values.count()); @@ -1032,19 +1167,25 @@ void FlatListModel::move(int from, int to, int n) moveNodes(from, to, n); } -bool FlatListModel::addValue(const QScriptValue &value, QHash *row, QList *roles) +bool FlatListModel::addValue(v8::Handle value, QHash *row, QList *roles) { - QScriptValueIterator it(value); - while (it.hasNext()) { - it.next(); - QScriptValue value = it.value(); - if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { + if (!value->IsObject()) + return false; + + v8::Local properties = engine()->getOwnPropertyNames(value->ToObject()); + uint32_t length = properties->Length(); + for (uint32_t ii = 0; ii < length; ++ii) { + // XXX TryCatch? + v8::Handle property = properties->Get(ii); + v8::Handle jsv = value->ToObject()->Get(property); + + if (!jsv->IsRegExp() && !jsv->IsDate() && jsv->IsObject() && !engine()->isVariant(jsv)) { qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; return false; } - QString name = it.name(); - QVariant v = it.value().toVariant(); + QString name = engine()->toString(property); + QVariant v = engine()->toVariant(jsv, -1); QHash::Iterator iter = m_strings.find(name); if (iter == m_strings.end()) { @@ -1100,27 +1241,26 @@ void FlatListModel::moveNodes(int from, int to, int n) } } - - FlatNodeData::~FlatNodeData() { - for (QSet::Iterator iter = objects.begin(); iter != objects.end(); ++iter) { - FlatNodeObjectData *data = *iter; + for (QSet::Iterator iter = objects.begin(); iter != objects.end(); ++iter) { + QV8ListModelResource *data = *iter; data->nodeData = 0; } } -void FlatNodeData::addData(FlatNodeObjectData *data) +void FlatNodeData::addData(QV8ListModelResource *data) { objects.insert(data); } -void FlatNodeData::removeData(FlatNodeObjectData *data) +void FlatNodeData::removeData(QV8ListModelResource *data) { objects.remove(data); } +#if 0 FlatListScriptClass::FlatListScriptClass(FlatListModel *model, QScriptEngine *seng) : QScriptDeclarativeClass(seng), m_model(model) @@ -1193,11 +1333,10 @@ bool FlatListScriptClass::compare(Object *obj1, Object *obj2) return data1->nodeData->index == data2->nodeData->index; } - - +#endif NestedListModel::NestedListModel(QDeclarativeListModel *base) - : _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false) +: _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false) { } @@ -1315,7 +1454,7 @@ void NestedListModel::remove(int index) delete node; } -bool NestedListModel::insert(int index, const QScriptValue& valuemap) +bool NestedListModel::insert(int index, v8::Handle valuemap) { if (!_root) { _root = new ModelNode(this); @@ -1336,44 +1475,47 @@ void NestedListModel::move(int from, int to, int n) qdeclarativelistmodel_move(from, to, n, &_root->values); } -QScriptValue NestedListModel::get(int index) const +v8::Handle NestedListModel::get(int index) const { QDeclarativeEngine *eng = qmlEngine(m_listModel); if (!eng) - return 0; + return v8::Undefined();; - if (index < 0 || index >= count()) { - QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(eng); - if (seng) - return seng->undefinedValue(); - return 0; - } + if (index < 0 || index >= count()) + return v8::Undefined(); ModelNode *node = qvariant_cast(_root->values.at(index)); if (!node) - return 0; - -#if 0 - return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng); -#endif + return v8::Undefined();; + + return QDeclarativeEnginePrivate::get(eng)->v8engine.newQObject(node->object(this)); } -void NestedListModel::set(int index, const QScriptValue& valuemap, QList *roles) +void NestedListModel::set(int index, v8::Handle valuemap, QList *roles) { Q_ASSERT(index >=0 && index < count()); + if (!valuemap->IsObject()) + return; + ModelNode *node = qvariant_cast(_root->values.at(index)); bool emitItemsChanged = node->setObjectValue(valuemap); if (!emitItemsChanged) return; - QScriptValueIterator it(valuemap); - while (it.hasNext()) { - it.next(); - int r = roleStrings.indexOf(it.name()); + QV8Engine *v8engine = engine(); + + v8::Local properties = v8engine->getOwnPropertyNames(valuemap->ToObject()); + uint32_t length = properties->Length(); + for (uint32_t ii = 0; ii < length; ++ii) { + // XXX TryCatch? + v8::Handle property = properties->Get(ii); + QString name = v8engine->toString(property); + + int r = roleStrings.indexOf(name); if (r < 0) { r = roleStrings.count(); - roleStrings << it.name(); + roleStrings << name; } roles->append(r); } @@ -1458,55 +1600,75 @@ void ModelNode::clear() properties.clear(); } -bool ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache) +bool ModelNode::setObjectValue(v8::Handle valuemap, bool writeToCache) { + if (!valuemap->IsObject()) + return false; + bool emitItemsChanged = false; - QScriptValueIterator it(valuemap); - while (it.hasNext()) { - it.next(); - ModelNode *prev = properties.value(it.name()); + QV8Engine *v8engine = m_model->engine(); + + v8::Local propertyNames = v8engine->getOwnPropertyNames(valuemap->ToObject()); + uint32_t length = propertyNames->Length(); + + for (uint32_t ii = 0; ii < length; ++ii) { + // XXX TryCatch? + v8::Handle property = propertyNames->Get(ii); + v8::Handle v = valuemap->ToObject()->Get(property); + + QString name = v8engine->toString(property); + ModelNode *prev = properties.value(name); ModelNode *value = new ModelNode(m_model); - QScriptValue v = it.value(); - if (v.isArray()) { + if (v->IsArray()) { value->isArray = true; value->setListValue(v); if (writeToCache && objectCache) - objectCache->setValue(it.name().toUtf8(), QVariant::fromValue(value->model(m_model))); + objectCache->setValue(name.toUtf8(), QVariant::fromValue(value->model(m_model))); emitItemsChanged = true; // for now, too inefficient to check whether list and sublists have changed } else { - value->values << v.toVariant(); + value->values << v8engine->toVariant(v, -1); if (writeToCache && objectCache) - objectCache->setValue(it.name().toUtf8(), value->values.last()); + objectCache->setValue(name.toUtf8(), value->values.last()); if (!emitItemsChanged && prev && prev->values.count() == 1 && prev->values[0] != value->values.last()) { emitItemsChanged = true; } } - if (properties.contains(it.name())) - delete properties[it.name()]; - properties.insert(it.name(), value); + if (properties.contains(name)) + delete properties[name]; + properties.insert(name, value); } return emitItemsChanged; } -void ModelNode::setListValue(const QScriptValue& valuelist) { +void ModelNode::setListValue(v8::Handle valuelist) +{ + Q_ASSERT(valuelist->IsArray()); values.clear(); - int size = valuelist.property(QLatin1String("length")).toInt32(); - for (int i=0; iengine(); + + v8::Handle array = v8::Handle::Cast(valuelist); + uint32_t length = array->Length(); + for (uint32_t ii = 0; ii < length; ++ii) { ModelNode *value = new ModelNode(m_model); - QScriptValue v = valuelist.property(i); - if (v.isArray()) { + + // XXX TryCatch? + v8::Handle v = array->Get(ii); + + if (v->IsArray()) { value->isArray = true; value->setListValue(v); - } else if (v.isObject()) { - value->listIndex = i; + } else if (v->IsObject()) { + value->listIndex = ii; value->setObjectValue(v); } else { - value->listIndex = i; - value->values << v.toVariant(); + value->listIndex = ii; + value->values << engine->toVariant(v, -1); } + values.append(QVariant::fromValue(value)); } } @@ -1583,10 +1745,8 @@ void ModelNode::dump(ModelNode *node, int ind) } } -ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng) - : m_model(model), - m_node(node), - m_meta(new ModelNodeMetaObject(seng, this)) +ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QV8Engine *eng) +: m_model(model), m_node(node), m_meta(new ModelNodeMetaObject(eng, this)) { } @@ -1601,12 +1761,8 @@ void ModelObject::setNodeUpdatesEnabled(bool enable) m_meta->m_enabled = enable; } - -ModelNodeMetaObject::ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object) - : QDeclarativeOpenMetaObject(object), - m_enabled(false), - m_seng(seng), - m_obj(object) +ModelNodeMetaObject::ModelNodeMetaObject(QV8Engine *eng, ModelObject *object) +: QDeclarativeOpenMetaObject(object), m_enabled(false), m_engine(eng), m_obj(object) { } @@ -1618,12 +1774,13 @@ void ModelNodeMetaObject::propertyWritten(int index) QString propName = QString::fromUtf8(name(index)); QVariant value = operator[](index); - QScriptValue sv = m_seng->newObject(); - sv.setProperty(propName, m_seng->newVariant(value)); - bool changed = m_obj->m_node->setObjectValue(sv, false); + v8::HandleScope handle_scope; + v8::Context::Scope scope(m_engine->context()); + v8::Local object = v8::Object::New(); + object->Set(m_engine->toString(propName), m_engine->variantWrapper()->newVariant(value)); + bool changed = m_obj->m_node->setObjectValue(object, false); if (changed) m_obj->m_node->changedProperty(propName); } - QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativelistmodel_p.h b/src/declarative/util/qdeclarativelistmodel_p.h index 93a3183..5962f37 100644 --- a/src/declarative/util/qdeclarativelistmodel_p.h +++ b/src/declarative/util/qdeclarativelistmodel_p.h @@ -53,6 +53,8 @@ #include #include +#include + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -80,10 +82,10 @@ public: Q_INVOKABLE void clear(); Q_INVOKABLE void remove(int index); - Q_INVOKABLE void append(const QScriptValue&); - Q_INVOKABLE void insert(int index, const QScriptValue&); - Q_INVOKABLE QScriptValue get(int index) const; - Q_INVOKABLE void set(int index, const QScriptValue&); + Q_INVOKABLE void append(const QDeclarativeV8Handle &); + Q_INVOKABLE void insert(int index, const QDeclarativeV8Handle &); + Q_INVOKABLE QDeclarativeV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QDeclarativeV8Handle &); Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); Q_INVOKABLE void move(int from, int to, int count); Q_INVOKABLE void sync(); @@ -97,13 +99,13 @@ private: friend class QDeclarativeListModelParser; friend class QDeclarativeListModelWorkerAgent; friend class FlatListModel; - friend class FlatListScriptClass; + friend class QDeclarativeListModelV8Data; friend struct ModelNode; // Constructs a flat list model for a worker agent QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent); - void set(int index, const QScriptValue&, QList *roles); + void set(int index, const QDeclarativeV8Handle &, QList *roles); void setProperty(int index, const QString& property, const QVariant& value, QList *roles); bool flatten(); diff --git a/src/declarative/util/qdeclarativelistmodel_p_p.h b/src/declarative/util/qdeclarativelistmodel_p_p.h index 84a6e90..b7fbd0e 100644 --- a/src/declarative/util/qdeclarativelistmodel_p_p.h +++ b/src/declarative/util/qdeclarativelistmodel_p_p.h @@ -67,7 +67,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QDeclarativeOpenMetaObject; -class QScriptEngine; class QDeclarativeListModelWorkerAgent; struct ModelNode; class FlatListScriptClass; @@ -87,24 +86,25 @@ public: int count() const; void clear(); void remove(int index); - bool insert(int index, const QScriptValue&); - QScriptValue get(int index) const; - void set(int index, const QScriptValue&, QList *roles); + bool insert(int index, v8::Handle); + v8::Handle get(int index) const; + void set(int index, v8::Handle, QList *roles); void setProperty(int index, const QString& property, const QVariant& value, QList *roles); void move(int from, int to, int count); private: friend class QDeclarativeListModelWorkerAgent; friend class QDeclarativeListModel; - friend class FlatListScriptClass; + friend class QDeclarativeListModelV8Data; friend class FlatNodeData; - bool addValue(const QScriptValue &value, QHash *row, QList *roles); + bool addValue(v8::Handle value, QHash *row, QList *roles); void insertedNode(int index); void removedNode(int index); void moveNodes(int from, int to, int n); - QScriptEngine *m_scriptEngine; + QV8Engine *engine() const; + QV8Engine *m_engine; QHash m_roles; QHash m_strings; QList > m_values; @@ -116,6 +116,7 @@ private: }; +#if 0 /* Created when get() is called on a FlatListModel. This allows changes to the object returned by get() to be tracked, and passed onto the model. @@ -133,12 +134,13 @@ public: private: FlatListModel *m_model; }; +#endif /* FlatNodeData and FlatNodeObjectData allow objects returned by get() to still point to the correct list index if move(), insert() or remove() are called. */ -struct FlatNodeObjectData; +class QV8ListModelResource; class FlatNodeData { public: @@ -147,31 +149,26 @@ public: ~FlatNodeData(); - void addData(FlatNodeObjectData *data); - void removeData(FlatNodeObjectData *data); + void addData(QV8ListModelResource *data); + void removeData(QV8ListModelResource *data); int index; private: - QSet objects; + QSet objects; }; -struct FlatNodeObjectData : public QScriptDeclarativeClass::Object +class QV8ListModelResource : public QV8ObjectResource { - FlatNodeObjectData(FlatNodeData *data) : nodeData(data) { - nodeData->addData(this); - } - - ~FlatNodeObjectData() { - if (nodeData) - nodeData->removeData(this); - } + V8_RESOURCE_TYPE(ListModelType); +public: + QV8ListModelResource(FlatListModel *model, FlatNodeData *data, QV8Engine *engine); + ~QV8ListModelResource(); + FlatListModel *model; FlatNodeData *nodeData; }; - - class NestedListModel { public: @@ -187,9 +184,9 @@ public: int count() const; void clear(); void remove(int index); - bool insert(int index, const QScriptValue&); - QScriptValue get(int index) const; - void set(int index, const QScriptValue&, QList *roles); + bool insert(int index, v8::Handle); + v8::Handle get(int index) const; + void set(int index, v8::Handle, QList *roles); void setProperty(int index, const QString& property, const QVariant& value, QList *roles); void move(int from, int to, int count); @@ -200,6 +197,7 @@ public: bool m_ownsRoot; QDeclarativeListModel *m_listModel; + QV8Engine *engine() const; private: friend struct ModelNode; mutable QStringList roleStrings; @@ -212,7 +210,7 @@ class ModelObject : public QObject { Q_OBJECT public: - ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng); + ModelObject(ModelNode *node, NestedListModel *model, QV8Engine *eng); void setValue(const QByteArray &name, const QVariant &val); void setNodeUpdatesEnabled(bool enable); @@ -226,7 +224,7 @@ private: class ModelNodeMetaObject : public QDeclarativeOpenMetaObject { public: - ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object); + ModelNodeMetaObject(QV8Engine *eng, ModelObject *object); bool m_enabled; @@ -234,11 +232,10 @@ protected: void propertyWritten(int index); private: - QScriptEngine *m_seng; + QV8Engine *m_engine; ModelObject *m_obj; }; - /* A ModelNode is created for each item in a NestedListModel. */ @@ -255,8 +252,8 @@ struct ModelNode QDeclarativeListModel *model(const NestedListModel *model); ModelObject *object(const NestedListModel *model); - bool setObjectValue(const QScriptValue& valuemap, bool writeToCache = true); - void setListValue(const QScriptValue& valuelist); + bool setObjectValue(v8::Handle valuemap, bool writeToCache = true); + void setListValue(v8::Handle valuelist); bool setProperty(const QString& prop, const QVariant& val); void changedProperty(const QString &name) const; void updateListIndexes(); diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent.cpp b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp index efc83a5..c6855d3 100644 --- a/src/declarative/util/qdeclarativelistmodelworkeragent.cpp +++ b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp @@ -83,10 +83,7 @@ void QDeclarativeListModelWorkerAgent::Data::changedChange(int index, int count, } QDeclarativeListModelWorkerAgent::QDeclarativeListModelWorkerAgent(QDeclarativeListModel *model) - : m_engine(0), - m_ref(1), - m_orig(model), - m_copy(new QDeclarativeListModel(model, this)) +: m_engine(0), m_ref(1), m_orig(model), m_copy(new QDeclarativeListModel(model, this)) { } @@ -94,14 +91,14 @@ QDeclarativeListModelWorkerAgent::~QDeclarativeListModelWorkerAgent() { } -void QDeclarativeListModelWorkerAgent::setScriptEngine(QScriptEngine *eng) +void QDeclarativeListModelWorkerAgent::setV8Engine(QV8Engine *eng) { m_engine = eng; if (m_copy->m_flat) - m_copy->m_flat->m_scriptEngine = eng; + m_copy->m_flat->m_engine = eng; } -QScriptEngine *QDeclarativeListModelWorkerAgent::scriptEngine() const +QV8Engine *QDeclarativeListModelWorkerAgent::v8engine() const { return m_engine; } @@ -140,7 +137,7 @@ void QDeclarativeListModelWorkerAgent::remove(int index) data.removeChange(index, 1); } -void QDeclarativeListModelWorkerAgent::append(const QScriptValue &value) +void QDeclarativeListModelWorkerAgent::append(const QDeclarativeV8Handle &value) { int count = m_copy->count(); m_copy->append(value); @@ -149,7 +146,7 @@ void QDeclarativeListModelWorkerAgent::append(const QScriptValue &value) data.insertChange(m_copy->count() - 1, 1); } -void QDeclarativeListModelWorkerAgent::insert(int index, const QScriptValue &value) +void QDeclarativeListModelWorkerAgent::insert(int index, const QDeclarativeV8Handle &value) { int count = m_copy->count(); m_copy->insert(index, value); @@ -158,12 +155,12 @@ void QDeclarativeListModelWorkerAgent::insert(int index, const QScriptValue &val data.insertChange(index, 1); } -QScriptValue QDeclarativeListModelWorkerAgent::get(int index) const +QDeclarativeV8Handle QDeclarativeListModelWorkerAgent::get(int index) const { return m_copy->get(index); } -void QDeclarativeListModelWorkerAgent::set(int index, const QScriptValue &value) +void QDeclarativeListModelWorkerAgent::set(int index, const QDeclarativeV8Handle &value) { QList roles; m_copy->set(index, value, &roles); diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent_p.h b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h index fa8c773..d8aae68 100644 --- a/src/declarative/util/qdeclarativelistmodelworkeragent_p.h +++ b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h @@ -60,6 +60,8 @@ #include #include +#include + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -78,8 +80,8 @@ public: QDeclarativeListModelWorkerAgent(QDeclarativeListModel *); ~QDeclarativeListModelWorkerAgent(); - void setScriptEngine(QScriptEngine *eng); - QScriptEngine *scriptEngine() const; + void setV8Engine(QV8Engine *eng); + QV8Engine *v8engine() const; void addref(); void release(); @@ -88,10 +90,10 @@ public: Q_INVOKABLE void clear(); Q_INVOKABLE void remove(int index); - Q_INVOKABLE void append(const QScriptValue &); - Q_INVOKABLE void insert(int index, const QScriptValue&); - Q_INVOKABLE QScriptValue get(int index) const; - Q_INVOKABLE void set(int index, const QScriptValue &); + Q_INVOKABLE void append(const QDeclarativeV8Handle &); + Q_INVOKABLE void insert(int index, const QDeclarativeV8Handle &); + Q_INVOKABLE QDeclarativeV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QDeclarativeV8Handle &); Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); Q_INVOKABLE void move(int from, int to, int count); Q_INVOKABLE void sync(); @@ -116,8 +118,8 @@ protected: private: friend class QDeclarativeWorkerScriptEnginePrivate; - friend class FlatListScriptClass; - QScriptEngine *m_engine; + friend class QDeclarativeListModelV8Data; + QV8Engine *m_engine; struct Change { enum { Inserted, Removed, Moved, Changed } type; diff --git a/tests/auto/declarative/qdeclarativelistmodel/data/model.qml b/tests/auto/declarative/qdeclarativelistmodel/data/model.qml index bfd547e..a1a599c 100644 --- a/tests/auto/declarative/qdeclarativelistmodel/data/model.qml +++ b/tests/auto/declarative/qdeclarativelistmodel/data/model.qml @@ -19,4 +19,8 @@ Item { item.done = true } } + + function runEval(js) { + eval(js); + } } diff --git a/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp b/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp index d923a7a..0cd9df2 100644 --- a/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp +++ b/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp @@ -108,6 +108,7 @@ private slots: void property_changes_worker_data(); void clear(); }; + int tst_qdeclarativelistmodel::roleFromName(const QDeclarativeListModel *model, const QString &roleName) { QList roles = model->roles(); @@ -152,19 +153,74 @@ void tst_qdeclarativelistmodel::waitForWorker(QDeclarativeItem *item) QVERIFY(timer.isActive()); } +void tst_qdeclarativelistmodel::static_types_data() +{ + QTest::addColumn("qml"); + QTest::addColumn("value"); + + QTest::newRow("string") + << "ListElement { foo: \"bar\" }" + << QVariant(QString("bar")); + + QTest::newRow("real") + << "ListElement { foo: 10.5 }" + << QVariant(10.5); + + QTest::newRow("real0") + << "ListElement { foo: 0 }" + << QVariant(double(0)); + + QTest::newRow("bool") + << "ListElement { foo: false }" + << QVariant(false); + + QTest::newRow("bool") + << "ListElement { foo: true }" + << QVariant(true); + + QTest::newRow("enum") + << "ListElement { foo: Text.AlignHCenter }" + << QVariant(double(QDeclarativeText::AlignHCenter)); +} + +void tst_qdeclarativelistmodel::static_types() +{ + QFETCH(QString, qml); + QFETCH(QVariant, value); + + qml = "import QtQuick 1.0\nItem { property variant test: model.get(0).foo; ListModel { id: model; " + qml + " } }"; + + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine); + component.setData(qml.toUtf8(), + QUrl::fromLocalFile(QString("dummy.qml"))); + + QVERIFY(!component.isError()); + + QObject *obj = component.create(); + QVERIFY(obj != 0); + + QVariant actual = obj->property("test"); + + QCOMPARE(actual, value); + QCOMPARE(actual.toString(), value.toString()); + + delete obj; +} + void tst_qdeclarativelistmodel::static_i18n() { QString expect = QString::fromUtf8("na\303\257ve"); - QString componentStr = "import QtQuick 1.0\nListModel { ListElement { prop1: \""+expect+"\"; prop2: QT_TR_NOOP(\""+expect+"\") } }"; + QString componentStr = "import QtQuick 1.0\nItem { property string prop1: model.get(0).prop1; property string prop2: model.get(0).prop2; ListModel { id: model; ListElement { prop1: \""+expect+"\"; prop2: QT_TR_NOOP(\""+expect+"\") } } }"; QDeclarativeEngine engine; QDeclarativeComponent component(&engine); component.setData(componentStr.toUtf8(), QUrl::fromLocalFile("")); - QDeclarativeListModel *obj = qobject_cast(component.create()); + QObject *obj = component.create(); QVERIFY(obj != 0); - QString prop1 = obj->get(0).property(QLatin1String("prop1")).toString(); + QString prop1 = obj->property("prop1").toString(); QCOMPARE(prop1,expect); - QString prop2 = obj->get(0).property(QLatin1String("prop2")).toString(); + QString prop2 = obj->property("prop2").toString(); QCOMPARE(prop2,expect); // (no, not translated, QT_TR_NOOP is a no-op) delete obj; } @@ -180,25 +236,29 @@ void tst_qdeclarativelistmodel::static_nestedElements() QString componentStr = "import QtQuick 1.0\n" - "ListModel {\n" - " ListElement {\n" - " attributes: [\n"; + "Item {\n" + " property variant count: model.get(0).attributes.count\n" + " ListModel {\n" + " id: model\n" + " ListElement {\n" + " attributes: [\n"; componentStr += elementsStr.toUtf8().constData(); componentStr += - " ]\n" - " }\n" + " ]\n" + " }\n" + " }\n" "}"; QDeclarativeEngine engine; QDeclarativeComponent component(&engine); component.setData(componentStr.toUtf8(), QUrl::fromLocalFile("")); - QDeclarativeListModel *obj = qobject_cast(component.create()); + QObject *obj = component.create(); QVERIFY(obj != 0); - QScriptValue prop = obj->get(0).property(QLatin1String("attributes")).property(QLatin1String("count")); - QVERIFY(prop.isNumber()); - QCOMPARE(prop.toInt32(), qint32(elementCount)); + QVariant count = obj->property("count"); + QCOMPARE(count.type(), QVariant::Int); + QCOMPARE(count.toInt(), elementCount); delete obj; } @@ -384,8 +444,6 @@ void tst_qdeclarativelistmodel::dynamic_worker() qApp->processEvents(); } - - void tst_qdeclarativelistmodel::dynamic_worker_sync_data() { dynamic_data(); @@ -438,6 +496,17 @@ void tst_qdeclarativelistmodel::dynamic_worker_sync() qApp->processEvents(); } +#define RUNEVAL(object, string) \ + QVERIFY(QMetaObject::invokeMethod(object, "runEval", Q_ARG(QVariant, QString(string)))); + +inline QVariant runexpr(QDeclarativeEngine *engine, const QString &str) +{ + QDeclarativeExpression expr(engine->rootContext(), 0, str); + return expr.evaluate(); +} + +#define RUNEXPR(string) runexpr(&engine, QString(string)) + void tst_qdeclarativelistmodel::convertNestedToFlat_fail() { // If a model has nested data, it cannot be used at all from a worker script @@ -450,11 +519,9 @@ void tst_qdeclarativelistmodel::convertNestedToFlat_fail() QDeclarativeItem *item = createWorkerTest(&eng, &component, &model); QVERIFY(item != 0); - QScriptEngine s_eng; - QScriptValue plainData = s_eng.newObject(); - plainData.setProperty("foo", QScriptValue(123)); - model.append(plainData); - model.append(nestedListValue(&s_eng)); + RUNEVAL(item, "model.append({foo: 123})"); + RUNEVAL(item, "model.append({foo: [{}, {}]})"); + QCOMPARE(model.count(), 2); QTest::ignoreMessage(QtWarningMsg, ": QML ListModel: List contains list-type data and cannot be used from a worker script"); @@ -482,6 +549,7 @@ void tst_qdeclarativelistmodel::convertNestedToFlat_fail_data() } void tst_qdeclarativelistmodel::convertNestedToFlat_ok() + { // If a model only has plain data, it can be modified from a worker script. However, // once the model is used from a worker script, it no longer accepts nested data @@ -494,10 +562,8 @@ void tst_qdeclarativelistmodel::convertNestedToFlat_ok() QDeclarativeItem *item = createWorkerTest(&eng, &component, &model); QVERIFY(item != 0); - QScriptEngine s_eng; - QScriptValue plainData = s_eng.newObject(); - plainData.setProperty("foo", QScriptValue(123)); - model.append(plainData); + RUNEVAL(item, "model.append({foo: 123})"); + QCOMPARE(model.count(), 1); QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", Q_ARG(QVariant, script))); @@ -505,20 +571,21 @@ void tst_qdeclarativelistmodel::convertNestedToFlat_ok() // can still add plain data int count = model.count(); - model.append(plainData); + + RUNEVAL(item, "model.append({foo: 123})"); + QCOMPARE(model.count(), count+1); - QScriptValue nested = nestedListValue(&s_eng); const char *warning = ": QML ListModel: Cannot add list-type data when modifying or after modification from a worker script"; QTest::ignoreMessage(QtWarningMsg, warning); - model.append(nested); + RUNEVAL(item, "model.append({foo: [{}, {}]})"); QTest::ignoreMessage(QtWarningMsg, warning); - model.insert(0, nested); + RUNEVAL(item, "model.insert(0, {foo: [{}, {}]})"); QTest::ignoreMessage(QtWarningMsg, warning); - model.set(0, nested); + RUNEVAL(item, "model.set(0, {foo: [{}, {}]})"); QCOMPARE(model.count(), count+1); @@ -531,67 +598,6 @@ void tst_qdeclarativelistmodel::convertNestedToFlat_ok_data() convertNestedToFlat_fail_data(); } -void tst_qdeclarativelistmodel::static_types_data() -{ - QTest::addColumn("qml"); - QTest::addColumn("value"); - - QTest::newRow("string") - << "ListElement { foo: \"bar\" }" - << QVariant(QString("bar")); - - QTest::newRow("real") - << "ListElement { foo: 10.5 }" - << QVariant(10.5); - - QTest::newRow("real0") - << "ListElement { foo: 0 }" - << QVariant(double(0)); - - QTest::newRow("bool") - << "ListElement { foo: false }" - << QVariant(false); - - QTest::newRow("bool") - << "ListElement { foo: true }" - << QVariant(true); - - QTest::newRow("enum") - << "ListElement { foo: Text.AlignHCenter }" - << QVariant(double(QDeclarativeText::AlignHCenter)); -} - -void tst_qdeclarativelistmodel::static_types() -{ - QFETCH(QString, qml); - QFETCH(QVariant, value); - - qml = "import QtQuick 1.0\nListModel { " + qml + " }"; - - QDeclarativeEngine engine; - QDeclarativeComponent component(&engine); - component.setData(qml.toUtf8(), - QUrl::fromLocalFile(QString("dummy.qml"))); - - if (value.toString().startsWith("QTBUG-")) - QEXPECT_FAIL("",value.toString().toLatin1(),Abort); - - QVERIFY(!component.isError()); - - QDeclarativeListModel *obj = qobject_cast(component.create()); - QVERIFY(obj != 0); - - QScriptValue actual = obj->get(0).property(QLatin1String("foo")); - - QCOMPARE(actual.isString(), value.type() == QVariant::String); - QCOMPARE(actual.isBoolean(), value.type() == QVariant::Bool); - QCOMPARE(actual.isNumber(), value.type() == QVariant::Double); - - QCOMPARE(actual.toString(), value.toString()); - - delete obj; -} - void tst_qdeclarativelistmodel::enumerate() { QDeclarativeEngine eng; @@ -608,7 +614,6 @@ void tst_qdeclarativelistmodel::enumerate() delete item; } - void tst_qdeclarativelistmodel::error_data() { QTest::addColumn("qml"); @@ -701,21 +706,16 @@ void tst_qdeclarativelistmodel::set() QDeclarativeEngine engine; QDeclarativeListModel model; QDeclarativeEngine::setContextForObject(&model,engine.rootContext()); - engine.rootContext()->setContextObject(&model); - QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(&engine); + engine.rootContext()->setContextProperty("model", &model); - QScriptValue sv = seng->newObject(); - sv.setProperty("test", QScriptValue(false)); - model.append(sv); + RUNEXPR("model.append({test:false})"); + RUNEXPR("model.set(0, {test:true})"); - sv.setProperty("test", QScriptValue(true)); - model.set(0, sv); - QCOMPARE(model.get(0).property("test").toBool(), true); // triggers creation of model cache + QCOMPARE(RUNEXPR("model.get(0).test").toBool(), true); // triggers creation of model cache QCOMPARE(model.data(0, model.roles()[0]), qVariantFromValue(true)); - sv.setProperty("test", QScriptValue(false)); - model.set(0, sv); - QCOMPARE(model.get(0).property("test").toBool(), false); // tests model cache is updated + RUNEXPR("model.set(0, {test:false})"); + QCOMPARE(RUNEXPR("model.get(0).test").toBool(), false); // tests model cache is updated QCOMPARE(model.data(0, model.roles()[0]), qVariantFromValue(false)); } @@ -729,8 +729,8 @@ void tst_qdeclarativelistmodel::get() QFETCH(QString, roleName); QFETCH(QVariant, roleValue); - QDeclarativeEngine eng; - QDeclarativeComponent component(&eng); + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine); component.setData( "import QtQuick 1.0\n" "ListModel { \n" @@ -743,7 +743,7 @@ void tst_qdeclarativelistmodel::get() QVERIFY(role >= 0); QSignalSpy spy(model, SIGNAL(itemsChanged(int, int, QList))); - QDeclarativeExpression expr(eng.rootContext(), model, expression); + QDeclarativeExpression expr(engine.rootContext(), model, expression); expr.evaluate(); QVERIFY(!expr.hasError()); @@ -792,17 +792,12 @@ void tst_qdeclarativelistmodel::get_worker() QDeclarativeComponent component(&eng, QUrl::fromLocalFile(SRCDIR "/data/model.qml")); QDeclarativeItem *item = createWorkerTest(&eng, &component, &model); QVERIFY(item != 0); - QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(&eng); // Add some values like get() test - QScriptValue sv = seng->newObject(); - sv.setProperty(QLatin1String("roleA"), seng->newVariant(QVariant::fromValue(100))); - model.append(sv); - sv = seng->newObject(); - sv.setProperty(QLatin1String("roleA"), seng->newVariant(QVariant::fromValue(200))); - sv.setProperty(QLatin1String("roleB"), seng->newVariant(QVariant::fromValue(400))); - model.append(sv); - model.append(sv); + RUNEVAL(item, "model.append({roleA: 100})"); + RUNEVAL(item, "model.append({roleA: 200, roleB: 400})"); + RUNEVAL(item, "model.append({roleA: 200, roleB: 400})"); + int role = roleFromName(&model, roleName); QVERIFY(role >= 0); @@ -1055,7 +1050,6 @@ void tst_qdeclarativelistmodel::property_changes_data() << "b" << 0 << true << "get(0).b.count == 0"; } - void tst_qdeclarativelistmodel::property_changes_worker() { // nested models are not supported when WorkerScript is involved @@ -1108,33 +1102,26 @@ void tst_qdeclarativelistmodel::clear() QDeclarativeEngine engine; QDeclarativeListModel model; QDeclarativeEngine::setContextForObject(&model, engine.rootContext()); - engine.rootContext()->setContextObject(&model); - - QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(&engine); - QScriptValue sv = seng->newObject(); - QVariant result; + engine.rootContext()->setContextProperty("model", &model); model.clear(); QCOMPARE(model.count(), 0); - sv.setProperty("propertyA", "value a"); - sv.setProperty("propertyB", "value b"); - model.append(sv); + RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); QCOMPARE(model.count(), 1); model.clear(); QCOMPARE(model.count(), 0); - model.append(sv); - model.append(sv); + RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); + RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); QCOMPARE(model.count(), 2); model.clear(); QCOMPARE(model.count(), 0); // clearing does not remove the roles - sv.setProperty("propertyC", "value c"); - model.append(sv); + RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\", propertyC: \"value c\"})"); QList roles = model.roles(); model.clear(); QCOMPARE(model.count(), 0); -- 1.7.2.5