Make QQmlPropertyCache available in QJSEngine
authorSimon Hausmann <simon.hausmann@theqtcompany.com>
Thu, 20 Nov 2014 08:07:52 +0000 (09:07 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Mon, 29 Dec 2014 13:39:22 +0000 (14:39 +0100)
The QQmlEngine has two containers for property caches, one for QML types and
one for all-purpose meta-objects. The latter is rather useful and now being
moved to QJSEngine to be available there.

Change-Id: Ieab65c400b8a2e410e5f9eee6d603162dbb864d9
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/jsapi/qjsengine.cpp
src/qml/jsapi/qjsengine_p.h
src/qml/jsruntime/qv4script.cpp
src/qml/qml/qqmlboundsignal.cpp
src/qml/qml/qqmlengine.cpp
src/qml/qml/qqmlengine_p.h
src/qml/qml/qqmlopenmetaobject.cpp
src/qml/qml/qqmlpropertycache.cpp
src/qml/qml/qqmlpropertycache_p.h

index e4daa64..04ed341 100644 (file)
@@ -539,6 +539,27 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
     \sa toScriptValue()
 */
 
+
+QJSEnginePrivate::~QJSEnginePrivate()
+{
+    for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator iter = propertyCache.begin(); iter != propertyCache.end(); ++iter)
+        (*iter)->release();
+}
+
+QQmlPropertyCache *QJSEnginePrivate::createCache(const QMetaObject *mo)
+{
+    if (!mo->superClass()) {
+        QQmlPropertyCache *rv = new QQmlPropertyCache(q_func(), mo);
+        propertyCache.insert(mo, rv);
+        return rv;
+    } else {
+        QQmlPropertyCache *super = cache(mo->superClass());
+        QQmlPropertyCache *rv = super->copyAndAppend(mo);
+        propertyCache.insert(mo, rv);
+        return rv;
+    }
+}
+
 QT_END_NAMESPACE
 
 #include "moc_qjsengine.cpp"
index 654d0de..eba8ffb 100644 (file)
 //
 
 #include <QtCore/private/qobject_p.h>
+#include <QtCore/qmutex.h>
 #include "qjsengine.h"
 
 
 QT_BEGIN_NAMESPACE
 
+class QQmlPropertyCache;
 
 class QJSEnginePrivate : public QObjectPrivate
 {
@@ -58,10 +60,131 @@ class QJSEnginePrivate : public QObjectPrivate
 
 public:
     static QJSEnginePrivate* get(QJSEngine*e) { return e->d_func(); }
+    static const QJSEnginePrivate* get(const QJSEngine*e) { return e->d_func(); }
 
-    QJSEnginePrivate() {}
+    QJSEnginePrivate() : mutex(QMutex::Recursive) {}
+    ~QJSEnginePrivate();
+
+    // Locker locks the QQmlEnginePrivate data structures for read and write, if necessary.
+    // Currently, locking is only necessary if the threaded loader is running concurrently.  If it is
+    // either idle, or is running with the main thread blocked, no locking is necessary.  This way
+    // we only pay for locking when we have to.
+    // Consequently, this class should only be used to protect simple accesses or modifications of the
+    // QQmlEnginePrivate structures or operations that can be guaranteed not to start activity
+    // on the loader thread.
+    // The Locker API is identical to QMutexLocker.  Locker reuses the QQmlEnginePrivate::mutex
+    // QMutex instance and multiple Lockers are recursive in the same thread.
+    class Locker
+    {
+    public:
+        inline Locker(const QJSEngine *);
+        inline Locker(const QJSEnginePrivate *);
+        inline ~Locker();
+
+        inline void unlock();
+        inline void relock();
+
+    private:
+        const QJSEnginePrivate *m_ep;
+        quint32 m_locked:1;
+    };
+
+    // Shared by QQmlEngine
+    mutable QMutex mutex;
+
+
+    // These methods may be called from the QML loader thread
+    inline QQmlPropertyCache *cache(QObject *obj);
+    inline QQmlPropertyCache *cache(const QMetaObject *);
+
+private:
+    // Must be called locked
+    QQmlPropertyCache *createCache(const QMetaObject *);
+
+    // These members must be protected by a QJSEnginePrivate::Locker as they are required by
+    // the threaded loader.  Only access them through their respective accessor methods.
+    QHash<const QMetaObject *, QQmlPropertyCache *> propertyCache;
 };
 
