From 1f3038d2144603c687d85b0a7962322d3c9ae422 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Sat, 8 Dec 2012 13:57:12 -0800 Subject: [PATCH] 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 --- src/qml/qml/qqmlimport.cpp | 14 +++-- src/qml/qml/qqmltypeloader.cpp | 59 ++++++++++++++++------ src/qml/qml/qqmltypeloader_p.h | 2 + tests/auto/qml/qqmllanguage/data/LocalLast2.qml | 2 + .../auto/qml/qqmllanguage/data/localOrderTest.qml | 7 +++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 31 ++++++++++++ .../qqmlmoduleplugin/data/implicit2/temptest2.qml | 2 + 7 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/LocalLast2.qml create mode 100644 tests/auto/qml/qqmllanguage/data/localOrderTest.qml diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index d3ec9e6..f793ca9 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 *errors); + QList *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 *errors) + QList *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 *errors) { diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 9547204..e800eb8 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 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 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 errors; if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) { setError(errors); @@ -2054,6 +2066,8 @@ void QQmlTypeData::dataReceived(const Data &data) } } + QList 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 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 b4ecfb7..68b8f33 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -465,6 +465,8 @@ private: QList 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 0000000..a6acfcd --- /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 0000000..a6a9a4d --- /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 3121d10..5a924a9 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(component.create()); + QVERIFY(object != 0); + QVERIFY(QString(object->metaObject()->className()).startsWith(QLatin1String("QQuickMouseArea"))); + QObject* object2 = object->property("item").value(); + 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 0fa9f6e..051c6f8 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{} } -- 2.7.4