aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Alpert <aalpert@rim.com>2012-12-08 13:57:12 -0800
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-03-27 20:40:28 +0100
commit1f3038d2144603c687d85b0a7962322d3c9ae422 (patch)
tree461b411e8b5b98a0f37495a51e24687d4ca675f5
parentf7ada9b9325c7adc10da6a3a4e7f887452682260 (diff)
Delay loading implicit import
As a performance improvement to avoid extra filesystem access, only import "." if it is needed for type resolution. Change-Id: If9be25deb3205f8c81f9f418404d9fb41bebb84f Reviewed-by: Christopher Adams <chris.adams@jollamobile.com>
-rw-r--r--src/qml/qml/qqmlimport.cpp14
-rw-r--r--src/qml/qml/qqmltypeloader.cpp59
-rw-r--r--src/qml/qml/qqmltypeloader_p.h2
-rw-r--r--tests/auto/qml/qqmllanguage/data/LocalLast2.qml2
-rw-r--r--tests/auto/qml/qqmllanguage/data/localOrderTest.qml7
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp31
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/data/implicit2/temptest2.qml2
7 files changed, 97 insertions, 20 deletions
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index d3ec9e67f7..f793ca9604 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -260,7 +260,7 @@ public:
QQmlImportNamespace::Import *addImportToNamespace(QQmlImportNamespace *nameSpace,
const QString &uri, const QString &url,
int vmaj, int vmin, QQmlScript::Import::Type type,
- QList<QQmlError> *errors);
+ QList<QQmlError> *errors, bool lowPrecedence = false);
};
/*!
@@ -1003,7 +1003,7 @@ QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix)
QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace,
const QString &uri, const QString &url, int vmaj, int vmin,
QQmlScript::Import::Type type,
- QList<QQmlError> *errors)
+ QList<QQmlError> *errors, bool lowPrecedence)
{
Q_ASSERT(nameSpace);
Q_ASSERT(errors);
@@ -1017,7 +1017,11 @@ QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImport
import->minversion = vmin;
import->isLibrary = (type == QQmlScript::Import::Library);
- nameSpace->imports.prepend(import);
+ if (lowPrecedence)
+ nameSpace->imports.append(import);
+ else
+ nameSpace->imports.prepend(import);
+
return import;
}
@@ -1162,7 +1166,7 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix
if (!url.endsWith(Slash) && !url.endsWith(Backslash))
url += Slash;
- QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QQmlScript::Import::File, errors);
+ QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QQmlScript::Import::File, errors, isImplicitImport);
Q_ASSERT(inserted);
if (!incomplete && !qmldirIdentifier.isEmpty()) {
@@ -1238,6 +1242,8 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString &
Adds an implicit "." file import. This is equivalent to calling addFileImport(), but error
messages related to the path or qmldir file not existing are suppressed.
+
+ Additionally, this will add the import with lowest instead of highest precedence.
*/
bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors)
{
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 9547204760..e800eb815d 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -1902,7 +1902,7 @@ QQmlTypeData::TypeDataCallback::~TypeDataCallback()
QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader::Options options,
QQmlTypeLoader *manager)
: QQmlTypeLoader::Blob(url, QmlFile, manager), m_options(options),
- m_typesResolved(false), m_compiledData(0), m_implicitImport(0)
+ m_typesResolved(false), m_compiledData(0), m_implicitImport(0), m_implicitImportLoaded(false)
{
}
@@ -2008,23 +2008,14 @@ void QQmlTypeData::completed()
}
}
-void QQmlTypeData::dataReceived(const Data &data)
+bool QQmlTypeData::loadImplicitImport()
{
- QString code = QString::fromUtf8(data.data(), data.size());
- QByteArray preparseData;
-
- if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse"));
-
- if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) {
- setError(scriptParser.errors());
- return;
- }
+ m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error)
m_imports.setBaseUrl(finalUrl(), finalUrlString());
QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
-
- // For local urls, add an implicit import "." as first (most overridden) lookup.
+ // For local urls, add an implicit import "." as most overridden lookup.
// This will also trigger the loading of the qmldir and the import of any native
// types from available plugins.
QList<QQmlError> implicitImportErrors;
@@ -2032,20 +2023,41 @@ void QQmlTypeData::dataReceived(const Data &data)
if (!implicitImportErrors.isEmpty()) {
setError(implicitImportErrors);
+ return false;
+ }
+
+ return true;
+}
+
+void QQmlTypeData::dataReceived(const Data &data)
+{
+ QString code = QString::fromUtf8(data.data(), data.size());
+ QByteArray preparseData;
+
+ if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse"));
+
+ if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) {
+ setError(scriptParser.errors());
return;
}
- QList<QQmlError> errors;
+ m_imports.setBaseUrl(finalUrl(), finalUrlString());
+ // For remote URLs, we don't delay the loading of the implicit import
+ // because the loading probably requires an asynchronous fetch of the
+ // qmldir (so we can't load it just in time).
if (!finalUrl().scheme().isEmpty()) {
QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
if (!QQmlImports::isLocal(qmldirUrl)) {
+ if (!loadImplicitImport())
+ return;
// This qmldir is for the implicit import
m_implicitImport = new QQmlScript::Import;
m_implicitImport->uri = QLatin1String(".");
m_implicitImport->qualifier = QString();
m_implicitImport->majorVersion = -1;
m_implicitImport->minorVersion = -1;
+ QList<QQmlError> errors;
if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) {
setError(errors);
@@ -2054,6 +2066,8 @@ void QQmlTypeData::dataReceived(const Data &data)
}
}
+ QList<QQmlError> errors;
+
foreach (const QQmlScript::Import &import, scriptParser.imports()) {
if (!addImport(import, &errors)) {
Q_ASSERT(errors.size());
@@ -2155,8 +2169,21 @@ void QQmlTypeData::resolveTypes()
QQmlImportNamespace *typeNamespace = 0;
QList<QQmlError> errors;
- if (!m_imports.resolveType(parserRef->name, &ref.type, &majorVersion, &minorVersion,
- &typeNamespace, &errors) || typeNamespace) {
+ bool typeFound = m_imports.resolveType(parserRef->name, &ref.type,
+ &majorVersion, &minorVersion, &typeNamespace, &errors);
+ if (!typeNamespace && !typeFound && !m_implicitImportLoaded) {
+ // Lazy loading of implicit import
+ if (loadImplicitImport()) {
+ // Try again to find the type
+ errors.clear();
+ typeFound = m_imports.resolveType(parserRef->name, &ref.type,
+ &majorVersion, &minorVersion, &typeNamespace, &errors);
+ } else {
+ return; //loadImplicitImport() hit an error, and called setError already
+ }
+ }
+
+ if (!typeFound || typeNamespace) {
// Known to not be a type:
// - known to be a namespace (Namespace {})
// - type with unknown namespace (UnknownNamespace.SomeType {})
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index b4ecfb77d1..68b8f33f88 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -465,6 +465,8 @@ private:
QList<TypeDataCallback *> m_callbacks;
QQmlScript::Import *m_implicitImport;
+ bool m_implicitImportLoaded;
+ bool loadImplicitImport();
};
// QQmlScriptData instances are created, uninitialized, by the loader in the
diff --git a/tests/auto/qml/qqmllanguage/data/LocalLast2.qml b/tests/auto/qml/qqmllanguage/data/LocalLast2.qml
new file mode 100644
index 0000000000..a6acfcde7c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/LocalLast2.qml
@@ -0,0 +1,2 @@
+import QtQuick 2.0
+MouseArea {}
diff --git a/tests/auto/qml/qqmllanguage/data/localOrderTest.qml b/tests/auto/qml/qqmllanguage/data/localOrderTest.qml
new file mode 100644
index 0000000000..a6a9a4d627
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/localOrderTest.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+import org.qtproject.installedtest 1.0
+
+LocalLast2 {
+ property QtObject item: LocalLast {}
+}
+
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 3121d10265..5a924a901a 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -140,6 +140,7 @@ private slots:
void readonly();
void receivers();
void registeredCompositeType();
+ void implicitImportsLast();
void basicRemote_data();
void basicRemote();
@@ -2470,6 +2471,12 @@ void tst_qqmllanguage::importsOrder_data()
<< (!qmlCheckTypes()?"QQuickRectangle":"")// i.e. from org.qtproject.installedtest, not data/LocalLast.qml
<< (!qmlCheckTypes()?"":"LocalLast is ambiguous. Found in lib/org/qtproject/installedtest/ and in ")
<< false;
+ QTest::newRow("local last 3") << //Forces it to load the local qmldir to resolve types, but they shouldn't override anything
+ "import org.qtproject.installedtest 1.0\n"
+ "LocalLast {LocalLast2{}}"
+ << (!qmlCheckTypes()?"QQuickRectangle":"")// i.e. from org.qtproject.installedtest, not data/LocalLast.qml
+ << (!qmlCheckTypes()?"":"LocalLast is ambiguous. Found in lib/org/qtproject/installedtest/ and in ")
+ << false;
}
void tst_qqmllanguage::importsOrder()
@@ -3142,6 +3149,30 @@ void tst_qqmllanguage::scopedProperties()
QVERIFY(o->property("success").toBool());
}
+// Tests that the implicit import has lowest precedence, in the case where
+// there are conflicting types and types only found in the local import.
+// Tests that just check one (or the root) type are in ::importsOrder
+void tst_qqmllanguage::implicitImportsLast()
+{
+ if (qmlCheckTypes())
+ QSKIP("This test is about maintaining the same choice when type is ambiguous.");
+
+ if (engine.importPathList() == defaultImportPathList)
+ engine.addImportPath(testFile("lib"));
+
+ QQmlComponent component(&engine, testFile("localOrderTest.qml"));
+ VERIFY_ERRORS(0);
+ QObject *object = qobject_cast<QObject *>(component.create());
+ QVERIFY(object != 0);
+ QVERIFY(QString(object->metaObject()->className()).startsWith(QLatin1String("QQuickMouseArea")));
+ QObject* object2 = object->property("item").value<QObject*>();
+ QVERIFY(object2 != 0);
+ QCOMPARE(QString(object2->metaObject()->className()), QLatin1String("QQuickRectangle"));
+
+ engine.setImportPathList(defaultImportPathList);
+}
+
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmlmoduleplugin/data/implicit2/temptest2.qml b/tests/auto/qml/qqmlmoduleplugin/data/implicit2/temptest2.qml
index 0fa9f6e051..051c6f8904 100644
--- a/tests/auto/qml/qqmlmoduleplugin/data/implicit2/temptest2.qml
+++ b/tests/auto/qml/qqmlmoduleplugin/data/implicit2/temptest2.qml
@@ -1,8 +1,10 @@
import QtQuick 2.0
// the type loader will implicitly search "." for a qmldir
+// to try and find the missing type of AItem
// and the qmldir has various syntax errors in it.
Item {
id: root
+ AItem{}
}