aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-10-16 17:21:55 +0200
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-11-03 09:00:11 +0000
commit164af37710e5721cbc7d79a0af20f2387181c59c (patch)
tree90949e65a650c6f10a968e83e83dbf7574c518b5
parent051684f125bbc31bd882c4cd313c3482bcd145a0 (diff)
Load component synchronously in method_createQmlObject
setData() is not guaranteed to synchronously load the type. In fact, most of the time, it doesn't. However, by adding a private Synchronous mode to QQmlTypeLoader we can prefetch the type synchronously and then inject it into the component. Task-number: QTBUG-45418 Change-Id: I640f12ad20c01b778b5bc41f43574d8aea504195 Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
-rw-r--r--src/qml/qml/qqmltypeloader.cpp149
-rw-r--r--src/qml/qml/qqmltypeloader_p.h11
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/load_synchronous.qml42
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp11
5 files changed, 152 insertions, 68 deletions
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 270c28c399..29f1ca7959 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -911,97 +911,116 @@ void QQmlTypeLoader::unlock()
m_thread->unlock();
}
-/*!
-Load the provided \a blob from the network or filesystem.
+struct PlainLoader {
+ void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->loadThread(blob);
+ }
+ void load(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->m_thread->load(blob);
+ }
+ void loadAsync(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->m_thread->loadAsync(blob);
+ }
+};
-The loader must be locked.
-*/
-void QQmlTypeLoader::load(QQmlDataBlob *blob, Mode mode)
+struct StaticLoader {
+ const QByteArray &data;
+ StaticLoader(const QByteArray &data) : data(data) {}
+
+ void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->loadWithStaticDataThread(blob, data);
+ }
+ void load(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->m_thread->loadWithStaticData(blob, data);
+ }
+ void loadAsync(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->m_thread->loadWithStaticDataAsync(blob, data);
+ }
+};
+
+struct CachedLoader {
+ const QQmlPrivate::CachedQmlUnit *unit;
+ CachedLoader(const QQmlPrivate::CachedQmlUnit *unit) : unit(unit) {}
+
+ void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->loadWithCachedUnitThread(blob, unit);
+ }
+ void load(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->m_thread->loadWithCachedUnit(blob, unit);
+ }
+ void loadAsync(QQmlTypeLoader *loader, QQmlDataBlob *blob) const
+ {
+ loader->m_thread->loadWithCachedUnit(blob, unit);
+ }
+};
+
+template<typename Loader>
+void QQmlTypeLoader::doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode)
{
#ifdef DATABLOB_DEBUG
- qWarning("QQmlTypeLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()),
+ qWarning("QQmlTypeLoader::doLoad(%s): %s thread", qPrintable(blob->m_url.toString()),
m_thread->isThisThread()?"Compile":"Engine");
#endif
blob->startLoading();
if (m_thread->isThisThread()) {
unlock();
- loadThread(blob);
+ loader.loadThread(this, blob);
lock();
- } else if (mode == PreferSynchronous) {
+ } else if (mode == Asynchronous) {
+ blob->m_data.setIsAsync(true);
unlock();
- m_thread->load(blob);
+ loader.loadAsync(this, 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);
+ loader.load(this, blob);
lock();
+ if (mode == PreferSynchronous) {
+ if (!blob->isCompleteOrError())
+ blob->m_data.setIsAsync(true);
+ } else {
+ Q_ASSERT(mode == Synchronous);
+ while (!blob->isCompleteOrError()) {
+ unlock();
+ m_thread->waitForNextMessage();
+ lock();
+ }
+ }
}
}
/*!
-Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case.
+Load the provided \a blob from the network or filesystem.
The loader must be locked.
*/
-void QQmlTypeLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &data, Mode mode)
+void QQmlTypeLoader::load(QQmlDataBlob *blob, Mode mode)
{
-#ifdef DATABLOB_DEBUG
- qWarning("QQmlTypeLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()),
- m_thread->isThisThread()?"Compile":"Engine");
-#endif
+ doLoad(PlainLoader(), blob, mode);
+}
- blob->startLoading();
+/*!
+Load the provided \a blob with \a data. The blob's URL is not used by the data loader in this case.
- 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();
- }
+The loader must be locked.
+*/
+void QQmlTypeLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &data, Mode mode)
+{
+ doLoad(StaticLoader(data), blob, mode);
}
void QQmlTypeLoader::loadWithCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit, Mode mode)
{
-#ifdef DATABLOB_DEBUG
- qWarning("QQmlTypeLoader::loadWithUnitFcatory(%s, data): %s thread", qPrintable(blob->m_url.toString()),
- m_thread->isThisThread()?"Compile":"Engine");
-#endif
-
- blob->startLoading();
-
- if (m_thread->isThisThread()) {
- unlock();
- loadWithCachedUnitThread(blob, unit);
- lock();
- } else if (mode == PreferSynchronous) {
- unlock();
- m_thread->loadWithCachedUnit(blob, unit);
- lock();
- if (!blob->isCompleteOrError())
- blob->m_data.setIsAsync(true);
- } else {
- Q_ASSERT(mode == Asynchronous);
- blob->m_data.setIsAsync(true);
- unlock();
- m_thread->loadWithCachedUnitAsync(blob, unit);
- lock();
- }
+ doLoad(CachedLoader(unit), blob, mode);
}
void QQmlTypeLoader::loadWithStaticDataThread(QQmlDataBlob *blob, const QByteArray &data)
@@ -1618,7 +1637,7 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode)
} else {
QQmlTypeLoader::load(typeData, mode);
}
- } else if ((mode == PreferSynchronous) && QQmlFile::isSynchronous(url)) {
+ } else if ((mode == PreferSynchronous || mode == Synchronous) && QQmlFile::isSynchronous(url)) {
// this was started Asynchronous, but we need to force Synchronous
// completion now (if at all possible with this type of URL).
@@ -1643,12 +1662,12 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode)
Returns a QQmlTypeData for the given \a data with the provided base \a url. The
QQmlTypeData will not be cached.
*/
-QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url)
+QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Mode mode)
{
LockHolder<QQmlTypeLoader> holder(this);
QQmlTypeData *typeData = new QQmlTypeData(url, this);
- QQmlTypeLoader::loadWithStaticData(typeData, data);
+ QQmlTypeLoader::loadWithStaticData(typeData, data, mode);
return typeData;
}
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index e7b4b8f95b..6433601ba8 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -215,7 +215,7 @@ class Q_AUTOTEST_EXPORT QQmlTypeLoader
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader)
public:
- enum Mode { PreferSynchronous, Asynchronous };
+ enum Mode { PreferSynchronous, Asynchronous, Synchronous };
class Q_QML_PRIVATE_EXPORT Blob : public QQmlDataBlob
{
@@ -283,7 +283,7 @@ public:
QQmlImportDatabase *importDatabase();
QQmlTypeData *getType(const QUrl &url, Mode mode = PreferSynchronous);
- QQmlTypeData *getType(const QByteArray &, const QUrl &url);
+ QQmlTypeData *getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous);
QQmlScriptBlob *getScript(const QUrl &);
QQmlQmldirData *getQmldir(const QUrl &);
@@ -362,6 +362,13 @@ private:
QmldirCache m_qmldirCache;
ImportDirCache m_importDirCache;
ImportQmlDirCache m_importQmlDirCache;
+
+ template<typename Loader>
+ void doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode);
+
+ friend struct PlainLoader;
+ friend struct CachedLoader;
+ friend struct StaticLoader;
};
class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 3fc7ac7748..72a3fe1537 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -1013,8 +1013,13 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx)
if (!parentArg)
V4THROW_ERROR("Qt.createQmlObject(): Missing parent object");
+ QQmlTypeData *typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType(
+ qml.toUtf8(), url, QQmlTypeLoader::Synchronous);
+ Q_ASSERT(typeData->isCompleteOrError());
QQmlComponent component(engine);
- component.setData(qml.toUtf8(), url);
+ QQmlComponentPrivate *componentPrivate = QQmlComponentPrivate::get(&component);
+ componentPrivate->fromTypeData(typeData);
+ componentPrivate->progress = 1.0;
if (component.isError()) {
ScopedValue v(scope, Error::create(ctx->d()->engine, component.errors()));
diff --git a/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml b/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml
new file mode 100644
index 0000000000..5e9c4c2bdc
--- /dev/null
+++ b/tests/auto/qml/qqmltypeloader/data/load_synchronous.qml
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQml 2.2
+
+QtObject {
+ id: top
+
+ Component.onCompleted: {
+ Qt.createQmlObject('QtObject {}', top, 'nonprotocol:');
+ }
+}
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index 77cdaae9f0..4c5b1f7e63 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -43,6 +43,7 @@ class tst_QQMLTypeLoader : public QQmlDataTest
private slots:
void testLoadComplete();
+ void loadComponentSynchronously();
};
void tst_QQMLTypeLoader::testLoadComplete()
@@ -62,6 +63,16 @@ void tst_QQMLTypeLoader::testLoadComplete()
delete window;
}
+void tst_QQMLTypeLoader::loadComponentSynchronously()
+{
+ QQmlEngine engine;
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(
+ QLatin1String(".*nonprotocol::1:1: QtObject is not a type.*")));
+ QQmlComponent component(&engine, testFileUrl("load_synchronous.qml"));
+ QObject *o = component.create();
+ QVERIFY(o);
+}
+
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"