diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2011-06-07 13:19:27 +1000 |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2011-06-07 17:00:11 +1000 |
commit | 1c3dd8f22da09bf9a110715d159374a09c27dc77 (patch) | |
tree | 034a820adb81885c5082e8102605e99e5b5db767 /src/declarative/qml/v8/qv8qobjectwrapper.cpp | |
parent | 26942a682c82bd347da65b143442d53d58533263 (diff) |
Allow the same QObject to be used in multiple QDeclarativeEngines
Diffstat (limited to 'src/declarative/qml/v8/qv8qobjectwrapper.cpp')
-rw-r--r-- | src/declarative/qml/v8/qv8qobjectwrapper.cpp | 144 |
1 files changed, 120 insertions, 24 deletions
diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp index 99cede2072..2647197586 100644 --- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp +++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp @@ -52,6 +52,7 @@ #include <QtScript/qscriptvalue.h> #include <QtCore/qvarlengtharray.h> #include <QtCore/qtimer.h> +#include <QtCore/qatomic.h> QT_BEGIN_NAMESPACE @@ -79,6 +80,31 @@ public: QDeclarativeGuard<QObject> object; }; +class QV8QObjectInstance : public QDeclarativeGuard<QObject> +{ +public: + QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w) + : QDeclarativeGuard<QObject>(o), wrapper(w) + { + } + + ~QV8QObjectInstance() + { + v8object.Dispose(); + v8object.Clear(); + } + + virtual void objectDestroyed(QObject *o) + { + if (wrapper) + wrapper->m_taintedObjects.remove(o); + delete this; + } + + v8::Persistent<v8::Object> v8object; + QV8QObjectWrapper *wrapper; +}; + namespace { struct MetaCallArgument { inline MetaCallArgument(); @@ -105,13 +131,21 @@ QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object) { } +static QAtomicInt objectIdCounter(1); + QV8QObjectWrapper::QV8QObjectWrapper() -: m_engine(0) +: m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1)) { } QV8QObjectWrapper::~QV8QObjectWrapper() { + for (TaintedHash::Iterator iter = m_taintedObjects.begin(); + iter != m_taintedObjects.end(); + ++iter) { + (*iter)->wrapper = 0; + } + m_taintedObjects.clear(); } void QV8QObjectWrapper::destroy() @@ -619,8 +653,13 @@ static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void } } - // XXX do we want to use the objectDataRefCount to support multiple concurrent engines? + handle.Dispose(); +} +static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data) +{ + QV8QObjectInstance *instance = (QV8QObjectInstance *)data; + instance->v8object.Clear(); handle.Dispose(); } @@ -628,10 +667,8 @@ v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8 { Q_ASSERT(object); - QDeclarativeData *ddata = QDeclarativeData::get(object, false); - - Q_ASSERT(ddata && ddata->propertyCache && ddata->propertyCache == this); - Q_ASSERT(ddata->v8object.IsEmpty()); + Q_ASSERT(QDeclarativeData::get(object, false)); + Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this); // Setup constructor if (constructor.IsEmpty()) { @@ -709,16 +746,42 @@ v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8 v8::Local<v8::Object> result = constructor->NewInstance(); QV8QObjectResource *r = new QV8QObjectResource(engine, object); result->SetExternalResource(r); - - ddata->v8object = v8::Persistent<v8::Object>::New(result); - ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); return result; } -v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object) +v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine) { - // XXX aakenned QDeclarativeObjectScriptClass::newQObject() does a lot more + v8::Local<v8::Object> rv; + if (ddata->propertyCache) { + rv = ddata->propertyCache->newQObject(object, engine); + } else { + // XXX aakenned - NewInstance() is slow for our case + rv = m_constructor->NewInstance(); + QV8QObjectResource *r = new QV8QObjectResource(engine, object); + rv->SetExternalResource(r); + } + + return rv; +} + +/* +As V8 doesn't support an equality callback, for QObject's we have to return exactly the same +V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two +pronged strategy: + 1. If there is no current outstanding V8 handle to the QObject, we create one and store a + persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that + "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this + QV8QObjectWrapper. + 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create + an entry in the m_taintedObject hash where we store the handle and mark the object as + "tainted" in the QDeclarativeData::hasTaintedV8Object flag. +We have to mark the object as tainted to ensure that we search our m_taintedObject hash even +in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has +released the handle. +*/ +v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object) +{ if (!object) return v8::Null(); @@ -730,23 +793,56 @@ v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object) if (!ddata) return v8::Undefined(); - if (ddata->v8object.IsEmpty()) { - - if (ddata->propertyCache) { - return ddata->propertyCache->newQObject(object, m_engine); - } + if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) { + // We own the v8object + return v8::Local<v8::Object>::New(ddata->v8object); + } else if (ddata->v8object.IsEmpty() && + (ddata->v8objectid == m_id || // We own the QObject + ddata->v8objectid == 0 || // No one owns the QObject + !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted - // XXX aakenned - NewInstance() is slow for our case - v8::Local<v8::Object> rv = m_constructor->NewInstance(); - QV8QObjectResource *r = new QV8QObjectResource(m_engine, object); - rv->SetExternalResource(r); + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); ddata->v8object = v8::Persistent<v8::Object>::New(rv); - } + ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); + ddata->v8objectid = m_id; + return rv; + + } else { - // XXX do we have to check that the v8object isn't "owned" by another engine? + // If this object is tainted, we have to check to see if it is in our + // tainted object list + TaintedHash::Iterator iter = + ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end(); + bool found = iter != m_taintedObjects.end(); + + // If our tainted handle doesn't exist or has been collected, and there isn't + // a handle in the ddata, we can assume ownership of the ddata->v8object + if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) { + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); + ddata->v8object = v8::Persistent<v8::Object>::New(rv); + ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); + ddata->v8objectid = m_id; + + if (found) { + delete (*iter); + m_taintedObjects.erase(iter); + } + + return rv; + } else if (!found) { + QV8QObjectInstance *instance = new QV8QObjectInstance(object, this); + iter = m_taintedObjects.insert(object, instance); + ddata->hasTaintedV8Object = true; + } - ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); - return v8::Local<v8::Object>::New(ddata->v8object); + if ((*iter)->v8object.IsEmpty()) { + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); + (*iter)->v8object = v8::Persistent<v8::Object>::New(rv); + (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback); + } + + return v8::Local<v8::Object>::New((*iter)->v8object); + } } QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function) |