From: Aaron Kennedy Date: Mon, 5 Sep 2011 07:31:41 +0000 (+1000) Subject: Asynchronous component instantiation X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=703c808a5649169dd6b9605af273374cd62951d1;p=konrad%2Fqtdeclarative.git Asynchronous component instantiation This introduces two main: * the QML compiler executes in a separate thread * item instantiation can be interrupted and resumed to allow it to be split across multiple frames. Task-number: QTBUG-21151 Change-Id: I9631c62bb77da3a2e0c37f0da3719533fdce4fef Reviewed-on: http://codereview.qt-project.org/5676 Reviewed-by: Martin Jones --- diff --git a/src/declarative/qml/ftw/ftw.pri b/src/declarative/qml/ftw/ftw.pri index d6e7df5..2d6729b 100644 --- a/src/declarative/qml/ftw/ftw.pri +++ b/src/declarative/qml/ftw/ftw.pri @@ -11,12 +11,14 @@ HEADERS += \ $$PWD/qfieldlist_p.h \ $$PWD/qfastmetabuilder_p.h \ $$PWD/qhashfield_p.h \ + $$PWD/qdeclarativethread_p.h \ + $$PWD/qfinitestack_p.h \ SOURCES += \ $$PWD/qintrusivelist.cpp \ $$PWD/qmetaobjectbuilder.cpp \ $$PWD/qhashedstring.cpp \ - $$PWD/qdeclarativerefcount.cpp \ $$PWD/qdeclarativepool.cpp \ $$PWD/qfastmetabuilder.cpp \ + $$PWD/qdeclarativethread.cpp \ diff --git a/src/declarative/qml/ftw/qdeclarativerefcount_p.h b/src/declarative/qml/ftw/qdeclarativerefcount_p.h index 3813270..8ae9618 100644 --- a/src/declarative/qml/ftw/qdeclarativerefcount_p.h +++ b/src/declarative/qml/ftw/qdeclarativerefcount_p.h @@ -54,6 +54,7 @@ // #include +#include QT_BEGIN_HEADER @@ -61,16 +62,19 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class Q_DECLARATIVE_EXPORT QDeclarativeRefCount +class QDeclarativeRefCount { public: - QDeclarativeRefCount(); - virtual ~QDeclarativeRefCount(); - void addref(); - void release(); + inline QDeclarativeRefCount(); + inline virtual ~QDeclarativeRefCount(); + inline void addref(); + inline void release(); + +protected: + inline virtual void destroy(); private: - int refCount; + QAtomicInt refCount; }; template @@ -92,10 +96,40 @@ public: inline operator T*() const { return o; } inline T* data() const { return o; } + inline QDeclarativeRefPointer &take(T *); + private: T *o; }; +QDeclarativeRefCount::QDeclarativeRefCount() +: refCount(1) +{ +} + +QDeclarativeRefCount::~QDeclarativeRefCount() +{ + Q_ASSERT(refCount == 0); +} + +void QDeclarativeRefCount::addref() +{ + Q_ASSERT(refCount > 0); + refCount.ref(); +} + +void QDeclarativeRefCount::release() +{ + Q_ASSERT(refCount > 0); + if (!refCount.deref()) + destroy(); +} + +void QDeclarativeRefCount::destroy() +{ + delete this; +} + template QDeclarativeRefPointer::QDeclarativeRefPointer() : o(0) @@ -140,6 +174,18 @@ QDeclarativeRefPointer &QDeclarativeRefPointer::operator=(T *other) return *this; } +/*! +Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership +of the callers reference of other. +*/ +template +QDeclarativeRefPointer &QDeclarativeRefPointer::take(T *other) +{ + if (o) o->release(); + o = other; + return *this; +} + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/declarative/qml/ftw/qdeclarativethread.cpp b/src/declarative/qml/ftw/qdeclarativethread.cpp new file mode 100644 index 0000000..4a21c90 --- /dev/null +++ b/src/declarative/qml/ftw/qdeclarativethread.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativethread_p.h" + +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeThreadPrivate : public QThread +{ +public: + QDeclarativeThreadPrivate(QDeclarativeThread *); + QDeclarativeThread *q; + + virtual void run(); + + inline void lock() { _mutex.lock(); } + inline void unlock() { _mutex.unlock(); } + inline void wait() { _wait.wait(&_mutex); } + inline void wakeOne() { _wait.wakeOne(); } + inline void wakeAll() { _wait.wakeAll(); } + + quint32 m_threadProcessing:1; // Set when the thread is processing messages + quint32 m_mainProcessing:1; // Set when the main thread is processing messages + quint32 m_shutdown:1; // Set by main thread to request a shutdown + quint32 m_mainThreadWaiting:1; // Set by main thread if it is waiting for the message queue to empty + + typedef QFieldList MessageList; + MessageList threadList; + MessageList mainList; + + QDeclarativeThread::Message *mainSync; + + void triggerMainEvent(); + void triggerThreadEvent(); + + void mainEvent(); + void threadEvent(); + +protected: + virtual bool event(QEvent *); + +private: + struct MainObject : public QObject { + MainObject(QDeclarativeThreadPrivate *p); + virtual bool event(QEvent *e); + QDeclarativeThreadPrivate *p; + }; + MainObject m_mainObject; + + QMutex _mutex; + QWaitCondition _wait; +}; + +QDeclarativeThreadPrivate::MainObject::MainObject(QDeclarativeThreadPrivate *p) +: p(p) +{ +} + +// Trigger mainEvent in main thread. Must be called from thread. +void QDeclarativeThreadPrivate::triggerMainEvent() +{ + Q_ASSERT(q->isThisThread()); + QCoreApplication::postEvent(&m_mainObject, new QEvent(QEvent::User)); +} + +// Trigger even in thread. Must be called from main thread. +void QDeclarativeThreadPrivate::triggerThreadEvent() +{ + Q_ASSERT(!q->isThisThread()); + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QDeclarativeThreadPrivate::MainObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) + p->mainEvent(); + return QObject::event(e); +} + +QDeclarativeThreadPrivate::QDeclarativeThreadPrivate(QDeclarativeThread *q) +: q(q), m_threadProcessing(false), m_mainProcessing(false), m_shutdown(false), + m_mainThreadWaiting(false), mainSync(0), m_mainObject(this) +{ +} + +bool QDeclarativeThreadPrivate::event(QEvent *e) +{ + if (e->type() == QEvent::User) + threadEvent(); + return QThread::event(e); +} + +void QDeclarativeThreadPrivate::run() +{ + lock(); + + wakeOne(); + + unlock(); + + q->startupThread(); + exec(); +} + +void QDeclarativeThreadPrivate::mainEvent() +{ + lock(); + + m_mainProcessing = true; + + while (!mainList.isEmpty() || mainSync) { + bool isSync = mainSync != 0; + QDeclarativeThread::Message *message = isSync?mainSync:mainList.takeFirst(); + unlock(); + + message->call(q); + delete message; + + lock(); + + if (isSync) { + mainSync = 0; + wakeOne(); + } + } + + m_mainProcessing = false; + + unlock(); +} + +void QDeclarativeThreadPrivate::threadEvent() +{ + lock(); + + if (m_shutdown) { + quit(); + wakeOne(); + unlock(); + q->shutdownThread(); + } else { + m_threadProcessing = true; + + while (!threadList.isEmpty()) { + QDeclarativeThread::Message *message = threadList.first(); + + unlock(); + + message->call(q); + + lock(); + + delete threadList.takeFirst(); + } + + wakeOne(); + + m_threadProcessing = false; + + unlock(); + } +} + +QDeclarativeThread::QDeclarativeThread() +: d(new QDeclarativeThreadPrivate(this)) +{ + d->lock(); + d->start(); + d->wait(); + d->unlock(); + d->moveToThread(d); + +} + +QDeclarativeThread::~QDeclarativeThread() +{ + delete d; +} + +void QDeclarativeThread::shutdown() +{ + d->lock(); + Q_ASSERT(!d->m_shutdown); + d->m_shutdown = true; + if (d->threadList.isEmpty() && d->m_threadProcessing == false) + d->triggerThreadEvent(); + d->wait(); + d->unlock(); + d->QThread::wait(); +} + +void QDeclarativeThread::lock() +{ + d->lock(); +} + +void QDeclarativeThread::unlock() +{ + d->unlock(); +} + +void QDeclarativeThread::wakeOne() +{ + d->wakeOne(); +} + +void QDeclarativeThread::wakeAll() +{ + d->wakeAll(); +} + +void QDeclarativeThread::wait() +{ + d->wait(); +} + +bool QDeclarativeThread::isThisThread() const +{ + return QThread::currentThread() == d; +} + +QThread *QDeclarativeThread::thread() const +{ + return const_cast(static_cast(d)); +} + +// Called when the thread starts. Do startup stuff in here. +void QDeclarativeThread::startupThread() +{ +} + +// Called when the thread shuts down. Do cleanup in here. +void QDeclarativeThread::shutdownThread() +{ +} + +void QDeclarativeThread::internalCallMethodInThread(Message *message) +{ + Q_ASSERT(!isThisThread()); + d->lock(); + Q_ASSERT(d->m_mainThreadWaiting == false); + + bool wasEmpty = d->threadList.isEmpty(); + d->threadList.append(message); + if (wasEmpty && d->m_threadProcessing == false) + d->triggerThreadEvent(); + + d->m_mainThreadWaiting = true; + + do { + if (d->mainSync) { + QDeclarativeThread::Message *message = d->mainSync; + unlock(); + message->call(this); + delete message; + lock(); + d->mainSync = 0; + wakeOne(); + } else { + d->wait(); + } + } while (d->mainSync || !d->threadList.isEmpty()); + + d->m_mainThreadWaiting = false; + d->unlock(); +} + +void QDeclarativeThread::internalCallMethodInMain(Message *message) +{ + Q_ASSERT(isThisThread()); + + d->lock(); + + Q_ASSERT(d->mainSync == 0); + d->mainSync = message; + + if (d->m_mainThreadWaiting) { + d->wakeOne(); + } else if (d->m_mainProcessing) { + // Do nothing - it is already looping + } else { + d->triggerMainEvent(); + } + + while (d->mainSync && !d->m_shutdown) + d->wait(); + + d->unlock(); +} + +void QDeclarativeThread::internalPostMethodToThread(Message *message) +{ + Q_ASSERT(!isThisThread()); + d->lock(); + bool wasEmpty = d->threadList.isEmpty(); + d->threadList.append(message); + if (wasEmpty && d->m_threadProcessing == false) + d->triggerThreadEvent(); + d->unlock(); +} + +void QDeclarativeThread::internalPostMethodToMain(Message *message) +{ + Q_ASSERT(isThisThread()); + d->lock(); + bool wasEmpty = d->mainList.isEmpty(); + d->mainList.append(message); + if (wasEmpty && d->m_mainProcessing == false) + d->triggerMainEvent(); + d->unlock(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/ftw/qdeclarativethread_p.h b/src/declarative/qml/ftw/qdeclarativethread_p.h new file mode 100644 index 0000000..2f58155 --- /dev/null +++ b/src/declarative/qml/ftw/qdeclarativethread_p.h @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETHREAD_P_H +#define QDECLARATIVETHREAD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QThread; + +class QDeclarativeThreadPrivate; +class QDeclarativeThread +{ +public: + QDeclarativeThread(); + virtual ~QDeclarativeThread(); + void shutdown(); + + void lock(); + void unlock(); + void wakeOne(); + void wakeAll(); + void wait(); + + QThread *thread() const; + bool isThisThread() const; + + // Synchronously invoke a method in the thread + template + inline void callMethodInThread(void (O::*Member)()); + template + inline void callMethodInThread(void (O::*Member)(V), const T &); + template + inline void callMethodInThread(void (O::*Member)(V, V2), const T &, const T2 &); + + // Synchronously invoke a method in the main thread. If the main thread is + // blocked in a callMethodInThread() call, the call is made from within that + // call. + template + inline void callMethodInMain(void (O::*Member)()); + template + inline void callMethodInMain(void (O::*Member)(V), const T &); + template + inline void callMethodInMain(void (O::*Member)(V, V2), const T &, const T2 &); + + // Asynchronously invoke a method in the thread. + template + inline void postMethodToThread(void (O::*Member)()); + template + inline void postMethodToThread(void (O::*Member)(V), const T &); + template + inline void postMethodToThread(void (O::*Member)(V, V2), const T &, const T2 &); + + // Asynchronously invoke a method in the main thread. + template + inline void postMethodToMain(void (O::*Member)()); + template + inline void postMethodToMain(void (O::*Member)(V), const T &); + template + inline void postMethodToMain(void (O::*Member)(V, V2), const T &, const T2 &); + +protected: + virtual void startupThread(); + virtual void shutdownThread(); + +private: + friend class QDeclarativeThreadPrivate; + + struct Message { + Message() : next(0) {} + virtual ~Message() {} + Message *next; + virtual void call(QDeclarativeThread *) = 0; + }; + void internalCallMethodInThread(Message *); + void internalCallMethodInMain(Message *); + void internalPostMethodToThread(Message *); + void internalPostMethodToMain(Message *); + QDeclarativeThreadPrivate *d; +}; + +template +void QDeclarativeThread::callMethodInThread(void (O::*Member)()) +{ + struct I : public Message { + void (O::*Member)(); + I(void (O::*Member)()) : Member(Member) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(); + } + }; + internalCallMethodInThread(new I(Member)); +} + +template +void QDeclarativeThread::callMethodInThread(void (O::*Member)(V), const T &arg) +{ + struct I : public Message { + void (O::*Member)(V); + T arg; + I(void (O::*Member)(V), const T &arg) : Member(Member), arg(arg) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(arg); + } + }; + internalCallMethodInThread(new I(Member, arg)); +} + +template +void QDeclarativeThread::callMethodInThread(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) +{ + struct I : public Message { + void (O::*Member)(V, V2); + T arg; + T2 arg2; + I(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) : Member(Member), arg(arg), arg2(arg2) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(arg, arg2); + } + }; + internalCallMethodInThread(new I(Member, arg, arg2)); +} + +template +void QDeclarativeThread::callMethodInMain(void (O::*Member)()) +{ + struct I : public Message { + void (O::*Member)(); + I(void (O::*Member)()) : Member(Member) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(); + } + }; + internalCallMethodInMain(new I(Member)); +} + +template +void QDeclarativeThread::callMethodInMain(void (O::*Member)(V), const T &arg) +{ + struct I : public Message { + void (O::*Member)(V); + T arg; + I(void (O::*Member)(V), const T &arg) : Member(Member), arg(arg) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(arg); + } + }; + internalCallMethodInMain(new I(Member, arg)); +} + +template +void QDeclarativeThread::callMethodInMain(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) +{ + struct I : public Message { + void (O::*Member)(V, V2); + T arg; + T2 arg2; + I(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) : Member(Member), arg(arg), arg2(arg2) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(arg, arg2); + } + }; + internalCallMethodInMain(new I(Member, arg, arg2)); +} + +template +void QDeclarativeThread::postMethodToThread(void (O::*Member)()) +{ + struct I : public Message { + void (O::*Member)(); + I(void (O::*Member)()) : Member(Member) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(); + } + }; + internalPostMethodToThread(new I(Member)); +} + +template +void QDeclarativeThread::postMethodToThread(void (O::*Member)(V), const T &arg) +{ + struct I : public Message { + void (O::*Member)(V); + T arg; + I(void (O::*Member)(V), const T &arg) : Member(Member), arg(arg) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(arg); + } + }; + internalPostMethodToThread(new I(Member, arg)); +} + +template +void QDeclarativeThread::postMethodToThread(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) +{ + struct I : public Message { + void (O::*Member)(V, V2); + T arg; + T2 arg2; + I(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) : Member(Member), arg(arg), arg2(arg2) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(arg, arg2); + } + }; + internalPostMethodToThread(new I(Member, arg, arg2)); +} + +template +void QDeclarativeThread::postMethodToMain(void (O::*Member)()) +{ + struct I : public Message { + void (O::*Member)(); + I(void (O::*Member)()) : Member(Member) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(); + } + }; + internalPostMethodToMain(new I(Member)); +} + +template +void QDeclarativeThread::postMethodToMain(void (O::*Member)(V), const T &arg) +{ + struct I : public Message { + void (O::*Member)(V); + T arg; + I(void (O::*Member)(V), const T &arg) : Member(Member), arg(arg) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(arg); + } + }; + internalPostMethodToMain(new I(Member, arg)); +} + +template +void QDeclarativeThread::postMethodToMain(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) +{ + struct I : public Message { + void (O::*Member)(V, V2); + T arg; + T2 arg2; + I(void (O::*Member)(V, V2), const T &arg, const T2 &arg2) : Member(Member), arg(arg), arg2(arg2) {} + virtual void call(QDeclarativeThread *thread) { + O *me = static_cast(thread); + (me->*Member)(arg, arg2); + } + }; + internalPostMethodToMain(new I(Member, arg, arg2)); +} + +QT_END_NAMESPACE + +#endif // QDECLARATIVETHREAD_P_H diff --git a/src/declarative/qml/ftw/qfieldlist_p.h b/src/declarative/qml/ftw/qfieldlist_p.h index ff6b89b..f0efd16 100644 --- a/src/declarative/qml/ftw/qfieldlist_p.h +++ b/src/declarative/qml/ftw/qfieldlist_p.h @@ -61,6 +61,8 @@ class QFieldList public: inline QFieldList(); inline N *first() const; + inline N *takeFirst(); + inline void append(N *); inline void prepend(N *); @@ -73,6 +75,8 @@ public: inline void prepend(QFieldList &); inline void insertAfter(N *, QFieldList &); + inline void copyAndClear(QFieldList &); + static inline N *next(N *v); private: @@ -94,6 +98,21 @@ N *QFieldList::first() const } template +N *QFieldList::takeFirst() +{ + N *value = _first; + if (value) { + _first = next(value); + if (_last == value) { + Q_ASSERT(_first == 0); + _last = 0; + } + --_count; + } + return value; +} + +template void QFieldList::append(N *v) { Q_ASSERT(v->*nextMember == 0); @@ -207,4 +226,14 @@ void QFieldList::insertAfter(N *after, QFieldList } } +template +void QFieldList::copyAndClear(QFieldList &o) +{ + _first = o._first; + _last = o._last; + _count = o._count; + o._first = o._last = 0; + o._count = 0; +} + #endif // QFIELDLIST_P_H diff --git a/src/declarative/qml/ftw/qfinitestack_p.h b/src/declarative/qml/ftw/qfinitestack_p.h new file mode 100644 index 0000000..de9d833 --- /dev/null +++ b/src/declarative/qml/ftw/qfinitestack_p.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFINITESTACK_P_H +#define QFINITESTACK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +template +struct QFiniteStack { + inline QFiniteStack(); + inline ~QFiniteStack(); + + inline void deallocate(); + inline void allocate(int size); + + inline bool isEmpty() const; + inline const T &top() const; + inline T &top(); + inline void push(const T &o); + inline T pop(); + inline int count() const; + inline const T &at(int index) const; + inline T &operator[](int index); +private: + T *_array; + int _alloc; + int _size; +}; + +template +QFiniteStack::QFiniteStack() +: _array(0), _alloc(0), _size(0) +{ +} + +template +QFiniteStack::~QFiniteStack() +{ + deallocate(); +} + +template +bool QFiniteStack::isEmpty() const +{ + return _size == 0; +} + +template +const T &QFiniteStack::top() const +{ + return _array[_size - 1]; +} + +template +T &QFiniteStack::top() +{ + return _array[_size - 1]; +} + +template +void QFiniteStack::push(const T &o) +{ + if (QTypeInfo::isComplex) { + new (_array + _size++) T(o); + } else { + _array[_size++] = o; + } +} + +template +T QFiniteStack::pop() +{ + --_size; + + if (QTypeInfo::isComplex) { + T rv = _array[_size]; + (_array + _size)->~T(); + return rv; + } else { + return _array[_size]; + } +} + +template +int QFiniteStack::count() const +{ + return _size; +} + +template +const T &QFiniteStack::at(int index) const +{ + return _array[index]; +} + +template +T &QFiniteStack::operator[](int index) +{ + return _array[index]; +} + +template +void QFiniteStack::allocate(int size) +{ + Q_ASSERT(_array == 0); + Q_ASSERT(_alloc == 0); + Q_ASSERT(_size == 0); + + if (!size) return; + + _array = (T *)qMalloc(size * sizeof(T)); + _alloc = size; +} + +template +void QFiniteStack::deallocate() +{ + if (QTypeInfo::isComplex) { + T *i = _array + _size; + while (i != _array) + (--i)->~T(); + } + + qFree(_array); + + _array = 0; + _alloc = 0; + _size = 0; +} + +QT_END_NAMESPACE + +#endif // QFINITESTACK_P_H + diff --git a/src/declarative/qml/qdeclarativecleanup.cpp b/src/declarative/qml/qdeclarativecleanup.cpp index db671a3..d0d3687 100644 --- a/src/declarative/qml/qdeclarativecleanup.cpp +++ b/src/declarative/qml/qdeclarativecleanup.cpp @@ -50,22 +50,43 @@ QT_BEGIN_NAMESPACE \class QDeclarativeCleanup \brief The QDeclarativeCleanup provides a callback when a QDeclarativeEngine is deleted. -Any object that needs cleanup to occur before the QDeclarativeEngine's QScriptEngine is +Any object that needs cleanup to occur before the QDeclarativeEngine's V8 engine is destroyed should inherit from QDeclarativeCleanup. The clear() virtual method will be -called by QDeclarativeEngine just before it deletes the QScriptEngine. +called by QDeclarativeEngine just before it destroys the context. */ -/*! -\internal +/* +Create a QDeclarativeCleanup that is not associated with any engine. +*/ +QDeclarativeCleanup::QDeclarativeCleanup() +: prev(0), next(0), engine(0) +{ +} + +/*! Create a QDeclarativeCleanup for \a engine */ QDeclarativeCleanup::QDeclarativeCleanup(QDeclarativeEngine *engine) -: prev(0), next(0) +: prev(0), next(0), engine(0) { if (!engine) return; + addToEngine(engine); +} + +/*! +Adds this object to \a engine's cleanup list. hasEngine() must be false +before calling this method. +*/ +void QDeclarativeCleanup::addToEngine(QDeclarativeEngine *engine) +{ + Q_ASSERT(engine); + Q_ASSERT(QDeclarativeEnginePrivate::isEngineThread(engine)); + + this->engine = engine; + QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); if (p->cleanup) next = p->cleanup; @@ -75,13 +96,23 @@ QDeclarativeCleanup::QDeclarativeCleanup(QDeclarativeEngine *engine) } /*! +\fn bool QDeclarativeCleanup::hasEngine() const + +Returns true if this QDeclarativeCleanup is associated with an engine, otherwise false. +*/ + +/*! \internal */ QDeclarativeCleanup::~QDeclarativeCleanup() { + Q_ASSERT(!prev || engine); + Q_ASSERT(!prev || QDeclarativeEnginePrivate::isEngineThread(engine)); + if (prev) *prev = next; if (next) next->prev = prev; prev = 0; next = 0; } + QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecleanup_p.h b/src/declarative/qml/qdeclarativecleanup_p.h index 1efe564..272b0c8 100644 --- a/src/declarative/qml/qdeclarativecleanup_p.h +++ b/src/declarative/qml/qdeclarativecleanup_p.h @@ -63,9 +63,12 @@ class QDeclarativeEngine; class Q_DECLARATIVE_EXPORT QDeclarativeCleanup { public: + QDeclarativeCleanup(); QDeclarativeCleanup(QDeclarativeEngine *); virtual ~QDeclarativeCleanup(); + bool hasEngine() const { return prev != 0; } + void addToEngine(QDeclarativeEngine *); protected: virtual void clear() = 0; @@ -73,6 +76,9 @@ private: friend class QDeclarativeEnginePrivate; QDeclarativeCleanup **prev; QDeclarativeCleanup *next; + + // Only used for asserts + QDeclarativeEngine *engine; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecompileddata.cpp b/src/declarative/qml/qdeclarativecompileddata.cpp index a1bdca2..6815967 100644 --- a/src/declarative/qml/qdeclarativecompileddata.cpp +++ b/src/declarative/qml/qdeclarativecompileddata.cpp @@ -101,13 +101,25 @@ int QDeclarativeCompiledData::indexForUrl(const QUrl &data) } QDeclarativeCompiledData::QDeclarativeCompiledData(QDeclarativeEngine *engine) -: QDeclarativeCleanup(engine), importCache(0), root(0), rootPropertyCache(0) +: engine(engine), importCache(0), root(0), rootPropertyCache(0) { + Q_ASSERT(engine); + bytecode.reserve(1024); } +void QDeclarativeCompiledData::destroy() +{ + if (engine && hasEngine()) + QDeclarativeEnginePrivate::deleteInEngineThread(engine, this); + else + delete this; +} + QDeclarativeCompiledData::~QDeclarativeCompiledData() { + clear(); + for (int ii = 0; ii < types.count(); ++ii) { if (types.at(ii).component) types.at(ii).component->release(); @@ -129,18 +141,13 @@ QDeclarativeCompiledData::~QDeclarativeCompiledData() if (rootPropertyCache) rootPropertyCache->release(); - - qDeleteAll(cachedClosures); - - for (int ii = 0; ii < v8bindings.count(); ++ii) - qPersistentDispose(v8bindings[ii]); } void QDeclarativeCompiledData::clear() { - qDeleteAll(cachedClosures); - for (int ii = 0; ii < cachedClosures.count(); ++ii) - cachedClosures[ii] = 0; + for (int ii = 0; ii < v8bindings.count(); ++ii) + qPersistentDispose(v8bindings[ii]); + v8bindings.clear(); } const QMetaObject *QDeclarativeCompiledData::TypeReference::metaObject() const @@ -246,4 +253,10 @@ QDeclarativeInstruction::Type QDeclarativeCompiledData::instructionType(const QD #endif } +void QDeclarativeCompiledData::initialize(QDeclarativeEngine *engine) +{ + Q_ASSERT(!hasEngine()); + QDeclarativeCleanup::addToEngine(engine); +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index fe0375f..8c26a3b 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -650,7 +650,14 @@ bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine, Q_ASSERT(out); reset(out); - output = out; + QDeclarativeScript::Object *root = unit->parser().tree(); + Q_ASSERT(root); + + this->engine = engine; + this->enginePrivate = QDeclarativeEnginePrivate::get(engine); + this->unit = unit; + this->unitRoot = root; + this->output = out; // Compile types const QList &resolvedTypes = unit->resolvedTypes(); @@ -673,12 +680,10 @@ bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine, if (ref.type->containsRevisionedAttributes()) { QDeclarativeError cacheError; - ref.typePropertyCache = - QDeclarativeEnginePrivate::get(engine)->cache(ref.type, resolvedTypes.at(ii).minorVersion, cacheError); - - if (!ref.typePropertyCache) { + ref.typePropertyCache = enginePrivate->cache(ref.type, resolvedTypes.at(ii).minorVersion, + cacheError); + if (!ref.typePropertyCache) COMPILE_EXCEPTION(parserRef->refObjects.first(), cacheError.description()); - } ref.typePropertyCache->addref(); } @@ -689,13 +694,6 @@ bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine, out->types << ref; } - QDeclarativeScript::Object *root = unit->parser().tree(); - Q_ASSERT(root); - - this->engine = engine; - this->enginePrivate = QDeclarativeEnginePrivate::get(engine); - this->unit = unit; - this->unitRoot = root; compileTree(root); if (!isError()) { @@ -731,20 +729,12 @@ void QDeclarativeCompiler::compileTree(QDeclarativeScript::Object *tree) foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) { importedScriptIndexes.append(script.qualifier); - - Instruction::StoreImportedScript import; - import.value = output->scripts.count(); - - QDeclarativeScriptData *scriptData = script.script->scriptData(); - scriptData->addref(); - output->scripts << scriptData; - output->addInstruction(import); } // We generate the importCache before we build the tree so that // it can be used in the binding compiler. Given we "expect" the // QML compilation to succeed, this isn't a waste. - output->importCache = new QDeclarativeTypeNameCache(engine); + output->importCache = new QDeclarativeTypeNameCache(); for (int ii = 0; ii < importedScriptIndexes.count(); ++ii) output->importCache->add(importedScriptIndexes.at(ii), ii); unit->imports().populateCache(output->importCache, engine); @@ -753,15 +743,27 @@ void QDeclarativeCompiler::compileTree(QDeclarativeScript::Object *tree) return; Instruction::Init init; - init.bindingsSize = compileState->bindings.count(); + init.bindingsSize = compileState->totalBindingsCount; init.parserStatusSize = compileState->parserStatusCount; init.contextCache = genContextCache(); + init.objectStackSize = compileState->objectDepth.maxDepth(); + init.listStackSize = compileState->listDepth.maxDepth(); if (compileState->compiledBindingData.isEmpty()) init.compiledBinding = -1; else init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData); output->addInstruction(init); + foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) { + Instruction::StoreImportedScript import; + import.value = output->scripts.count(); + + QDeclarativeScriptData *scriptData = script.script->scriptData(); + scriptData->addref(); + output->scripts << scriptData; + output->addInstruction(import); + } + if (!compileState->v8BindingProgram.isEmpty()) { Instruction::InitV8Bindings bindings; bindings.program = output->indexForString(compileState->v8BindingProgram); @@ -805,8 +807,7 @@ bool QDeclarativeCompiler::buildObject(QDeclarativeScript::Object *obj, const Bi componentStats->componentStat.objects++; Q_ASSERT (obj->type != -1); - const QDeclarativeCompiledData::TypeReference &tr = - output->types.at(obj->type); + const QDeclarativeCompiledData::TypeReference &tr = output->types.at(obj->type); obj->metatype = tr.metaObject(); if (tr.type) @@ -818,6 +819,20 @@ bool QDeclarativeCompiler::buildObject(QDeclarativeScript::Object *obj, const Bi return true; } + if (tr.component) { + typedef QDeclarativeInstruction I; + const I *init = ((const I *)tr.component->bytecode.constData()); + Q_ASSERT(init && tr.component->instructionType(init) == QDeclarativeInstruction::Init); + + // Adjust stack depths to include nested components + compileState->objectDepth.pushPop(init->init.objectStackSize); + compileState->listDepth.pushPop(init->init.listStackSize); + compileState->parserStatusCount += init->init.parserStatusSize; + compileState->totalBindingsCount += init->init.bindingsSize; + } + + compileState->objectDepth.push(); + // Object instantiations reset the binding context BindingContext objCtxt(obj); @@ -1006,6 +1021,8 @@ bool QDeclarativeCompiler::buildObject(QDeclarativeScript::Object *obj, const Bi } } + compileState->objectDepth.pop(); + return true; } @@ -1031,23 +1048,35 @@ void QDeclarativeCompiler::genObject(QDeclarativeScript::Object *obj) } else { - Instruction::CreateObject create; - create.line = obj->location.start.line; - create.column = obj->location.start.column; - create.data = -1; - if (!obj->custom.isEmpty()) - create.data = output->indexForByteArray(obj->custom); - create.type = obj->type; - if (!output->types.at(create.type).type && - !obj->bindingBitmask.isEmpty()) { - Q_ASSERT(obj->bindingBitmask.size() % 4 == 0); - create.bindingBits = - output->indexForByteArray(obj->bindingBitmask); + if (output->types.at(obj->type).type) { + Instruction::CreateCppObject create; + create.line = obj->location.start.line; + create.column = obj->location.start.column; + create.data = -1; + if (!obj->custom.isEmpty()) + create.data = output->indexForByteArray(obj->custom); + create.type = obj->type; + create.isRoot = (compileState->root == obj); + output->addInstruction(create); } else { - create.bindingBits = -1; - } - output->addInstruction(create); + Instruction::CreateQMLObject create; + create.type = obj->type; + create.isRoot = (compileState->root == obj); + if (!obj->bindingBitmask.isEmpty()) { + Q_ASSERT(obj->bindingBitmask.size() % 4 == 0); + create.bindingBits = output->indexForByteArray(obj->bindingBitmask); + } else { + create.bindingBits = -1; + } + output->addInstruction(create); + + Instruction::CompleteQMLObject complete; + complete.line = obj->location.start.line; + complete.column = obj->location.start.column; + complete.isRoot = (compileState->root == obj); + output->addInstruction(complete); + } } // Setup the synthesized meta object if necessary @@ -1132,12 +1161,13 @@ void QDeclarativeCompiler::genObjectBody(QDeclarativeScript::Object *obj) int deferIdx = output->addInstruction(defer); int nextInstructionIndex = output->nextInstructionIndex(); - Instruction::Init init; - init.bindingsSize = compileState->bindings.count(); // XXX - bigger than necessary - init.parserStatusSize = compileState->parserStatusCount; // XXX - bigger than necessary - init.contextCache = -1; - init.compiledBinding = -1; - output->addInstruction(init); + Instruction::DeferInit dinit; + // XXX - these are now massive over allocations + dinit.bindingsSize = compileState->totalBindingsCount; + dinit.parserStatusSize = compileState->parserStatusCount; + dinit.objectStackSize = compileState->objectDepth.maxDepth(); + dinit.listStackSize = compileState->listDepth.maxDepth(); + output->addInstruction(dinit); for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) { if (!prop->isDeferred) @@ -1269,6 +1299,7 @@ void QDeclarativeCompiler::genComponent(QDeclarativeScript::Object *obj) create.line = root->location.start.line; create.column = root->location.start.column; create.endLine = root->location.end.line; + create.isRoot = (compileState->root == obj); int createInstruction = output->addInstruction(create); int nextInstructionIndex = output->nextInstructionIndex(); @@ -1276,9 +1307,11 @@ void QDeclarativeCompiler::genComponent(QDeclarativeScript::Object *obj) compileState = componentState(root); Instruction::Init init; - init.bindingsSize = compileState->bindings.count(); + init.bindingsSize = compileState->totalBindingsCount; init.parserStatusSize = compileState->parserStatusCount; init.contextCache = genContextCache(); + init.objectStackSize = compileState->objectDepth.maxDepth(); + init.listStackSize = compileState->listDepth.maxDepth(); if (compileState->compiledBindingData.isEmpty()) init.compiledBinding = -1; else @@ -1325,6 +1358,8 @@ bool QDeclarativeCompiler::buildComponent(QDeclarativeScript::Object *obj, // The special "Component" element can only have the id property and a // default property, that actually defines the component's tree + compileState->objectDepth.push(); + // Find, check and set the "id" property (if any) Property *idProp = 0; if (obj->properties.isMany() || @@ -1371,6 +1406,8 @@ bool QDeclarativeCompiler::buildComponent(QDeclarativeScript::Object *obj, // Build the component tree COMPILE_CHECK(buildComponentFromRoot(root, ctxt)); + compileState->objectDepth.pop(); + return true; } @@ -1849,6 +1886,7 @@ bool QDeclarativeCompiler::buildIdProperty(QDeclarativeScript::Property *prop, void QDeclarativeCompiler::addId(const QString &id, QDeclarativeScript::Object *obj) { + Q_UNUSED(id); Q_ASSERT(!compileState->ids.value(id)); Q_ASSERT(obj->id == id); obj->idIndex = compileState->ids.count(); @@ -1859,6 +1897,7 @@ void QDeclarativeCompiler::addBindingReference(BindingReference *ref) { Q_ASSERT(ref->value && !ref->value->bindingReference); ref->value->bindingReference = ref; + compileState->totalBindingsCount++; compileState->bindings.prepend(ref); } @@ -1892,10 +1931,14 @@ bool QDeclarativeCompiler::buildAttachedProperty(QDeclarativeScript::Property *p Q_ASSERT(prop->value); Q_ASSERT(prop->index != -1); // This is set in buildProperty() + compileState->objectDepth.push(); + obj->addAttachedProperty(prop); COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); + compileState->objectDepth.pop(); + return true; } @@ -1953,7 +1996,11 @@ bool QDeclarativeCompiler::buildGroupedProperty(QDeclarativeScript::Property *pr obj->addGroupedProperty(prop); + compileState->objectDepth.push(); + COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); + + compileState->objectDepth.pop(); } return true; @@ -1964,6 +2011,8 @@ bool QDeclarativeCompiler::buildValueTypeProperty(QObject *type, QDeclarativeScript::Object *baseObj, const BindingContext &ctxt) { + compileState->objectDepth.push(); + if (obj->defaultProperty) COMPILE_EXCEPTION(obj, tr("Invalid property use")); obj->metatype = type->metaObject(); @@ -2026,6 +2075,8 @@ bool QDeclarativeCompiler::buildValueTypeProperty(QObject *type, obj->addValueProperty(prop); } + compileState->objectDepth.pop(); + return true; } @@ -2038,6 +2089,8 @@ bool QDeclarativeCompiler::buildListProperty(QDeclarativeScript::Property *prop, { Q_ASSERT(prop->core.isQList()); + compileState->listDepth.push(); + int t = prop->type; obj->addValueProperty(prop); @@ -2071,6 +2124,8 @@ bool QDeclarativeCompiler::buildListProperty(QDeclarativeScript::Property *prop, } } + compileState->listDepth.pop(); + return true; } @@ -2408,7 +2463,6 @@ bool QDeclarativeCompiler::checkDynamicMeta(QDeclarativeScript::Object *obj) QHashField methodNames; // Check properties - int dpCount = obj->dynamicProperties.count(); for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { const QDeclarativeScript::Object::DynamicProperty &prop = *p; @@ -2983,7 +3037,7 @@ bool QDeclarativeCompiler::compileAlias(QFastMetaBuilder &builder, // update the property type type = aliasProperty.type(); - if (type >= QVariant::UserType) + if (type >= (int)QVariant::UserType) type = 0; } @@ -3070,6 +3124,7 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *bindi ((prop->index & 0xFF) << 24); else store.property = prop->index; + store.isRoot = (compileState->root == obj); store.line = binding->location.start.line; output->addInstruction(store); } else if (ref.dataType == BindingReference::V8) { @@ -3077,6 +3132,7 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *bindi store.value = ref.compiledIndex; store.context = ref.bindingContext.stack; store.owner = ref.bindingContext.owner; + store.isRoot = (compileState->root == obj); store.line = binding->location.start.line; Q_ASSERT(ref.bindingContext.owner == 0 || @@ -3093,6 +3149,7 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeScript::Value *bindi store.assignBinding.value = output->indexForString(ref.rewrittenExpression); store.assignBinding.context = ref.bindingContext.stack; store.assignBinding.owner = ref.bindingContext.owner; + store.assignBinding.isRoot = (compileState->root == obj); store.assignBinding.line = binding->location.start.line; Q_ASSERT(ref.bindingContext.owner == 0 || @@ -3241,6 +3298,10 @@ bool QDeclarativeCompiler::completeComponentBuild() if (bindingCompiler.isValid()) compileState->compiledBindingData = bindingCompiler.program(); + // Check pop()'s matched push()'s + Q_ASSERT(compileState->objectDepth.depth() == 0); + Q_ASSERT(compileState->listDepth.depth() == 0); + saveComponentState(); return true; @@ -3302,8 +3363,7 @@ void QDeclarativeCompiler::dumpStats() */ bool QDeclarativeCompiler::canCoerce(int to, QDeclarativeScript::Object *from) { - const QMetaObject *toMo = - enginePrivate->rawMetaObjectForType(to); + const QMetaObject *toMo = enginePrivate->rawMetaObjectForType(to); const QMetaObject *fromMo = from->metaObject(); while (fromMo) { diff --git a/src/declarative/qml/qdeclarativecompiler_p.h b/src/declarative/qml/qdeclarativecompiler_p.h index a4def99..993c733 100644 --- a/src/declarative/qml/qdeclarativecompiler_p.h +++ b/src/declarative/qml/qdeclarativecompiler_p.h @@ -76,12 +76,15 @@ class QDeclarativeComponent; class QDeclarativeContext; class QDeclarativeContextData; -class Q_AUTOTEST_EXPORT QDeclarativeCompiledData : public QDeclarativeRefCount, public QDeclarativeCleanup +class Q_AUTOTEST_EXPORT QDeclarativeCompiledData : public QDeclarativeRefCount, + public QDeclarativeCleanup { public: QDeclarativeCompiledData(QDeclarativeEngine *engine); virtual ~QDeclarativeCompiledData(); + QDeclarativeEngine *engine; + QString name; QUrl url; QDeclarativeTypeNameCache *importCache; @@ -96,7 +99,6 @@ public: QDeclarativePropertyCache *typePropertyCache; QDeclarativeCompiledData *component; - QObject *createInstance(QDeclarativeContextData *, const QBitField &, QList *) const; const QMetaObject *metaObject() const; QDeclarativePropertyCache *propertyCache() const; QDeclarativePropertyCache *createPropertyCache(QDeclarativeEngine *); @@ -111,7 +113,6 @@ public: QList primitives; QList datas; QByteArray bytecode; - QList cachedClosures; QList propertyCaches; QList contextCaches; QList scripts; @@ -138,16 +139,21 @@ public: QDeclarativeInstruction *instruction(int index); QDeclarativeInstruction::Type instructionType(const QDeclarativeInstruction *instr); + bool isInitialized() const { return hasEngine(); } + void initialize(QDeclarativeEngine *); + protected: + virtual void destroy(); // From QDeclarativeRefCount virtual void clear(); // From QDeclarativeCleanup private: + friend class QDeclarativeCompiler; + int addInstructionHelper(QDeclarativeInstruction::Type type, QDeclarativeInstruction &instr); void dump(QDeclarativeInstruction *, int idx = -1); QDeclarativeCompiledData(const QDeclarativeCompiledData &other); QDeclarativeCompiledData &operator=(const QDeclarativeCompiledData &other); QByteArray packData; - friend class QDeclarativeCompiler; int pack(const char *, size_t); int indexForString(const QString &); @@ -204,16 +210,35 @@ namespace QDeclarativeCompilerTypes { } }; + struct DepthStack { + DepthStack() : _depth(0), _maxDepth(0) {} + DepthStack(const DepthStack &o) : _depth(o._depth), _maxDepth(o._maxDepth) {} + DepthStack &operator=(const DepthStack &o) { _depth = o._depth; _maxDepth = o._maxDepth; return *this; } + + int depth() const { return _depth; } + int maxDepth() const { return _maxDepth; } + + void push() { ++_depth; _maxDepth = qMax(_depth, _maxDepth); } + void pop() { --_depth; Q_ASSERT(_depth >= 0); Q_ASSERT(_maxDepth > _depth); } + + void pushPop(int count) { _maxDepth = qMax(_depth + count, _maxDepth); } + private: + int _depth; + int _maxDepth; + }; + // Contains all the incremental compiler state about a component. As // a single QML file can have multiple components defined, there may be // more than one of these for each compile struct ComponentCompileState : public QDeclarativePool::Class { ComponentCompileState() - : parserStatusCount(0), pushedProperties(0), nested(false), v8BindingProgramLine(-1), root(0) {} + : parserStatusCount(0), totalBindingsCount(0), pushedProperties(0), nested(false), + v8BindingProgramLine(-1), root(0) {} IdList ids; int parserStatusCount; + int totalBindingsCount; int pushedProperties; bool nested; @@ -222,6 +247,9 @@ namespace QDeclarativeCompilerTypes { int v8BindingProgramLine; int v8BindingProgramIndex; + DepthStack objectDepth; + DepthStack listDepth; + typedef QDeclarativeCompilerTypes::BindingReference B; typedef QFieldList BindingReferenceList; BindingReferenceList bindings; @@ -386,7 +414,6 @@ private: QDeclarativeScript::Object *unitRoot; QDeclarativeTypeData *unit; - // Compiler component statistics. Only collected if QML_COMPILER_STATS=1 struct ComponentStat { diff --git a/src/declarative/qml/qdeclarativecomponent.cpp b/src/declarative/qml/qdeclarativecomponent.cpp index d5a55fe..50c6471 100644 --- a/src/declarative/qml/qdeclarativecomponent.cpp +++ b/src/declarative/qml/qdeclarativecomponent.cpp @@ -54,6 +54,8 @@ #include "private/qdeclarativescript_p.h" #include "private/qdeclarativedebugtrace_p.h" #include "private/qdeclarativeenginedebugservice_p.h" +#include "qdeclarativeincubator.h" +#include "private/qdeclarativeincubator_p.h" #include "private/qv8engine_p.h" #include "private/qv8include_p.h" @@ -65,7 +67,45 @@ QT_BEGIN_NAMESPACE -class QByteArray; +class QDeclarativeComponentExtension : public QV8Engine::Deletable +{ +public: + QDeclarativeComponentExtension(QV8Engine *); + virtual ~QDeclarativeComponentExtension(); + + v8::Persistent incubationConstructor; + v8::Persistent initialProperties; + v8::Persistent forceIncubation; +}; +static V8_DEFINE_EXTENSION(QDeclarativeComponentExtension, componentExtension); + +/* + Try to do what's necessary for a reasonable display of the type + name, but no more (just enough for the client to do more extensive cleanup). + + Should only be called when debugging is enabled. +*/ +static inline QString buildTypeNameForDebug(const QMetaObject *metaObject) +{ + static const QString qmlMarker(QLatin1String("_QML")); + static const QChar underscore(QLatin1Char('_')); + static const QChar asterisk(QLatin1Char('*')); + QDeclarativeType *type = QDeclarativeMetaType::qmlType(metaObject); + QString typeName = type ? QLatin1String(type->qmlTypeName()) : QLatin1String(metaObject->className()); + if (!type) { + //### optimize further? + int marker = typeName.indexOf(qmlMarker); + if (marker != -1 && marker < typeName.count() - 1) { + if (typeName[marker + 1] == underscore) { + const QString className = typeName.left(marker) + asterisk; + type = QDeclarativeMetaType::qmlType(QMetaType::type(className.toLatin1())); + if (type) + typeName = QLatin1String(type->qmlTypeName()); + } + } + } + return typeName; +} /*! \class QDeclarativeComponent @@ -615,165 +655,6 @@ QDeclarativeComponent::QDeclarativeComponent(QDeclarativeComponentPrivate &dd, Q } /*! - \qmlmethod object Component::createObject(Item parent, object properties) - - Creates and returns an object instance of this component that will have - the given \a parent and \a properties. The \a properties argument is optional. - Returns null if object creation fails. - - The object will be created in the same context as the one in which the component - was created. This function will always return null when called on components - which were not created in QML. - - If you wish to create an object without setting a parent, specify \c null for - the \a parent value. Note that if the returned object is to be displayed, you - must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent} - property, or else the object will not be visible. - - If a \a parent is not provided to createObject(), a reference to the returned object must be held so that - it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards, - since setting the Item parent does not change object ownership; only the graphical parent is changed. - - As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a - map of initial property values for the created object. These values are applied before object - creation is finalized. (This is more efficient than setting property values after object creation, - particularly where large sets of property values are defined, and also allows property bindings - to be set up before the object is created.) - - The \a properties argument is specified as a map of property-value items. For example, the code - below creates an object with initial \c x and \c y values of 100 and 200, respectively: - - \js - var component = Qt.createComponent("Button.qml"); - if (component.status == Component.Ready) - component.createObject(parent, {"x": 100, "y": 100}); - \endjs - - Dynamically created instances can be deleted with the \c destroy() method. - See \l {Dynamic Object Management in QML} for more information. -*/ - -/*! - \internal - A version of create which returns a scriptObject, for use in script. - This function will only work on components created in QML. - - Sets graphics object parent because forgetting to do this is a frequent - and serious problem. -*/ -void QDeclarativeComponent::createObject(QDeclarativeV8Function *args) -{ - Q_ASSERT(args); - - Q_D(QDeclarativeComponent); - - Q_ASSERT(d->engine); - - QObject *parent = args->Length()?QDeclarativeEnginePrivate::get(d->engine)->v8engine()->toQObject((*args)[0]):0; - - v8::Local valuemap; - if (args->Length() >= 2) { - v8::Local v = (*args)[1]; - if (!v->IsObject() || v->IsArray()) { - qmlInfo(this) << tr("createObject: value is not an object"); - args->returnValue(v8::Null()); - return; - } - valuemap = v8::Local::Cast(v); - } - - QV8Engine *v8engine = QDeclarativeEnginePrivate::get(d->engine)->v8engine(); - QObject *retn = d->createObjectWithInitialProperties(args->qmlGlobal(), valuemap, parent); - if (!retn) - args->returnValue(v8::Null()); - else - args->returnValue(v8engine->newQObject(retn)); -} - -QObject *QDeclarativeComponentPrivate::createObjectWithInitialProperties(v8::Handle qmlGlobal, v8::Handle valuemap, QObject *parentObject) -{ - Q_Q(QDeclarativeComponent); - Q_ASSERT(engine); - - QDeclarativeContext *ctxt = q->creationContext(); - if (!ctxt) ctxt = engine->rootContext(); - - QObject *parent = parentObject; - - QObject *ret = q->beginCreate(ctxt); - if (!ret) { - q->completeCreate(); - return 0; - } - - if (parent) { - ret->setParent(parent); - - QList functions = QDeclarativeMetaType::parentFunctions(); - - bool needParent = false; - - for (int ii = 0; ii < functions.count(); ++ii) { - QDeclarativePrivate::AutoParentResult res = functions.at(ii)(ret, parent); - if (res == QDeclarativePrivate::Parented) { - needParent = false; - break; - } else if (res == QDeclarativePrivate::IncompatibleParent) { - needParent = true; - } - } - - if (needParent) - qWarning("QDeclarativeComponent: Created graphical object was not placed in the graphics scene."); - } - - return completeCreateObjectWithInitialProperties(qmlGlobal, valuemap, ret); -} - -QObject *QDeclarativeComponentPrivate::completeCreateObjectWithInitialProperties(v8::Handle qmlGlobal, v8::Handle valuemap, QObject *toCreate) -{ - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - QV8Engine *v8engine = ep->v8engine(); - v8::Handle ov = v8engine->newQObject(toCreate); - Q_ASSERT(ov->IsObject()); - v8::Handle object = v8::Handle::Cast(ov); - - if (!valuemap.IsEmpty()) { - -#define SET_ARGS_SOURCE \ - "(function(object, values) {"\ - "try {"\ - "for(var property in values) {"\ - "try {"\ - "var properties = property.split(\".\");"\ - "var o = object;"\ - "for (var ii = 0; ii < properties.length - 1; ++ii) {"\ - "o = o[properties[ii]];"\ - "}"\ - "o[properties[properties.length - 1]] = values[property];"\ - "} catch(e) {}"\ - "}"\ - "} catch(e) {}"\ - "})" - - v8::Local script = v8engine->qmlModeCompile(SET_ARGS_SOURCE); - v8::Local function = v8::Local::Cast(script->Run(qmlGlobal)); - - // Try catch isn't needed as the function itself is loaded with try/catch - v8::Handle args[] = { object, valuemap }; - function->Call(v8engine->global(), 2, args); - } - - completeCreate(); - - QDeclarativeData *ddata = QDeclarativeData::get(toCreate); - Q_ASSERT(ddata); - ddata->setImplicitDestructible(); - - return v8engine->toQObject(object); -} - -/*! Create an object instance from this component. Returns 0 if creation failed. \a context specifies the context within which to create the object instance. @@ -799,7 +680,7 @@ QObject *QDeclarativeComponent::create(QDeclarativeContext *context) component. Create an object instance from this component. Returns 0 if creation - failed. \a context specifies the context within which to create the object + failed. \a publicContext specifies the context within which to create the object instance. When QDeclarativeComponent constructs an instance, it occurs in three steps: @@ -816,20 +697,18 @@ QObject *QDeclarativeComponent::create(QDeclarativeContext *context) communicate information to an instantiated component, as it allows their initial values to be configured before property bindings take effect. */ -QObject *QDeclarativeComponent::beginCreate(QDeclarativeContext *context) +QObject *QDeclarativeComponent::beginCreate(QDeclarativeContext *publicContext) { Q_D(QDeclarativeComponent); - QObject *rv = d->beginCreate(context?QDeclarativeContextData::get(context):0, QBitField()); - if (rv) { - QDeclarativeData *ddata = QDeclarativeData::get(rv); - Q_ASSERT(ddata); - ddata->indestructible = true; - } - return rv; + + Q_ASSERT(publicContext); + QDeclarativeContextData *context = QDeclarativeContextData::get(publicContext); + + return d->beginCreate(context); } QObject * -QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context, const QBitField &bindings) +QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context) { Q_Q(QDeclarativeComponent); if (!context) { @@ -857,105 +736,61 @@ QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context, cons return 0; } - return begin(context, creationContext, cc, start, &state, 0, bindings); -} + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); -/* - Try to do what's necessary for a reasonable display of the type - name, but no more (just enough for the client to do more extensive cleanup). + bool isRoot = enginePriv->inProgressCreations == 0; + enginePriv->inProgressCreations++; + state.errors.clear(); + state.completePending = true; - Should only be called when debugging is enabled. -*/ -static inline QString buildTypeNameForDebug(const QMetaObject *metaObject) -{ - static const QString qmlMarker(QLatin1String("_QML")); - static const QChar underscore(QLatin1Char('_')); - static const QChar asterisk(QLatin1Char('*')); - QDeclarativeType *type = QDeclarativeMetaType::qmlType(metaObject); - QString typeName = type ? QLatin1String(type->qmlTypeName()) : QLatin1String(metaObject->className()); - if (!type) { - //### optimize further? - int marker = typeName.indexOf(qmlMarker); - if (marker != -1 && marker < typeName.count() - 1) { - if (typeName[marker + 1] == underscore) { - const QString className = typeName.left(marker) + asterisk; - type = QDeclarativeMetaType::qmlType(QMetaType::type(className.toLatin1())); - if (type) - typeName = QLatin1String(type->qmlTypeName()); - } - } - } - return typeName; -} - -QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentContext, - QDeclarativeContextData *componentCreationContext, - QDeclarativeCompiledData *component, int start, - ConstructionState *state, QList *errors, - const QBitField &bindings) -{ - QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(parentContext->engine); - bool isRoot = !enginePriv->inBeginCreate; - - Q_ASSERT(!isRoot || state); // Either this isn't a root component, or a state data must be provided - Q_ASSERT((state != 0) ^ (errors != 0)); // One of state or errors (but not both) must be provided - - if (isRoot) + if (isRoot) QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Creating); - QDeclarativeContextData *ctxt = new QDeclarativeContextData; - ctxt->isInternal = true; - ctxt->url = component->url; - ctxt->imports = component->importCache; - - // Nested global imports - if (componentCreationContext && start != -1) { - ctxt->importedScripts = componentCreationContext->importedScripts; - for (int ii = 0; ii < ctxt->importedScripts.count(); ++ii) - ctxt->importedScripts[ii] = qPersistentNew(ctxt->importedScripts[ii]); + enginePriv->referenceScarceResources(); + state.vme.init(context, cc, start); + QObject *rv = state.vme.execute(&state.errors); + enginePriv->dereferenceScarceResources(); + + if (rv && creationContext && start != -1) { + // A component that is logically created within another component instance shares the same + // instances of script imports. For example: + // + // import QtQuick 1.0 + // import "test.js" as Test + // ListView { + // model: Test.getModel() + // delegate: Component { + // Text { text: Test.getValue(index); } + // } + // } + // + // Has the same "Test" instance. To implement this, we simply copy the v8 handles into + // the inner context. We have to create a fresh persistent handle for each to prevent + // double dispose. It is possible we could do this more efficiently using some form of + // referencing instead. + QDeclarativeContextData *objectContext = QDeclarativeData::get(rv, false)->outerContext; + objectContext->importedScripts = creationContext->importedScripts; + for (int ii = 0; ii < objectContext->importedScripts.count(); ++ii) + objectContext->importedScripts[ii] = qPersistentNew(objectContext->importedScripts[ii]); } - component->importCache->addref(); - ctxt->setParent(parentContext); - - enginePriv->inBeginCreate = true; - - QDeclarativeVME vme; - enginePriv->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. - QObject *rv = vme.run(ctxt, component, start, bindings); - enginePriv->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. - - if (vme.isError()) { - if(errors) *errors = vme.errors(); - else state->errors = vme.errors(); - } - - if (isRoot) { - enginePriv->inBeginCreate = false; - - state->bindValues = enginePriv->bindValues; - state->parserStatus = enginePriv->parserStatus; - state->finalizedParserStatus = enginePriv->finalizedParserStatus; - state->componentAttached = enginePriv->componentAttached; - if (state->componentAttached) - state->componentAttached->prev = &state->componentAttached; - - enginePriv->componentAttached = 0; - enginePriv->bindValues.clear(); - enginePriv->parserStatus.clear(); - enginePriv->finalizedParserStatus.clear(); - state->completePending = true; - enginePriv->inProgressCreations++; + if (rv) { + QDeclarativeData *ddata = QDeclarativeData::get(rv); + Q_ASSERT(ddata); + ddata->indestructible = true; } if (enginePriv->isDebugging && rv) { - if (!parentContext->isInternal) - parentContext->asQDeclarativeContextPrivate()->instances.append(rv); - QDeclarativeEngineDebugService::instance()->objectCreated(parentContext->engine, rv); + if (!context->isInternal) + context->asQDeclarativeContextPrivate()->instances.append(rv); + QDeclarativeEngineDebugService::instance()->objectCreated(engine, rv); if (isRoot) { - QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating, buildTypeNameForDebug(rv->metaObject())); + QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Creating, + buildTypeNameForDebug(rv->metaObject())); QDeclarativeData *data = QDeclarativeData::get(rv); - QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Creating, component->url, data ? data->lineNumber : -1); + Q_ASSERT(data); + QDeclarativeDebugTrace::rangeLocation(QDeclarativeDebugTrace::Creating, + cc->url, data->lineNumber); } } @@ -965,106 +800,23 @@ QObject * QDeclarativeComponentPrivate::begin(QDeclarativeContextData *parentCon void QDeclarativeComponentPrivate::beginDeferred(QDeclarativeEnginePrivate *enginePriv, QObject *object, ConstructionState *state) { - bool isRoot = !enginePriv->inBeginCreate; - enginePriv->inBeginCreate = true; - - QDeclarativeVME vme; - vme.runDeferred(object); - - if (vme.isError()) - state->errors = vme.errors(); - - if (isRoot) { - enginePriv->inBeginCreate = false; - - state->bindValues = enginePriv->bindValues; - state->parserStatus = enginePriv->parserStatus; - state->finalizedParserStatus = enginePriv->finalizedParserStatus; - state->componentAttached = enginePriv->componentAttached; - if (state->componentAttached) - state->componentAttached->prev = &state->componentAttached; - - enginePriv->componentAttached = 0; - enginePriv->bindValues.clear(); - enginePriv->parserStatus.clear(); - enginePriv->finalizedParserStatus.clear(); - state->completePending = true; - enginePriv->inProgressCreations++; - } + enginePriv->inProgressCreations++; + state->errors.clear(); + state->completePending = true; + + state->vme.initDeferred(object); + state->vme.execute(&state->errors); } void QDeclarativeComponentPrivate::complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state) { if (state->completePending) { + state->vme.complete(); - for (int ii = 0; ii < state->bindValues.count(); ++ii) { - QDeclarativeEnginePrivate::SimpleList bv = - state->bindValues.at(ii); - for (int jj = 0; jj < bv.count; ++jj) { - if(bv.at(jj)) { - bv.at(jj)->m_mePtr = 0; - bv.at(jj)->setEnabled(true, QDeclarativePropertyPrivate::BypassInterceptor | - QDeclarativePropertyPrivate::DontRemoveBinding); - } - } - QDeclarativeEnginePrivate::clear(bv); - } - - for (int ii = 0; ii < state->parserStatus.count(); ++ii) { - QDeclarativeEnginePrivate::SimpleList ps = - state->parserStatus.at(ii); - - for (int jj = ps.count - 1; jj >= 0; --jj) { - QDeclarativeParserStatus *status = ps.at(jj); - if (status && status->d) { - status->d = 0; - status->componentComplete(); - } - } - QDeclarativeEnginePrivate::clear(ps); - } - - for (int ii = 0; ii < state->finalizedParserStatus.count(); ++ii) { - QPair, int> status = state->finalizedParserStatus.at(ii); - QObject *obj = status.first; - if (obj) { - void *args[] = { 0 }; - QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, - status.second, args); - } - } - - //componentComplete() can register additional finalization objects - //that are then never handled. Handle them manually here. - if (1 == enginePriv->inProgressCreations) { - for (int ii = 0; ii < enginePriv->finalizedParserStatus.count(); ++ii) { - QPair, int> status = enginePriv->finalizedParserStatus.at(ii); - QObject *obj = status.first; - if (obj) { - void *args[] = { 0 }; - QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, - status.second, args); - } - } - enginePriv->finalizedParserStatus.clear(); - } - - while (state->componentAttached) { - QDeclarativeComponentAttached *a = state->componentAttached; - a->rem(); - QDeclarativeData *d = QDeclarativeData::get(a->parent()); - Q_ASSERT(d); - Q_ASSERT(d->context); - a->add(&d->context->componentAttached); - emit a->completed(); - } - - state->bindValues.clear(); - state->parserStatus.clear(); - state->finalizedParserStatus.clear(); state->completePending = false; enginePriv->inProgressCreations--; + if (0 == enginePriv->inProgressCreations) { while (enginePriv->erroredBindings) { enginePriv->warning(enginePriv->erroredBindings->error); @@ -1121,9 +873,9 @@ QDeclarativeComponentAttached *QDeclarativeComponent::qmlAttachedProperties(QObj if (!engine) return a; - if (QDeclarativeEnginePrivate::get(engine)->inBeginCreate) { + if (QDeclarativeEnginePrivate::get(engine)->activeVME) { // XXX should only be allowed during begin QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); - a->add(&p->componentAttached); + a->add(&p->activeVME->componentAttached); } else { QDeclarativeData *d = QDeclarativeData::get(obj); Q_ASSERT(d); @@ -1134,4 +886,427 @@ QDeclarativeComponentAttached *QDeclarativeComponent::qmlAttachedProperties(QObj return a; } +void QDeclarativeComponent::create(QDeclarativeIncubator &i, QDeclarativeContext *context, + QDeclarativeContext *forContext) +{ + Q_D(QDeclarativeComponent); + + if (!context) { + qWarning("QDeclarativeComponent: Cannot create a component in a null context"); + return; + } + + QDeclarativeContextData *contextData = QDeclarativeContextData::get(context); + QDeclarativeContextData *forContextData = contextData; + if (forContext) forContextData = QDeclarativeContextData::get(forContext); + + if (!contextData->isValid()) { + qWarning("QDeclarativeComponent: Cannot create a component in an invalid context"); + return; + } + + if (contextData->engine != d->engine) { + qWarning("QDeclarativeComponent: Must create component in context from the same QDeclarativeEngine"); + return; + } + + if (!isReady()) { + qWarning("QDeclarativeComponent: Component is not ready"); + return; + } + + i.clear(); + QDeclarativeIncubatorPrivate *p = i.d; + + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(d->engine); + + p->component = d->cc; p->component->addref(); + p->vme.init(contextData, d->cc, d->start); + + enginePriv->incubate(i, forContextData); +} + +class QV8IncubatorResource : public QV8ObjectResource, + public QDeclarativeIncubator +{ +V8_RESOURCE_TYPE(IncubatorType) +public: + QV8IncubatorResource(QV8Engine *engine, IncubationMode = Asynchronous); + + static v8::Handle StatusChangedGetter(v8::Local, + const v8::AccessorInfo& info); + static v8::Handle StatusGetter(v8::Local, + const v8::AccessorInfo& info); + static v8::Handle ObjectGetter(v8::Local, + const v8::AccessorInfo& info); + static v8::Handle ForceIncubationGetter(v8::Local, + const v8::AccessorInfo& info); + static v8::Handle ForceIncubation(const v8::Arguments &args); + + static void StatusChangedSetter(v8::Local, v8::Local value, + const v8::AccessorInfo& info); + + v8::Persistent me; + QDeclarativeGuard parent; + v8::Persistent valuemap; + v8::Persistent qmlGlobal; +protected: + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); +}; + +static void QDeclarativeComponent_setQmlParent(QObject *me, QObject *parent) +{ + if (parent) { + me->setParent(parent); + typedef QDeclarativePrivate::AutoParentFunction APF; + QList functions = QDeclarativeMetaType::parentFunctions(); + + bool needParent = false; + for (int ii = 0; ii < functions.count(); ++ii) { + QDeclarativePrivate::AutoParentResult res = functions.at(ii)(me, parent); + if (res == QDeclarativePrivate::Parented) { + needParent = false; + break; + } else if (res == QDeclarativePrivate::IncompatibleParent) { + needParent = true; + } + } + if (needParent) + qWarning("QDeclarativeComponent: Created graphical object was not " + "placed in the graphics scene."); + } +} + +/*! + \qmlmethod object Component::createObject(Item parent, object properties) + + Creates and returns an object instance of this component that will have + the given \a parent and \a properties. The \a properties argument is optional. + Returns null if object creation fails. + + The object will be created in the same context as the one in which the component + was created. This function will always return null when called on components + which were not created in QML. + + If you wish to create an object without setting a parent, specify \c null for + the \a parent value. Note that if the returned object is to be displayed, you + must provide a valid \a parent value or set the returned object's \l{Item::parent}{parent} + property, or else the object will not be visible. + + If a \a parent is not provided to createObject(), a reference to the returned object must be held so that + it is not destroyed by the garbage collector. This is true regardless of whether \l{Item::parent} is set afterwards, + since setting the Item parent does not change object ownership; only the graphical parent is changed. + + As of QtQuick 1.1, this method accepts an optional \a properties argument that specifies a + map of initial property values for the created object. These values are applied before object + creation is finalized. (This is more efficient than setting property values after object creation, + particularly where large sets of property values are defined, and also allows property bindings + to be set up before the object is created.) + + The \a properties argument is specified as a map of property-value items. For example, the code + below creates an object with initial \c x and \c y values of 100 and 200, respectively: + + \js + var component = Qt.createComponent("Button.qml"); + if (component.status == Component.Ready) + component.createObject(parent, {"x": 100, "y": 100}); + \endjs + + Dynamically created instances can be deleted with the \c destroy() method. + See \l {Dynamic Object Management in QML} for more information. +*/ +void QDeclarativeComponent::createObject(QDeclarativeV8Function *args) +{ + Q_D(QDeclarativeComponent); + Q_ASSERT(d->engine); + Q_ASSERT(args); + + QObject *parent = 0; + v8::Local valuemap; + + if (args->Length() >= 1) + parent = args->engine()->toQObject((*args)[0]); + + if (args->Length() >= 2) { + v8::Local v = (*args)[1]; + if (!v->IsObject() || v->IsArray()) { + qmlInfo(this) << tr("createObject: value is not an object"); + args->returnValue(v8::Null()); + return; + } + valuemap = v8::Local::Cast(v); + } + + QV8Engine *v8engine = args->engine(); + + QDeclarativeContext *ctxt = creationContext(); + if (!ctxt) ctxt = d->engine->rootContext(); + + QObject *rv = beginCreate(ctxt); + + if (!rv) { + args->returnValue(v8::Null()); + return; + } + + QDeclarativeComponent_setQmlParent(rv, parent); + + v8::Handle ov = v8engine->newQObject(rv); + Q_ASSERT(ov->IsObject()); + v8::Handle object = v8::Handle::Cast(ov); + + if (!valuemap.IsEmpty()) { + QDeclarativeComponentExtension *e = componentExtension(v8engine); + // Try catch isn't needed as the function itself is loaded with try/catch + v8::Handle function = e->initialProperties->Run(args->qmlGlobal()); + v8::Handle args[] = { object, valuemap }; + v8::Handle::Cast(function)->Call(v8engine->global(), 2, args); + } + + d->completeCreate(); + + Q_ASSERT(QDeclarativeData::get(rv)); + QDeclarativeData::get(rv)->setImplicitDestructible(); + + if (!rv) + args->returnValue(v8::Null()); + else + args->returnValue(object); +} + +void QDeclarativeComponent::incubateObject(QDeclarativeV8Function *args) +{ + Q_D(QDeclarativeComponent); + Q_ASSERT(d->engine); + Q_ASSERT(args); + + QObject *parent = 0; + v8::Local valuemap; + QDeclarativeIncubator::IncubationMode mode = QDeclarativeIncubator::Asynchronous; + + if (args->Length() >= 1) + parent = args->engine()->toQObject((*args)[0]); + + if (args->Length() >= 2) { + v8::Local v = (*args)[1]; + if (!v->IsObject() || v->IsArray()) { + qmlInfo(this) << tr("createObject: value is not an object"); + args->returnValue(v8::Null()); + return; + } + valuemap = v8::Local::Cast(v); + } + + if (args->Length() >= 3) { + quint32 v = (*args)[2]->Uint32Value(); + if (v == QDeclarativeIncubator::Asynchronous) + mode = QDeclarativeIncubator::Asynchronous; + else if (v == QDeclarativeIncubator::AsynchronousIfNested) + mode = QDeclarativeIncubator::AsynchronousIfNested; + else if (v == QDeclarativeIncubator::Synchronous) + mode = QDeclarativeIncubator::Synchronous; + } + + QDeclarativeComponentExtension *e = componentExtension(args->engine()); + + QV8IncubatorResource *r = new QV8IncubatorResource(args->engine(), mode); + if (!valuemap.IsEmpty()) { + r->valuemap = qPersistentNew(valuemap); + r->qmlGlobal = qPersistentNew(args->qmlGlobal()); + } + r->parent = parent; + + create(*r, creationContext()); + + if (r->status() == QDeclarativeIncubator::Null) { + delete r; + args->returnValue(v8::Null()); + } else { + v8::Local o = e->incubationConstructor->NewInstance(); + o->SetExternalResource(r); + + if (r->status() == QDeclarativeIncubator::Loading) + r->me = qPersistentNew(o); + + args->returnValue(o); + } +} + +// XXX used by QSGLoader +QObject *QDeclarativeComponentPrivate::completeCreateObjectWithInitialProperties(v8::Handle qmlGlobal, v8::Handle valuemap, QObject *toCreate) +{ + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QV8Engine *v8engine = ep->v8engine(); + v8::Handle ov = v8engine->newQObject(toCreate); + Q_ASSERT(ov->IsObject()); + v8::Handle object = v8::Handle::Cast(ov); + + if (!valuemap.IsEmpty()) { + QDeclarativeComponentExtension *e = componentExtension(v8engine); + // Try catch isn't needed as the function itself is loaded with try/catch + v8::Handle function = e->initialProperties->Run(qmlGlobal); + v8::Handle args[] = { object, valuemap }; + v8::Handle::Cast(function)->Call(v8engine->global(), 2, args); + } + + completeCreate(); + + QDeclarativeData *ddata = QDeclarativeData::get(toCreate); + Q_ASSERT(ddata); + ddata->setImplicitDestructible(); + + return v8engine->toQObject(object); +} + + +QDeclarativeComponentExtension::QDeclarativeComponentExtension(QV8Engine *engine) +{ + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + forceIncubation = qPersistentNew(V8FUNCTION(QV8IncubatorResource::ForceIncubation, engine)); + + { + v8::Local ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetInternalFieldCount(1); + ft->InstanceTemplate()->SetAccessor(v8::String::New("onStatusChanged"), + QV8IncubatorResource::StatusChangedGetter, + QV8IncubatorResource::StatusChangedSetter); + ft->InstanceTemplate()->SetAccessor(v8::String::New("status"), + QV8IncubatorResource::StatusGetter); + ft->InstanceTemplate()->SetAccessor(v8::String::New("object"), + QV8IncubatorResource::ObjectGetter); + ft->InstanceTemplate()->SetAccessor(v8::String::New("forceIncubation"), + QV8IncubatorResource::ForceIncubationGetter); + incubationConstructor = qPersistentNew(ft->GetFunction()); + } + + { +#define INITIALPROPERTIES_SOURCE \ + "(function(object, values) {"\ + "try {"\ + "for(var property in values) {"\ + "try {"\ + "var properties = property.split(\".\");"\ + "var o = object;"\ + "for (var ii = 0; ii < properties.length - 1; ++ii) {"\ + "o = o[properties[ii]];"\ + "}"\ + "o[properties[properties.length - 1]] = values[property];"\ + "} catch(e) {}"\ + "}"\ + "} catch(e) {}"\ + "})" + initialProperties = qPersistentNew(engine->qmlModeCompile(QLatin1String(INITIALPROPERTIES_SOURCE))); +#undef INITIALPROPERTIES_SOURCE + } +} + +v8::Handle QV8IncubatorResource::ObjectGetter(v8::Local, + const v8::AccessorInfo& info) +{ + QV8IncubatorResource *r = v8_resource_check(info.This()); + return r->engine->newQObject(r->object()); +} + +v8::Handle QV8IncubatorResource::ForceIncubationGetter(v8::Local, + const v8::AccessorInfo& info) +{ + QV8IncubatorResource *r = v8_resource_check(info.This()); + return componentExtension(r->engine)->forceIncubation; +} + +v8::Handle QV8IncubatorResource::ForceIncubation(const v8::Arguments &args) +{ + QV8IncubatorResource *r = v8_resource_cast(args.This()); + if (!r) + V8THROW_TYPE("Not an incubator object"); + + r->forceIncubation(); + + return v8::Undefined(); +} + +v8::Handle QV8IncubatorResource::StatusGetter(v8::Local, + const v8::AccessorInfo& info) +{ + QV8IncubatorResource *r = v8_resource_check(info.This()); + return v8::Integer::NewFromUnsigned(r->status()); +} + +v8::Handle QV8IncubatorResource::StatusChangedGetter(v8::Local, + const v8::AccessorInfo& info) +{ + return info.This()->GetInternalField(0); +} + +void QV8IncubatorResource::StatusChangedSetter(v8::Local, v8::Local value, + const v8::AccessorInfo& info) +{ + info.This()->SetInternalField(0, value); +} + +QDeclarativeComponentExtension::~QDeclarativeComponentExtension() +{ + qPersistentDispose(incubationConstructor); + qPersistentDispose(initialProperties); +} + +QV8IncubatorResource::QV8IncubatorResource(QV8Engine *engine, IncubationMode m) +: QV8ObjectResource(engine), QDeclarativeIncubator(m) +{ +} + +void QV8IncubatorResource::setInitialState(QObject *o) +{ + QDeclarativeComponent_setQmlParent(o, parent); + + if (!valuemap.IsEmpty()) { + QDeclarativeComponentExtension *e = componentExtension(engine); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + v8::Handle function = e->initialProperties->Run(qmlGlobal); + v8::Handle args[] = { me, valuemap }; + v8::Handle::Cast(function)->Call(engine->global(), 2, args); + + qPersistentDispose(valuemap); + qPersistentDispose(qmlGlobal); + } +} + +void QV8IncubatorResource::statusChanged(Status s) +{ + if (s == Ready) { + Q_ASSERT(QDeclarativeData::get(object())); + QDeclarativeData::get(object())->setImplicitDestructible(); + } + + if (!me.IsEmpty()) { // Will be false in synchronous mode + v8::HandleScope scope; + v8::Local callback = me->GetInternalField(0); + + if (!callback.IsEmpty() && !callback->IsUndefined()) { + + if (callback->IsFunction()) { + v8::Context::Scope context_scope(engine->context()); + v8::Local f = v8::Local::Cast(callback); + v8::Handle args[] = { v8::Integer::NewFromUnsigned(s) }; + // XXX TryCatch + f->Call(me, 1, args); + } + } + } + + if (s == Ready || s == Error) { + qPersistentDispose(valuemap); + qPersistentDispose(qmlGlobal); + // No further status changes are forthcoming, so we no long need a self reference + qPersistentDispose(me); + } +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecomponent.h b/src/declarative/qml/qdeclarativecomponent.h index a3457d1..92af689 100644 --- a/src/declarative/qml/qdeclarativecomponent.h +++ b/src/declarative/qml/qdeclarativecomponent.h @@ -55,12 +55,15 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) -class QDeclarativeCompiledData; class QByteArray; -class QDeclarativeComponentPrivate; class QDeclarativeEngine; -class QDeclarativeComponentAttached; +class QDeclarativeComponent; +class QDeclarativeIncubator; class QDeclarativeV8Function; +class QDeclarativeCompiledData; +class QDeclarativeComponentPrivate; +class QDeclarativeComponentAttached; + class Q_DECLARATIVE_EXPORT QDeclarativeComponent : public QObject { Q_OBJECT @@ -97,6 +100,9 @@ public: virtual QObject *beginCreate(QDeclarativeContext *); virtual void completeCreate(); + void create(QDeclarativeIncubator &, QDeclarativeContext *context, + QDeclarativeContext *forContext = 0); + QDeclarativeContext *creationContext() const; static QDeclarativeComponentAttached *qmlAttachedProperties(QObject *); @@ -112,6 +118,7 @@ Q_SIGNALS: protected: QDeclarativeComponent(QDeclarativeComponentPrivate &dd, QObject* parent); Q_INVOKABLE void createObject(QDeclarativeV8Function *); + Q_INVOKABLE void incubateObject(QDeclarativeV8Function *); private: QDeclarativeComponent(QDeclarativeEngine *, QDeclarativeCompiledData *, int, QObject *parent); diff --git a/src/declarative/qml/qdeclarativecomponent_p.h b/src/declarative/qml/qdeclarativecomponent_p.h index ba93749..7e6f9c2 100644 --- a/src/declarative/qml/qdeclarativecomponent_p.h +++ b/src/declarative/qml/qdeclarativecomponent_p.h @@ -59,6 +59,7 @@ #include "private/qdeclarativeengine_p.h" #include "private/qdeclarativetypeloader_p.h" #include "private/qbitfield_p.h" +#include "private/qdeclarativevme_p.h" #include "qdeclarativeerror.h" #include "qdeclarative.h" @@ -84,9 +85,8 @@ class Q_AUTOTEST_EXPORT QDeclarativeComponentPrivate : public QObjectPrivate, pu public: QDeclarativeComponentPrivate() : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0) {} - QObject *beginCreate(QDeclarativeContextData *, const QBitField &); + QObject *beginCreate(QDeclarativeContextData *); void completeCreate(); - QObject *createObjectWithInitialProperties(v8::Handle qmlGlobal, v8::Handle valuemap, QObject *parentObject); QObject *completeCreateObjectWithInitialProperties(v8::Handle qmlGlobal, v8::Handle valuemap, QObject *toCreate); QDeclarativeTypeData *typeData; @@ -102,25 +102,18 @@ public: QDeclarativeCompiledData *cc; struct ConstructionState { - ConstructionState() : componentAttached(0), completePending(false) {} - QList > bindValues; - QList > parserStatus; - QList, int> > finalizedParserStatus; - QDeclarativeComponentAttached *componentAttached; + ConstructionState() : completePending(false) {} + + QDeclarativeVME vme; QList errors; bool completePending; }; ConstructionState state; - static QObject *begin(QDeclarativeContextData *parentContext, QDeclarativeContextData *componentCreationContext, - QDeclarativeCompiledData *component, int start, - ConstructionState *state, QList *errors, - const QBitField &bindings = QBitField()); static void beginDeferred(QDeclarativeEnginePrivate *enginePriv, QObject *object, ConstructionState *state); static void complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state); - QDeclarativeEngine *engine; QDeclarativeGuardedContextData creationContext; @@ -155,8 +148,8 @@ Q_SIGNALS: void destruction(); private: + friend class QDeclarativeVME; friend class QDeclarativeContextData; - friend class QDeclarativeComponentPrivate; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativecontext.cpp b/src/declarative/qml/qdeclarativecontext.cpp index ff6e628..73f05e4 100644 --- a/src/declarative/qml/qdeclarativecontext.cpp +++ b/src/declarative/qml/qdeclarativecontext.cpp @@ -510,17 +510,19 @@ QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListProperty *, int); }; +class QDeclarativeVME; class QDeclarativeComponentAttached; class QDeclarativeGuardedContextData; class Q_DECLARATIVE_EXPORT QDeclarativeContextData @@ -145,6 +146,10 @@ public: quint32 dummy:28; QDeclarativeContext *publicContext; + // VME that is constructing this context if any + // XXX remove if possible + QDeclarativeVME *activeVME; + // Property name cache QDeclarativeIntegerCache *propertyNames; diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index f55c290..4c8d48f 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -68,6 +68,7 @@ #include "private/qdeclarativedebugtrace_p.h" #include "private/qdeclarativeapplication_p.h" #include "private/qv8debugservice_p.h" +#include "qdeclarativeincubator.h" #include #include @@ -90,6 +91,7 @@ #include #include #include +#include #include @@ -340,10 +342,10 @@ QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e) : captureProperties(false), rootContext(0), isDebugging(false), outputWarningsToStdErr(true), sharedContext(0), sharedScope(0), cleanup(0), erroredBindings(0), inProgressCreations(0), - workerScriptEngine(0), componentAttached(0), inBeginCreate(false), + workerScriptEngine(0), activeVME(0), networkAccessManager(0), networkAccessManagerFactory(0), scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), - sgContext(0) + incubatorCount(0), incubationController(0), sgContext(0), mutex(QMutex::Recursive) { if (!qt_QmlQtModule_registered) { qt_QmlQtModule_registered = true; @@ -358,8 +360,6 @@ QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e) QDeclarativeEnginePrivate::~QDeclarativeEnginePrivate() { Q_ASSERT(inProgressCreations == 0); - Q_ASSERT(bindValues.isEmpty()); - Q_ASSERT(parserStatus.isEmpty()); while (cleanup) { QDeclarativeCleanup *c = cleanup; @@ -370,6 +370,11 @@ QDeclarativeEnginePrivate::~QDeclarativeEnginePrivate() c->clear(); } + doDeleteInEngineThread(); + + if (incubationController) incubationController->d = 0; + incubationController = 0; + delete rootContext; rootContext = 0; @@ -385,21 +390,6 @@ QDeclarativeEnginePrivate::~QDeclarativeEnginePrivate() } } -void QDeclarativeEnginePrivate::clear(SimpleList &bvs) -{ - bvs.clear(); -} - -void QDeclarativeEnginePrivate::clear(SimpleList &pss) -{ - for (int ii = 0; ii < pss.count; ++ii) { - QDeclarativeParserStatus *ps = pss.at(ii); - if(ps) - ps->d = 0; - } - pss.clear(); -} - void QDeclarativePrivate::qdeclarativeelement_destructor(QObject *o) { QObjectPrivate *p = QObjectPrivate::get(o); @@ -430,6 +420,18 @@ void QDeclarativeData::objectNameChanged(QAbstractDeclarativeData *d, QObject *o void QDeclarativeEnginePrivate::init() { Q_Q(QDeclarativeEngine); + + static bool firstTime = true; + if (firstTime) { + // This is a nasty hack as QNetworkAccessManager will issue a + // BlockingQueuedConnection to the main thread if it is initialized for the + // first time on a non-main thread. This can cause a lockup if the main thread + // is blocking on the thread that initialize the network access manager. + QNetworkConfigurationManager man; + + firstTime = false; + } + qRegisterMetaType("QVariant"); qRegisterMetaType("QDeclarativeScriptString"); qRegisterMetaType("QJSValue"); @@ -597,6 +599,16 @@ QDeclarativeNetworkAccessManagerFactory *QDeclarativeEngine::networkAccessManage return d->networkAccessManagerFactory; } +void QDeclarativeEnginePrivate::registerFinalizeCallback(QObject *obj, int index) +{ + if (activeVME) { + activeVME->finalizeCallbacks.append(qMakePair(QDeclarativeGuard(obj), index)); + } else { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args); + } +} + QNetworkAccessManager *QDeclarativeEnginePrivate::createNetworkAccessManager(QObject *parent) const { QMutexLocker locker(&mutex); @@ -912,6 +924,26 @@ QDeclarativeEngine::ObjectOwnership QDeclarativeEngine::objectOwnership(QObject return ddata->indestructible?CppOwnership:JavaScriptOwnership; } +bool QDeclarativeEngine::event(QEvent *e) +{ + Q_D(QDeclarativeEngine); + if (e->type() == QEvent::User) + d->doDeleteInEngineThread(); + + return QJSEngine::event(e); +} + +void QDeclarativeEnginePrivate::doDeleteInEngineThread() +{ + QFieldList list; + mutex.lock(); + list.copyAndClear(toDeleteInEngineThread); + mutex.unlock(); + + while (Deletable *d = list.takeFirst()) + delete d; +} + Q_AUTOTEST_EXPORT void qmlExecuteDeferred(QObject *object) { QDeclarativeData *data = QDeclarativeData::get(object); @@ -1468,6 +1500,7 @@ QDeclarativePropertyCache *QDeclarativeEnginePrivate::createCache(QDeclarativeTy int maxMinorVersion = 0; const QMetaObject *metaObject = type->metaObject(); + while (metaObject) { QDeclarativeType *t = QDeclarativeMetaType::qmlType(metaObject, type->module(), type->majorVersion(), minorVersion); @@ -1562,44 +1595,31 @@ QDeclarativePropertyCache *QDeclarativeEnginePrivate::createCache(QDeclarativeTy return raw; } -void QDeclarativeEnginePrivate::registerCompositeType(QDeclarativeCompiledData *data) +QDeclarativeMetaType::ModuleApiInstance * +QDeclarativeEnginePrivate::moduleApiInstance(const QDeclarativeMetaType::ModuleApi &module) { - QByteArray name = data->root->className(); - - QByteArray ptr = name + '*'; - QByteArray lst = "QDeclarativeListProperty<" + name + '>'; + Locker locker(this); - int ptr_type = QMetaType::registerType(ptr.constData(), voidptr_destructor, - voidptr_constructor); - int lst_type = QMetaType::registerType(lst.constData(), voidptr_destructor, - voidptr_constructor); - - m_qmlLists.insert(lst_type, ptr_type); - m_compositeTypes.insert(ptr_type, data); - data->addref(); -} - -bool QDeclarativeEnginePrivate::isList(int t) const -{ - return m_qmlLists.contains(t) || QDeclarativeMetaType::isList(t); -} + QDeclarativeMetaType::ModuleApiInstance *a = moduleApiInstances.value(module); + if (!a) { + a = new QDeclarativeMetaType::ModuleApiInstance; + a->scriptCallback = module.script; + a->qobjectCallback = module.qobject; + moduleApiInstances.insert(module, a); + } -int QDeclarativeEnginePrivate::listType(int t) const -{ - QHash::ConstIterator iter = m_qmlLists.find(t); - if (iter != m_qmlLists.end()) - return *iter; - else - return QDeclarativeMetaType::listType(t); + return a; } bool QDeclarativeEnginePrivate::isQObject(int t) { + Locker locker(this); return m_compositeTypes.contains(t) || QDeclarativeMetaType::isQObject(t); } QObject *QDeclarativeEnginePrivate::toQObject(const QVariant &v, bool *ok) const { + Locker locker(this); int t = v.userType(); if (t == QMetaType::QObjectStar || m_compositeTypes.contains(t)) { if (ok) *ok = true; @@ -1611,6 +1631,7 @@ QObject *QDeclarativeEnginePrivate::toQObject(const QVariant &v, bool *ok) const QDeclarativeMetaType::TypeCategory QDeclarativeEnginePrivate::typeCategory(int t) const { + Locker locker(this); if (m_compositeTypes.contains(t)) return QDeclarativeMetaType::Object; else if (m_qmlLists.contains(t)) @@ -1619,8 +1640,25 @@ QDeclarativeMetaType::TypeCategory QDeclarativeEnginePrivate::typeCategory(int t return QDeclarativeMetaType::typeCategory(t); } +bool QDeclarativeEnginePrivate::isList(int t) const +{ + Locker locker(this); + return m_qmlLists.contains(t) || QDeclarativeMetaType::isList(t); +} + +int QDeclarativeEnginePrivate::listType(int t) const +{ + Locker locker(this); + QHash::ConstIterator iter = m_qmlLists.find(t); + if (iter != m_qmlLists.end()) + return *iter; + else + return QDeclarativeMetaType::listType(t); +} + const QMetaObject *QDeclarativeEnginePrivate::rawMetaObjectForType(int t) const { + Locker locker(this); QHash::ConstIterator iter = m_compositeTypes.find(t); if (iter != m_compositeTypes.end()) { return (*iter)->root; @@ -1632,6 +1670,7 @@ const QMetaObject *QDeclarativeEnginePrivate::rawMetaObjectForType(int t) const const QMetaObject *QDeclarativeEnginePrivate::metaObjectForType(int t) const { + Locker locker(this); QHash::ConstIterator iter = m_compositeTypes.find(t); if (iter != m_compositeTypes.end()) { return (*iter)->root; @@ -1641,6 +1680,25 @@ const QMetaObject *QDeclarativeEnginePrivate::metaObjectForType(int t) const } } +void QDeclarativeEnginePrivate::registerCompositeType(QDeclarativeCompiledData *data) +{ + QByteArray name = data->root->className(); + + QByteArray ptr = name + '*'; + QByteArray lst = "QDeclarativeListProperty<" + name + '>'; + + int ptr_type = QMetaType::registerType(ptr.constData(), voidptr_destructor, + voidptr_constructor); + int lst_type = QMetaType::registerType(lst.constData(), voidptr_destructor, + voidptr_constructor); + + data->addref(); + + Locker locker(this); + m_qmlLists.insert(lst_type, ptr_type); + m_compositeTypes.insert(ptr_type, data); +} + bool QDeclarative_isFileCaseCorrect(const QString &fileName) { #if defined(Q_OS_MAC) || defined(Q_OS_WIN32) diff --git a/src/declarative/qml/qdeclarativeengine.h b/src/declarative/qml/qdeclarativeengine.h index 25b254f..4d5aa34 100644 --- a/src/declarative/qml/qdeclarativeengine.h +++ b/src/declarative/qml/qdeclarativeengine.h @@ -66,6 +66,7 @@ class QScriptContext; class QDeclarativeImageProvider; class QNetworkAccessManager; class QDeclarativeNetworkAccessManagerFactory; +class QDeclarativeIncubationController; class Q_DECLARATIVE_EXPORT QDeclarativeEngine : public QJSEngine { Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath) @@ -98,6 +99,9 @@ public: QDeclarativeImageProvider *imageProvider(const QString &id) const; void removeImageProvider(const QString &id); + void setIncubationController(QDeclarativeIncubationController *); + QDeclarativeIncubationController *incubationController() const; + void setOfflineStoragePath(const QString& dir); QString offlineStoragePath() const; @@ -116,6 +120,9 @@ public: static void setObjectOwnership(QObject *, ObjectOwnership); static ObjectOwnership objectOwnership(QObject *); +protected: + virtual bool event(QEvent *); + Q_SIGNALS: void quit(); void warnings(const QList &warnings); diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h index be4f714..03d56e2 100644 --- a/src/declarative/qml/qdeclarativeengine_p.h +++ b/src/declarative/qml/qdeclarativeengine_p.h @@ -70,11 +70,12 @@ #include "private/qdeclarativedirparser_p.h" #include "private/qintrusivelist_p.h" -#include #include #include #include #include +#include +#include #include @@ -87,7 +88,6 @@ class QDeclarativeEngine; class QDeclarativeContextPrivate; class QDeclarativeExpression; class QDeclarativeImportDatabase; -class ScarceResourceData; class QNetworkReply; class QNetworkAccessManager; class QDeclarativeNetworkAccessManagerFactory; @@ -97,8 +97,10 @@ class QDeclarativeComponentAttached; class QDeclarativeCleanup; class QDeclarativeDelayedError; class QDeclarativeWorkerScriptEngine; +class QDeclarativeVME; class QDir; class QSGTexture; +class QDeclarativeIncubator; class QSGContext; class Q_DECLARATIVE_EXPORT QDeclarativeEnginePrivate : public QObjectPrivate @@ -146,42 +148,10 @@ public: QUrl baseUrl; - template - struct SimpleList { - SimpleList() - : count(0), values(0) {} - SimpleList(int r) - : count(0), values(new T*[r]) {} - - int count; - T **values; - - void append(T *v) { - values[count++] = v; - } - - T *at(int idx) const { - return values[idx]; - } - - void clear() { - delete [] values; - } - }; - - static void clear(SimpleList &); - static void clear(SimpleList &); - - QList > bindValues; - QList > parserStatus; - QList,int> > finalizedParserStatus; - QDeclarativeComponentAttached *componentAttached; + typedef QPair,int> FinalizeCallback; + void registerFinalizeCallback(QObject *obj, int index); - void registerFinalizedParserStatusObject(QObject *obj, int index) { - finalizedParserStatus.append(qMakePair(QDeclarativeGuard(obj), index)); - } - - bool inBeginCreate; + QDeclarativeVME *activeVME; QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const; QNetworkAccessManager *getNetworkAccessManager() const; @@ -211,32 +181,43 @@ public: void referenceScarceResources(); void dereferenceScarceResources(); - mutable QMutex mutex; - QDeclarativeTypeLoader typeLoader; QDeclarativeImportDatabase importDatabase; QString offlineStoragePath; mutable quint32 uniqueId; - quint32 getUniqueId() const { + inline quint32 getUniqueId() const { return uniqueId++; } QDeclarativeValueTypeFactory valueTypes; - QHash moduleApiInstances; - - QHash propertyCache; - QHash, QDeclarativePropertyCache *> typePropertyCache; + // Unfortunate workaround to avoid a circular dependency between + // qdeclarativeengine_p.h and qdeclarativeincubator_p.h + struct Incubator { QIntrusiveListNode next; }; + QIntrusiveList incubatorList; + unsigned int incubatorCount; + QDeclarativeIncubationController *incubationController; + void incubate(QDeclarativeIncubator &, QDeclarativeContextData *); + + // These methods may be called from any thread + inline bool isEngineThread() const; + inline static bool isEngineThread(const QDeclarativeEngine *); + template + inline void deleteInEngineThread(T *); + template + inline static void deleteInEngineThread(QDeclarativeEngine *, T *); + + // These methods may be called from the loader thread + QDeclarativeMetaType::ModuleApiInstance *moduleApiInstance(const QDeclarativeMetaType::ModuleApi &module); + + // These methods may be called from the loader thread inline QDeclarativePropertyCache *cache(QObject *obj); inline QDeclarativePropertyCache *cache(const QMetaObject *); inline QDeclarativePropertyCache *cache(QDeclarativeType *, int, QDeclarativeError &error); - QDeclarativePropertyCache *createCache(const QMetaObject *); - QDeclarativePropertyCache *createCache(QDeclarativeType *, int, QDeclarativeError &error); - - void registerCompositeType(QDeclarativeCompiledData *); + // These methods may be called from the loader thread bool isQObject(int); QObject *toQObject(const QVariant &, bool *ok = 0) const; QDeclarativeMetaType::TypeCategory typeCategory(int) const; @@ -244,8 +225,7 @@ public: int listType(int) const; const QMetaObject *rawMetaObjectForType(int) const; const QMetaObject *metaObjectForType(int) const; - QHash m_qmlLists; - QHash m_compositeTypes; + void registerCompositeType(QDeclarativeCompiledData *); void sendQuit(); void warning(const QDeclarativeError &); @@ -255,11 +235,12 @@ public: static void warning(QDeclarativeEnginePrivate *, const QDeclarativeError &); static void warning(QDeclarativeEnginePrivate *, const QList &); - static QV8Engine *getV8Engine(QDeclarativeEngine *e) { return e->d_func()->v8engine(); } - static QDeclarativeEnginePrivate *get(QDeclarativeEngine *e) { return e->d_func(); } - static QDeclarativeEnginePrivate *get(QDeclarativeContext *c) { return (c && c->engine()) ? QDeclarativeEnginePrivate::get(c->engine()) : 0; } - static QDeclarativeEnginePrivate *get(QDeclarativeContextData *c) { return (c && c->engine) ? QDeclarativeEnginePrivate::get(c->engine) : 0; } - static QDeclarativeEngine *get(QDeclarativeEnginePrivate *p) { return p->q_func(); } + inline static QV8Engine *getV8Engine(QDeclarativeEngine *e); + inline static QDeclarativeEnginePrivate *get(QDeclarativeEngine *e); + inline static const QDeclarativeEnginePrivate *get(const QDeclarativeEngine *e); + inline static QDeclarativeEnginePrivate *get(QDeclarativeContext *c); + inline static QDeclarativeEnginePrivate *get(QDeclarativeContextData *c); + inline static QDeclarativeEngine *get(QDeclarativeEnginePrivate *p); static QString urlToLocalFileOrQrc(const QUrl& url); static QString urlToLocalFileOrQrc(const QString& url); @@ -270,8 +251,149 @@ public: static bool qml_debugging_enabled; QSGContext *sgContext; + + mutable QMutex mutex; + +private: + // Locker locks the QDeclarativeEnginePrivate 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 + // QDeclarativeEnginePrivate structures or operations that can be guarenteed not to start activity + // on the loader thread. + // The Locker API is identical to QMutexLocker. Locker reuses the QDeclarativeEnginePrivate::mutex + // QMutex instance and multiple Lockers are recursive in the same thread. + class Locker + { + public: + inline Locker(const QDeclarativeEngine *); + inline Locker(const QDeclarativeEnginePrivate *); + inline ~Locker(); + + inline void unlock(); + inline void relock(); + + private: + const QDeclarativeEnginePrivate *m_ep; + quint32 m_locked:1; + }; + + // Must be called locked + QDeclarativePropertyCache *createCache(const QMetaObject *); + QDeclarativePropertyCache *createCache(QDeclarativeType *, int, QDeclarativeError &error); + + // These members must be protected by a QDeclarativeEnginePrivate::Locker as they are required by + // the threaded loader. Only access them through their respective accessor methods. + QHash moduleApiInstances; + QHash propertyCache; + QHash, QDeclarativePropertyCache *> typePropertyCache; + QHash m_qmlLists; + QHash m_compositeTypes; + + // These members is protected by the full QDeclarativeEnginePrivate::mutex mutex + struct Deletable { Deletable():next(0) {} virtual ~Deletable() {} Deletable *next; }; + QFieldList toDeleteInEngineThread; + void doDeleteInEngineThread(); }; +QDeclarativeEnginePrivate::Locker::Locker(const QDeclarativeEngine *e) +: m_ep(QDeclarativeEnginePrivate::get(e)) +{ + relock(); +} + +QDeclarativeEnginePrivate::Locker::Locker(const QDeclarativeEnginePrivate *e) +: m_ep(e), m_locked(false) +{ + relock(); +} + +QDeclarativeEnginePrivate::Locker::~Locker() +{ + unlock(); +} + +void QDeclarativeEnginePrivate::Locker::unlock() +{ + if (m_locked) { + m_ep->mutex.unlock(); + m_locked = false; + } +} + +void QDeclarativeEnginePrivate::Locker::relock() +{ + Q_ASSERT(!m_locked); + if (m_ep->typeLoader.isConcurrent()) { + m_ep->mutex.lock(); + m_locked = true; + } +} + +/*! +Returns true if the calling thread is the QDeclarativeEngine thread. +*/ +bool QDeclarativeEnginePrivate::isEngineThread() const +{ + Q_Q(const QDeclarativeEngine); + return QThread::currentThread() == q->thread(); +} + +/*! +Returns true if the calling thread is the QDeclarativeEngine \a engine thread. +*/ +bool QDeclarativeEnginePrivate::isEngineThread(const QDeclarativeEngine *engine) +{ + Q_ASSERT(engine); + return QDeclarativeEnginePrivate::get(engine)->isEngineThread(); +} + +/*! +Delete \a value in the engine thread. If the calling thread is the engine +thread, \a value will be deleted immediately. + +This method should be used for *any* type that has resources that need to +be freed in the engine thread. This is generally types that use V8 handles. +As there is some small overhead in checking the current thread, it is best +practice to check if any V8 handles actually need to be freed and delete +the instance directly if not. +*/ +template +void QDeclarativeEnginePrivate::deleteInEngineThread(T *value) +{ + Q_Q(QDeclarativeEngine); + + Q_ASSERT(value); + if (isEngineThread()) { + delete value; + } else { + struct I : public Deletable { + I(T *value) : value(value) {} + ~I() { delete value; } + T *value; + }; + I *i = new I(value); + mutex.lock(); + bool wasEmpty = toDeleteInEngineThread.isEmpty(); + toDeleteInEngineThread.append(i); + mutex.unlock(); + if (wasEmpty) + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + } +} + +/*! +Delete \a value in the \a engine thread. If the calling thread is the engine +thread, \a value will be deleted immediately. +*/ +template +void QDeclarativeEnginePrivate::deleteInEngineThread(QDeclarativeEngine *engine, T *value) +{ + Q_ASSERT(engine); + QDeclarativeEnginePrivate::get(engine)->deleteInEngineThread(value); +} + /*! Returns a QDeclarativePropertyCache for \a obj if one is available. @@ -279,12 +401,20 @@ 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 QDeclarativePropertyCache 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 +QDeclarativePropertyCache until the QDeclarativeEngine is destroyed. */ QDeclarativePropertyCache *QDeclarativeEnginePrivate::cache(QObject *obj) { if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted) return 0; + Locker locker(this); const QMetaObject *mo = obj->metaObject(); QDeclarativePropertyCache *rv = propertyCache.value(mo); if (!rv) rv = createCache(mo); @@ -304,6 +434,7 @@ QDeclarativePropertyCache *QDeclarativeEnginePrivate::cache(const QMetaObject *m { Q_ASSERT(metaObject); + Locker locker(this); QDeclarativePropertyCache *rv = propertyCache.value(metaObject); if (!rv) rv = createCache(metaObject); return rv; @@ -321,11 +452,42 @@ QDeclarativePropertyCache *QDeclarativeEnginePrivate::cache(QDeclarativeType *ty if (minorVersion == -1 || !type->containsRevisionedAttributes()) return cache(type->metaObject()); + Locker locker(this); QDeclarativePropertyCache *rv = typePropertyCache.value(qMakePair(type, minorVersion)); if (!rv) rv = createCache(type, minorVersion, error); return rv; } +QV8Engine *QDeclarativeEnginePrivate::getV8Engine(QDeclarativeEngine *e) +{ + return e->d_func()->v8engine(); +} + +QDeclarativeEnginePrivate *QDeclarativeEnginePrivate::get(QDeclarativeEngine *e) +{ + return e->d_func(); +} + +const QDeclarativeEnginePrivate *QDeclarativeEnginePrivate::get(const QDeclarativeEngine *e) +{ + return e->d_func(); +} + +QDeclarativeEnginePrivate *QDeclarativeEnginePrivate::get(QDeclarativeContext *c) +{ + return (c && c->engine()) ? QDeclarativeEnginePrivate::get(c->engine()) : 0; +} + +QDeclarativeEnginePrivate *QDeclarativeEnginePrivate::get(QDeclarativeContextData *c) +{ + return (c && c->engine) ? QDeclarativeEnginePrivate::get(c->engine) : 0; +} + +QDeclarativeEngine *QDeclarativeEnginePrivate::get(QDeclarativeEnginePrivate *p) +{ + return p->q_func(); +} + QT_END_NAMESPACE #endif // QDECLARATIVEENGINE_P_H diff --git a/src/declarative/qml/qdeclarativeextensioninterface.h b/src/declarative/qml/qdeclarativeextensioninterface.h index 29e856b..3d2b3c6 100644 --- a/src/declarative/qml/qdeclarativeextensioninterface.h +++ b/src/declarative/qml/qdeclarativeextensioninterface.h @@ -52,13 +52,19 @@ QT_MODULE(Declarative) class QDeclarativeEngine; -struct Q_DECLARATIVE_EXPORT QDeclarativeExtensionInterface +struct Q_DECLARATIVE_EXPORT QDeclarativeTypesExtensionInterface { - virtual ~QDeclarativeExtensionInterface() {} + virtual ~QDeclarativeTypesExtensionInterface() {} virtual void registerTypes(const char *uri) = 0; +}; + +struct Q_DECLARATIVE_EXPORT QDeclarativeExtensionInterface : public QDeclarativeTypesExtensionInterface +{ + virtual ~QDeclarativeExtensionInterface() {} virtual void initializeEngine(QDeclarativeEngine *engine, const char *uri) = 0; }; +Q_DECLARE_INTERFACE(QDeclarativeTypesExtensionInterface, "com.trolltech.Qt.QDeclarativeTypesExtensionInterface/1.0") Q_DECLARE_INTERFACE(QDeclarativeExtensionInterface, "com.trolltech.Qt.QDeclarativeExtensionInterface/1.0") QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeextensionplugin.h b/src/declarative/qml/qdeclarativeextensionplugin.h index a249fba..fbc6099 100644 --- a/src/declarative/qml/qdeclarativeextensionplugin.h +++ b/src/declarative/qml/qdeclarativeextensionplugin.h @@ -54,10 +54,12 @@ QT_MODULE(Declarative) class QDeclarativeEngine; -class Q_DECLARATIVE_EXPORT QDeclarativeExtensionPlugin : public QObject, public QDeclarativeExtensionInterface +class Q_DECLARATIVE_EXPORT QDeclarativeExtensionPlugin : public QObject, + public QDeclarativeExtensionInterface { Q_OBJECT Q_INTERFACES(QDeclarativeExtensionInterface) + Q_INTERFACES(QDeclarativeTypesExtensionInterface) public: explicit QDeclarativeExtensionPlugin(QObject *parent = 0); ~QDeclarativeExtensionPlugin(); diff --git a/src/declarative/qml/qdeclarativeimport.cpp b/src/declarative/qml/qdeclarativeimport.cpp index 64bdaf1..21e91de 100644 --- a/src/declarative/qml/qdeclarativeimport.cpp +++ b/src/declarative/qml/qdeclarativeimport.cpp @@ -213,19 +213,10 @@ void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDecla QDeclarativeMetaType::ModuleApi moduleApi = QDeclarativeMetaType::moduleApi(data.uri, data.majversion, data.minversion); if (moduleApi.script || moduleApi.qobject) { QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - QDeclarativeMetaType::ModuleApiInstance *a = ep->moduleApiInstances.value(moduleApi); - if (!a) { - a = new QDeclarativeMetaType::ModuleApiInstance; - a->scriptCallback = moduleApi.script; - a->qobjectCallback = moduleApi.qobject; - ep->moduleApiInstances.insert(moduleApi, a); - } - import.moduleApi = a; + import.moduleApi = ep->moduleApiInstance(moduleApi); } } } - - } /*! @@ -1092,22 +1083,29 @@ bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QSt return false; } - if (QDeclarativeExtensionInterface *iface = qobject_cast(loader.instance())) { + QObject *instance = loader.instance(); + if (QDeclarativeTypesExtensionInterface *iface = qobject_cast(instance)) { const QByteArray bytes = uri.toUtf8(); const char *moduleId = bytes.constData(); if (!typesRegistered) { - // ### this code should probably be protected with a mutex. + // XXX thread this code should probably be protected with a mutex. qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri); iface->registerTypes(moduleId); } if (!engineInitialized) { - // things on the engine (eg. adding new global objects) have to be done for every engine. - - // protect against double initialization + // things on the engine (eg. adding new global objects) have to be done for every + // engine. + // XXX protect against double initialization initializedPlugins.insert(absoluteFilePath); - iface->initializeEngine(engine, moduleId); + + QDeclarativeExtensionInterface *eiface = + qobject_cast(instance); + if (eiface) { + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + ep->typeLoader.initializeEngine(eiface, moduleId); + } } } else { if (errors) { diff --git a/src/declarative/qml/qdeclarativeimport_p.h b/src/declarative/qml/qdeclarativeimport_p.h index 7f0b499..7da4aa5 100644 --- a/src/declarative/qml/qdeclarativeimport_p.h +++ b/src/declarative/qml/qdeclarativeimport_p.h @@ -134,6 +134,7 @@ private: const QString &baseName); + // XXX thread QStringList filePluginPath; QStringList fileImportPath; diff --git a/src/declarative/qml/qdeclarativeincubator.cpp b/src/declarative/qml/qdeclarativeincubator.cpp new file mode 100644 index 0000000..e10575f --- /dev/null +++ b/src/declarative/qml/qdeclarativeincubator.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeincubator.h" +#include "qdeclarativeincubator_p.h" + +#include +#include + +// XXX TODO +// - check that the Component.onCompleted behavior is the same as 4.8 in the synchronous and +// async if nested cases +void QDeclarativeEnginePrivate::incubate(QDeclarativeIncubator &i, QDeclarativeContextData *forContext) +{ + QDeclarativeIncubatorPrivate *p = i.d; + + QDeclarativeIncubator::IncubationMode mode = i.incubationMode(); + + if (mode == QDeclarativeIncubator::AsynchronousIfNested) { + mode = QDeclarativeIncubator::Synchronous; + + // Need to find the first constructing context and see if it is asynchronous + QDeclarativeIncubatorPrivate *parentIncubator = 0; + QDeclarativeContextData *cctxt = forContext; + while (cctxt) { + if (cctxt->activeVME) { + parentIncubator = (QDeclarativeIncubatorPrivate *)cctxt->activeVME->data; + break; + } + cctxt = cctxt->parent; + } + + if (parentIncubator && parentIncubator->mode != QDeclarativeIncubator::Synchronous) { + mode = QDeclarativeIncubator::Asynchronous; + p->waitingOnMe = parentIncubator; + parentIncubator->waitingFor.insert(p); + } + } + + if (mode == QDeclarativeIncubator::Synchronous) { + QDeclarativeVME::Interrupt i; + p->incubate(i); + } else { + inProgressCreations++; + incubatorList.insert(p); + incubatorCount++; + + if (incubationController) + incubationController->incubatingObjectCountChanged(incubatorCount); + } +} + +void QDeclarativeEngine::setIncubationController(QDeclarativeIncubationController *i) +{ + Q_D(QDeclarativeEngine); + d->incubationController = i; + if (i) i->d = d; +} + +QDeclarativeIncubationController *QDeclarativeEngine::incubationController() const +{ + Q_D(const QDeclarativeEngine); + return d->incubationController; +} + +QDeclarativeIncubatorPrivate::QDeclarativeIncubatorPrivate(QDeclarativeIncubator *q, + QDeclarativeIncubator::IncubationMode m) +: q(q), mode(m), progress(Execute), result(0), component(0), vme(this), waitingOnMe(0) +{ +} + +QDeclarativeIncubatorPrivate::~QDeclarativeIncubatorPrivate() +{ + clear(); +} + +void QDeclarativeIncubatorPrivate::clear() +{ + if (next.isInList()) { + next.remove(); + Q_ASSERT(component); + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(component->engine); + component->release(); + + enginePriv->incubatorCount--; + QDeclarativeIncubationController *controller = enginePriv->incubationController; + if (controller) + controller->incubatingObjectCountChanged(enginePriv->incubatorCount); + } + + if (nextWaitingFor.isInList()) { + Q_ASSERT(waitingOnMe); + nextWaitingFor.remove(); + waitingOnMe = 0; + } + +} + +QDeclarativeIncubationController::QDeclarativeIncubationController() +: d(0) +{ +} + +QDeclarativeIncubationController::~QDeclarativeIncubationController() +{ + if (d) QDeclarativeEnginePrivate::get(d)->setIncubationController(0); + d = 0; +} + +QDeclarativeEngine *QDeclarativeIncubationController::engine() const +{ + return QDeclarativeEnginePrivate::get(d); +} + +int QDeclarativeIncubationController::incubatingObjectCount() const +{ + if (d) + return d->incubatorCount; + else + return 0; +} + +void QDeclarativeIncubationController::incubatingObjectCountChanged(int) +{ +} + +void QDeclarativeIncubatorPrivate::incubate(QDeclarativeVME::Interrupt &i) +{ + QDeclarativeEngine *engine = component->engine; + QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); + + if (progress == QDeclarativeIncubatorPrivate::Execute) { + enginePriv->referenceScarceResources(); + result = vme.execute(&errors, i); + enginePriv->dereferenceScarceResources(); + + if (errors.isEmpty() && result == 0) + return; // Interrupted + + if (result) { + QDeclarativeData *ddata = QDeclarativeData::get(result); + Q_ASSERT(ddata); + ddata->indestructible = true; + + q->setInitialState(result); + } + + if (errors.isEmpty()) + progress = QDeclarativeIncubatorPrivate::Completing; + else + progress = QDeclarativeIncubatorPrivate::Completed; + + q->statusChanged(q->status()); + + if (i.shouldInterrupt()) + goto finishIncubate; + } + + if (progress == QDeclarativeIncubatorPrivate::Completing) { + do { + if (vme.complete(i)) { + progress = QDeclarativeIncubatorPrivate::Completed; + goto finishIncubate; + } + } while (!i.shouldInterrupt()); + } + +finishIncubate: + if (progress == QDeclarativeIncubatorPrivate::Completed && waitingFor.isEmpty()) { + QDeclarativeIncubatorPrivate *isWaiting = waitingOnMe; + clear(); + if (isWaiting) isWaiting->incubate(i); + + enginePriv->inProgressCreations--; + + q->statusChanged(q->status()); + + if (0 == enginePriv->inProgressCreations) { + while (enginePriv->erroredBindings) { + enginePriv->warning(enginePriv->erroredBindings->error); + enginePriv->erroredBindings->removeError(); + } + } + } +} + +void QDeclarativeIncubationController::incubateFor(int msecs) +{ + if (!d || d->incubatorCount == 0) + return; + + QDeclarativeVME::Interrupt i(msecs * 1000000); + i.reset(); + do { + QDeclarativeIncubatorPrivate *p = (QDeclarativeIncubatorPrivate*)d->incubatorList.first(); + p->incubate(i); + } while (d && d->incubatorCount != 0 && !i.shouldInterrupt()); +} + +void QDeclarativeIncubationController::incubateWhile(bool *flag) +{ + if (!d || d->incubatorCount == 0) + return; + + QDeclarativeVME::Interrupt i(flag); + do { + QDeclarativeIncubatorPrivate *p = (QDeclarativeIncubatorPrivate*)d->incubatorList.first(); + p->incubate(i); + } while (d && d->incubatorCount != 0 && !i.shouldInterrupt()); +} + +QDeclarativeIncubator::QDeclarativeIncubator(IncubationMode m) +: d(new QDeclarativeIncubatorPrivate(this, m)) +{ +} + +QDeclarativeIncubator::~QDeclarativeIncubator() +{ + delete d; d = 0; +} + +void QDeclarativeIncubator::clear() +{ +} + +void QDeclarativeIncubator::forceIncubation() +{ + QDeclarativeVME::Interrupt i; + while (Loading == status()) { + while (Loading == status() && !d->waitingFor.isEmpty()) + d->waitingFor.first()->incubate(i); + if (Loading == status()) + d->incubate(i); + } + +} + +bool QDeclarativeIncubator::isNull() const +{ + return status() == Null; +} + +bool QDeclarativeIncubator::isReady() const +{ + return status() == Ready; +} + +bool QDeclarativeIncubator::isError() const +{ + return status() == Error; +} + +bool QDeclarativeIncubator::isLoading() const +{ + return status() == Loading; +} + +QList QDeclarativeIncubator::errors() const +{ + return d->errors; +} + +QDeclarativeIncubator::IncubationMode QDeclarativeIncubator::incubationMode() const +{ + return d->mode; +} + +QDeclarativeIncubator::Status QDeclarativeIncubator::status() const +{ + if (!d->errors.isEmpty()) return Error; + else if (d->result && d->progress == QDeclarativeIncubatorPrivate::Completed && + d->waitingFor.isEmpty()) return Ready; + else if (d->component) return Loading; + else return Null; +} + +QObject *QDeclarativeIncubator::object() const +{ + if (status() != Ready) return 0; + else return d->result; +} + +void QDeclarativeIncubator::statusChanged(Status) +{ +} + +void QDeclarativeIncubator::setInitialState(QObject *) +{ +} diff --git a/src/declarative/qml/qdeclarativeincubator.h b/src/declarative/qml/qdeclarativeincubator.h new file mode 100644 index 0000000..5b57cc8 --- /dev/null +++ b/src/declarative/qml/qdeclarativeincubator.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEINCUBATOR_H +#define QDECLARATIVEINCUBATOR_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeEngine; + +class QDeclarativeIncubatorPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeIncubator +{ + Q_DISABLE_COPY(QDeclarativeIncubator); +public: + enum IncubationMode { + Asynchronous, + AsynchronousIfNested, + Synchronous + }; + enum Status { + Null, + Ready, + Loading, + Error + }; + + QDeclarativeIncubator(IncubationMode = Asynchronous); + virtual ~QDeclarativeIncubator(); + + void clear(); + void forceIncubation(); + + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + QList errors() const; + + IncubationMode incubationMode() const; + + Status status() const; + + QObject *object() const; + +protected: + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); + +private: + friend class QDeclarativeComponent; + friend class QDeclarativeEnginePrivate; + friend class QDeclarativeIncubatorPrivate; + QDeclarativeIncubatorPrivate *d; +}; + +class QDeclarativeEnginePrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeIncubationController +{ + Q_DISABLE_COPY(QDeclarativeIncubationController); +public: + QDeclarativeIncubationController(); + virtual ~QDeclarativeIncubationController(); + + QDeclarativeEngine *engine() const; + int incubatingObjectCount() const; + + void incubateFor(int msecs); + void incubateWhile(bool *flag); + +protected: + virtual void incubatingObjectCountChanged(int); + +private: + friend class QDeclarativeEngine; + friend class QDeclarativeEnginePrivate; + friend class QDeclarativeIncubatorPrivate; + QDeclarativeEnginePrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEINCUBATOR_H diff --git a/src/declarative/qml/ftw/qdeclarativerefcount.cpp b/src/declarative/qml/qdeclarativeincubator_p.h similarity index 58% rename from src/declarative/qml/ftw/qdeclarativerefcount.cpp rename to src/declarative/qml/qdeclarativeincubator_p.h index a17d5b7..8210f6d 100644 --- a/src/declarative/qml/ftw/qdeclarativerefcount.cpp +++ b/src/declarative/qml/qdeclarativeincubator_p.h @@ -39,32 +39,58 @@ ** ****************************************************************************/ -#include "private/qdeclarativerefcount_p.h" +#ifndef QDECLARATIVEINCUBATOR_P_H +#define QDECLARATIVEINCUBATOR_P_H + +#include +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// QT_BEGIN_NAMESPACE -QDeclarativeRefCount::QDeclarativeRefCount() -: refCount(1) +class QDeclarativeCompiledData; +class QDeclarativeIncubator; +class QDeclarativeIncubatorPrivate : public QDeclarativeEnginePrivate::Incubator { -} + QIntrusiveListNode nextWaitingFor; +public: + QDeclarativeIncubatorPrivate(QDeclarativeIncubator *q, QDeclarativeIncubator::IncubationMode m); + ~QDeclarativeIncubatorPrivate(); -QDeclarativeRefCount::~QDeclarativeRefCount() -{ -} + QDeclarativeIncubator *q; -void QDeclarativeRefCount::addref() -{ - Q_ASSERT(refCount > 0); - ++refCount; -} + QDeclarativeIncubator::IncubationMode mode; -void QDeclarativeRefCount::release() -{ - Q_ASSERT(refCount > 0); - --refCount; - if (refCount == 0) - delete this; -} + QList errors; + + enum Progress { Execute, Completing, Completed }; + Progress progress; + + QObject *result; + QDeclarativeCompiledData *component; + QDeclarativeVME vme; + + typedef QDeclarativeIncubatorPrivate QIP; + QIP *waitingOnMe; + QIntrusiveList waitingFor; + + void clear(); + + void incubate(QDeclarativeVME::Interrupt &i); +}; QT_END_NAMESPACE +#endif // QDECLARATIVEINCUBATOR_P_H + diff --git a/src/declarative/qml/qdeclarativeinstruction.cpp b/src/declarative/qml/qdeclarativeinstruction.cpp index 3093ddc..3d505c7 100644 --- a/src/declarative/qml/qdeclarativeinstruction.cpp +++ b/src/declarative/qml/qdeclarativeinstruction.cpp @@ -57,11 +57,20 @@ void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx) case QDeclarativeInstruction::Init: qWarning().nospace() << idx << "\t\t" << "INIT\t\t\t" << instr->init.bindingsSize << "\t" << instr->init.parserStatusSize << "\t" << instr->init.contextCache << "\t" << instr->init.compiledBinding; break; + case QDeclarativeInstruction::DeferInit: + qWarning().nospace() << idx << "\t\t" << "DEFER_INIT\t\t" << instr->deferInit.bindingsSize << "\t" << instr->deferInit.parserStatusSize; + break; case QDeclarativeInstruction::Done: qWarning().nospace() << idx << "\t\t" << "DONE"; break; - case QDeclarativeInstruction::CreateObject: - qWarning().nospace() << idx << "\t\t" << "CREATE\t\t\t" << instr->create.type << "\t" << instr->create.bindingBits << "\t\t" << types.at(instr->create.type).className; + case QDeclarativeInstruction::CreateCppObject: + qWarning().nospace() << idx << "\t\t" << "CREATECPP\t\t\t" << instr->create.type << "\t\t\t" << types.at(instr->create.type).className; + break; + case QDeclarativeInstruction::CreateQMLObject: + qWarning().nospace() << idx << "\t\t" << "CREATEQML\t\t\t" << instr->createQml.type << "\t" << instr->createQml.bindingBits << "\t\t" << types.at(instr->createQml.type).className; + break; + case QDeclarativeInstruction::CompleteQMLObject: + qWarning().nospace() << idx << "\t\t" << "COMPLETEQML"; break; case QDeclarativeInstruction::CreateSimpleObject: qWarning().nospace() << idx << "\t\t" << "CREATE_SIMPLE\t\t" << instr->createSimple.typeSize; @@ -166,7 +175,7 @@ void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx) qWarning().nospace() << idx << "\t\t" << "STORE_SCRIPT_STRING\t" << instr->storeScriptString.propertyIndex << "\t" << instr->storeScriptString.value << "\t" << instr->storeScriptString.scope << "\t" << instr->storeScriptString.bindingId; break; case QDeclarativeInstruction::AssignSignalObject: - qWarning().nospace() << idx << "\t\t" << "ASSIGN_SIGNAL_OBJECT\t" << instr->assignSignalObject.signal << "\t\t\t" << datas.at(instr->assignSignalObject.signal); + qWarning().nospace() << idx << "\t\t" << "ASSIGN_SIGNAL_OBJECT\t" << instr->assignSignalObject.signal << "\t\t\t" << primitives.at(instr->assignSignalObject.signal); break; case QDeclarativeInstruction::AssignCustomType: qWarning().nospace() << idx << "\t\t" << "ASSIGN_CUSTOMTYPE\t" << instr->assignCustomType.propertyIndex << "\t" << instr->assignCustomType.primitive << "\t" << instr->assignCustomType.type; diff --git a/src/declarative/qml/qdeclarativeinstruction_p.h b/src/declarative/qml/qdeclarativeinstruction_p.h index 35de8b2..e64ca5d 100644 --- a/src/declarative/qml/qdeclarativeinstruction_p.h +++ b/src/declarative/qml/qdeclarativeinstruction_p.h @@ -59,8 +59,11 @@ QT_BEGIN_NAMESPACE #define FOR_EACH_QML_INSTR(F) \ F(Init, init) \ + F(DeferInit, deferInit) \ F(Done, common) \ - F(CreateObject, create) \ + F(CreateCppObject, create) \ + F(CreateQMLObject, createQml) \ + F(CompleteQMLObject, completeQml) \ F(CreateSimpleObject, createSimple) \ F(SetId, setId) \ F(SetDefault, common) \ @@ -151,14 +154,35 @@ union QDeclarativeInstruction int parserStatusSize; int contextCache; int compiledBinding; + int objectStackSize; + int listStackSize; + }; + struct instr_deferInit { + QML_INSTR_HEADER + int bindingsSize; + int parserStatusSize; + int objectStackSize; + int listStackSize; + }; + struct instr_createQml { + QML_INSTR_HEADER + int type; + int bindingBits; + bool isRoot; + }; + struct instr_completeQml { + QML_INSTR_HEADER + ushort column; + ushort line; + bool isRoot; }; struct instr_create { QML_INSTR_HEADER int type; int data; - int bindingBits; ushort column; ushort line; + bool isRoot; }; struct instr_createSimple { QML_INSTR_HEADER @@ -203,6 +227,7 @@ union QDeclarativeInstruction int value; short context; short owner; + bool isRoot; ushort line; }; struct instr_fetch { @@ -357,6 +382,7 @@ union QDeclarativeInstruction int metaObject; ushort column; ushort line; + bool isRoot; }; struct instr_fetchAttached { QML_INSTR_HEADER @@ -425,7 +451,10 @@ union QDeclarativeInstruction instr_common common; instr_init init; + instr_deferInit deferInit; instr_create create; + instr_createQml createQml; + instr_completeQml completeQml; instr_createSimple createSimple; instr_storeMeta storeMeta; instr_setId setId; diff --git a/src/declarative/qml/qdeclarativepropertycache.cpp b/src/declarative/qml/qdeclarativepropertycache.cpp index e35fa52..6337750 100644 --- a/src/declarative/qml/qdeclarativepropertycache.cpp +++ b/src/declarative/qml/qdeclarativepropertycache.cpp @@ -200,7 +200,7 @@ void QDeclarativePropertyCache::Data::lazyLoad(const QMetaMethod &m) Creates a new empty QDeclarativePropertyCache. */ QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e) -: QDeclarativeCleanup(e), engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0) +: engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0) { Q_ASSERT(engine); } @@ -209,7 +209,7 @@ QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e) Creates a new QDeclarativePropertyCache of \a metaObject. */ QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e, const QMetaObject *metaObject) -: QDeclarativeCleanup(e), engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0) +: engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0) { Q_ASSERT(engine); Q_ASSERT(metaObject); @@ -223,6 +223,16 @@ QDeclarativePropertyCache::~QDeclarativePropertyCache() if (parent) parent->release(); parent = 0; + engine = 0; +} + +void QDeclarativePropertyCache::destroy() +{ + Q_ASSERT(engine || constructor.IsEmpty()); + if (constructor.IsEmpty()) + delete this; + else + QDeclarativeEnginePrivate::deleteInEngineThread(engine, this); } // This is inherited from QDeclarativeCleanup, so it should only clear the things @@ -305,8 +315,7 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags) { Q_UNUSED(revision); - - qPersistentDispose(constructor); // Now invalid + Q_ASSERT(constructor.IsEmpty()); // We should not be appending to an in-use property cache bool dynamicMetaObject = isDynamicMetaObject(metaObject); diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h index 33565c4..fefcf7f 100644 --- a/src/declarative/qml/qdeclarativepropertycache_p.h +++ b/src/declarative/qml/qdeclarativepropertycache_p.h @@ -198,7 +198,9 @@ public: static Data *property(QDeclarativeEngine *, QObject *, const QHashedV8String &, Data &); static bool isDynamicMetaObject(const QMetaObject *); + protected: + virtual void destroy(); virtual void clear(); private: diff --git a/src/declarative/qml/qdeclarativescript.cpp b/src/declarative/qml/qdeclarativescript.cpp index 55a6b2b..9e6e09e 100644 --- a/src/declarative/qml/qdeclarativescript.cpp +++ b/src/declarative/qml/qdeclarativescript.cpp @@ -307,7 +307,6 @@ bool QDeclarativeScript::Variant::asBoolean() const QString QDeclarativeScript::Variant::asString() const { if (t == String) { - // XXX aakenned return l->value.toString(); } else { return asWritten.toString(); @@ -379,7 +378,6 @@ QString QDeclarativeScript::Variant::asScript() const return escapedString(asString()); case Script: if (AST::IdentifierExpression *i = AST::cast(n)) { - // XXX aakenned return i->name.toString(); } else return asWritten.toString(); diff --git a/src/declarative/qml/qdeclarativetypeloader.cpp b/src/declarative/qml/qdeclarativetypeloader.cpp index 8bb40ce..ebda11f 100644 --- a/src/declarative/qml/qdeclarativetypeloader.cpp +++ b/src/declarative/qml/qdeclarativetypeloader.cpp @@ -42,24 +42,117 @@ #include "qdeclarativetypeloader_p.h" #include +#include +#include #include #include -#include #include -#include -#include #include #include +#include +#include +#include #include +#include +#include +#include #if defined (Q_OS_UNIX) #include #include #endif +// #define DATABLOB_DEBUG + +#ifdef DATABLOB_DEBUG + +#define ASSERT_MAINTHREAD() do { if(m_thread->isThisThread()) qFatal("QDeclarativeDataLoader: Caller not in main thread"); } while(false) +#define ASSERT_LOADTHREAD() do { if(!m_thread->isThisThread()) qFatal("QDeclarativeDataLoader: Caller not in load thread"); } while(false) +#define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QDeclarativeDataBlob: An API call was made outside a callback"); } while(false) + +#else + +#define ASSERT_MAINTHREAD() +#define ASSERT_LOADTHREAD() +#define ASSERT_CALLBACK() + +#endif + QT_BEGIN_NAMESPACE +// This is a lame object that we need to ensure that slots connected to +// QNetworkReply get called in the correct thread (the loader thread). +// As QDeclarativeDataLoader lives in the main thread, and we can't use +// Qt::DirectConnection connections from a QNetworkReply (because then +// sender() wont work), we need to insert this object in the middle. +class QDeclarativeDataLoaderNetworkReplyProxy : public QObject +{ + Q_OBJECT +public: + QDeclarativeDataLoaderNetworkReplyProxy(QDeclarativeDataLoader *l); + +public slots: + void finished(); + void downloadProgress(qint64, qint64); + +private: + QDeclarativeDataLoader *l; +}; + +class QDeclarativeDataLoaderThread : public QDeclarativeThread +{ + typedef QDeclarativeDataLoaderThread This; + +public: + QDeclarativeDataLoaderThread(QDeclarativeDataLoader *loader); + QNetworkAccessManager *networkAccessManager() const; + QDeclarativeDataLoaderNetworkReplyProxy *networkReplyProxy() const; + + void load(QDeclarativeDataBlob *b); + void loadAsync(QDeclarativeDataBlob *b); + void loadWithStaticData(QDeclarativeDataBlob *b, const QByteArray &); + void loadWithStaticDataAsync(QDeclarativeDataBlob *b, const QByteArray &); + void callCompleted(QDeclarativeDataBlob *b); + void callDownloadProgressChanged(QDeclarativeDataBlob *b, qreal p); + void initializeEngine(QDeclarativeExtensionInterface *, const char *); + +protected: + virtual void shutdownThread(); + +private: + void loadThread(QDeclarativeDataBlob *b); + void loadWithStaticDataThread(QDeclarativeDataBlob *b, const QByteArray &); + void callCompletedMain(QDeclarativeDataBlob *b); + void callDownloadProgressChangedMain(QDeclarativeDataBlob *b, qreal p); + void initializeEngineMain(QDeclarativeExtensionInterface *iface, const char *uri); + + QDeclarativeDataLoader *m_loader; + mutable QNetworkAccessManager *m_networkAccessManager; + mutable QDeclarativeDataLoaderNetworkReplyProxy *m_networkReplyProxy; +}; + + +QDeclarativeDataLoaderNetworkReplyProxy::QDeclarativeDataLoaderNetworkReplyProxy(QDeclarativeDataLoader *l) +: l(l) +{ +} + +void QDeclarativeDataLoaderNetworkReplyProxy::finished() +{ + Q_ASSERT(sender()); + Q_ASSERT(qobject_cast(sender())); + QNetworkReply *reply = static_cast(sender()); + l->networkReplyFinished(reply); +} + +void QDeclarativeDataLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + Q_ASSERT(sender()); + Q_ASSERT(qobject_cast(sender())); + QNetworkReply *reply = static_cast(sender()); + l->networkReplyProgress(reply, bytesReceived, bytesTotal); +} /* Returns the set of QML files in path (qmldir, *.qml, *.js). The caller @@ -182,8 +275,8 @@ This enum describes the type of the data blob. Create a new QDeclarativeDataBlob for \a url and of the provided \a type. */ QDeclarativeDataBlob::QDeclarativeDataBlob(const QUrl &url, Type type) -: m_type(type), m_status(Null), m_progress(0), m_url(url), m_finalUrl(url), m_manager(0), - m_redirectCount(0), m_inCallback(false), m_isDone(false) +: m_type(type), m_url(url), m_finalUrl(url), m_manager(0), m_redirectCount(0), + m_inCallback(false), m_isDone(false) { } @@ -208,7 +301,7 @@ Returns the blob's status. */ QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const { - return m_status; + return m_data.status(); } /*! @@ -216,7 +309,7 @@ Returns true if the status is Null. */ bool QDeclarativeDataBlob::isNull() const { - return m_status == Null; + return status() == Null; } /*! @@ -224,7 +317,7 @@ Returns true if the status is Loading. */ bool QDeclarativeDataBlob::isLoading() const { - return m_status == Loading; + return status() == Loading; } /*! @@ -232,7 +325,7 @@ Returns true if the status is WaitingForDependencies. */ bool QDeclarativeDataBlob::isWaiting() const { - return m_status == WaitingForDependencies; + return status() == WaitingForDependencies; } /*! @@ -240,7 +333,7 @@ Returns true if the status is Complete. */ bool QDeclarativeDataBlob::isComplete() const { - return m_status == Complete; + return status() == Complete; } /*! @@ -248,7 +341,7 @@ Returns true if the status is Error. */ bool QDeclarativeDataBlob::isError() const { - return m_status == Error; + return status() == Error; } /*! @@ -256,7 +349,8 @@ Returns true if the status is Complete or Error. */ bool QDeclarativeDataBlob::isCompleteOrError() const { - return isComplete() || isError(); + Status s = status(); + return s == Error || s == Complete; } /*! @@ -264,7 +358,9 @@ Returns the data download progress from 0 to 1. */ qreal QDeclarativeDataBlob::progress() const { - return m_progress; + quint8 p = m_data.progress(); + if (p == 0xFF) return 1.; + else return qreal(p) / qreal(0xFF); } /*! @@ -282,17 +378,23 @@ QUrl QDeclarativeDataBlob::url() const Returns the final url of the data. Initially this is the same as url(), but if a network redirect happens while fetching the data, this url is updated to reflect the new location. + +May only be called from the load thread, or after the blob isCompleteOrError(). */ QUrl QDeclarativeDataBlob::finalUrl() const { + Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread())); return m_finalUrl; } /*! Return the errors on this blob. + +May only be called from the load thread, or after the blob isCompleteOrError(). */ QList QDeclarativeDataBlob::errors() const { + Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread())); return m_errors; } @@ -300,11 +402,14 @@ QList QDeclarativeDataBlob::errors() const Mark this blob as having \a errors. All outstanding dependencies will be cancelled. Requests to add new dependencies -will be ignored. Entry into the Error state is irreversable, although you can change the -specific errors by additional calls to setError. +will be ignored. Entry into the Error state is irreversable. + +The setError() method may only be called from within a QDeclarativeDataBlob callback. */ void QDeclarativeDataBlob::setError(const QDeclarativeError &errors) { + ASSERT_CALLBACK(); + QList l; l << errors; setError(l); @@ -315,8 +420,13 @@ void QDeclarativeDataBlob::setError(const QDeclarativeError &errors) */ void QDeclarativeDataBlob::setError(const QList &errors) { - m_status = Error; - m_errors = errors; + ASSERT_CALLBACK(); + + Q_ASSERT(status() != Error); + Q_ASSERT(m_errors.isEmpty()); + + m_errors = errors; // Must be set before the m_data fence + m_data.setStatus(Error); cancelAllWaitingFor(); @@ -327,19 +437,25 @@ void QDeclarativeDataBlob::setError(const QList &errors) /*! Wait for \a blob to become complete or to error. If \a blob is already complete or in error, or this blob is already complete, this has no effect. + +The setError() method may only be called from within a QDeclarativeDataBlob callback. */ void QDeclarativeDataBlob::addDependency(QDeclarativeDataBlob *blob) { + ASSERT_CALLBACK(); + Q_ASSERT(status() != Null); if (!blob || blob->status() == Error || blob->status() == Complete || - status() == Error || status() == Complete || + status() == Error || status() == Complete || m_isDone || m_waitingFor.contains(blob)) return; blob->addref(); - m_status = WaitingForDependencies; + + m_data.setStatus(WaitingForDependencies); + m_waitingFor.append(blob); blob->m_waitingOnMe.append(this); } @@ -360,6 +476,9 @@ You can set an error in this method, but you cannot add new dependencies. Imple should use this callback to finalize processing of data. The default implementation does nothing. + +XXX Rename processData() or some such to avoid confusion between done() (processing thread) +and completed() (main thread) */ void QDeclarativeDataBlob::done() { @@ -451,22 +570,63 @@ void QDeclarativeDataBlob::allDependenciesDone() /*! Called when the download progress of this blob changes. \a progress goes from 0 to 1. + +This callback is only invoked if an asynchronous load for this blob is +made. An asynchronous load is one in which the Asynchronous mode is +specified explicitly, or one that is implicitly delayed due to a network +operation. + +The default implementation does nothing. */ void QDeclarativeDataBlob::downloadProgressChanged(qreal progress) { Q_UNUSED(progress); } +/*! +Invoked on the main thread sometime after done() was called on the load thread. + +You cannot modify the blobs state at all in this callback and cannot depend on the +order or timeliness of these callbacks. Implementors should use this callback to notify +dependencies on the main thread that the blob is done and not a lot else. + +This callback is only invoked if an asynchronous load for this blob is +made. An asynchronous load is one in which the Asynchronous mode is +specified explicitly, or one that is implicitly delayed due to a network +operation. + +The default implementation does nothing. +*/ +void QDeclarativeDataBlob::completed() +{ +} + + void QDeclarativeDataBlob::tryDone() { if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) { - if (status() != Error) - m_status = Complete; - m_isDone = true; addref(); + +#ifdef DATABLOB_DEBUG + qWarning("QDeclarativeDataBlob::done() %s", qPrintable(url().toString())); +#endif done(); + + if (status() != Error) + m_data.setStatus(Complete); + notifyAllWaitingOnMe(); + + // Locking is not required here, as anyone expecting callbacks must + // already be protected against the blob being completed (as set above); + if (m_data.isAsync()) { +#ifdef DATABLOB_DEBUG + qWarning("QDeclarativeDataBlob: Dispatching completed"); +#endif + m_manager->m_thread->callCompleted(this); + } + release(); } } @@ -519,6 +679,170 @@ void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob) tryDone(); } +#define TD_STATUS_MASK 0x0000FFFF +#define TD_STATUS_SHIFT 0 +#define TD_PROGRESS_MASK 0x00FF0000 +#define TD_PROGRESS_SHIFT 16 +#define TD_ASYNC_MASK 0x80000000 + +QDeclarativeDataBlob::ThreadData::ThreadData() +: _p(0) +{ +} + +QDeclarativeDataBlob::Status QDeclarativeDataBlob::ThreadData::status() const +{ + return QDeclarativeDataBlob::Status((_p & TD_STATUS_MASK) >> TD_STATUS_SHIFT); +} + +void QDeclarativeDataBlob::ThreadData::setStatus(QDeclarativeDataBlob::Status status) +{ + while (true) { + int d = _p; + int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } +} + +bool QDeclarativeDataBlob::ThreadData::isAsync() const +{ + return _p & TD_ASYNC_MASK; +} + +void QDeclarativeDataBlob::ThreadData::setIsAsync(bool v) +{ + while (true) { + int d = _p; + int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } +} + +quint8 QDeclarativeDataBlob::ThreadData::progress() const +{ + return quint8((_p & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT); +} + +void QDeclarativeDataBlob::ThreadData::setProgress(quint8 v) +{ + while (true) { + int d = _p; + int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } +} + +QDeclarativeDataLoaderThread::QDeclarativeDataLoaderThread(QDeclarativeDataLoader *loader) +: m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0) +{ +} + +QNetworkAccessManager *QDeclarativeDataLoaderThread::networkAccessManager() const +{ + Q_ASSERT(isThisThread()); + if (!m_networkAccessManager) { + m_networkAccessManager = QDeclarativeEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(0); + m_networkReplyProxy = new QDeclarativeDataLoaderNetworkReplyProxy(m_loader); + } + + return m_networkAccessManager; +} + +QDeclarativeDataLoaderNetworkReplyProxy *QDeclarativeDataLoaderThread::networkReplyProxy() const +{ + Q_ASSERT(isThisThread()); + Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first + return m_networkReplyProxy; +} + +void QDeclarativeDataLoaderThread::load(QDeclarativeDataBlob *b) +{ + b->addref(); + callMethodInThread(&This::loadThread, b); +} + +void QDeclarativeDataLoaderThread::loadAsync(QDeclarativeDataBlob *b) +{ + b->addref(); + postMethodToThread(&This::loadThread, b); +} + +void QDeclarativeDataLoaderThread::loadWithStaticData(QDeclarativeDataBlob *b, const QByteArray &d) +{ + b->addref(); + callMethodInThread(&This::loadWithStaticDataThread, b, d); +} + +void QDeclarativeDataLoaderThread::loadWithStaticDataAsync(QDeclarativeDataBlob *b, const QByteArray &d) +{ + b->addref(); + postMethodToThread(&This::loadWithStaticDataThread, b, d); +} + +void QDeclarativeDataLoaderThread::callCompleted(QDeclarativeDataBlob *b) +{ + b->addref(); + postMethodToMain(&This::callCompletedMain, b); +} + +void QDeclarativeDataLoaderThread::callDownloadProgressChanged(QDeclarativeDataBlob *b, qreal p) +{ + b->addref(); + postMethodToMain(&This::callDownloadProgressChangedMain, b, p); +} + +void QDeclarativeDataLoaderThread::initializeEngine(QDeclarativeExtensionInterface *iface, + const char *uri) +{ + callMethodInMain(&This::initializeEngineMain, iface, uri); +} + +void QDeclarativeDataLoaderThread::shutdownThread() +{ + delete m_networkAccessManager; + m_networkAccessManager = 0; + delete m_networkReplyProxy; + m_networkReplyProxy = 0; +} + +void QDeclarativeDataLoaderThread::loadThread(QDeclarativeDataBlob *b) +{ + m_loader->loadThread(b); + b->release(); +} + +void QDeclarativeDataLoaderThread::loadWithStaticDataThread(QDeclarativeDataBlob *b, const QByteArray &d) +{ + m_loader->loadWithStaticDataThread(b, d); + b->release(); +} + +void QDeclarativeDataLoaderThread::callCompletedMain(QDeclarativeDataBlob *b) +{ +#ifdef DATABLOB_DEBUG + qWarning("QDeclarativeDataLoaderThread: %s completed() callback", qPrintable(b->url().toString())); +#endif + b->completed(); + b->release(); +} + +void QDeclarativeDataLoaderThread::callDownloadProgressChangedMain(QDeclarativeDataBlob *b, qreal p) +{ +#ifdef DATABLOB_DEBUG + qWarning("QDeclarativeDataLoaderThread: %s downloadProgressChanged(%f) callback", + qPrintable(b->url().toString()), p); +#endif + b->downloadProgressChanged(p); + b->release(); +} + +void QDeclarativeDataLoaderThread::initializeEngineMain(QDeclarativeExtensionInterface *iface, + const char *uri) +{ + Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread()); + iface->initializeEngine(m_loader->engine(), uri); +} + /*! \class QDeclarativeDataLoader \brief The QDeclarativeDataLoader class abstracts loading files and their dependencies over the network. @@ -553,7 +877,7 @@ Thus QDeclarativeDataBlob::done() will always eventually be called, even if the Create a new QDeclarativeDataLoader for \a engine. */ QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine) -: m_engine(engine) +: m_engine(engine), m_thread(new QDeclarativeDataLoaderThread(this)) { } @@ -562,17 +886,105 @@ QDeclarativeDataLoader::~QDeclarativeDataLoader() { for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) (*iter)->release(); + + m_thread->shutdown(); + delete m_thread; +} + +void QDeclarativeDataLoader::lock() +{ + m_thread->lock(); +} + +void QDeclarativeDataLoader::unlock() +{ + m_thread->unlock(); } /*! Load the provided \a blob from the network or filesystem. + +The loader must be locked. +*/ +void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob, Mode mode) +{ +#ifdef DATABLOB_DEBUG + qWarning("QDeclarativeDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()), + m_thread->isThisThread()?"Compile":"Engine"); +#endif + + Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null); + Q_ASSERT(blob->m_manager == 0); + + blob->m_data.setStatus(QDeclarativeDataBlob::Loading); + blob->m_manager = this; + + if (m_thread->isThisThread()) { + unlock(); + loadThread(blob); + lock(); + } else if (mode == PreferSynchronous) { + unlock(); + m_thread->load(blob); + lock(); + if (!blob->isCompleteOrError()) + blob->m_data.setIsAsync(true); + } else { + Q_ASSERT(mode == Asynchronous); + blob->m_data.setIsAsync(true); + unlock(); + m_thread->loadAsync(blob); + lock(); + } +} + +/*! +Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case. + +The loader must be locked. */ -void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob) +void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data, Mode mode) { +#ifdef DATABLOB_DEBUG + qWarning("QDeclarativeDataLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()), + m_thread->isThisThread()?"Compile":"Engine"); +#endif + Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null); Q_ASSERT(blob->m_manager == 0); + + blob->m_data.setStatus(QDeclarativeDataBlob::Loading); + blob->m_manager = this; + + if (m_thread->isThisThread()) { + unlock(); + loadWithStaticDataThread(blob, data); + lock(); + } else if (mode == PreferSynchronous) { + unlock(); + m_thread->loadWithStaticData(blob, data); + lock(); + if (!blob->isCompleteOrError()) + blob->m_data.setIsAsync(true); + } else { + Q_ASSERT(mode == Asynchronous); + blob->m_data.setIsAsync(true); + unlock(); + m_thread->loadWithStaticDataAsync(blob, data); + lock(); + } +} - blob->m_status = QDeclarativeDataBlob::Loading; +void QDeclarativeDataLoader::loadWithStaticDataThread(QDeclarativeDataBlob *blob, const QByteArray &data) +{ + ASSERT_LOADTHREAD(); + + setData(blob, data); +} + +void QDeclarativeDataLoader::loadThread(QDeclarativeDataBlob *blob) +{ + ASSERT_LOADTHREAD(); if (blob->m_url.isEmpty()) { QDeclarativeError error; @@ -595,8 +1007,9 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob) if (file.open(QFile::ReadOnly)) { QByteArray data = file.readAll(); - blob->m_progress = 1.; - blob->downloadProgressChanged(1.); + blob->m_data.setProgress(0xFF); + if (blob->m_data.isAsync()) + m_thread->callDownloadProgressChanged(blob, 1.); setData(blob, data); } else { @@ -605,12 +1018,12 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob) } else { - blob->m_manager = this; - QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(blob->m_url)); + QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url)); + QObject *nrp = m_thread->networkReplyProxy(); QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), - this, SLOT(networkReplyProgress(qint64,qint64))); + nrp, SLOT(downloadProgress(qint64,qint64))); QObject::connect(reply, SIGNAL(finished()), - this, SLOT(networkReplyFinished())); + nrp, SLOT(finished())); m_networkReplies.insert(reply, blob); blob->addref(); @@ -619,9 +1032,10 @@ void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob) #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16 -void QDeclarativeDataLoader::networkReplyFinished() +void QDeclarativeDataLoader::networkReplyFinished(QNetworkReply *reply) { - QNetworkReply *reply = static_cast(sender()); + Q_ASSERT(m_thread->isThisThread()); + reply->deleteLater(); QDeclarativeDataBlob *blob = m_networkReplies.take(reply); @@ -636,8 +1050,9 @@ void QDeclarativeDataLoader::networkReplyFinished() QUrl url = reply->url().resolved(redirect.toUrl()); blob->m_finalUrl = url; - QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(url)); - QObject::connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url)); + QObject *nrp = m_thread->networkReplyProxy(); + QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished())); m_networkReplies.insert(reply, blob); return; } @@ -653,40 +1068,49 @@ void QDeclarativeDataLoader::networkReplyFinished() blob->release(); } -void QDeclarativeDataLoader::networkReplyProgress(qint64 bytesReceived, qint64 bytesTotal) +void QDeclarativeDataLoader::networkReplyProgress(QNetworkReply *reply, + qint64 bytesReceived, qint64 bytesTotal) { - QNetworkReply *reply = static_cast(sender()); + Q_ASSERT(m_thread->isThisThread()); + QDeclarativeDataBlob *blob = m_networkReplies.value(reply); Q_ASSERT(blob); if (bytesTotal != 0) { - blob->m_progress = bytesReceived / bytesTotal; - blob->downloadProgressChanged(blob->m_progress); + quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal)); + blob->m_data.setProgress(progress); + if (blob->m_data.isAsync()) + m_thread->callDownloadProgressChanged(blob, blob->m_data.progress()); } } /*! -Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case. +Return the QDeclarativeEngine associated with this loader */ -void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data) +QDeclarativeEngine *QDeclarativeDataLoader::engine() const { - Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null); - Q_ASSERT(blob->m_manager == 0); - - blob->m_status = QDeclarativeDataBlob::Loading; - - setData(blob, data); + return m_engine; } /*! -Return the QDeclarativeEngine associated with this loader +Call the initializeEngine() method on \a iface. Used by QDeclarativeImportDatabase to ensure it +gets called in the correct thread. */ -QDeclarativeEngine *QDeclarativeDataLoader::engine() const +void QDeclarativeDataLoader::initializeEngine(QDeclarativeExtensionInterface *iface, + const char *uri) { - return m_engine; + Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread()); + + if (m_thread->isThisThread()) { + m_thread->initializeEngine(iface, uri); + } else { + Q_ASSERT(engine()->thread() == QThread::currentThread()); + iface->initializeEngine(engine(), uri); + } } + void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data) { blob->m_inCallback = true; @@ -696,8 +1120,8 @@ void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArra if (!blob->isError() && !blob->isWaiting()) blob->allDependenciesDone(); - if (blob->status() != QDeclarativeDataBlob::Error) - blob->m_status = QDeclarativeDataBlob::WaitingForDependencies; + if (blob->status() != QDeclarativeDataBlob::Error) + blob->m_data.setStatus(QDeclarativeDataBlob::WaitingForDependencies); blob->m_inCallback = false; @@ -741,6 +1165,8 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url) (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url)))); + lock(); + QDeclarativeTypeData *typeData = m_typeCache.value(url); if (!typeData) { @@ -750,6 +1176,9 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url) } typeData->addref(); + + unlock(); + return typeData; } @@ -761,8 +1190,13 @@ The specified \a options control how the loader handles type data. */ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options) { + lock(); + QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this); QDeclarativeDataLoader::loadWithStaticData(typeData, data); + + unlock(); + return typeData; } @@ -775,6 +1209,8 @@ QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url) (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url)))); + lock(); + QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url); if (!scriptBlob) { @@ -783,6 +1219,10 @@ QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url) QDeclarativeDataLoader::load(scriptBlob); } + scriptBlob->addref(); + + unlock(); + return scriptBlob; } @@ -795,6 +1235,8 @@ QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url) (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url)))); + lock(); + QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url); if (!qmldirData) { @@ -804,6 +1246,9 @@ QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url) } qmldirData->addref(); + + unlock(); + return qmldirData; } @@ -914,6 +1359,7 @@ const QDeclarativeDirParser *QDeclarativeTypeLoader::qmlDirParser(const QString return qmldirParser; } + /*! Clears cached information about loaded files, including any type data, scripts and qmldir information. @@ -1004,8 +1450,6 @@ void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback) void QDeclarativeTypeData::done() { - addref(); - // Check all script dependencies for errors for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { const ScriptReference &script = m_scripts.at(ii); @@ -1046,14 +1490,15 @@ void QDeclarativeTypeData::done() if (!(m_options & QDeclarativeTypeLoader::PreserveParser)) scriptParser.clear(); +} +void QDeclarativeTypeData::completed() +{ // Notify callbacks while (!m_callbacks.isEmpty()) { TypeDataCallback *callback = m_callbacks.takeFirst(); callback->typeDataReady(this); } - - release(); } void QDeclarativeTypeData::dataReceived(const QByteArray &data) @@ -1082,7 +1527,6 @@ void QDeclarativeTypeData::dataReceived(const QByteArray &data) ref.location = import.location.start; ref.qualifier = import.qualifier; ref.script = blob; - blob->addref(); m_scripts << ref; } @@ -1270,15 +1714,13 @@ QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url) return 0; } -QDeclarativeScriptData::QDeclarativeScriptData(QDeclarativeEngine *engine) -: QDeclarativeCleanup(engine), importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None), - m_loaded(false) +QDeclarativeScriptData::QDeclarativeScriptData() +: importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None), m_loaded(false) { } QDeclarativeScriptData::~QDeclarativeScriptData() { - clear(); } void QDeclarativeScriptData::clear() @@ -1294,6 +1736,9 @@ void QDeclarativeScriptData::clear() qPersistentDispose(m_program); qPersistentDispose(m_value); + + // An addref() was made when the QDeclarativeCleanup was added to the engine. + release(); } QDeclarativeScriptBlob::QDeclarativeScriptBlob(const QUrl &url, QDeclarativeTypeLoader *loader) @@ -1361,7 +1806,6 @@ void QDeclarativeScriptBlob::dataReceived(const QByteArray &data) ref.location = import.location.start; ref.qualifier = import.qualifier; ref.script = blob; - blob->addref(); m_scripts << ref; } else { Q_ASSERT(import.type == QDeclarativeScript::Import::Library); @@ -1408,9 +1852,9 @@ void QDeclarativeScriptBlob::done() return; QDeclarativeEngine *engine = typeLoader()->engine(); - m_scriptData = new QDeclarativeScriptData(engine); + m_scriptData = new QDeclarativeScriptData(); m_scriptData->url = finalUrl(); - m_scriptData->importCache = new QDeclarativeTypeNameCache(engine); + m_scriptData->importCache = new QDeclarativeTypeNameCache(); for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) { const ScriptReference &script = m_scripts.at(ii); @@ -1422,13 +1866,7 @@ void QDeclarativeScriptBlob::done() m_imports.populateCache(m_scriptData->importCache, engine); m_scriptData->pragmas = m_pragmas; - - // XXX TODO: Handle errors that occur duing the script compile - QV8Engine *v8engine = QDeclarativeEnginePrivate::get(engine)->v8engine(); - v8::HandleScope handle_scope; - v8::Context::Scope scope(v8engine->context()); - v8::Local program = v8engine->qmlModeCompile(m_source, finalUrl().toString(), 1); - m_scriptData->m_program = qPersistentNew(program); + m_scriptData->m_programSource = m_source; } QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url) @@ -1451,3 +1889,4 @@ void QDeclarativeQmldirData::dataReceived(const QByteArray &data) QT_END_NAMESPACE +#include "qdeclarativetypeloader.moc" diff --git a/src/declarative/qml/qdeclarativetypeloader_p.h b/src/declarative/qml/qdeclarativetypeloader_p.h index af1a2f7..1eaeebd 100644 --- a/src/declarative/qml/qdeclarativetypeloader_p.h +++ b/src/declarative/qml/qdeclarativetypeloader_p.h @@ -54,17 +54,17 @@ // #include +#include #include -#include #include #include -#include -#include -#include -#include -#include "private/qhashedstring_p.h" #include +#include +#include +#include +#include +#include QT_BEGIN_NAMESPACE @@ -76,6 +76,7 @@ class QDeclarativeCompiledData; class QDeclarativeComponentPrivate; class QDeclarativeTypeData; class QDeclarativeDataLoader; +class QDeclarativeExtensionInterface; // Exported for QtQuick1 class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDataBlob : public QDeclarativeRefCount @@ -115,33 +116,53 @@ public: QList errors() const; +protected: + // Can be called from within callbacks void setError(const QDeclarativeError &); void setError(const QList &errors); - void addDependency(QDeclarativeDataBlob *); -protected: + // Callbacks made in load thread virtual void dataReceived(const QByteArray &) = 0; - virtual void done(); virtual void networkError(QNetworkReply::NetworkError); - virtual void dependencyError(QDeclarativeDataBlob *); virtual void dependencyComplete(QDeclarativeDataBlob *); virtual void allDependenciesDone(); - - virtual void downloadProgressChanged(qreal); + // Callbacks made in main thread + virtual void downloadProgressChanged(qreal); + virtual void completed(); private: friend class QDeclarativeDataLoader; + friend class QDeclarativeDataLoaderThread; + void tryDone(); void cancelAllWaitingFor(); void notifyAllWaitingOnMe(); void notifyComplete(QDeclarativeDataBlob *); + struct ThreadData { + inline ThreadData(); + inline QDeclarativeDataBlob::Status status() const; + inline void setStatus(QDeclarativeDataBlob::Status); + inline bool isAsync() const; + inline void setIsAsync(bool); + inline quint8 progress() const; + inline void setProgress(quint8); + + private: + QAtomicInt _p; + }; + ThreadData m_data; + + // m_errors should *always* be written before the status is set to Error. + // We use the status change as a memory fence around m_errors so that locking + // isn't required. Once the status is set to Error (or Complete), m_errors + // cannot be changed. + QList m_errors; + Type m_type; - Status m_status; - qreal m_progress; QUrl m_url; QUrl m_finalUrl; @@ -157,39 +178,52 @@ private: int m_redirectCount:30; bool m_inCallback:1; bool m_isDone:1; - - QList m_errors; }; +class QDeclarativeDataLoaderThread; // Exported for QtQuick1 -class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDataLoader : public QObject +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeDataLoader { - Q_OBJECT public: QDeclarativeDataLoader(QDeclarativeEngine *); ~QDeclarativeDataLoader(); - void load(QDeclarativeDataBlob *); - void loadWithStaticData(QDeclarativeDataBlob *, const QByteArray &); + void lock(); + void unlock(); - QDeclarativeEngine *engine() const; + bool isConcurrent() const { return true; } + + enum Mode { PreferSynchronous, Asynchronous }; -private slots: - void networkReplyFinished(); - void networkReplyProgress(qint64,qint64); + void load(QDeclarativeDataBlob *, Mode = PreferSynchronous); + void loadWithStaticData(QDeclarativeDataBlob *, const QByteArray &, Mode = PreferSynchronous); + + QDeclarativeEngine *engine() const; + void initializeEngine(QDeclarativeExtensionInterface *, const char *); private: + friend class QDeclarativeDataBlob; + friend class QDeclarativeDataLoaderThread; + friend class QDeclarativeDataLoaderNetworkReplyProxy; + + void loadThread(QDeclarativeDataBlob *); + void loadWithStaticDataThread(QDeclarativeDataBlob *, const QByteArray &); + void networkReplyFinished(QNetworkReply *); + void networkReplyProgress(QNetworkReply *, qint64, qint64); + + typedef QHash NetworkReplies; + void setData(QDeclarativeDataBlob *, const QByteArray &); QDeclarativeEngine *m_engine; - typedef QHash NetworkReplies; + QDeclarativeDataLoaderThread *m_thread; NetworkReplies m_networkReplies; }; // Exported for QtQuick1 class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeTypeLoader : public QDeclarativeDataLoader { - Q_OBJECT + Q_DECLARE_TR_FUNCTIONS(QDeclarativeTypeLoader) public: QDeclarativeTypeLoader(QDeclarativeEngine *); ~QDeclarativeTypeLoader(); @@ -210,7 +244,6 @@ public: QString absoluteFilePath(const QString &path); bool directoryExists(const QString &path); const QDeclarativeDirParser *qmlDirParser(const QString &absoluteFilePath); - private: typedef QHash TypeCache; typedef QHash ScriptCache; @@ -275,6 +308,7 @@ public: protected: virtual void done(); + virtual void completed(); virtual void dataReceived(const QByteArray &); virtual void allDependenciesDone(); virtual void downloadProgressChanged(qreal); @@ -303,10 +337,18 @@ private: QDeclarativeTypeLoader *m_typeLoader; }; -class Q_AUTOTEST_EXPORT QDeclarativeScriptData : public QDeclarativeRefCount, public QDeclarativeCleanup +// QDeclarativeScriptData instances are created, uninitialized, by the loader in the +// load thread. The first time they are used by the VME, they are initialized which +// creates their v8 objects and they are referenced and added to the engine's cleanup +// list. During QDeclarativeCleanup::clear() all v8 resources are destroyed, and the +// reference that was created is released but final deletion only occurs once all the +// references as released. This is all intended to ensure that the v8 resources are +// only created and destroyed in the main thread :) +class Q_AUTOTEST_EXPORT QDeclarativeScriptData : public QDeclarativeCleanup, + public QDeclarativeRefCount { public: - QDeclarativeScriptData(QDeclarativeEngine *); + QDeclarativeScriptData(); ~QDeclarativeScriptData(); QUrl url; @@ -314,6 +356,9 @@ public: QList scripts; QDeclarativeScript::Object::ScriptBlock::Pragmas pragmas; + bool isInitialized() const { return hasEngine(); } + void initialize(QDeclarativeEngine *); + protected: virtual void clear(); // From QDeclarativeCleanup @@ -322,10 +367,9 @@ private: friend class QDeclarativeScriptBlob; bool m_loaded; + QString m_programSource; v8::Persistent m_program; v8::Persistent m_value; -// QScriptProgram m_program; -// QScriptValue m_value; }; class Q_AUTOTEST_EXPORT QDeclarativeScriptBlob : public QDeclarativeDataBlob diff --git a/src/declarative/qml/qdeclarativetypenamecache.cpp b/src/declarative/qml/qdeclarativetypenamecache.cpp index b7e4259..31ad48a 100644 --- a/src/declarative/qml/qdeclarativetypenamecache.cpp +++ b/src/declarative/qml/qdeclarativetypenamecache.cpp @@ -45,21 +45,12 @@ QT_BEGIN_NAMESPACE -QDeclarativeTypeNameCache::QDeclarativeTypeNameCache(QDeclarativeEngine *e) -: QDeclarativeCleanup(e), engine(e) +QDeclarativeTypeNameCache::QDeclarativeTypeNameCache() { } QDeclarativeTypeNameCache::~QDeclarativeTypeNameCache() { - clear(); -} - -void QDeclarativeTypeNameCache::clear() -{ - m_namedImports.clear(); - m_anonymousImports.clear(); - engine = 0; } void QDeclarativeTypeNameCache::add(const QHashedString &name, int importedScriptIndex) diff --git a/src/declarative/qml/qdeclarativetypenamecache_p.h b/src/declarative/qml/qdeclarativetypenamecache_p.h index a80bb64..219d07a 100644 --- a/src/declarative/qml/qdeclarativetypenamecache_p.h +++ b/src/declarative/qml/qdeclarativetypenamecache_p.h @@ -65,10 +65,10 @@ QT_BEGIN_NAMESPACE class QDeclarativeType; class QDeclarativeEngine; -class QDeclarativeTypeNameCache : public QDeclarativeRefCount, public QDeclarativeCleanup +class QDeclarativeTypeNameCache : public QDeclarativeRefCount { public: - QDeclarativeTypeNameCache(QDeclarativeEngine *); + QDeclarativeTypeNameCache(); virtual ~QDeclarativeTypeNameCache(); inline bool isEmpty() const; @@ -94,9 +94,6 @@ public: Result query(const QHashedV8String &, const void *importNamespace); QDeclarativeMetaType::ModuleApiInstance *moduleApi(const void *importNamespace); -protected: - virtual void clear(); - private: friend class QDeclarativeImports; diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index 4ed7cf4..d3748a4 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -61,6 +61,7 @@ #include "private/qdeclarativev4bindings_p.h" #include "private/qv8bindings_p.h" #include "private/qdeclarativeglobal_p.h" +#include "private/qfinitestack_p.h" #include "qdeclarativescriptstring.h" #include "qdeclarativescriptstring_p.h" @@ -78,78 +79,107 @@ QT_BEGIN_NAMESPACE -// A simple stack wrapper around QVarLengthArray -template -class QDeclarativeVMEStack : private QVarLengthArray -{ -private: - typedef QVarLengthArray VLA; - int _index; - -public: - inline QDeclarativeVMEStack(); - inline bool isEmpty() const; - inline const T &top() const; - inline void push(const T &o); - inline T pop(); - inline int count() const; - inline const T &at(int index) const; -}; - -// We do this so we can forward declare the type in the qdeclarativevme_p.h header -class QDeclarativeVMEObjectStack : public QDeclarativeVMEStack {}; - -QDeclarativeVME::QDeclarativeVME() -{ -} +using namespace QDeclarativeVMETypes; #define VME_EXCEPTION(desc, line) \ { \ QDeclarativeError error; \ error.setDescription(desc.trimmed()); \ error.setLine(line); \ - error.setUrl(comp->url); \ - vmeErrors << error; \ + error.setUrl(COMP->url); \ + *errors << error; \ goto exceptionExit; \ } -struct ListInstance +void QDeclarativeVME::init(QDeclarativeContextData *ctxt, QDeclarativeCompiledData *comp, int start) { - ListInstance() - : type(0) {} - ListInstance(int t) - : type(t) {} + Q_ASSERT(ctxt); + Q_ASSERT(comp); - int type; - QDeclarativeListProperty qListProperty; -}; + if (start == -1) start = 0; -Q_DECLARE_TYPEINFO(ListInstance, Q_PRIMITIVE_TYPE | Q_MOVABLE_TYPE); + State initState; + initState.context = ctxt; + initState.compiledData = comp; + initState.instructionStream = comp->bytecode.constData() + start; + states.push(initState); -QObject *QDeclarativeVME::run(QDeclarativeContextData *ctxt, QDeclarativeCompiledData *comp, - int start, const QBitField &bindingSkipList) -{ - QDeclarativeVMEObjectStack stack; + typedef QDeclarativeInstruction I; + I *i = (I *)initState.instructionStream; - if (start == -1) start = 0; + Q_ASSERT(comp->instructionType(i) == I::Init); + + objects.allocate(i->init.objectStackSize); + lists.allocate(i->init.listStackSize); + bindValues.allocate(i->init.bindingsSize); + parserStatus.allocate(i->init.parserStatusSize); - return run(stack, ctxt, comp, start, bindingSkipList); + rootContext = 0; + engine = ctxt->engine; + bindValuesCount = 0; + parserStatusCount = 0; } -void QDeclarativeVME::runDeferred(QObject *object) +bool QDeclarativeVME::initDeferred(QObject *object) { QDeclarativeData *data = QDeclarativeData::get(object); if (!data || !data->context || !data->deferredComponent) - return; + return false; QDeclarativeContextData *ctxt = data->context; QDeclarativeCompiledData *comp = data->deferredComponent; int start = data->deferredIdx; - QDeclarativeVMEObjectStack stack; - stack.push(object); - run(stack, ctxt, comp, start, QBitField()); + State initState; + initState.context = ctxt; + initState.compiledData = comp; + initState.instructionStream = comp->bytecode.constData() + start; + states.push(initState); + + typedef QDeclarativeInstruction I; + I *i = (I *)initState.instructionStream; + + Q_ASSERT(comp->instructionType(i) == I::DeferInit); + + objects.allocate(i->deferInit.objectStackSize); + lists.allocate(i->deferInit.listStackSize); + bindValues.allocate(i->deferInit.bindingsSize); + parserStatus.allocate(i->deferInit.parserStatusSize); + + objects.push(object); + + rootContext = 0; + engine = ctxt->engine; + bindValuesCount = 0; + parserStatusCount = 0; + + return true; +} + +namespace { +struct ActiveVMERestorer +{ + ActiveVMERestorer(QDeclarativeVME *me, QDeclarativeEnginePrivate *ep) + : ep(ep), oldVME(ep->activeVME) { ep->activeVME = me; } + ~ActiveVMERestorer() { ep->activeVME = oldVME; } + + QDeclarativeEnginePrivate *ep; + QDeclarativeVME *oldVME; +}; +} + +QObject *QDeclarativeVME::execute(QList *errors, const Interrupt &interrupt) +{ + Q_ASSERT(states.count() >= 1); + + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(states.at(0).context->engine); + + ActiveVMERestorer restore(this, ep); + + QObject *rv = run(errors, interrupt); + + return rv; } inline bool fastHasBinding(QObject *o, int index) @@ -166,9 +196,12 @@ static void removeBindingOnProperty(QObject *o, int index) if (binding) binding->destroy(); } +// XXX we probably need some form of "work count" here to prevent us checking this +// for every instruction. #define QML_BEGIN_INSTR_COMMON(I) { \ + if (interrupt.shouldInterrupt()) return 0; \ const QDeclarativeInstructionMeta<(int)QDeclarativeInstruction::I>::DataType &instr = QDeclarativeInstructionMeta<(int)QDeclarativeInstruction::I>::data(*genericInstr); \ - instructionStream += QDeclarativeInstructionMeta<(int)QDeclarativeInstruction::I>::Size; \ + INSTRUCTIONSTREAM += QDeclarativeInstructionMeta<(int)QDeclarativeInstruction::I>::Size; \ Q_UNUSED(instr); #ifdef QML_THREADED_VME_INTERPRETER @@ -176,12 +209,12 @@ static void removeBindingOnProperty(QObject *o, int index) QML_BEGIN_INSTR_COMMON(I) # define QML_NEXT_INSTR(I) { \ - genericInstr = reinterpret_cast(instructionStream); \ + genericInstr = reinterpret_cast(INSTRUCTIONSTREAM); \ goto *genericInstr->common.code; \ } # define QML_END_INSTR(I) } \ - genericInstr = reinterpret_cast(instructionStream); \ + genericInstr = reinterpret_cast(INSTRUCTIONSTREAM); \ goto *genericInstr->common.code; #else @@ -195,10 +228,8 @@ static void removeBindingOnProperty(QObject *o, int index) #define CLEAN_PROPERTY(o, index) if (fastHasBinding(o, index)) removeBindingOnProperty(o, index) -QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, - QDeclarativeContextData *ctxt, - QDeclarativeCompiledData *comp, - int start, const QBitField &bindingSkipList +QObject *QDeclarativeVME::run(QList *errors, + const Interrupt &interrupt #ifdef QML_THREADED_VME_INTERPRETER , void ***storeJumpTable #endif @@ -215,121 +246,180 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, return 0; } #endif - Q_ASSERT(comp); - Q_ASSERT(ctxt); - const QList &types = comp->types; - const QList &primitives = comp->primitives; - const QList &datas = comp->datas; - const QList &propertyCaches = comp->propertyCaches; - const QList &scripts = comp->scripts; - const QList &urls = comp->urls; - - QDeclarativeEnginePrivate::SimpleList bindValues; - QDeclarativeEnginePrivate::SimpleList parserStatus; - - QDeclarativeVMEStack qliststack; + Q_ASSERT(errors->isEmpty()); + Q_ASSERT(states.count() >= 1); - vmeErrors.clear(); - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); + QDeclarativeEngine *engine = states.at(0).context->engine; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - int status = -1; //for dbus + int status = -1; // needed for dbus QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::RemoveBindingOnAliasWrite; - const char *instructionStream = comp->bytecode.constData() + start; +#define COMP states.top().compiledData +#define CTXT states.top().context +#define INSTRUCTIONSTREAM states.top().instructionStream +#define BINDINGSKIPLIST states.top().bindingSkipList + +#define TYPES COMP->types +#define PRIMITIVES COMP->primitives +#define DATAS COMP->datas +#define PROPERTYCACHES COMP->propertyCaches +#define SCRIPTS COMP->scripts +#define URLS COMP->urls #ifdef QML_THREADED_VME_INTERPRETER - const QDeclarativeInstruction *genericInstr = reinterpret_cast(instructionStream); + const QDeclarativeInstruction *genericInstr = reinterpret_cast(INSTRUCTIONSTREAM); goto *genericInstr->common.code; #else for (;;) { - const QDeclarativeInstruction *genericInstr = reinterpret_cast(instructionStream); + const QDeclarativeInstruction *genericInstr = reinterpret_cast(INSTRUCTIONSTREAM); switch (genericInstr->common.instructionType) { #endif QML_BEGIN_INSTR(Init) - if (instr.bindingsSize) - bindValues = QDeclarativeEnginePrivate::SimpleList(instr.bindingsSize); - if (instr.parserStatusSize) - parserStatus = QDeclarativeEnginePrivate::SimpleList(instr.parserStatusSize); + // Ensure that the compiled data has been initialized + if (!COMP->isInitialized()) COMP->initialize(engine); + + QDeclarativeContextData *parentCtxt = CTXT; + CTXT = new QDeclarativeContextData; + CTXT->isInternal = true; + CTXT->url = COMP->url; + CTXT->imports = COMP->importCache; + CTXT->imports->addref(); + CTXT->setParent(parentCtxt); if (instr.contextCache != -1) - ctxt->setIdPropertyData(comp->contextCaches.at(instr.contextCache)); + CTXT->setIdPropertyData(COMP->contextCaches.at(instr.contextCache)); if (instr.compiledBinding != -1) { - const char *v4data = datas.at(instr.compiledBinding).constData(); - ctxt->v4bindings = new QDeclarativeV4Bindings(v4data, ctxt, comp); + const char *v4data = DATAS.at(instr.compiledBinding).constData(); + CTXT->v4bindings = new QDeclarativeV4Bindings(v4data, CTXT, COMP); + } + if (states.count() == 1) { + rootContext = CTXT; + rootContext->activeVME = this; } QML_END_INSTR(Init) + QML_BEGIN_INSTR(DeferInit) + QML_END_INSTR(DeferInit) + QML_BEGIN_INSTR(Done) - goto normalExit; + states.pop(); + + if (states.isEmpty()) + goto normalExit; QML_END_INSTR(Done) - QML_BEGIN_INSTR(CreateObject) - QBitField bindings; + QML_BEGIN_INSTR(CreateQMLObject) + const QDeclarativeCompiledData::TypeReference &type = TYPES.at(instr.type); + Q_ASSERT(type.component); + + states.push(State()); + + State *cState = &states[states.count() - 2]; + State *nState = &states[states.count() - 1]; + + nState->context = cState->context; + nState->compiledData = type.component; + nState->instructionStream = type.component->bytecode.constData(); + if (instr.bindingBits != -1) { - const QByteArray &bits = datas.at(instr.bindingBits); - bindings = QBitField((const quint32*)bits.constData(), - bits.size() * 8); + const QByteArray &bits = cState->compiledData->datas.at(instr.bindingBits); + nState->bindingSkipList = QBitField((const quint32*)bits.constData(), + bits.size() * 8); } - if (stack.isEmpty()) - bindings = bindings.united(bindingSkipList); + if (instr.isRoot) + nState->bindingSkipList = nState->bindingSkipList.united(cState->bindingSkipList); - QObject *o = - types.at(instr.type).createInstance(ctxt, bindings, &vmeErrors); + // As the state in the state stack changed, execution will continue in the new program. + QML_END_INSTR(CreateQMLObject) - if (!o) { - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Unable to create object of type %1").arg(types.at(instr.type).className), instr.line); - } + QML_BEGIN_INSTR(CompleteQMLObject) + QObject *o = objects.top(); QDeclarativeData *ddata = QDeclarativeData::get(o); Q_ASSERT(ddata); - if (stack.isEmpty()) { + if (instr.isRoot) { + if (ddata->context) { + Q_ASSERT(ddata->context != CTXT); + Q_ASSERT(ddata->outerContext); + Q_ASSERT(ddata->outerContext != CTXT); + QDeclarativeContextData *c = ddata->context; + while (c->linkedContext) c = c->linkedContext; + c->linkedContext = CTXT; + } else { + CTXT->addObject(o); + } + + ddata->ownContext = true; + } else if (!ddata->context) { + CTXT->addObject(o); + } + + ddata->setImplicitDestructible(); + ddata->outerContext = CTXT; + ddata->lineNumber = instr.line; + ddata->columnNumber = instr.column; + QML_END_INSTR(CompleteQMLObject) + + QML_BEGIN_INSTR(CreateCppObject) + const QDeclarativeCompiledData::TypeReference &type = TYPES.at(instr.type); + Q_ASSERT(type.type); + + QObject *o = 0; + void *memory = 0; + type.type->create(&o, &memory, sizeof(QDeclarativeData)); + QDeclarativeData *ddata = new (memory) QDeclarativeData; + ddata->ownMemory = false; + QObjectPrivate::get(o)->declarativeData = ddata; + + if (type.typePropertyCache && !ddata->propertyCache) { + ddata->propertyCache = type.typePropertyCache; + ddata->propertyCache->addref(); + } + + if (!o) + VME_EXCEPTION(tr("Unable to create object of type %1").arg(type.className), instr.line); + + if (instr.isRoot) { if (ddata->context) { - Q_ASSERT(ddata->context != ctxt); + Q_ASSERT(ddata->context != CTXT); Q_ASSERT(ddata->outerContext); - Q_ASSERT(ddata->outerContext != ctxt); + Q_ASSERT(ddata->outerContext != CTXT); QDeclarativeContextData *c = ddata->context; while (c->linkedContext) c = c->linkedContext; - c->linkedContext = ctxt; + c->linkedContext = CTXT; } else { - ctxt->addObject(o); + CTXT->addObject(o); } ddata->ownContext = true; } else if (!ddata->context) { - ctxt->addObject(o); + CTXT->addObject(o); } ddata->setImplicitDestructible(); - ddata->outerContext = ctxt; + ddata->outerContext = CTXT; ddata->lineNumber = instr.line; ddata->columnNumber = instr.column; if (instr.data != -1) { QDeclarativeCustomParser *customParser = - types.at(instr.type).type->customParser(); - customParser->setCustomData(o, datas.at(instr.data)); + TYPES.at(instr.type).type->customParser(); + customParser->setCustomData(o, DATAS.at(instr.data)); } - if (!stack.isEmpty()) { - QObject *parent = stack.top(); + if (!objects.isEmpty()) { + QObject *parent = objects.top(); #if 0 // ### refactor - if (o->isWidgetType()) { - QWidget *widget = static_cast(o); - if (parent->isWidgetType()) { - QWidget *parentWidget = static_cast(parent); - widget->setParent(parentWidget); - } else { - // TODO: parent might be a layout - } - } else + if (o->isWidgetType() && parent->isWidgetType()) + static_cast(o)->setParent(static_cast(parent)); + else #endif - { QDeclarative_setParent_noEvent(o, parent); - } } - stack.push(o); - QML_END_INSTR(CreateObject) + objects.push(o); + QML_END_INSTR(CreateCppObject) QML_BEGIN_INSTR(CreateSimpleObject) QObject *o = (QObject *)operator new(instr.typeSize + sizeof(QDeclarativeData)); @@ -337,7 +427,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, instr.create(o); QDeclarativeData *ddata = (QDeclarativeData *)(((const char *)o) + instr.typeSize); - const QDeclarativeCompiledData::TypeReference &ref = types.at(instr.type); + const QDeclarativeCompiledData::TypeReference &ref = TYPES.at(instr.type); if (!ddata->propertyCache && ref.typePropertyCache) { ddata->propertyCache = ref.typePropertyCache; ddata->propertyCache->addref(); @@ -346,87 +436,85 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, ddata->columnNumber = instr.column; QObjectPrivate::get(o)->declarativeData = ddata; - ddata->context = ddata->outerContext = ctxt; - ddata->nextContextObject = ctxt->contextObjects; + ddata->context = ddata->outerContext = CTXT; + ddata->nextContextObject = CTXT->contextObjects; if (ddata->nextContextObject) ddata->nextContextObject->prevContextObject = &ddata->nextContextObject; - ddata->prevContextObject = &ctxt->contextObjects; - ctxt->contextObjects = ddata; + ddata->prevContextObject = &CTXT->contextObjects; + CTXT->contextObjects = ddata; - QObject *parent = stack.top(); + QObject *parent = objects.top(); QDeclarative_setParent_noEvent(o, parent); - stack.push(o); + objects.push(o); QML_END_INSTR(CreateSimpleObject) QML_BEGIN_INSTR(SetId) - QObject *target = stack.top(); - ctxt->setIdProperty(instr.index, target); + QObject *target = objects.top(); + CTXT->setIdProperty(instr.index, target); QML_END_INSTR(SetId) QML_BEGIN_INSTR(SetDefault) - ctxt->contextObject = stack.top(); + CTXT->contextObject = objects.top(); QML_END_INSTR(SetDefault) QML_BEGIN_INSTR(CreateComponent) QDeclarativeComponent *qcomp = - new QDeclarativeComponent(ctxt->engine, comp, instructionStream - comp->bytecode.constData(), - stack.isEmpty() ? 0 : stack.top()); + new QDeclarativeComponent(CTXT->engine, COMP, INSTRUCTIONSTREAM - COMP->bytecode.constData(), + objects.isEmpty() ? 0 : objects.top()); QDeclarativeData *ddata = QDeclarativeData::get(qcomp, true); Q_ASSERT(ddata); - ctxt->addObject(qcomp); + CTXT->addObject(qcomp); - if (stack.isEmpty()) + if (instr.isRoot) ddata->ownContext = true; ddata->setImplicitDestructible(); - ddata->outerContext = ctxt; + ddata->outerContext = CTXT; ddata->lineNumber = instr.line; ddata->columnNumber = instr.column; - QDeclarativeComponentPrivate::get(qcomp)->creationContext = ctxt; + QDeclarativeComponentPrivate::get(qcomp)->creationContext = CTXT; - stack.push(qcomp); - instructionStream += instr.count; + objects.push(qcomp); + INSTRUCTIONSTREAM += instr.count; QML_END_INSTR(CreateComponent) QML_BEGIN_INSTR(StoreMetaObject) - QObject *target = stack.top(); + QObject *target = objects.top(); QMetaObject mo; - const QByteArray &metadata = datas.at(instr.data); + const QByteArray &metadata = DATAS.at(instr.data); QFastMetaBuilder::fromData(&mo, 0, metadata); -// QMetaObjectBuilder::fromRelocatableData(&mo, 0, metadata); - const QDeclarativeVMEMetaData *data = - (const QDeclarativeVMEMetaData *)datas.at(instr.aliasData).constData(); + (const QDeclarativeVMEMetaData *)DATAS.at(instr.aliasData).constData(); - (void)new QDeclarativeVMEMetaObject(target, &mo, data, comp); + (void)new QDeclarativeVMEMetaObject(target, &mo, data, COMP); if (instr.propertyCache != -1) { QDeclarativeData *ddata = QDeclarativeData::get(target, true); if (ddata->propertyCache) ddata->propertyCache->release(); - ddata->propertyCache = propertyCaches.at(instr.propertyCache); + ddata->propertyCache = PROPERTYCACHES.at(instr.propertyCache); ddata->propertyCache->addref(); } QML_END_INSTR(StoreMetaObject) QML_BEGIN_INSTR(StoreVariant) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); // XXX - can be more efficient - QVariant v = QDeclarativeStringConverters::variantFromString(primitives.at(instr.value)); + QVariant v = QDeclarativeStringConverters::variantFromString(PRIMITIVES.at(instr.value)); void *a[] = { &v, 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); QML_END_INSTR(StoreVariant) QML_BEGIN_INSTR(StoreVariantInteger) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QVariant v(instr.value); @@ -436,7 +524,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreVariantInteger) QML_BEGIN_INSTR(StoreVariantDouble) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QVariant v(instr.value); @@ -446,7 +534,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreVariantDouble) QML_BEGIN_INSTR(StoreVariantBool) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QVariant v(instr.value); @@ -456,32 +544,32 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreVariantBool) QML_BEGIN_INSTR(StoreString) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); - void *a[] = { (void *)&primitives.at(instr.value), 0, &status, &flags }; + void *a[] = { (void *)&PRIMITIVES.at(instr.value), 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); QML_END_INSTR(StoreString) QML_BEGIN_INSTR(StoreByteArray) - QObject *target = stack.top(); - void *a[] = { (void *)&datas.at(instr.value), 0, &status, &flags }; + QObject *target = objects.top(); + void *a[] = { (void *)&DATAS.at(instr.value), 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); QML_END_INSTR(StoreByteArray) QML_BEGIN_INSTR(StoreUrl) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); - void *a[] = { (void *)&urls.at(instr.value), 0, &status, &flags }; + void *a[] = { (void *)&URLS.at(instr.value), 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, instr.propertyIndex, a); QML_END_INSTR(StoreUrl) QML_BEGIN_INSTR(StoreFloat) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); float f = instr.value; @@ -491,7 +579,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreFloat) QML_BEGIN_INSTR(StoreDouble) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); double d = instr.value; @@ -501,7 +589,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreDouble) QML_BEGIN_INSTR(StoreBool) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); void *a[] = { (void *)&instr.value, 0, &status, &flags }; @@ -510,7 +598,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreBool) QML_BEGIN_INSTR(StoreInteger) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); void *a[] = { (void *)&instr.value, 0, &status, &flags }; @@ -519,7 +607,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreInteger) QML_BEGIN_INSTR(StoreColor) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QColor c = QColor::fromRgba(instr.value); @@ -529,7 +617,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreColor) QML_BEGIN_INSTR(StoreDate) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QDate d = QDate::fromJulianDay(instr.value); @@ -539,7 +627,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreDate) QML_BEGIN_INSTR(StoreTime) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QTime *t = (QTime *)&instr.time; @@ -549,7 +637,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreTime) QML_BEGIN_INSTR(StoreDateTime) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QTime *t = (QTime *)&instr.time; @@ -560,7 +648,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreDateTime) QML_BEGIN_INSTR(StorePoint) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QPoint *p = (QPoint *)&instr.point; @@ -570,7 +658,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StorePoint) QML_BEGIN_INSTR(StorePointF) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QPointF *p = (QPointF *)&instr.point; @@ -580,7 +668,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StorePointF) QML_BEGIN_INSTR(StoreSize) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QSize *s = (QSize *)&instr.size; @@ -590,7 +678,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreSize) QML_BEGIN_INSTR(StoreSizeF) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QSizeF *s = (QSizeF *)&instr.size; @@ -600,7 +688,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreSizeF) QML_BEGIN_INSTR(StoreRect) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QRect *r = (QRect *)&instr.rect; @@ -610,7 +698,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreRect) QML_BEGIN_INSTR(StoreRectF) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QRectF *r = (QRectF *)&instr.rect; @@ -620,7 +708,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreRectF) QML_BEGIN_INSTR(StoreVector3D) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QVector3D *v = (QVector3D *)&instr.vector; @@ -630,7 +718,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreVector3D) QML_BEGIN_INSTR(StoreVector4D) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QVector4D *v = (QVector4D *)&instr.vector; @@ -640,8 +728,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreVector4D) QML_BEGIN_INSTR(StoreObject) - QObject *assignObj = stack.pop(); - QObject *target = stack.top(); + QObject *assignObj = objects.pop(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); void *a[] = { (void *)&assignObj, 0, &status, &flags }; @@ -650,10 +738,10 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreObject) QML_BEGIN_INSTR(AssignCustomType) - QObject *target = stack.top(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); - const QString &primitive = primitives.at(instr.primitive); + const QString &primitive = PRIMITIVES.at(instr.primitive); int type = instr.type; QDeclarativeMetaType::StringConverter converter = QDeclarativeMetaType::customStringConverter(type); QVariant v = (*converter)(primitive); @@ -661,7 +749,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QMetaProperty prop = target->metaObject()->property(instr.propertyIndex); if (v.isNull() || ((int)prop.type() != type && prop.userType() != type)) - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign value %1 to property %2").arg(primitive).arg(QString::fromUtf8(prop.name())), instr.line); + VME_EXCEPTION(tr("Cannot assign value %1 to property %2").arg(primitive).arg(QString::fromUtf8(prop.name())), instr.line); void *a[] = { (void *)v.data(), 0, &status, &flags }; QMetaObject::metacall(target, QMetaObject::WriteProperty, @@ -671,55 +759,55 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_BEGIN_INSTR(AssignSignalObject) // XXX optimize - QObject *assign = stack.pop(); - QObject *target = stack.top(); + QObject *assign = objects.pop(); + QObject *target = objects.top(); int sigIdx = instr.signal; - const QString &pr = primitives.at(sigIdx); + const QString &pr = PRIMITIVES.at(sigIdx); QDeclarativeProperty prop(target, pr); if (prop.type() & QDeclarativeProperty::SignalProperty) { QMetaMethod method = QDeclarativeMetaType::defaultMethod(assign); if (method.signature() == 0) - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign object type %1 with no default method").arg(QString::fromLatin1(assign->metaObject()->className())), instr.line); + VME_EXCEPTION(tr("Cannot assign object type %1 with no default method").arg(QString::fromLatin1(assign->metaObject()->className())), instr.line); if (!QMetaObject::checkConnectArgs(prop.method().signature(), method.signature())) - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot connect mismatched signal/slot %1 %vs. %2").arg(QString::fromLatin1(method.signature())).arg(QString::fromLatin1(prop.method().signature())), instr.line); + VME_EXCEPTION(tr("Cannot connect mismatched signal/slot %1 %vs. %2").arg(QString::fromLatin1(method.signature())).arg(QString::fromLatin1(prop.method().signature())), instr.line); QDeclarativePropertyPrivate::connect(target, prop.index(), assign, method.methodIndex()); } else { - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign an object to signal property %1").arg(pr), instr.line); + VME_EXCEPTION(tr("Cannot assign an object to signal property %1").arg(pr), instr.line); } QML_END_INSTR(AssignSignalObject) QML_BEGIN_INSTR(StoreSignal) - QObject *target = stack.top(); - QObject *context = stack.at(stack.count() - 1 - instr.context); + QObject *target = objects.top(); + QObject *context = objects.at(objects.count() - 1 - instr.context); QMetaMethod signal = target->metaObject()->method(instr.signalIndex); QDeclarativeBoundSignal *bs = new QDeclarativeBoundSignal(target, signal, target); QDeclarativeExpression *expr = - new QDeclarativeExpression(ctxt, context, primitives.at(instr.value)); - expr->setSourceLocation(comp->name, instr.line); - static_cast(QObjectPrivate::get(expr))->name = datas.at(instr.name); + new QDeclarativeExpression(CTXT, context, PRIMITIVES.at(instr.value)); + expr->setSourceLocation(COMP->name, instr.line); + static_cast(QObjectPrivate::get(expr))->name = DATAS.at(instr.name); bs->setExpression(expr); QML_END_INSTR(StoreSignal) QML_BEGIN_INSTR(StoreImportedScript) - ctxt->importedScripts << run(ctxt, scripts.at(instr.value)); + CTXT->importedScripts << run(CTXT, SCRIPTS.at(instr.value)); QML_END_INSTR(StoreImportedScript) QML_BEGIN_INSTR(StoreScriptString) - QObject *target = stack.top(); - QObject *scope = stack.at(stack.count() - 1 - instr.scope); + QObject *target = objects.top(); + QObject *scope = objects.at(objects.count() - 1 - instr.scope); QDeclarativeScriptString ss; - ss.setContext(ctxt->asQDeclarativeContext()); + ss.setContext(CTXT->asQDeclarativeContext()); ss.setScopeObject(scope); - ss.setScript(primitives.at(instr.value)); + ss.setScript(PRIMITIVES.at(instr.value)); ss.d.data()->bindingId = instr.bindingId; ss.d.data()->lineNumber = instr.line; @@ -729,37 +817,37 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreScriptString) QML_BEGIN_INSTR(BeginObject) - QObject *target = stack.top(); + QObject *target = objects.top(); QDeclarativeParserStatus *status = reinterpret_cast(reinterpret_cast(target) + instr.castValue); - parserStatus.append(status); - status->d = &parserStatus.values[parserStatus.count - 1]; + parserStatus.push(status); + status->d = &parserStatus.top(); status->classBegin(); QML_END_INSTR(BeginObject) QML_BEGIN_INSTR(InitV8Bindings) - ctxt->v8bindings = new QV8Bindings(primitives.at(instr.program), instr.programIndex, - instr.line, comp, ctxt); + CTXT->v8bindings = new QV8Bindings(PRIMITIVES.at(instr.program), instr.programIndex, + instr.line, COMP, CTXT); QML_END_INSTR(InitV8Bindings) QML_BEGIN_INSTR(StoreBinding) QObject *target = - stack.at(stack.count() - 1 - instr.owner); + objects.at(objects.count() - 1 - instr.owner); QObject *context = - stack.at(stack.count() - 1 - instr.context); + objects.at(objects.count() - 1 - instr.context); QDeclarativeProperty mp = - QDeclarativePropertyPrivate::restore(datas.at(instr.property), target, ctxt); + QDeclarativePropertyPrivate::restore(DATAS.at(instr.property), target, CTXT); int coreIndex = mp.index(); - if ((stack.count() - instr.owner) == 1 && bindingSkipList.testBit(coreIndex)) + if (instr.isRoot && BINDINGSKIPLIST.testBit(coreIndex)) QML_NEXT_INSTR(StoreBinding); - QDeclarativeBinding *bind = new QDeclarativeBinding(primitives.at(instr.value), true, - context, ctxt, comp->name, instr.line); - bindValues.append(bind); - bind->m_mePtr = &bindValues.values[bindValues.count - 1]; + QDeclarativeBinding *bind = new QDeclarativeBinding(PRIMITIVES.at(instr.value), true, + context, CTXT, COMP->name, instr.line); + bindValues.push(bind); + bind->m_mePtr = &bindValues.top(); bind->setTarget(mp); bind->addToObject(target, QDeclarativePropertyPrivate::bindingIndex(mp)); @@ -767,22 +855,22 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_BEGIN_INSTR(StoreBindingOnAlias) QObject *target = - stack.at(stack.count() - 1 - instr.owner); + objects.at(objects.count() - 1 - instr.owner); QObject *context = - stack.at(stack.count() - 1 - instr.context); + objects.at(objects.count() - 1 - instr.context); QDeclarativeProperty mp = - QDeclarativePropertyPrivate::restore(datas.at(instr.property), target, ctxt); + QDeclarativePropertyPrivate::restore(DATAS.at(instr.property), target, CTXT); int coreIndex = mp.index(); - if ((stack.count() - instr.owner) == 1 && bindingSkipList.testBit(coreIndex)) + if (instr.isRoot && BINDINGSKIPLIST.testBit(coreIndex)) QML_NEXT_INSTR(StoreBindingOnAlias); - QDeclarativeBinding *bind = new QDeclarativeBinding(primitives.at(instr.value), true, - context, ctxt, comp->name, instr.line); - bindValues.append(bind); - bind->m_mePtr = &bindValues.values[bindValues.count - 1]; + QDeclarativeBinding *bind = new QDeclarativeBinding(PRIMITIVES.at(instr.value), true, + context, CTXT, COMP->name, instr.line); + bindValues.push(bind); + bind->m_mePtr = &bindValues.top(); bind->setTarget(mp); QDeclarativeAbstractBinding *old = QDeclarativePropertyPrivate::setBindingNoEnable(target, coreIndex, QDeclarativePropertyPrivate::valueTypeCoreIndex(mp), bind); @@ -791,59 +879,59 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_BEGIN_INSTR(StoreV4Binding) QObject *target = - stack.at(stack.count() - 1 - instr.owner); + objects.at(objects.count() - 1 - instr.owner); QObject *scope = - stack.at(stack.count() - 1 - instr.context); + objects.at(objects.count() - 1 - instr.context); int property = instr.property; - if (stack.count() == 1 && bindingSkipList.testBit(property & 0xFFFF)) + if (instr.isRoot && BINDINGSKIPLIST.testBit(property & 0xFFFF)) QML_NEXT_INSTR(StoreV4Binding); QDeclarativeAbstractBinding *binding = - ctxt->v4bindings->configBinding(instr.value, target, scope, property); - bindValues.append(binding); - binding->m_mePtr = &bindValues.values[bindValues.count - 1]; + CTXT->v4bindings->configBinding(instr.value, target, scope, property); + bindValues.push(binding); + binding->m_mePtr = &bindValues.top(); binding->addToObject(target, property); QML_END_INSTR(StoreV4Binding) QML_BEGIN_INSTR(StoreV8Binding) QObject *target = - stack.at(stack.count() - 1 - instr.owner); + objects.at(objects.count() - 1 - instr.owner); QObject *scope = - stack.at(stack.count() - 1 - instr.context); + objects.at(objects.count() - 1 - instr.context); QDeclarativeProperty mp = - QDeclarativePropertyPrivate::restore(datas.at(instr.property), target, ctxt); + QDeclarativePropertyPrivate::restore(DATAS.at(instr.property), target, CTXT); int coreIndex = mp.index(); - if ((stack.count() - instr.owner) == 1 && bindingSkipList.testBit(coreIndex)) + if (instr.isRoot && BINDINGSKIPLIST.testBit(coreIndex)) QML_NEXT_INSTR(StoreV8Binding); QDeclarativeAbstractBinding *binding = - ctxt->v8bindings->configBinding(instr.value, target, scope, mp, instr.line); - bindValues.append(binding); - binding->m_mePtr = &bindValues.values[bindValues.count - 1]; + CTXT->v8bindings->configBinding(instr.value, target, scope, mp, instr.line); + bindValues.push(binding); + binding->m_mePtr = &bindValues.top(); binding->addToObject(target, QDeclarativePropertyPrivate::bindingIndex(mp)); QML_END_INSTR(StoreV8Binding) QML_BEGIN_INSTR(StoreValueSource) - QObject *obj = stack.pop(); + QObject *obj = objects.pop(); QDeclarativePropertyValueSource *vs = reinterpret_cast(reinterpret_cast(obj) + instr.castValue); - QObject *target = stack.at(stack.count() - 1 - instr.owner); + QObject *target = objects.at(objects.count() - 1 - instr.owner); QDeclarativeProperty prop = - QDeclarativePropertyPrivate::restore(datas.at(instr.property), target, ctxt); + QDeclarativePropertyPrivate::restore(DATAS.at(instr.property), target, CTXT); obj->setParent(target); vs->setTarget(prop); QML_END_INSTR(StoreValueSource) QML_BEGIN_INSTR(StoreValueInterceptor) - QObject *obj = stack.pop(); + QObject *obj = objects.pop(); QDeclarativePropertyValueInterceptor *vi = reinterpret_cast(reinterpret_cast(obj) + instr.castValue); - QObject *target = stack.at(stack.count() - 1 - instr.owner); + QObject *target = objects.at(objects.count() - 1 - instr.owner); QDeclarativeProperty prop = - QDeclarativePropertyPrivate::restore(datas.at(instr.property), target, ctxt); + QDeclarativePropertyPrivate::restore(DATAS.at(instr.property), target, CTXT); obj->setParent(target); vi->setTarget(prop); QDeclarativeVMEMetaObject *mo = static_cast((QMetaObject*)target->metaObject()); @@ -851,16 +939,16 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreValueInterceptor) QML_BEGIN_INSTR(StoreObjectQList) - QObject *assign = stack.pop(); + QObject *assign = objects.pop(); - const ListInstance &list = qliststack.top(); + const List &list = lists.top(); list.qListProperty.append((QDeclarativeListProperty*)&list.qListProperty, assign); QML_END_INSTR(StoreObjectQList) QML_BEGIN_INSTR(AssignObjectList) // This is only used for assigning interfaces - QObject *assign = stack.pop(); - const ListInstance &list = qliststack.top(); + QObject *assign = objects.pop(); + const List &list = lists.top(); int type = list.type; @@ -870,15 +958,15 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, if (iid) ptr = assign->qt_metacast(iid); if (!ptr) - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign object to list"), instr.line); + VME_EXCEPTION(tr("Cannot assign object to list"), instr.line); list.qListProperty.append((QDeclarativeListProperty*)&list.qListProperty, ptr); QML_END_INSTR(AssignObjectList) QML_BEGIN_INSTR(StoreVariantObject) - QObject *assign = stack.pop(); - QObject *target = stack.top(); + QObject *assign = objects.pop(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); QVariant v = QVariant::fromValue(assign); @@ -888,8 +976,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QML_END_INSTR(StoreVariantObject) QML_BEGIN_INSTR(StoreInterface) - QObject *assign = stack.pop(); - QObject *target = stack.top(); + QObject *assign = objects.pop(); + QObject *target = objects.top(); CLEAN_PROPERTY(target, instr.propertyIndex); int coreIdx = instr.propertyIndex; @@ -909,33 +997,33 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, } if (!ok) - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot assign object to interface property"), instr.line); + VME_EXCEPTION(tr("Cannot assign object to interface property"), instr.line); QML_END_INSTR(StoreInterface) QML_BEGIN_INSTR(FetchAttached) - QObject *target = stack.top(); + QObject *target = objects.top(); QObject *qmlObject = qmlAttachedPropertiesObjectById(instr.id, target); if (!qmlObject) - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Unable to create attached object"), instr.line); + VME_EXCEPTION(tr("Unable to create attached object"), instr.line); - stack.push(qmlObject); + objects.push(qmlObject); QML_END_INSTR(FetchAttached) QML_BEGIN_INSTR(FetchQList) - QObject *target = stack.top(); + QObject *target = objects.top(); - qliststack.push(ListInstance(instr.type)); + lists.push(List(instr.type)); void *a[1]; - a[0] = (void *)&(qliststack.top().qListProperty); + a[0] = (void *)&(lists.top().qListProperty); QMetaObject::metacall(target, QMetaObject::ReadProperty, instr.property, a); QML_END_INSTR(FetchQList) QML_BEGIN_INSTR(FetchObject) - QObject *target = stack.top(); + QObject *target = objects.top(); QObject *obj = 0; // NOTE: This assumes a cast to QObject does not alter the @@ -946,33 +1034,33 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, instr.property, a); if (!obj) - VME_EXCEPTION(QCoreApplication::translate("QDeclarativeVME","Cannot set properties on %1 as it is null").arg(QString::fromUtf8(target->metaObject()->property(instr.property).name())), instr.line); + VME_EXCEPTION(tr("Cannot set properties on %1 as it is null").arg(QString::fromUtf8(target->metaObject()->property(instr.property).name())), instr.line); - stack.push(obj); + objects.push(obj); QML_END_INSTR(FetchObject) QML_BEGIN_INSTR(PopQList) - qliststack.pop(); + lists.pop(); QML_END_INSTR(PopQList) QML_BEGIN_INSTR(Defer) if (instr.deferCount) { - QObject *target = stack.top(); + QObject *target = objects.top(); QDeclarativeData *data = QDeclarativeData::get(target, true); - comp->addref(); - data->deferredComponent = comp; - data->deferredIdx = instructionStream - comp->bytecode.constData(); - instructionStream += instr.deferCount; + COMP->addref(); + data->deferredComponent = COMP; + data->deferredIdx = INSTRUCTIONSTREAM - COMP->bytecode.constData(); + INSTRUCTIONSTREAM += instr.deferCount; } QML_END_INSTR(Defer) QML_BEGIN_INSTR(PopFetchedObject) - stack.pop(); + objects.pop(); QML_END_INSTR(PopFetchedObject) QML_BEGIN_INSTR(FetchValueType) - QObject *target = stack.top(); + QObject *target = objects.top(); if (instr.bindingSkipList != 0) { // Possibly need to clear bindings @@ -994,13 +1082,13 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, QDeclarativeValueType *valueHandler = ep->valueTypes[instr.type]; valueHandler->read(target, instr.property); - stack.push(valueHandler); + objects.push(valueHandler); QML_END_INSTR(FetchValueType) QML_BEGIN_INSTR(PopValueType) QDeclarativeValueType *valueHandler = - static_cast(stack.pop()); - QObject *target = stack.top(); + static_cast(objects.pop()); + QObject *target = objects.top(); valueHandler->write(target, instr.property, QDeclarativePropertyPrivate::BypassInterceptor); QML_END_INSTR(PopValueType) @@ -1014,67 +1102,56 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEObjectStack &stack, } #endif - exceptionExit: - Q_ASSERT(isError()); - if (!stack.isEmpty()) { - delete stack.at(0); // ### What about failures in deferred creation? - } else { - ctxt->destroy(); - } +exceptionExit: + if (!objects.isEmpty()) + delete objects.at(0); // XXX What about failures in deferred creation? + + // XXX does context get leaked in this case? + + Q_ASSERT(!errors->isEmpty()); + + // Remove the QDeclarativeParserStatus and QDeclarativeAbstractBinding back pointers + blank(parserStatus); + blank(bindValues); + + objects.deallocate(); + lists.deallocate(); + states.clear(); + bindValues.deallocate(); + parserStatus.deallocate(); + finalizeCallbacks.clear(); - QDeclarativeEnginePrivate::clear(bindValues); - QDeclarativeEnginePrivate::clear(parserStatus); return 0; - normalExit: - if (bindValues.count) - ep->bindValues << bindValues; - else if (bindValues.values) - bindValues.clear(); +normalExit: + Q_ASSERT(objects.count() == 1); - if (parserStatus.count) - ep->parserStatus << parserStatus; - else if (parserStatus.values) - parserStatus.clear(); + QObject *rv = objects.top(); - Q_ASSERT(stack.count() == 1); - return stack.top(); -} + objects.deallocate(); + lists.deallocate(); + states.clear(); -bool QDeclarativeVME::isError() const -{ - return !vmeErrors.isEmpty(); + return rv; } -QList QDeclarativeVME::errors() const +// Must be called with a handle scope and context +void QDeclarativeScriptData::initialize(QDeclarativeEngine *engine) { - return vmeErrors; -} + Q_ASSERT(m_program.IsEmpty()); + Q_ASSERT(engine); + Q_ASSERT(!hasEngine()); -QObject * -QDeclarativeCompiledData::TypeReference::createInstance(QDeclarativeContextData *ctxt, - const QBitField &bindings, - QList *errors) const -{ - if (type) { - QObject *rv = 0; - void *memory = 0; - - type->create(&rv, &memory, sizeof(QDeclarativeData)); - QDeclarativeData *ddata = new (memory) QDeclarativeData; - ddata->ownMemory = false; - QObjectPrivate::get(rv)->declarativeData = ddata; - - if (typePropertyCache && !ddata->propertyCache) { - ddata->propertyCache = typePropertyCache; - ddata->propertyCache->addref(); - } + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QV8Engine *v8engine = ep->v8engine(); - return rv; - } else { - Q_ASSERT(component); - return QDeclarativeComponentPrivate::begin(ctxt, 0, component, -1, 0, errors, bindings); - } + // XXX Handle errors during the script compile! + v8::Local program = v8engine->qmlModeCompile(m_programSource, url.toString(), 1); + m_program = qPersistentNew(program); + + addToEngine(engine); + + addref(); } v8::Persistent QDeclarativeVME::run(QDeclarativeContextData *parentCtxt, QDeclarativeScriptData *script) @@ -1126,6 +1203,9 @@ v8::Persistent QDeclarativeVME::run(QDeclarativeContextData *parentC v8::HandleScope handle_scope; v8::Context::Scope scope(v8engine->context()); + if (!script->isInitialized()) + script->initialize(parentCtxt->engine); + v8::Local qmlglobal = v8engine->qmlScope(ctxt, 0); v8::TryCatch try_catch; @@ -1157,55 +1237,91 @@ void **QDeclarativeVME::instructionJumpTable() static void **jumpTable = 0; if (!jumpTable) { QDeclarativeVME dummy; - QDeclarativeVMEObjectStack stack; - dummy.run(stack, 0, 0, 0, QBitField(), &jumpTable); + QDeclarativeVME::Interrupt i; + dummy.run(0, i, &jumpTable); } return jumpTable; } #endif -template -QDeclarativeVMEStack::QDeclarativeVMEStack() -: _index(-1) +bool QDeclarativeVME::complete(const Interrupt &interrupt) { -} + ActiveVMERestorer restore(this, QDeclarativeEnginePrivate::get(engine)); + + while (bindValuesCount < bindValues.count()) { + if(bindValues.at(bindValuesCount)) { + QDeclarativeAbstractBinding *b = bindValues.at(bindValuesCount); + b->m_mePtr = 0; + b->setEnabled(true, QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::DontRemoveBinding); + } + ++bindValuesCount; -template -bool QDeclarativeVMEStack::isEmpty() const { - return _index == -1; -} + if (interrupt.shouldInterrupt()) + return false; + } + bindValues.deallocate(); -template -const T &QDeclarativeVMEStack::top() const { - return at(_index); -} + while (parserStatusCount < parserStatus.count()) { + QDeclarativeParserStatus *status = + parserStatus.at(parserStatus.count() - parserStatusCount - 1); -template -void QDeclarativeVMEStack::push(const T &o) { - _index++; + if (status && status->d) { + status->d = 0; + status->componentComplete(); + } + + ++parserStatusCount; - Q_ASSERT(_index <= VLA::size()); - if (_index == VLA::size()) - this->append(o); - else - VLA::data()[_index] = o; -} + if (interrupt.shouldInterrupt()) + return false; + } + parserStatus.deallocate(); + + while (componentAttached) { + QDeclarativeComponentAttached *a = componentAttached; + a->rem(); + QDeclarativeData *d = QDeclarativeData::get(a->parent()); + Q_ASSERT(d); + Q_ASSERT(d->context); + a->add(&d->context->componentAttached); + emit a->completed(); + + if (interrupt.shouldInterrupt()) + return false; + } + + // XXX (what if its deleted?) + if (rootContext) + rootContext->activeVME = 0; + + for (int ii = 0; ii < finalizeCallbacks.count(); ++ii) { + QDeclarativeEnginePrivate::FinalizeCallback callback = finalizeCallbacks.at(ii); + QObject *obj = callback.first; + if (obj) { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args); + } + } + finalizeCallbacks.clear(); -template -T QDeclarativeVMEStack::pop() { - Q_ASSERT(_index >= 0); - --_index; - return VLA::data()[_index + 1]; + return true; } -template -int QDeclarativeVMEStack::count() const { - return _index + 1; +void QDeclarativeVME::blank(QFiniteStack &bs) +{ + for (int ii = 0; ii < bs.count(); ++ii) { + QDeclarativeAbstractBinding *b = bs.at(ii); + if (b) b->m_mePtr = 0; + } } -template -const T &QDeclarativeVMEStack::at(int index) const { - return VLA::data()[index]; +void QDeclarativeVME::blank(QFiniteStack &pss) +{ + for (int ii = 0; ii < pss.count(); ++ii) { + QDeclarativeParserStatus *ps = pss.at(ii); + if(ps) ps->d = 0; + } } QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativevme_p.h b/src/declarative/qml/qdeclarativevme_p.h index a0dae77..3d79f9d 100644 --- a/src/declarative/qml/qdeclarativevme_p.h +++ b/src/declarative/qml/qdeclarativevme_p.h @@ -57,10 +57,14 @@ #include "private/qbitfield_p.h" #include "private/qdeclarativeinstruction_p.h" -#include #include +#include +#include +#include #include +#include +#include QT_BEGIN_NAMESPACE @@ -70,39 +74,127 @@ class QDeclarativeScriptData; class QDeclarativeCompiledData; class QDeclarativeCompiledData; class QDeclarativeContextData; -class QDeclarativeVMEObjectStack; + +namespace QDeclarativeVMETypes { + struct List + { + List() : type(0) {} + List(int t) : type(t) {} + + int type; + QDeclarativeListProperty qListProperty; + }; +} +Q_DECLARE_TYPEINFO(QDeclarativeVMETypes::List, Q_PRIMITIVE_TYPE | Q_MOVABLE_TYPE); class QDeclarativeVME { + Q_DECLARE_TR_FUNCTIONS(QDeclarativeVME) public: - QDeclarativeVME(); - - QObject *run(QDeclarativeContextData *, QDeclarativeCompiledData *, - int start = -1, const QBitField & = QBitField()); - - void runDeferred(QObject *); - - bool isError() const; - QList errors() const; + class Interrupt { + public: + inline Interrupt(); + inline Interrupt(bool *runWhile); + inline Interrupt(int nsecs); + + inline void reset(); + inline bool shouldInterrupt() const; + private: + enum Mode { None, Time, Flag }; + Mode mode; + union { + struct { + QElapsedTimer timer; + int nsecs; + }; + bool *runWhile; + }; + }; + + QDeclarativeVME() : data(0), componentAttached(0) {} + QDeclarativeVME(void *data) : data(data), componentAttached(0) {} + + void *data; + QDeclarativeComponentAttached *componentAttached; + QList finalizeCallbacks; + + void init(QDeclarativeContextData *, QDeclarativeCompiledData *, int start); + bool initDeferred(QObject *); + + QObject *execute(QList *errors, const Interrupt & = Interrupt()); + bool complete(const Interrupt & = Interrupt()); private: - v8::Persistent run(QDeclarativeContextData *, QDeclarativeScriptData *); - - QObject *run(QDeclarativeVMEObjectStack &, - QDeclarativeContextData *, QDeclarativeCompiledData *, - int start, const QBitField & + QObject *run(QList *errors, const Interrupt & #ifdef QML_THREADED_VME_INTERPRETER , void ***storeJumpTable = 0 #endif - ); - QList vmeErrors; + ); + v8::Persistent run(QDeclarativeContextData *, QDeclarativeScriptData *); #ifdef QML_THREADED_VME_INTERPRETER static void **instructionJumpTable(); friend class QDeclarativeCompiledData; #endif + + QDeclarativeEngine *engine; + QFiniteStack objects; + QFiniteStack lists; + + int bindValuesCount; + int parserStatusCount; + QFiniteStack bindValues; + QFiniteStack parserStatus; + QDeclarativeContextData *rootContext; + + struct State { + State() : context(0), compiledData(0), instructionStream(0) {} + QDeclarativeContextData *context; + QDeclarativeCompiledData *compiledData; + const char *instructionStream; + QBitField bindingSkipList; + }; + + QStack states; + + static void blank(QFiniteStack &); + static void blank(QFiniteStack &); }; +QDeclarativeVME::Interrupt::Interrupt() +: mode(None) +{ +} + +QDeclarativeVME::Interrupt::Interrupt(bool *runWhile) +: mode(Flag), runWhile(runWhile) +{ +} + +QDeclarativeVME::Interrupt::Interrupt(int nsecs) +: mode(Time), nsecs(nsecs) +{ +} + +void QDeclarativeVME::Interrupt::reset() +{ + if (mode == Time) + timer.start(); +} + +bool QDeclarativeVME::Interrupt::shouldInterrupt() const +{ + if (mode == None) { + return false; + } else if (mode == Time) { + return timer.nsecsElapsed() > nsecs; + } else if (mode == Flag) { + return !*runWhile; + } else { + return false; + } +} + QT_END_NAMESPACE #endif // QDECLARATIVEVME_P_H diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 61e3aa3..608b212 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -7,6 +7,7 @@ SOURCES += \ $$PWD/qdeclarativebinding.cpp \ $$PWD/qdeclarativeproperty.cpp \ $$PWD/qdeclarativecomponent.cpp \ + $$PWD/qdeclarativeincubator.cpp \ $$PWD/qdeclarativecontext.cpp \ $$PWD/qdeclarativecustomparser.cpp \ $$PWD/qdeclarativepropertyvaluesource.cpp \ @@ -54,6 +55,8 @@ HEADERS += \ $$PWD/qdeclarativeproperty.h \ $$PWD/qdeclarativecomponent.h \ $$PWD/qdeclarativecomponent_p.h \ + $$PWD/qdeclarativeincubator.h \ + $$PWD/qdeclarativeincubator_p.h \ $$PWD/qdeclarativecustomparser_p.h \ $$PWD/qdeclarativecustomparser_p_p.h \ $$PWD/qdeclarativepropertyvaluesource.h \ diff --git a/src/declarative/qml/v4/qdeclarativev4bindings.cpp b/src/declarative/qml/v4/qdeclarativev4bindings.cpp index 1a1f5cc..7aa5496 100644 --- a/src/declarative/qml/v4/qdeclarativev4bindings.cpp +++ b/src/declarative/qml/v4/qdeclarativev4bindings.cpp @@ -749,9 +749,12 @@ void **QDeclarativeV4Bindings::getDecodeInstrTable() { static void **decode_instr; if (!decode_instr) { - QDeclarativeV4Bindings dummy(0, 0, 0); + QDeclarativeV4Bindings *dummy = new QDeclarativeV4Bindings(0, 0, 0); quint32 executedBlocks = 0; - dummy.d_func()->run(0, executedBlocks, 0, 0, 0, 0, QDeclarativePropertyPrivate::BypassInterceptor, &decode_instr); + dummy->d_func()->run(0, executedBlocks, 0, 0, 0, 0, + QDeclarativePropertyPrivate::BypassInterceptor, + &decode_instr); + dummy->release(); } return decode_instr; } @@ -786,7 +789,6 @@ void QDeclarativeV4BindingsPrivate::run(int instrIndex, quint32 &executedBlocks, executedBlocks = 0; - QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(context->engine); const char *code = program->instructions(); code += instrIndex * QML_V4_INSTR_SIZE(Jump, jump); const V4Instr *instr = reinterpret_cast(code); diff --git a/src/declarative/qml/v8/qv8engine_p.h b/src/declarative/qml/v8/qv8engine_p.h index 6fa47d8..114584c 100644 --- a/src/declarative/qml/v8/qv8engine_p.h +++ b/src/declarative/qml/v8/qv8engine_p.h @@ -135,7 +135,8 @@ public: QV8ObjectResource(QV8Engine *engine) : engine(engine) { Q_ASSERT(engine); } enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType, ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType, - ListModelType, Context2DType, Context2DStyleType, Context2DPixelArrayType, ParticleDataType, SignalHandlerType}; + ListModelType, Context2DType, Context2DStyleType, Context2DPixelArrayType, + ParticleDataType, SignalHandlerType, IncubatorType }; virtual ResourceType resourceType() const = 0; QV8Engine *engine; diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp index 2649976..9481bb5 100644 --- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp +++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp @@ -861,6 +861,7 @@ static void WeakQObjectInstanceCallback(v8::Persistent handle, void * v8::Local QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine) { Q_ASSERT(object); + Q_ASSERT(this->engine); Q_ASSERT(QDeclarativeData::get(object, false)); Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this); @@ -938,6 +939,8 @@ v8::Local QDeclarativePropertyCache::newQObject(QObject *object, QV8 ft->InstanceTemplate()->SetHasExternalResource(true); constructor = qPersistentNew(ft->GetFunction()); } + + QDeclarativeCleanup::addToEngine(this->engine); } v8::Local result = constructor->NewInstance(); diff --git a/src/declarative/util/qdeclarativeanimation.cpp b/src/declarative/util/qdeclarativeanimation.cpp index e642f11..bbb5e33 100644 --- a/src/declarative/util/qdeclarativeanimation.cpp +++ b/src/declarative/util/qdeclarativeanimation.cpp @@ -188,7 +188,7 @@ void QDeclarativeAbstractAnimation::setRunning(bool r) else if (!d->registered) { d->registered = true; QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); - engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); + engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()")); } return; } diff --git a/src/declarative/util/qdeclarativebehavior.cpp b/src/declarative/util/qdeclarativebehavior.cpp index 3038d3a..71bbc7f 100644 --- a/src/declarative/util/qdeclarativebehavior.cpp +++ b/src/declarative/util/qdeclarativebehavior.cpp @@ -220,7 +220,7 @@ void QDeclarativeBehavior::setTarget(const QDeclarativeProperty &property) d->animation->setDefaultTarget(property); QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); - engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); + engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()")); } void QDeclarativeBehavior::componentFinalized() diff --git a/src/qtquick1/util/qdeclarativeanimation.cpp b/src/qtquick1/util/qdeclarativeanimation.cpp index 8204e33..cc1ada1 100644 --- a/src/qtquick1/util/qdeclarativeanimation.cpp +++ b/src/qtquick1/util/qdeclarativeanimation.cpp @@ -191,7 +191,7 @@ void QDeclarative1AbstractAnimation::setRunning(bool r) else if (!d->registered) { d->registered = true; QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); - engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); + engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()")); } return; } diff --git a/src/qtquick1/util/qdeclarativebehavior.cpp b/src/qtquick1/util/qdeclarativebehavior.cpp index 0475eea..eb0f588 100644 --- a/src/qtquick1/util/qdeclarativebehavior.cpp +++ b/src/qtquick1/util/qdeclarativebehavior.cpp @@ -221,7 +221,7 @@ void QDeclarative1Behavior::setTarget(const QDeclarativeProperty &property) d->animation->setDefaultTarget(property); QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); - engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); + engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()")); } void QDeclarative1Behavior::componentFinalized() diff --git a/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp b/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp index 98c5f7d..fa860be 100644 --- a/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp +++ b/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp @@ -75,7 +75,9 @@ static void msgHandler(QtMsgType, const char *msg) void tst_qdeclarativeinstruction::dump() { - QDeclarativeCompiledData *data = new QDeclarativeCompiledData(0); + QDeclarativeEngine engine; + QDeclarativeCompiledData *data = new QDeclarativeCompiledData(&engine); + { QDeclarativeCompiledData::Instruction::Init i; i.bindingsSize = 0; @@ -90,10 +92,9 @@ void tst_qdeclarativeinstruction::dump() ref.className = "Test"; data->types << ref; - QDeclarativeCompiledData::Instruction::CreateObject i; + QDeclarativeCompiledData::Instruction::CreateCppObject i; i.type = 0; i.data = -1; - i.bindingBits = -1; i.column = 10; data->addInstruction(i); } @@ -309,10 +310,10 @@ void tst_qdeclarativeinstruction::dump() } { - data->datas << "mySignal"; + data->primitives << "mySignal"; QDeclarativeCompiledData::Instruction::AssignSignalObject i; - i.signal = 0; + i.signal = data->primitives.count() - 1; data->addInstruction(i); } @@ -459,7 +460,7 @@ void tst_qdeclarativeinstruction::dump() << "Index\tOperation\t\tData1\tData2\tData3\tComments" << "-------------------------------------------------------------------------------" << "0\t\tINIT\t\t\t0\t3\t-1\t-1" - << "1\t\tCREATE\t\t\t0\t-1\t\t\"Test\"" + << "1\t\tCREATECPP\t\t\t0\t\t\t\"Test\"" << "2\t\tSETID\t\t\t0\t\t\t\"testId\"" << "3\t\tSET_DEFAULT" << "4\t\tCREATE_COMPONENT\t3" @@ -487,7 +488,7 @@ void tst_qdeclarativeinstruction::dump() << "26\t\tSTORE_INTERFACE\t\t23" << "27\t\tSTORE_SIGNAL\t\t2\t3\t\t\"console.log(1921)\"" << "28\t\tSTORE_SCRIPT_STRING\t24\t3\t1\t4" - << "29\t\tASSIGN_SIGNAL_OBJECT\t0\t\t\t\"mySignal\"" + << "29\t\tASSIGN_SIGNAL_OBJECT\t4\t\t\t\"mySignal\"" << "30\t\tASSIGN_CUSTOMTYPE\t25\t6\t9" << "31\t\tSTORE_BINDING\t26\t3\t2" << "32\t\tSTORE_COMPILED_BINDING\t27\t2\t4" @@ -513,6 +514,7 @@ void tst_qdeclarativeinstruction::dump() messages = QStringList(); QtMsgHandler old = qInstallMsgHandler(msgHandler); + data->dumpInstructions(); qInstallMsgHandler(old);