+QJSEnginePrivate::Locker::Locker(const QJSEngine *e)
+: m_ep(QJSEnginePrivate::get(e))
+{
+    relock();
+}
+
+QJSEnginePrivate::Locker::Locker(const QJSEnginePrivate *e)
+: m_ep(e), m_locked(false)
+{
+    relock();
+}
+
+QJSEnginePrivate::Locker::~Locker()
+{
+    unlock();
+}
+
+void QJSEnginePrivate::Locker::unlock()
+{
+    if (m_locked) {
+        m_ep->mutex.unlock();
+        m_locked = false;
+    }
+}
+
+void QJSEnginePrivate::Locker::relock()
+{
+    Q_ASSERT(!m_locked);
+    m_ep->mutex.lock();
+    m_locked = true;
+}
+
+/*!
+Returns a QQmlPropertyCache for \a obj if one is available.
+
+If \a obj is null, being deleted or contains a dynamic meta object 0
+is returned.
+
+The returned cache is not referenced, so if it is to be stored, call addref().
+
+XXX thread There is a potential future race condition in this and all the cache()
+functions.  As the QQmlPropertyCache is returned unreferenced, when called
+from the loader thread, it is possible that the cache will have been dereferenced
+and deleted before the loader thread has a chance to use or reference it.  This
+can't currently happen as the cache holds a reference to the
+QQmlPropertyCache until the QQmlEngine is destroyed.
+*/
+QQmlPropertyCache *QJSEnginePrivate::cache(QObject *obj)
+{
+    if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted)
+        return 0;
+
+    Locker locker(this);
+    const QMetaObject *mo = obj->metaObject();
+    QQmlPropertyCache *rv = propertyCache.value(mo);
+    if (!rv) rv = createCache(mo);
+    return rv;
+}
+
+/*!
+Returns a QQmlPropertyCache for \a metaObject.
+
+As the cache is persisted for the life of the engine, \a metaObject must be
+a static "compile time" meta-object, or a meta-object that is otherwise known to
+exist for the lifetime of the QQmlEngine.
+
+The returned cache is not referenced, so if it is to be stored, call addref().
+*/
+QQmlPropertyCache *QJSEnginePrivate::cache(const QMetaObject *metaObject)
+{
+    Q_ASSERT(metaObject);
+
+    Locker locker(this);
+    QQmlPropertyCache *rv = propertyCache.value(metaObject);
+    if (!rv) rv = createCache(metaObject);
+    return rv;
+}
+
+
 QT_END_NAMESPACE
 
 #endif // QJSENGINE_P_H
index 088e961..38d2c12 100644 (file)
@@ -176,7 +176,7 @@ Heap::FunctionObject *QmlBindingWrapper::createQmlCallableForFunction(QQmlContex
 
     if (!signalParameters.isEmpty()) {
         if (error)
-            QQmlPropertyCache::signalParameterStringForJS(qmlContext->engine, signalParameters, error);
+            QQmlPropertyCache::signalParameterStringForJS(engine, signalParameters, error);
         QV4::ScopedProperty p(valueScope);
         QV4::ScopedString s(valueScope);
         int index = 0;
index f8718ad..8baeefe 100644 (file)
@@ -217,7 +217,7 @@ void QQmlBoundSignalExpression::evaluate(void **a)
                 //TODO: look at using the property cache here (as in the compiler)
                 //      for further optimization
                 QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index);
-                expression += QQmlPropertyCache::signalParameterStringForJS(engine(), signal.parameterNames(), &error);
+                expression += QQmlPropertyCache::signalParameterStringForJS(scope.engine, signal.parameterNames(), &error);
 
                 if (!error.isEmpty()) {
                     qmlInfo(scopeObject()) << error;
index f9de7ad..8f2c554 100644 (file)
@@ -559,7 +559,7 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e)
   activeObjectCreator(0),
   networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0),
   scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1),
