X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fdeclarative%2Fqml%2Fqdeclarativetypeloader.cpp;h=b507f20f49e41b6fb0b809c00327a3fc1e4ff302;hb=45b14259fc0cf704692df1c00da511527d1fba1d;hp=a788b4b91521c84ea0b218ce7928eecc9d637cb5;hpb=dc2b89dcb0022f9352932cf09ae716b4ac0dc043;p=profile%2Fivi%2Fqtdeclarative.git diff --git a/src/declarative/qml/qdeclarativetypeloader.cpp b/src/declarative/qml/qdeclarativetypeloader.cpp index a788b4b..b507f20 100644 --- a/src/declarative/qml/qdeclarativetypeloader.cpp +++ b/src/declarative/qml/qdeclarativetypeloader.cpp @@ -1,8 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** @@ -35,6 +34,7 @@ ** ** ** +** ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -42,18 +42,196 @@ #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 +is responsible for deleting the returned data. +Returns 0 if the directory does not exist. +*/ +#if defined (Q_OS_UNIX) && !defined(Q_OS_DARWIN) +static QStringHash *qmlFilesInDirectory(const QString &path) +{ + QByteArray name(QFile::encodeName(path)); + DIR *dd = opendir(name); + if (!dd) + return 0; + + struct dirent *result; + union { + struct dirent d; + char b[offsetof (struct dirent, d_name) + NAME_MAX + 1]; + } u; + + QStringHash *files = new QStringHash; + while (readdir_r(dd, &u.d, &result) == 0 && result != 0) { + if (!strcmp(u.d.d_name, "qmldir")) { + files->insert(QLatin1String("qmldir"), true); + continue; + } + int len = strlen(u.d.d_name); + if (len < 4) + continue; + if (!strcmp(u.d.d_name+len-4, ".qml") || !strcmp(u.d.d_name+len-3, ".js")) + files->insert(QFile::decodeName(u.d.d_name), true); +#if defined(Q_OS_DARWIN) + else if ((len > 6 && !strcmp(u.d.d_name+len-6, ".dylib")) || !strcmp(u.d.d_name+len-3, ".so") + || (len > 7 && !strcmp(u.d.d_name+len-7, ".bundle"))) + files->insert(QFile::decodeName(u.d.d_name), true); +#else // Unix + else if (!strcmp(u.d.d_name+len-3, ".so") || !strcmp(u.d.d_name+len-3, ".sl")) + files->insert(QFile::decodeName(u.d.d_name), true); +#endif + } + + closedir(dd); + return files; +} +#else +static QStringHash *qmlFilesInDirectory(const QString &path) +{ + QDirIterator dir(path, QDir::Files); + if (!dir.hasNext()) + return 0; + QStringHash *files = new QStringHash; + while (dir.hasNext()) { + dir.next(); + QString fileName = dir.fileName(); + if (fileName == QLatin1String("qmldir") + || fileName.endsWith(QLatin1String(".qml")) + || fileName.endsWith(QLatin1String(".js")) +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + || fileName.endsWith(QLatin1String(".dll")) +#elif defined(Q_OS_DARWIN) + || fileName.endsWith(QLatin1String(".dylib")) + || fileName.endsWith(QLatin1String(".so")) + || fileName.endsWith(QLatin1String(".bundle")) +#else // Unix + || fileName.endsWith(QLatin1String(".so")) + || fileName.endsWith(QLatin1String(".sl")) +#endif + ) { +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + fileName = fileName.toLower(); +#endif + files->insert(fileName, true); + } + } + return files; +} +#endif + + /*! \class QDeclarativeDataBlob \brief The QDeclarativeDataBlob encapsulates a data request that can be issued to a QDeclarativeDataLoader. @@ -97,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) { } @@ -123,7 +301,7 @@ Returns the blob's status. */ QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const { - return m_status; + return m_data.status(); } /*! @@ -131,7 +309,7 @@ Returns true if the status is Null. */ bool QDeclarativeDataBlob::isNull() const { - return m_status == Null; + return status() == Null; } /*! @@ -139,7 +317,7 @@ Returns true if the status is Loading. */ bool QDeclarativeDataBlob::isLoading() const { - return m_status == Loading; + return status() == Loading; } /*! @@ -147,7 +325,7 @@ Returns true if the status is WaitingForDependencies. */ bool QDeclarativeDataBlob::isWaiting() const { - return m_status == WaitingForDependencies; + return status() == WaitingForDependencies; } /*! @@ -155,7 +333,7 @@ Returns true if the status is Complete. */ bool QDeclarativeDataBlob::isComplete() const { - return m_status == Complete; + return status() == Complete; } /*! @@ -163,7 +341,7 @@ Returns true if the status is Error. */ bool QDeclarativeDataBlob::isError() const { - return m_status == Error; + return status() == Error; } /*! @@ -171,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; } /*! @@ -179,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); } /*! @@ -197,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; } @@ -215,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); @@ -230,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(); @@ -242,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); } @@ -275,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() { @@ -366,21 +570,64 @@ 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(); } } @@ -432,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.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT); +} + +void QDeclarativeDataBlob::ThreadData::setStatus(QDeclarativeDataBlob::Status status) +{ + while (true) { + int d = _p.load(); + 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.load() & TD_ASYNC_MASK; +} + +void QDeclarativeDataBlob::ThreadData::setIsAsync(bool v) +{ + while (true) { + int d = _p.load(); + 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.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT); +} + +void QDeclarativeDataBlob::ThreadData::setProgress(quint8 v) +{ + while (true) { + int d = _p.load(); + 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. @@ -466,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)) { } @@ -475,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(); + } +} + +void QDeclarativeDataLoader::loadWithStaticDataThread(QDeclarativeDataBlob *blob, const QByteArray &data) +{ + ASSERT_LOADTHREAD(); + + setData(blob, data); +} - blob->m_status = QDeclarativeDataBlob::Loading; +void QDeclarativeDataLoader::loadThread(QDeclarativeDataBlob *blob) +{ + ASSERT_LOADTHREAD(); if (blob->m_url.isEmpty()) { QDeclarativeError error; @@ -508,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 { @@ -518,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(); @@ -532,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); @@ -549,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; } @@ -566,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; @@ -609,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; @@ -654,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) { @@ -663,6 +1176,9 @@ QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url) } typeData->addref(); + + unlock(); + return typeData; } @@ -674,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; } @@ -688,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) { @@ -696,6 +1219,10 @@ QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url) QDeclarativeDataLoader::load(scriptBlob); } + scriptBlob->addref(); + + unlock(); + return scriptBlob; } @@ -708,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) { @@ -717,10 +1246,121 @@ QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url) } qmldirData->addref(); + + unlock(); + return qmldirData; } /*! +Returns the absolute filename of path via a directory cache for files named +"qmldir", "*.qml", "*.js", and plugins. +Returns a empty string if the path does not exist. +*/ +QString QDeclarativeTypeLoader::absoluteFilePath(const QString &path) +{ + if (path.isEmpty()) + return QString(); + if (path.at(0) == QLatin1Char(':')) { + // qrc resource + QFileInfo fileInfo(path); + return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); + } +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QString lowPath = path.toLower(); + int lastSlash = lowPath.lastIndexOf(QLatin1Char('/')); + QString dirPath = lowPath.left(lastSlash); +#else + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + QStringRef dirPath(&path, 0, lastSlash); +#endif + + StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length())); + if (!fileSet) { +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QHashedString dirPathString(dirPath); +#else + QHashedString dirPathString(dirPath.toString()); +#endif + StringSet *files = qmlFilesInDirectory(dirPathString); + m_importDirCache.insert(dirPathString, files); + fileSet = m_importDirCache.value(dirPathString); + } + if (!(*fileSet)) + return QString(); + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(lowPath.constData()+lastSlash+1, lowPath.length()-lastSlash-1)) ? path : QString(); +#else + QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(path.constData()+lastSlash+1, path.length()-lastSlash-1)) ? path : QString(); +#endif + if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':')) + absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath(); + + return absoluteFilePath; +} + +/*! +Returns true if the path is a directory via a directory cache. Cache is +shared with absoluteFilePath(). +*/ +bool QDeclarativeTypeLoader::directoryExists(const QString &path) +{ + if (path.isEmpty()) + return false; + if (path.at(0) == QLatin1Char(':')) { + // qrc resource + QFileInfo fileInfo(path); + return fileInfo.exists() && fileInfo.isDir(); + } + + int length = path.length(); + if (path.endsWith(QLatin1Char('/'))) + --length; +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QString dirPath = path.left(length).toLower(); +#else + QStringRef dirPath(&path, 0, length); +#endif + + StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length())); + if (!fileSet) { +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN) + QHashedString dirPathString(dirPath); +#else + QHashedString dirPathString(dirPath.toString()); +#endif + StringSet *files = qmlFilesInDirectory(dirPathString); + m_importDirCache.insert(dirPathString, files); + fileSet = m_importDirCache.value(dirPathString); + } + + return (*fileSet); +} + + +/*! +Return a QDeclarativeDirParser for absoluteFilePath. The QDeclarativeDirParser may be cached. +*/ +const QDeclarativeDirParser *QDeclarativeTypeLoader::qmlDirParser(const QString &absoluteFilePath) +{ + QDeclarativeDirParser *qmldirParser; + QDeclarativeDirParser **val = m_importQmlDirCache.value(absoluteFilePath); + if (!val) { + qmldirParser = new QDeclarativeDirParser; + qmldirParser->setFileSource(absoluteFilePath); + qmldirParser->setUrl(QUrl::fromLocalFile(absoluteFilePath)); + qmldirParser->parse(); + m_importQmlDirCache.insert(absoluteFilePath, qmldirParser); + } else { + qmldirParser = *val; + } + + return qmldirParser; +} + + +/*! Clears cached information about loaded files, including any type data, scripts and qmldir information. */ @@ -732,17 +1372,21 @@ void QDeclarativeTypeLoader::clearCache() (*iter)->release(); for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) (*iter)->release(); + qDeleteAll(m_importDirCache); + qDeleteAll(m_importQmlDirCache); m_typeCache.clear(); m_scriptCache.clear(); m_qmldirCache.clear(); + m_importDirCache.clear(); + m_importQmlDirCache.clear(); } QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options, QDeclarativeTypeLoader *manager) -: QDeclarativeDataBlob(url, QmlFile), m_options(options), m_typesResolved(false), - m_compiledData(0), m_typeLoader(manager) +: QDeclarativeDataBlob(url, QmlFile), m_options(options), m_imports(manager), m_typesResolved(false), + m_compiledData(0), m_typeLoader(manager) { } @@ -768,7 +1412,7 @@ const QDeclarativeImports &QDeclarativeTypeData::imports() const return m_imports; } -const QDeclarativeScriptParser &QDeclarativeTypeData::parser() const +const QDeclarativeScript::Parser &QDeclarativeTypeData::parser() const { return scriptParser; } @@ -783,6 +1427,11 @@ const QList &QDeclarativeTypeData::resolv return m_scripts; } +const QSet &QDeclarativeTypeData::namespaces() const +{ + return m_namespaces; +} + QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const { if (m_compiledData) @@ -806,8 +1455,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); @@ -848,14 +1495,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) @@ -867,15 +1515,15 @@ void QDeclarativeTypeData::dataReceived(const QByteArray &data) m_imports.setBaseUrl(finalUrl()); - foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) { - if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) { + foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) { + if (import.type == QDeclarativeScript::Import::File && import.qualifier.isEmpty()) { QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir"))); if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) { QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl); addDependency(data); m_qmldirs << data; } - } else if (import.type == QDeclarativeScriptParser::Import::Script) { + } else if (import.type == QDeclarativeScript::Import::Script) { QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri)); QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl); addDependency(blob); @@ -884,7 +1532,6 @@ void QDeclarativeTypeData::dataReceived(const QByteArray &data) ref.location = import.location.start; ref.qualifier = import.qualifier; ref.script = blob; - blob->addref(); m_scripts << ref; } @@ -926,7 +1573,7 @@ void QDeclarativeTypeData::compile() m_compiledData->name = m_compiledData->url.toString(); QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name); - QDeclarativeCompiler compiler; + QDeclarativeCompiler compiler(&scriptParser._pool); if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) { setError(compiler.errors()); m_compiledData->release(); @@ -946,11 +1593,11 @@ void QDeclarativeTypeData::resolveTypes() QList errors; if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) { m_imports.addImport(importDatabase, QLatin1String("."), - QString(), -1, -1, QDeclarativeScriptParser::Import::File, + QString(), -1, -1, QDeclarativeScript::Import::File, qmldir->dirComponents(), &errors); } else { m_imports.addImport(importDatabase, QLatin1String("."), - QString(), -1, -1, QDeclarativeScriptParser::Import::File, + QString(), -1, -1, QDeclarativeScript::Import::File, QDeclarativeDirComponents(), &errors); } @@ -970,12 +1617,12 @@ void QDeclarativeTypeData::resolveTypes() return; } - foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) { + foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) { QDeclarativeDirComponents qmldircomponentsnetwork; - if (import.type == QDeclarativeScriptParser::Import::Script) + if (import.type == QDeclarativeScript::Import::Script) continue; - if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) { + if (import.type == QDeclarativeScript::Import::File && import.qualifier.isEmpty()) { QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir"))); if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl)) qmldircomponentsnetwork = qmldir->dirComponents(); @@ -1006,18 +1653,37 @@ void QDeclarativeTypeData::resolveTypes() } } - foreach (QDeclarativeScriptParser::TypeReference *parserRef, scriptParser.referencedTypes()) { - QByteArray typeName = parserRef->name.toUtf8(); + // Add any imported scripts to our resolved set + foreach (const QDeclarativeImports::ScriptReference &script, m_imports.resolvedScripts()) + { + QDeclarativeScriptBlob *blob = typeLoader()->getScript(script.location); + addDependency(blob); + + ScriptReference ref; + //ref.location = ... + ref.qualifier = script.nameSpace; + if (!script.qualifier.isEmpty()) + { + ref.qualifier.prepend(script.qualifier + QLatin1Char('.')); + + // Add a reference to the enclosing namespace + m_namespaces.insert(script.qualifier); + } + + ref.script = blob; + m_scripts << ref; + } + foreach (QDeclarativeScript::TypeReference *parserRef, scriptParser.referencedTypes()) { TypeReference ref; - QUrl url; + QString url; int majorVersion; int minorVersion; QDeclarativeImportedNamespace *typeNamespace = 0; QList errors; - if (!m_imports.resolveType(typeName, &ref.type, &url, &majorVersion, &minorVersion, + if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion, &typeNamespace, &errors) || typeNamespace) { // Known to not be a type: // - known to be a namespace (Namespace {}) @@ -1040,7 +1706,7 @@ void QDeclarativeTypeData::resolveTypes() } if (!parserRef->refObjects.isEmpty()) { - QDeclarativeParser::Object *obj = parserRef->refObjects.first(); + QDeclarativeScript::Object *obj = parserRef->refObjects.first(); error.setLine(obj->location.start.line); error.setColumn(obj->location.start.column); } @@ -1053,13 +1719,8 @@ void QDeclarativeTypeData::resolveTypes() if (ref.type) { ref.majorVersion = majorVersion; ref.minorVersion = minorVersion; - foreach (QDeclarativeParser::Object *obj, parserRef->refObjects) { - // store namespace for DOM - obj->majorVersion = majorVersion; - obj->minorVersion = minorVersion; - } } else { - ref.typeData = typeLoader()->get(url); + ref.typeData = typeLoader()->get(QUrl(url)); addDependency(ref.typeData); } @@ -1079,15 +1740,13 @@ QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url) return 0; } -QDeclarativeScriptData::QDeclarativeScriptData(QDeclarativeEngine *engine) -: QDeclarativeCleanup(engine), importCache(0), pragmas(QDeclarativeParser::Object::ScriptBlock::None), - m_loaded(false) +QDeclarativeScriptData::QDeclarativeScriptData() +: importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None), m_loaded(false) { } QDeclarativeScriptData::~QDeclarativeScriptData() { - clear(); } void QDeclarativeScriptData::clear() @@ -1103,11 +1762,14 @@ 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) -: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None), - m_scriptData(0), m_typeLoader(loader) +: QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeScript::Object::ScriptBlock::None), + m_imports(loader), m_scriptData(0), m_typeLoader(loader) { } @@ -1119,7 +1781,7 @@ QDeclarativeScriptBlob::~QDeclarativeScriptBlob() } } -QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const +QDeclarativeScript::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const { return m_pragmas; } @@ -1151,17 +1813,17 @@ void QDeclarativeScriptBlob::dataReceived(const QByteArray &data) m_source = QString::fromUtf8(data); - QDeclarativeScriptParser::JavaScriptMetaData metadata = - QDeclarativeScriptParser::extractMetaData(m_source); + QDeclarativeScript::Parser::JavaScriptMetaData metadata = + QDeclarativeScript::Parser::extractMetaData(m_source); m_imports.setBaseUrl(finalUrl()); m_pragmas = metadata.pragmas; - foreach (const QDeclarativeScriptParser::Import &import, metadata.imports) { - Q_ASSERT(import.type != QDeclarativeScriptParser::Import::File); + foreach (const QDeclarativeScript::Import &import, metadata.imports) { + Q_ASSERT(import.type != QDeclarativeScript::Import::File); - if (import.type == QDeclarativeScriptParser::Import::Script) { + if (import.type == QDeclarativeScript::Import::Script) { QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri)); QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl); addDependency(blob); @@ -1170,10 +1832,9 @@ 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 == QDeclarativeScriptParser::Import::Library); + Q_ASSERT(import.type == QDeclarativeScript::Import::Library); int vmaj = -1; int vmin = -1; import.extractVersion(&vmaj, &vmin); @@ -1217,9 +1878,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); @@ -1231,13 +1892,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) @@ -1260,3 +1915,4 @@ void QDeclarativeQmldirData::dataReceived(const QByteArray &data) QT_END_NAMESPACE +#include "qdeclarativetypeloader.moc"