-  incubatorCount(0), incubationController(0), mutex(QMutex::Recursive)
+  incubatorCount(0), incubationController(0)
 {
 }
 
@@ -582,8 +582,6 @@ QQmlEnginePrivate::~QQmlEnginePrivate()
     if (incubationController) incubationController->d = 0;
     incubationController = 0;
 
-    for(QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator iter = propertyCache.begin(); iter != propertyCache.end(); ++iter)
-        (*iter)->release();
     for(QHash<QPair<QQmlType *, int>, QQmlPropertyCache *>::Iterator iter = typePropertyCache.begin(); iter != typePropertyCache.end(); ++iter)
         (*iter)->release();
     for (QHash<int, QQmlCompiledData *>::Iterator iter = m_compositeTypes.begin(); iter != m_compositeTypes.end(); ++iter)
@@ -2043,22 +2041,6 @@ QString QQmlEngine::offlineStoragePath() const
     return d->offlineStoragePath;
 }
 
-QQmlPropertyCache *QQmlEnginePrivate::createCache(const QMetaObject *mo)
-{
-    Q_Q(QQmlEngine);
-
-    if (!mo->superClass()) {
-        QQmlPropertyCache *rv = new QQmlPropertyCache(q, mo);
-        propertyCache.insert(mo, rv);
-        return rv;
-    } else {
-        QQmlPropertyCache *super = cache(mo->superClass());
-        QQmlPropertyCache *rv = super->copyAndAppend(mo);
-        propertyCache.insert(mo, rv);
-        return rv;
-    }
-}
-
 QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion,
                                                                   QQmlError &error)
 {
index a1935b8..de2a2d3 100644 (file)
@@ -208,9 +208,8 @@ public:
     inline static void deleteInEngineThread(QQmlEngine *, T *);
 
     // These methods may be called from the loader thread
-    inline QQmlPropertyCache *cache(QObject *obj);
-    inline QQmlPropertyCache *cache(const QMetaObject *);
     inline QQmlPropertyCache *cache(QQmlType *, int, QQmlError &error);
+    using QJSEnginePrivate::cache;
 
     // These methods may be called from the loader thread
     bool isQObject(int);
@@ -260,40 +259,13 @@ public:
     static bool qml_debugging_enabled;
 
     mutable QMutex networkAccessManagerMutex;
-    mutable QMutex mutex;
 
 private:
-    // Locker locks the QQmlEnginePrivate data structures for read and write, if necessary.
-    // Currently, locking is only necessary if the threaded loader is running concurrently.  If it is
-    // either idle, or is running with the main thread blocked, no locking is necessary.  This way
-    // we only pay for locking when we have to.
-    // Consequently, this class should only be used to protect simple accesses or modifications of the
-    // QQmlEnginePrivate structures or operations that can be guaranteed not to start activity
-    // on the loader thread.
-    // The Locker API is identical to QMutexLocker.  Locker reuses the QQmlEnginePrivate::mutex
-    // QMutex instance and multiple Lockers are recursive in the same thread.
-    class Locker
-    {
-    public:
-        inline Locker(const QQmlEngine *);
-        inline Locker(const QQmlEnginePrivate *);
-        inline ~Locker();
-
-        inline void unlock();
-        inline void relock();
-
-    private:
-        const QQmlEnginePrivate *m_ep;
-        quint32 m_locked:1;
-    };
-
     // Must be called locked
-    QQmlPropertyCache *createCache(const QMetaObject *);
     QQmlPropertyCache *createCache(QQmlType *, int, QQmlError &error);
 
     // These members must be protected by a QQmlEnginePrivate::Locker as they are required by
     // the threaded loader.  Only access them through their respective accessor methods.
-    QHash<const QMetaObject *, QQmlPropertyCache *> propertyCache;
     QHash<QPair<QQmlType *, int>, QQmlPropertyCache *> typePropertyCache;
     QHash<int, int> m_qmlLists;
     QHash<int, QQmlCompiledData *> m_compositeTypes;
@@ -306,38 +278,6 @@ private:
     void doDeleteInEngineThread();
 };
 
-QQmlEnginePrivate::Locker::Locker(const QQmlEngine *e)
-: m_ep(QQmlEnginePrivate::get(e))
-{
-    relock();
-}
-
-QQmlEnginePrivate::Locker::Locker(const QQmlEnginePrivate *e)
-: m_ep(e), m_locked(false)
-{
-    relock();
-}
-
-QQmlEnginePrivate::Locker::~Locker()
-{
-    unlock();
-}
-
-void QQmlEnginePrivate::Locker::unlock()
-{
-    if (m_locked) {
-        m_ep->mutex.unlock();
-        m_locked = false;
-    }
-}
-
-void QQmlEnginePrivate::Locker::relock()
-{
-    Q_ASSERT(!m_locked);
-    m_ep->mutex.lock();
-    m_locked = true;
-}
-
 /*!
 Returns true if the calling thread is the QQmlEngine thread.
 */
@@ -402,52 +342,6 @@ void QQmlEnginePrivate::deleteInEngineThread(QQmlEngine *engine, T *value)
 }
 
 /*!
-Returns a QQmlPropertyCache for \a obj if one is available.
-
-If \a obj is null, being deleted or contains a dynamic meta object 0
-is returned.
-
-The returned cache is not referenced, so if it is to be stored, call addref().
-
-XXX thread There is a potential future race condition in this and all the cache()
-functions.  As the QQmlPropertyCache is returned unreferenced, when called
-from the loader thread, it is possible that the cache will have been dereferenced
-and deleted before the loader thread has a chance to use or reference it.  This
-can't currently happen as the cache holds a reference to the
-QQmlPropertyCache until the QQmlEngine is destroyed.
-*/
-QQmlPropertyCache *QQmlEnginePrivate::cache(QObject *obj)
-{
-    if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted)
-        return 0;
-
-    Locker locker(this);
-    const QMetaObject *mo = obj->metaObject();
-    QQmlPropertyCache *rv = propertyCache.value(mo);
-    if (!rv) rv = createCache(mo);
-    return rv;
-}
-
-/*!
-Returns a QQmlPropertyCache for \a metaObject.
-
-As the cache is persisted for the life of the engine, \a metaObject must be
-a static "compile time" meta-object, or a meta-object that is otherwise known to
-exist for the lifetime of the QQmlEngine.
-
-The returned cache is not referenced, so if it is to be stored, call addref().
-*/
-QQmlPropertyCache *QQmlEnginePrivate::cache(const QMetaObject *metaObject)
-{
-    Q_ASSERT(metaObject);
-
-    Locker locker(this);
-    QQmlPropertyCache *rv = propertyCache.value(metaObject);
-    if (!rv) rv = createCache(metaObject);
-    return rv;
-}
-
-/*!
 Returns a QQmlPropertyCache for \a type with \a minorVersion.
 
 The returned cache is not referenced, so if it is to be stored, call addref().
index 9715fdc..e234a0b 100644 (file)
@@ -35,6 +35,7 @@
 #include <private/qqmlpropertycache_p.h>
 #include <private/qqmldata_p.h>
 #include <private/qmetaobjectbuilder_p.h>
+#include <qqmlengine.h>
 #include <qdebug.h>
 
 QT_BEGIN_NAMESPACE
index 363fce4..b5bc29a 100644 (file)
@@ -231,7 +231,7 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
 /*!
 Creates a new empty QQmlPropertyCache.
 */
-QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e)
+QQmlPropertyCache::QQmlPropertyCache(QJSEngine *e)
 : engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
   signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false),
   _metaObject(0), argumentsCache(0)
@@ -242,7 +242,7 @@ QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e)
 /*!
 Creates a new QQmlPropertyCache of \a metaObject.
 */
-QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e, const QMetaObject *metaObject)
+QQmlPropertyCache::QQmlPropertyCache(QJSEngine *e, const QMetaObject *metaObject)
 : engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
   signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false),
   _metaObject(0), argumentsCache(0)
@@ -818,7 +818,7 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
                 data->propType = registerResult == -1 ? QMetaType::UnknownType : registerResult;
             }
         }
-        data->flags |= flagsForPropertyType(data->propType, engine);
+        data->flags |= flagsForPropertyType(data->propType, qobject_cast<QQmlEngine*>(engine));
     }
 
     data->flags &= ~QQmlPropertyData::NotFullyResolved;
@@ -1135,7 +1135,7 @@ QString QQmlPropertyCache::signalParameterStringForJS(int index, QString *errorS
     }
 
     QString error;
-    QString parameters = signalParameterStringForJS(engine, parameterNameList, &error);
+    QString parameters = signalParameterStringForJS(QV8Engine::getV4(engine), parameterNameList, &error);
 
     A *arguments = static_cast<A *>(signalData->arguments);
     arguments->signalParameterStringForJS = new QString(!error.isEmpty() ? error : parameters);
@@ -1148,11 +1148,10 @@ QString QQmlPropertyCache::signalParameterStringForJS(int index, QString *errorS
     return *arguments->signalParameterStringForJS;
 }
 
-QString QQmlPropertyCache::signalParameterStringForJS(QQmlEngine *engine, const QList<QByteArray> &parameterNameList, QString *errorString)
+QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> &parameterNameList, QString *errorString)
 {
-    QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
     bool unnamedParameter = false;
-    const QSet<QString> &illegalNames = ep->v8engine()->illegalNames();
+    const QSet<QString> &illegalNames = engine->v8Engine->illegalNames();
     QString error;
     QString parameters;
 
index d3ea336..224de94 100644 (file)
@@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE
 class QV8Engine;
 class QMetaProperty;
 class QQmlEngine;
+class QJSEngine;
 class QQmlPropertyData;
 class QQmlAccessors;
 class QMetaObjectBuilder;
@@ -242,8 +243,8 @@ class QQmlPropertyCacheMethodArguments;
 class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount, public QQmlCleanup
 {
 public:
-    QQmlPropertyCache(QQmlEngine *);
-    QQmlPropertyCache(QQmlEngine *, const QMetaObject *);
+    QQmlPropertyCache(QJSEngine *);
+    QQmlPropertyCache(QJSEngine *, const QMetaObject *);
     virtual ~QQmlPropertyCache();
 
     void update(const QMetaObject *);
@@ -311,7 +312,7 @@ public:
 
     QList<QByteArray> signalParameterNames(int index) const;
     QString signalParameterStringForJS(int index, QString *errorString = 0);
-    static QString signalParameterStringForJS(QQmlEngine *engine, const QList<QByteArray> &parameterNameList, QString *errorString = 0);
+    static QString signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> &parameterNameList, QString *errorString = 0);
 
     const char *className() const;
 
@@ -374,8 +375,7 @@ private:
         _hasPropertyOverrides |= isOverride;
     }
 
-    // Optional! Only used for calling flagsForPropertyType, in which it is also optional.
-    QQmlEngine *engine;
+    QJSEngine *engine;
 
     QQmlPropertyCache *_parent;
     int propertyIndexCacheStart;