diff options
52 files changed, 3498 insertions, 2574 deletions
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index 7b931c25d2..fbaf3bc4e2 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include <private/qv4scopedvalue_p.h> #include <QtQml/qqmlextensionplugin.h> #include <QtQml/qqml.h> #include <QtQml/qjsvalue.h> diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp index 9927089e5e..bb43f75c63 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp @@ -40,6 +40,8 @@ #include "qqmlpreviewfileloader.h" #include "qqmlpreviewservice.h" +#include <QtQml/qqmlfile.h> + #include <QtCore/qlibraryinfo.h> #include <QtCore/qstandardpaths.h> diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index b542d7f918..649f83070f 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -79,7 +79,6 @@ QT_BEGIN_NAMESPACE class QIODevice; class QQmlPropertyData; class QQmlTypeNameCache; -class QQmlScriptData; class QQmlType; class QQmlEngine; diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 2a8382a7b0..7d46ab2ab1 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -48,6 +48,7 @@ #include <private/qqmlengine_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qqmlvaluetypewrapper_p.h> +#include <private/qqmlscriptdata_p.h> #include <private/qv4module_p.h> #include <private/qv4compilationunitmapper_p.h> #include <private/qml_compile_hash_p.h> diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h index 010b8a2fd0..63a79ed656 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit_p.h +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE +class QQmlScriptData; class QQmlEnginePrivate; namespace QV4 { diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 92face6f94..28457de643 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -50,6 +50,7 @@ #include <QtQml/qqmlfile.h> #include <private/qqmlengine_p.h> +#include <private/qqmlirbuilder_p.h> #include <private/qv4engine_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index fa229cc062..c36da3815d 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -57,6 +57,7 @@ #include <private/qv4variantobject_p.h> #include <private/qv4identifiertable_p.h> #include <private/qv4lookup_p.h> +#include <private/qv4qmlcontext_p.h> #if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 38cce2a7a9..8a7cbdfb2a 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -59,6 +59,7 @@ #include <private/qqmltypewrapper_p.h> #include <private/qqmlengine_p.h> #include <private/qqmljavascriptexpression_p.h> +#include <private/qqmljsast_p.h> #include "qv4qobjectwrapper_p.h" #include "qv4symbol_p.h" #include "qv4generatorobject_p.h" diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 9b9268fd8e..f411a20476 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -52,6 +52,7 @@ #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> #include <private/qqmlengine_p.h> +#include <private/qqmlirbuilder_p.h> #include <private/qv4profiling_p.h> #include <qv4runtimecodegen_p.h> diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 15ea12cbe7..660a883e63 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -1,7 +1,14 @@ SOURCES += \ $$PWD/qqml.cpp \ + $$PWD/qqmldatablob.cpp \ + $$PWD/qqmldirdata.cpp \ $$PWD/qqmlerror.cpp \ $$PWD/qqmlopenmetaobject.cpp \ + $$PWD/qqmlscriptblob.cpp \ + $$PWD/qqmlscriptdata.cpp \ + $$PWD/qqmltypedata.cpp \ + $$PWD/qqmltypeloaderqmldircontent.cpp \ + $$PWD/qqmltypeloaderthread.cpp \ $$PWD/qqmlvmemetaobject.cpp \ $$PWD/qqmlengine.cpp \ $$PWD/qqmlexpression.cpp \ @@ -61,8 +68,15 @@ SOURCES += \ $$PWD/qqmlpropertyvalidator.cpp HEADERS += \ + $$PWD/qqmldatablob_p.h \ + $$PWD/qqmldirdata_p.h \ $$PWD/qqmlglobal_p.h \ $$PWD/qqmlopenmetaobject_p.h \ + $$PWD/qqmlscriptblob_p.h \ + $$PWD/qqmlscriptdata_p.h \ + $$PWD/qqmltypedata_p.h \ + $$PWD/qqmltypeloaderqmldircontent_p.h \ + $$PWD/qqmltypeloaderthread_p.h \ $$PWD/qqmlvmemetaobject_p.h \ $$PWD/qqml.h \ $$PWD/qqmlproperty.h \ @@ -163,5 +177,13 @@ qtConfig(qml-locale) { $$PWD/qqmllocale.cpp } +qtConfig(qml-network) { + HEADERS += \ + $$PWD/qqmltypeloadernetworkreplyproxy_p.h + + SOURCES += \ + $$PWD/qqmltypeloadernetworkreplyproxy.cpp +} + include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 7149f8c134..e93cfcadb9 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include <QtQml/qqmlfile.h> #include <QtCore/QCoreApplication> #include <QtCore/QTranslator> #include <QQmlComponent> diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 7fb15af570..3a437eab8d 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -49,6 +49,7 @@ #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlvaluetypewrapper_p.h> +#include <private/qv4qmlcontext_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qv4variantobject_p.h> #include <private/qv4jscall_p.h> diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 85b02dcde4..7f96b4df9f 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -63,6 +63,7 @@ #include <private/qqmlabstractbinding_p.h> #include <private/qqmljavascriptexpression_p.h> +#include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index dc973630a7..ff01e737ca 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -56,6 +56,7 @@ #include <private/qv4value_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4qmlcontext_p.h> #include <QtCore/qdebug.h> diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp new file mode 100644 index 0000000000..2183721d32 --- /dev/null +++ b/src/qml/qml/qqmldatablob.cpp @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmldatablob_p.h> +#include <private/qqmlglobal_p.h> +#include <private/qqmlprofiler_p.h> +#include <private/qqmltypeloader_p.h> +#include <private/qqmltypeloaderthread_p.h> + +#include <QtQml/qqmlengine.h> + +#ifdef DATABLOB_DEBUG +#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false) +#else +#define ASSERT_CALLBACK() +#endif + +DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); + +QT_BEGIN_NAMESPACE + +/*! +\class QQmlDataBlob +\brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlTypeLoader. +\internal + +QQmlDataBlob's are loaded by a QQmlTypeLoader. The user creates the QQmlDataBlob +and then calls QQmlTypeLoader::load() or QQmlTypeLoader::loadWithStaticData() to load it. +The QQmlTypeLoader invokes callbacks on the QQmlDataBlob as data becomes available. +*/ + +/*! +\enum QQmlDataBlob::Status + +This enum describes the status of the data blob. + +\list +\li Null The blob has not yet been loaded by a QQmlTypeLoader +\li Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been + invoked or has not yet returned. +\li WaitingForDependencies The blob is waiting for dependencies to be done before continuing. + This status only occurs after the QQmlDataBlob::setData() callback has been made, and when the + blob has outstanding dependencies. +\li Complete The blob's data has been loaded and all dependencies are done. +\li Error An error has been set on this blob. +\endlist +*/ + +/*! +\enum QQmlDataBlob::Type + +This enum describes the type of the data blob. + +\list +\li QmlFile This is a QQmlTypeData +\li JavaScriptFile This is a QQmlScriptData +\li QmldirFile This is a QQmlQmldirData +\endlist +*/ + +/*! +Create a new QQmlDataBlob for \a url and of the provided \a type. +*/ +QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type, QQmlTypeLoader *manager) +: m_typeLoader(manager), m_type(type), m_url(url), m_finalUrl(url), m_redirectCount(0), + m_inCallback(false), m_isDone(false) +{ + //Set here because we need to get the engine from the manager + if (m_typeLoader->engine() && m_typeLoader->engine()->urlInterceptor()) + m_url = m_typeLoader->engine()->urlInterceptor()->intercept(m_url, + (QQmlAbstractUrlInterceptor::DataType)m_type); +} + +/*! \internal */ +QQmlDataBlob::~QQmlDataBlob() +{ + Q_ASSERT(m_waitingOnMe.isEmpty()); + + cancelAllWaitingFor(); +} + +/*! + Must be called before loading can occur. +*/ +void QQmlDataBlob::startLoading() +{ + Q_ASSERT(status() == QQmlDataBlob::Null); + m_data.setStatus(QQmlDataBlob::Loading); +} + +/*! +Returns the type provided to the constructor. +*/ +QQmlDataBlob::Type QQmlDataBlob::type() const +{ + return m_type; +} + +/*! +Returns the blob's status. +*/ +QQmlDataBlob::Status QQmlDataBlob::status() const +{ + return m_data.status(); +} + +/*! +Returns true if the status is Null. +*/ +bool QQmlDataBlob::isNull() const +{ + return status() == Null; +} + +/*! +Returns true if the status is Loading. +*/ +bool QQmlDataBlob::isLoading() const +{ + return status() == Loading; +} + +/*! +Returns true if the status is WaitingForDependencies. +*/ +bool QQmlDataBlob::isWaiting() const +{ + return status() == WaitingForDependencies || + status() == ResolvingDependencies; +} + +/*! +Returns true if the status is Complete. +*/ +bool QQmlDataBlob::isComplete() const +{ + return status() == Complete; +} + +/*! +Returns true if the status is Error. +*/ +bool QQmlDataBlob::isError() const +{ + return status() == Error; +} + +/*! +Returns true if the status is Complete or Error. +*/ +bool QQmlDataBlob::isCompleteOrError() const +{ + Status s = status(); + return s == Error || s == Complete; +} + +/*! +Returns the data download progress from 0 to 1. +*/ +qreal QQmlDataBlob::progress() const +{ + quint8 p = m_data.progress(); + if (p == 0xFF) return 1.; + else return qreal(p) / qreal(0xFF); +} + +/*! +Returns the physical url of the data. Initially this is the same as +finalUrl(), but if a URL interceptor is set, it will work on this URL +and leave finalUrl() alone. + +\sa finalUrl() +*/ +QUrl QQmlDataBlob::url() const +{ + return m_url; +} + +QString QQmlDataBlob::urlString() const +{ + if (m_urlString.isEmpty()) + m_urlString = m_url.toString(); + + return m_urlString; +} + +/*! +Returns the logical URL to be used for resolving further URLs referred to in +the code. + +This is the blob url passed to the constructor. If a URL interceptor rewrites +the URL, this one stays the same. If a network redirect happens while fetching +the data, this url is updated to reflect the new location. Therefore, if both +an interception and a redirection happen, the final url will indirectly +incorporate the result of the interception, potentially breaking further +lookups. + +\sa url() +*/ +QUrl QQmlDataBlob::finalUrl() const +{ + return m_finalUrl; +} + +/*! +Returns the finalUrl() as a string. +*/ +QString QQmlDataBlob::finalUrlString() const +{ + if (m_finalUrlString.isEmpty()) + m_finalUrlString = m_finalUrl.toString(); + + return m_finalUrlString; +} + +/*! +Return the errors on this blob. + +May only be called from the load thread, or after the blob isCompleteOrError(). +*/ +QList<QQmlError> QQmlDataBlob::errors() const +{ + Q_ASSERT(isCompleteOrError() || (m_typeLoader && m_typeLoader->m_thread->isThisThread())); + return m_errors; +} + +/*! +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. + +The setError() method may only be called from within a QQmlDataBlob callback. +*/ +void QQmlDataBlob::setError(const QQmlError &errors) +{ + ASSERT_CALLBACK(); + + QList<QQmlError> l; + l << errors; + setError(l); +} + +/*! +\overload +*/ +void QQmlDataBlob::setError(const QList<QQmlError> &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); + + if (dumpErrors()) { + qWarning().nospace() << "Errors for " << urlString(); + for (int ii = 0; ii < errors.count(); ++ii) + qWarning().nospace() << " " << qPrintable(errors.at(ii).toString()); + } + cancelAllWaitingFor(); + + if (!m_inCallback) + tryDone(); +} + +void QQmlDataBlob::setError(const QQmlJS::DiagnosticMessage &error) +{ + QQmlError e; + e.setColumn(error.column); + e.setLine(error.line); + e.setDescription(error.message); + e.setUrl(url()); + setError(e); +} + +void QQmlDataBlob::setError(const QVector<QQmlJS::DiagnosticMessage> &errors) +{ + QList<QQmlError> finalErrors; + finalErrors.reserve(errors.count()); + for (const auto &error : errors) { + QQmlError e; + e.setColumn(error.column); + e.setLine(error.line); + e.setDescription(error.message); + e.setUrl(url()); + finalErrors << e; + } + setError(finalErrors); +} + +void QQmlDataBlob::setError(const QString &description) +{ + QQmlError e; + e.setDescription(description); + e.setUrl(url()); + setError(e); +} + +/*! +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 QQmlDataBlob callback. +*/ +void QQmlDataBlob::addDependency(QQmlDataBlob *blob) +{ + ASSERT_CALLBACK(); + + Q_ASSERT(status() != Null); + + if (!blob || + blob->status() == Error || blob->status() == Complete || + status() == Error || status() == Complete || m_isDone) + return; + + for (auto existingDep: qAsConst(m_waitingFor)) + if (existingDep.data() == blob) + return; + + m_data.setStatus(WaitingForDependencies); + + m_waitingFor.append(blob); + blob->m_waitingOnMe.append(this); +} + +/*! +\fn void QQmlDataBlob::dataReceived(const Data &data) + +Invoked when data for the blob is received. Implementors should use this callback +to determine a blob's dependencies. Within this callback you may call setError() +or addDependency(). +*/ + +/*! +Invoked once data has either been received or a network error occurred, and all +dependencies are complete. + +You can set an error in this method, but you cannot add new dependencies. Implementors +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 QQmlDataBlob::done() +{ +} + +#if QT_CONFIG(qml_network) +/*! +Invoked if there is a network error while fetching this blob. + +The default implementation sets an appropriate QQmlError. +*/ +void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError) +{ + Q_UNUSED(networkError); + + QQmlError error; + error.setUrl(m_url); + + const char *errorString = nullptr; + switch (networkError) { + default: + errorString = "Network error"; + break; + case QNetworkReply::ConnectionRefusedError: + errorString = "Connection refused"; + break; + case QNetworkReply::RemoteHostClosedError: + errorString = "Remote host closed the connection"; + break; + case QNetworkReply::HostNotFoundError: + errorString = "Host not found"; + break; + case QNetworkReply::TimeoutError: + errorString = "Timeout"; + break; + case QNetworkReply::ProxyConnectionRefusedError: + case QNetworkReply::ProxyConnectionClosedError: + case QNetworkReply::ProxyNotFoundError: + case QNetworkReply::ProxyTimeoutError: + case QNetworkReply::ProxyAuthenticationRequiredError: + case QNetworkReply::UnknownProxyError: + errorString = "Proxy error"; + break; + case QNetworkReply::ContentAccessDenied: + errorString = "Access denied"; + break; + case QNetworkReply::ContentNotFoundError: + errorString = "File not found"; + break; + case QNetworkReply::AuthenticationRequiredError: + errorString = "Authentication required"; + break; + }; + + error.setDescription(QLatin1String(errorString)); + + setError(error); +} +#endif // qml_network + +/*! +Called if \a blob, which was previously waited for, has an error. + +The default implementation does nothing. +*/ +void QQmlDataBlob::dependencyError(QQmlDataBlob *blob) +{ + Q_UNUSED(blob); +} + +/*! +Called if \a blob, which was previously waited for, has completed. + +The default implementation does nothing. +*/ +void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob) +{ + Q_UNUSED(blob); +} + +/*! +Called when all blobs waited for have completed. This occurs regardless of +whether they are in error, or complete state. + +The default implementation does nothing. +*/ +void QQmlDataBlob::allDependenciesDone() +{ + m_data.setStatus(QQmlDataBlob::ResolvingDependencies); +} + +/*! +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 QQmlDataBlob::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 QQmlDataBlob::completed() +{ +} + + +void QQmlDataBlob::tryDone() +{ + if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) { + m_isDone = true; + addref(); + +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataBlob::done() %s", qPrintable(urlString())); +#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); +#ifdef DATABLOB_DEBUG + qWarning("QQmlDataBlob: Dispatching completed"); +#endif + m_typeLoader->m_thread->callCompleted(this); + + release(); + } +} + +void QQmlDataBlob::cancelAllWaitingFor() +{ + while (m_waitingFor.count()) { + QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast(); + + Q_ASSERT(blob->m_waitingOnMe.contains(this)); + + blob->m_waitingOnMe.removeOne(this); + } +} + +void QQmlDataBlob::notifyAllWaitingOnMe() +{ + while (m_waitingOnMe.count()) { + QQmlDataBlob *blob = m_waitingOnMe.takeLast(); + + Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(), + [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; })); + + blob->notifyComplete(this); + } +} + +void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) +{ + Q_ASSERT(blob->status() == Error || blob->status() == Complete); + QQmlCompilingProfiler prof(typeLoader()->profiler(), blob); + + m_inCallback = true; + + QQmlRefPointer<QQmlDataBlob> blobRef; + for (int i = 0; i < m_waitingFor.count(); ++i) { + if (m_waitingFor.at(i).data() == blob) { + blobRef = m_waitingFor.takeAt(i); + break; + } + } + Q_ASSERT(blobRef); + + if (blob->status() == Error) { + dependencyError(blob); + } else if (blob->status() == Complete) { + dependencyComplete(blob); + } + + if (!isError() && m_waitingFor.isEmpty()) + allDependenciesDone(); + + m_inCallback = false; + + tryDone(); +} + +QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const +{ + error->clear(); + if (hasInlineSourceCode) + return inlineSourceCode; + + QFile f(fileInfo.absoluteFilePath()); + if (!f.open(QIODevice::ReadOnly)) { + *error = f.errorString(); + return QString(); + } + + const qint64 fileSize = fileInfo.size(); + + if (uchar *mappedData = f.map(0, fileSize)) { + QString source = QString::fromUtf8(reinterpret_cast<const char *>(mappedData), fileSize); + f.unmap(mappedData); + return source; + } + + QByteArray data(fileSize, Qt::Uninitialized); + if (f.read(data.data(), data.length()) != data.length()) { + *error = f.errorString(); + return QString(); + } + return QString::fromUtf8(data); +} + +QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const +{ + if (hasInlineSourceCode) + return QDateTime(); + + return fileInfo.lastModified(); +} + +bool QQmlDataBlob::SourceCodeData::exists() const +{ + if (hasInlineSourceCode) + return true; + return fileInfo.exists(); +} + +bool QQmlDataBlob::SourceCodeData::isEmpty() const +{ + if (hasInlineSourceCode) + return inlineSourceCode.isEmpty(); + return fileInfo.size() == 0; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmldatablob_p.h b/src/qml/qml/qqmldatablob_p.h new file mode 100644 index 0000000000..da3bbe2c1f --- /dev/null +++ b/src/qml/qml/qqmldatablob_p.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATABLOB_P_H +#define QQMLDATABLOB_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 <private/qqmlrefcount_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> +#include <private/qv4compileddata_p.h> + +#if QT_CONFIG(qml_network) +#include <QtNetwork/qnetworkreply.h> +#endif + +#include <QtQml/qqmlerror.h> +#include <QtQml/qqmlabstracturlinterceptor.h> + +#include <QtCore/qdatetime.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qurl.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypeLoader; +class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount +{ +public: + enum Status { + Null, // Prior to QQmlTypeLoader::load() + Loading, // Prior to data being received and dataReceived() being called + WaitingForDependencies, // While there are outstanding addDependency()s + ResolvingDependencies, // While resolving outstanding dependencies, to detect cycles + Complete, // Finished + Error // Error + }; + + enum Type { //Matched in QQmlAbstractUrlInterceptor + QmlFile = QQmlAbstractUrlInterceptor::QmlFile, + JavaScriptFile = QQmlAbstractUrlInterceptor::JavaScriptFile, + QmldirFile = QQmlAbstractUrlInterceptor::QmldirFile + }; + + QQmlDataBlob(const QUrl &, Type, QQmlTypeLoader* manager); + ~QQmlDataBlob() override; + + void startLoading(); + + QQmlTypeLoader *typeLoader() const { return m_typeLoader; } + + Type type() const; + + Status status() const; + bool isNull() const; + bool isLoading() const; + bool isWaiting() const; + bool isComplete() const; + bool isError() const; + bool isCompleteOrError() const; + + qreal progress() const; + + QUrl url() const; + QString urlString() const; + QUrl finalUrl() const; + QString finalUrlString() const; + + QList<QQmlError> errors() const; + + class SourceCodeData { + public: + QString readAll(QString *error) const; + QDateTime sourceTimeStamp() const; + bool exists() const; + bool isEmpty() const; + private: + friend class QQmlDataBlob; + friend class QQmlTypeLoader; + QString inlineSourceCode; + QFileInfo fileInfo; + bool hasInlineSourceCode = false; + }; + +protected: + // Can be called from within callbacks + void setError(const QQmlError &); + void setError(const QList<QQmlError> &errors); + void setError(const QQmlJS::DiagnosticMessage &error); + void setError(const QVector<QQmlJS::DiagnosticMessage> &errors); + void setError(const QString &description); + void addDependency(QQmlDataBlob *); + + // Callbacks made in load thread + virtual void dataReceived(const SourceCodeData &) = 0; + virtual void initializeFromCachedUnit(const QV4::CompiledData::Unit*) = 0; + virtual void done(); +#if QT_CONFIG(qml_network) + virtual void networkError(QNetworkReply::NetworkError); +#endif + virtual void dependencyError(QQmlDataBlob *); + virtual void dependencyComplete(QQmlDataBlob *); + virtual void allDependenciesDone(); + + // Callbacks made in main thread + virtual void downloadProgressChanged(qreal); + virtual void completed(); + +protected: + // Manager that is currently fetching data for me + QQmlTypeLoader *m_typeLoader; + +private: + friend class QQmlTypeLoader; + friend class QQmlTypeLoaderThread; + + void tryDone(); + void cancelAllWaitingFor(); + void notifyAllWaitingOnMe(); + void notifyComplete(QQmlDataBlob *); + + struct ThreadData { + private: + enum { + StatusMask = 0x0000FFFF, + StatusShift = 0, + ProgressMask = 0x00FF0000, + ProgressShift = 16, + AsyncMask = 0x80000000, + NoMask = 0 + }; + + public: + inline ThreadData() + : _p(0) + { + } + + inline QQmlDataBlob::Status status() const + { + return QQmlDataBlob::Status((_p.loadRelaxed() & StatusMask) >> StatusShift); + } + + inline void setStatus(QQmlDataBlob::Status status) + { + while (true) { + int d = _p.loadRelaxed(); + int nd = (d & ~StatusMask) | ((status << StatusShift) & StatusMask); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } + } + + inline bool isAsync() const + { + return _p.loadRelaxed() & AsyncMask; + } + + inline void setIsAsync(bool v) + { + while (true) { + int d = _p.loadRelaxed(); + int nd = (d & ~AsyncMask) | (v ? AsyncMask : NoMask); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } + } + + inline quint8 progress() const + { + return quint8((_p.loadRelaxed() & ProgressMask) >> ProgressShift); + } + + inline void setProgress(quint8 v) + { + while (true) { + int d = _p.loadRelaxed(); + int nd = (d & ~ProgressMask) | ((v << ProgressShift) & ProgressMask); + if (d == nd || _p.testAndSetOrdered(d, nd)) return; + } + } + + 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<QQmlError> m_errors; + + Type m_type; + + QUrl m_url; + QUrl m_finalUrl; + mutable QString m_urlString; + mutable QString m_finalUrlString; + + // List of QQmlDataBlob's that are waiting for me to complete. + QList<QQmlDataBlob *> m_waitingOnMe; + + // List of QQmlDataBlob's that I am waiting for to complete. + QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor; + + int m_redirectCount:30; + bool m_inCallback:1; + bool m_isDone:1; +}; + +QT_END_NAMESPACE + +#endif // QQMLDATABLOB_P_H diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp index 857b5be8b8..02fde97b3d 100644 --- a/src/qml/qml/qqmldelayedcallqueue.cpp +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -43,6 +43,7 @@ #include <private/qv4value_p.h> #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> +#include <private/qv4qmlcontext_p.h> #include <QQmlError> diff --git a/src/qml/qml/qqmldirdata.cpp b/src/qml/qml/qqmldirdata.cpp new file mode 100644 index 0000000000..ec398fa896 --- /dev/null +++ b/src/qml/qml/qqmldirdata.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmldirdata_p.h> + +QT_BEGIN_NAMESPACE + +QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader) + : QQmlTypeLoader::Blob(url, QmldirFile, loader) +{ +} + +const QString &QQmlQmldirData::content() const +{ + return m_content; +} + +const QV4::CompiledData::Import *QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const +{ + QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *>::const_iterator it = + m_imports.find(blob); + if (it == m_imports.end()) + return nullptr; + return *it; +} + +void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import) +{ + m_imports[blob] = import; +} + +int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const +{ + QHash<QQmlTypeLoader::Blob *, int>::const_iterator it = m_priorities.find(blob); + if (it == m_priorities.end()) + return 0; + return *it; +} + +void QQmlQmldirData::setPriority(QQmlTypeLoader::Blob *blob, int priority) +{ + m_priorities[blob] = priority; +} + +void QQmlQmldirData::dataReceived(const SourceCodeData &data) +{ + QString error; + m_content = data.readAll(&error); + if (!error.isEmpty()) { + setError(error); + return; + } +} + +void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *) +{ + Q_UNIMPLEMENTED(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmldirdata_p.h b/src/qml/qml/qqmldirdata_p.h new file mode 100644 index 0000000000..6af393c47f --- /dev/null +++ b/src/qml/qml/qqmldirdata_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDIRDATA_P_H +#define QQMLDIRDATA_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 <private/qqmltypeloader_p.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob +{ +private: + friend class QQmlTypeLoader; + + QQmlQmldirData(const QUrl &, QQmlTypeLoader *); + +public: + const QString &content() const; + + const QV4::CompiledData::Import *import(QQmlTypeLoader::Blob *) const; + void setImport(QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *); + + int priority(QQmlTypeLoader::Blob *) const; + void setPriority(QQmlTypeLoader::Blob *, int); + +protected: + void dataReceived(const SourceCodeData &) override; + void initializeFromCachedUnit(const QV4::CompiledData::Unit *) override; + +private: + QString m_content; + QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *> m_imports; + QHash<QQmlTypeLoader::Blob *, int> m_priorities; +}; + +QT_END_NAMESPACE + +#endif // QQMLDIRDATA_P_H diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index e726e0810d..f5a9e6aac4 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2325,6 +2325,17 @@ QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex()); } +// #### Qt 6: Remove this function, it exists only for binary compatibility. +/*! + * \internal + */ +bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName) +{ + Q_UNUSED(name) + Q_UNUSED(fileName) + return false; +} + QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const { Q_Q(const QQmlEngine); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index e58ff554a7..385ae02ce5 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -66,6 +66,7 @@ #include <private/qintrusivelist_p.h> #include <private/qrecyclepool_p.h> #include <private/qfieldlist_p.h> +#include <private/qv4engine_p.h> #include <QtCore/qlist.h> #include <QtCore/qpair.h> diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index be0adc54a7..f6a5afb891 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -46,6 +46,7 @@ #include "qqmlscriptstring_p.h" #include "qqmlbinding_p.h" #include <private/qqmlsourcecoordinate_p.h> +#include <private/qv4qmlcontext_p.h> #include <QtCore/qdebug.h> diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 95ab79070d..f4cdb45aff 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -55,6 +55,7 @@ #include <private/qqmlengine_p.h> #include <private/qfieldlist_p.h> #include <private/qqmltypemodule_p.h> +#include <private/qqmltypeloaderqmldircontent_p.h> #include <QtCore/qjsonobject.h> #include <QtCore/qjsonarray.h> diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 67fdf8847b..f21427ff69 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -44,6 +44,7 @@ #include <private/qqmltype_p_p.h> #include <private/qqmltypeloader_p.h> #include <private/qqmlextensionplugin_p.h> +#include <private/qv4executablecompilationunit_p.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qmutex.h> diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 59651246e4..dda6e96966 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -55,6 +55,7 @@ #include <private/qqmlvaluetypeproxybinding_p.h> #include <private/qqmldebugconnector_p.h> #include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qqmlscriptdata_p.h> #include <private/qjsvalue_p.h> #include <qtqml_tracepoints_p.h> diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 1cbad87920..8eaf8fa1f0 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -57,6 +57,7 @@ #include <private/qfinitestack_p.h> #include <private/qrecursionwatcher_p.h> #include <private/qqmlprofiler_p.h> +#include <private/qv4qmlcontext_p.h> #include <qpointer.h> diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 247145ac0c..9c7a69d571 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -54,6 +54,7 @@ #include <private/qqmlengine_p.h> #include <private/qqmlmetaobject_p.h> #include <private/qqmlpropertyresolver_p.h> +#include <private/qqmltypedata_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp new file mode 100644 index 0000000000..69b26894a8 --- /dev/null +++ b/src/qml/qml/qqmlscriptblob.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmlengine_p.h> +#include <private/qqmlirbuilder_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qqmlscriptdata_p.h> +#include <private/qv4runtimecodegen_p.h> +#include <private/qv4script_p.h> + +#include <QtCore/qloggingcategory.h> + +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) +Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") + +QT_BEGIN_NAMESPACE + +QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader) + : QQmlTypeLoader::Blob(url, JavaScriptFile, loader) + , m_isModule(url.path().endsWith(QLatin1String(".mjs"))) +{ +} + +QQmlScriptBlob::~QQmlScriptBlob() +{ +} + +QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const +{ + return m_scriptData; +} + +void QQmlScriptBlob::dataReceived(const SourceCodeData &data) +{ + if (!diskCacheDisabled() || diskCacheForced()) { + QQmlRefPointer<QV4::ExecutableCompilationUnit> unit + = QV4::ExecutableCompilationUnit::create(); + QString error; + if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { + initializeFromCompilationUnit(unit); + return; + } else { + qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error; + } + } + + if (!data.exists()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else + setError(QQmlTypeLoader::tr("No such file or directory")); + return; + } + + QString error; + QString source = data.readAll(&error); + if (!error.isEmpty()) { + setError(error); + return; + } + + QV4::CompiledData::CompilationUnit unit; + + if (m_isModule) { + QList<QQmlJS::DiagnosticMessage> diagnostics; + unit = QV4::Compiler::Codegen::compileModule(isDebugging(), urlString(), source, + data.sourceTimeStamp(), &diagnostics); + QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics); + if (!errors.isEmpty()) { + setError(errors); + return; + } + } else { + QmlIR::Document irUnit(isDebugging()); + + irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp(); + + QmlIR::ScriptDirectivesCollector collector(&irUnit); + irUnit.jsParserEngine.setDirectives(&collector); + + QList<QQmlError> errors; + irUnit.javaScriptCompilationUnit = QV4::Script::precompile( + &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(), + source, &errors, QV4::Compiler::ContextType::ScriptImportedByQML); + + source.clear(); + if (!errors.isEmpty()) { + setError(errors); + return; + } + + QmlIR::QmlUnitGenerator qmlGenerator; + qmlGenerator.generate(irUnit); + unit = std::move(irUnit.javaScriptCompilationUnit); + } + + auto executableUnit = QV4::ExecutableCompilationUnit::create(std::move(unit)); + + if ((!diskCacheDisabled() || diskCacheForced()) && !isDebugging()) { + QString errorString; + if (executableUnit->saveToDisk(url(), &errorString)) { + QString error; + if (!executableUnit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { + // ignore error, keep using the in-memory compilation unit. + } + } else { + qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" + << executableUnit->fileName() << "to disk:" << errorString; + } + } + + initializeFromCompilationUnit(executableUnit); +} + +void QQmlScriptBlob::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) +{ + initializeFromCompilationUnit(QV4::ExecutableCompilationUnit::create( + QV4::CompiledData::CompilationUnit(unit, urlString(), finalUrlString()))); +} + +void QQmlScriptBlob::done() +{ + if (isError()) + return; + + // Check all script dependencies for errors + for (int ii = 0; ii < m_scripts.count(); ++ii) { + const ScriptReference &script = m_scripts.at(ii); + Q_ASSERT(script.script->isCompleteOrError()); + if (script.script->isError()) { + QList<QQmlError> errors = script.script->errors(); + QQmlError error; + error.setUrl(url()); + error.setLine(script.location.line); + error.setColumn(script.location.column); + error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); + errors.prepend(error); + setError(errors); + return; + } + } + + if (!m_isModule) { + m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache); + + QSet<QString> ns; + + for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { + const ScriptReference &script = m_scripts.at(scriptIndex); + + m_scriptData->scripts.append(script.script); + + if (!script.nameSpace.isNull()) { + if (!ns.contains(script.nameSpace)) { + ns.insert(script.nameSpace); + m_scriptData->typeNameCache->add(script.nameSpace); + } + } + m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace); + } + + m_importCache.populateCache(m_scriptData->typeNameCache); + } + m_scripts.clear(); +} + +QString QQmlScriptBlob::stringAt(int index) const +{ + return m_scriptData->m_precompiledScript->stringAt(index); +} + +void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) +{ + ScriptReference ref; + ref.script = blob; + ref.location = location; + ref.qualifier = qualifier; + ref.nameSpace = nameSpace; + + m_scripts << ref; +} + +void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit) +{ + Q_ASSERT(!m_scriptData); + m_scriptData.adopt(new QQmlScriptData()); + m_scriptData->url = finalUrl(); + m_scriptData->urlString = finalUrlString(); + m_scriptData->m_precompiledScript = unit; + + m_importCache.setBaseUrl(finalUrl(), finalUrlString()); + + QQmlRefPointer<QV4::ExecutableCompilationUnit> script = m_scriptData->m_precompiledScript; + + if (!m_isModule) { + QList<QQmlError> errors; + for (quint32 i = 0, count = script->importCount(); i < count; ++i) { + const QV4::CompiledData::Import *import = script->importAt(i); + if (!addImport(import, &errors)) { + Q_ASSERT(errors.size()); + QQmlError error(errors.takeFirst()); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); // put it back on the list after filling out information. + setError(errors); + return; + } + } + } + + auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine()); + + v4->injectModule(unit); + + for (const QString &request: unit->moduleRequests()) { + if (v4->moduleForUrl(QUrl(request), unit.data())) + continue; + + const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request)); + QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(absoluteRequest); + addDependency(blob.data()); + scriptImported(blob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(), /*namespace*/QString()); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlscriptblob_p.h b/src/qml/qml/qqmlscriptblob_p.h new file mode 100644 index 0000000000..10c0437e7b --- /dev/null +++ b/src/qml/qml/qqmlscriptblob_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSCRIPTBLOB_P_H +#define QQMLSCRIPTBLOB_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 <private/qqmltypeloader_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlScriptData; +class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlTypeLoader::Blob +{ +private: + friend class QQmlTypeLoader; + + QQmlScriptBlob(const QUrl &, QQmlTypeLoader *); + +public: + ~QQmlScriptBlob() override; + + struct ScriptReference + { + QV4::CompiledData::Location location; + QString qualifier; + QString nameSpace; + QQmlRefPointer<QQmlScriptBlob> script; + }; + + QQmlRefPointer<QQmlScriptData> scriptData() const; + +protected: + void dataReceived(const SourceCodeData &) override; + void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override; + void done() override; + + QString stringAt(int index) const override; + +private: + void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; + void initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit); + + QList<ScriptReference> m_scripts; + QQmlRefPointer<QQmlScriptData> m_scriptData; + const bool m_isModule; +}; + +QT_END_NAMESPACE + +#endif // QQMLSCRIPTBLOB_P_H diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp new file mode 100644 index 0000000000..0725f40d2a --- /dev/null +++ b/src/qml/qml/qqmlscriptdata.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmlscriptdata_p.h> +#include <private/qqmlcontext_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qv4engine_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4object_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4module_p.h> + +QT_BEGIN_NAMESPACE + +QQmlScriptData::QQmlScriptData() + : typeNameCache(nullptr) + , m_loaded(false) +{ +} + +QQmlContextData *QQmlScriptData::qmlContextDataForContext(QQmlContextData *parentQmlContextData) +{ + Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); + + if (m_precompiledScript->isESModule()) + return nullptr; + + auto qmlContextData = new QQmlContextData(); + + qmlContextData->isInternal = true; + qmlContextData->isJSContext = true; + if (m_precompiledScript->isSharedLibrary()) + qmlContextData->isPragmaLibraryContext = true; + else + qmlContextData->isPragmaLibraryContext = parentQmlContextData->isPragmaLibraryContext; + qmlContextData->baseUrl = url; + qmlContextData->baseUrlString = urlString; + + // For backward compatibility, if there are no imports, we need to use the + // imports from the parent context. See QTBUG-17518. + if (!typeNameCache->isEmpty()) { + qmlContextData->imports = typeNameCache; + } else if (!m_precompiledScript->isSharedLibrary()) { + qmlContextData->imports = parentQmlContextData->imports; + qmlContextData->importedScripts = parentQmlContextData->importedScripts; + } + + if (!m_precompiledScript->isSharedLibrary()) { + qmlContextData->setParent(parentQmlContextData); + } else { + qmlContextData->engine = parentQmlContextData->engine; // Fix for QTBUG-21620 + } + + QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); + QV4::Scope scope(v4); + QV4::ScopedObject scriptsArray(scope); + if (qmlContextData->importedScripts.isNullOrUndefined()) { + scriptsArray = v4->newArrayObject(scripts.count()); + qmlContextData->importedScripts.set(v4, scriptsArray); + } else { + scriptsArray = qmlContextData->importedScripts.valueRef(); + } + QV4::ScopedValue v(scope); + for (int ii = 0; ii < scripts.count(); ++ii) + scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData))); + + return qmlContextData; +} + +QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentQmlContextData) +{ + if (m_loaded) + return m_value.value(); + + Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); + QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); + QV4::Scope scope(v4); + + if (!hasEngine()) { + addToEngine(parentQmlContextData->engine); + addref(); + } + + QQmlContextDataRef qmlContextData = qmlContextDataForContext(parentQmlContextData); + QV4::Scoped<QV4::QmlContext> qmlExecutionContext(scope); + if (qmlContextData) + qmlExecutionContext = + QV4::QmlContext::create(v4->rootContext(), qmlContextData, /* scopeObject: */ nullptr); + + QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4)); + if (module) { + if (qmlContextData) { + module->d()->scope->outer.set(v4, qmlExecutionContext->d()); + qmlExecutionContext->d()->qml()->module.set(v4, module->d()); + } + + module->evaluate(); + } + + if (v4->hasException) { + QQmlError error = v4->catchExceptionAsQmlError(); + if (error.isValid()) + QQmlEnginePrivate::get(v4)->warning(error); + } + + QV4::ScopedValue value(scope); + if (qmlContextData) + value = qmlExecutionContext->d()->qml(); + else if (module) + value = module->d(); + + if (m_precompiledScript->isSharedLibrary() || m_precompiledScript->isESModule()) { + m_loaded = true; + m_value.set(v4, value); + } + + return value->asReturnedValue(); +} + +void QQmlScriptData::clear() +{ + if (typeNameCache) { + typeNameCache->release(); + typeNameCache = nullptr; + } + + scripts.clear(); + + // An addref() was made when the QQmlCleanup was added to the engine. + release(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h new file mode 100644 index 0000000000..273ba3691f --- /dev/null +++ b/src/qml/qml/qqmlscriptdata_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSCRIPTDATA_P_H +#define QQMLSCRIPTDATA_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 <private/qqmlrefcount_p.h> +#include <private/qqmlcleanup_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qv4value_p.h> +#include <private/qv4persistent_p.h> +#include <private/qv4executablecompilationunit_p.h> + +#include <QtCore/qurl.h> + +QT_BEGIN_NAMESPACE + +class QQmlTypeNameCache; +class QQmlContextData; + +// QQmlScriptData 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 QQmlCleanup::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 QQmlScriptData : public QQmlCleanup, public QQmlRefCount +{ +private: + friend class QQmlTypeLoader; + + QQmlScriptData(); + +public: + QUrl url; + QString urlString; + QQmlTypeNameCache *typeNameCache; + QVector<QQmlRefPointer<QQmlScriptBlob>> scripts; + + QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); + + QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; } + +protected: + void clear() override; // From QQmlCleanup + +private: + friend class QQmlScriptBlob; + + void initialize(QQmlEngine *); + QQmlContextData *qmlContextDataForContext(QQmlContextData *parentQmlContextData); + + bool m_loaded; + QQmlRefPointer<QV4::ExecutableCompilationUnit> m_precompiledScript; + QV4::PersistentValue m_value; +}; + +QT_END_NAMESPACE + +#endif // QQMLSCRIPTDATA_P_H diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp new file mode 100644 index 0000000000..b04a887ee4 --- /dev/null +++ b/src/qml/qml/qqmltypedata.cpp @@ -0,0 +1,842 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmltypedata_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlpropertycachecreator_p.h> +#include <private/qqmlpropertyvalidator_p.h> +#include <private/qqmlirloader_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qqmlscriptdata_p.h> + +#include <QtCore/qloggingcategory.h> +#include <QtCore/qcryptographichash.h> + +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) + +QT_BEGIN_NAMESPACE + +QQmlTypeData::TypeDataCallback::~TypeDataCallback() +{ +} + +QString QQmlTypeData::TypeReference::qualifiedName() const +{ + QString result; + if (!prefix.isEmpty()) { + result = prefix + QLatin1Char('.'); + } + result.append(type.qmlTypeName()); + return result; +} + +QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) + : QQmlTypeLoader::Blob(url, QmlFile, manager), + m_typesResolved(false), m_implicitImportLoaded(false) +{ + +} + +QQmlTypeData::~QQmlTypeData() +{ + m_scripts.clear(); + m_compositeSingletons.clear(); + m_resolvedTypes.clear(); +} + +const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const +{ + return m_scripts; +} + +QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const +{ + return m_compiledData.data(); +} + +void QQmlTypeData::registerCallback(TypeDataCallback *callback) +{ + Q_ASSERT(!m_callbacks.contains(callback)); + m_callbacks.append(callback); +} + +void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) +{ + Q_ASSERT(m_callbacks.contains(callback)); + m_callbacks.removeOne(callback); + Q_ASSERT(!m_callbacks.contains(callback)); +} + +bool QQmlTypeData::tryLoadFromDiskCache() +{ + if (diskCacheDisabled() && !diskCacheForced()) + return false; + + if (isDebugging()) + return false; + + QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle(); + if (!v4) + return false; + + QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create(); + { + QString error; + if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { + qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error; + return false; + } + } + + if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) { + restoreIR(std::move(*unit)); + return true; + } + + m_compiledData = unit; + + for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) + m_typeReferences.collectFromObject(m_compiledData->objectAt(i)); + + m_importCache.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 false; + + // find the implicit import + for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) { + const QV4::CompiledData::Import *import = m_compiledData->importAt(i); + if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".") + && import->qualifierIndex == 0 + && import->majorVersion == -1 + && import->minorVersion == -1) { + QList<QQmlError> errors; + if (!fetchQmldir(qmldirUrl, import, 1, &errors)) { + setError(errors); + return false; + } + break; + } + } + } + } + + for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) { + const QV4::CompiledData::Import *import = m_compiledData->importAt(i); + QList<QQmlError> errors; + if (!addImport(import, &errors)) { + Q_ASSERT(errors.size()); + QQmlError error(errors.takeFirst()); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); // put it back on the list after filling out information. + setError(errors); + return false; + } + } + + return true; +} + +void QQmlTypeData::createTypeAndPropertyCaches( + const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + const QV4::ResolvedTypeReferenceMap &resolvedTypeCache) +{ + Q_ASSERT(m_compiledData); + m_compiledData->typeNameCache = typeNameCache; + m_compiledData->resolvedTypes = resolvedTypeCache; + + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + + QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; + + { + QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator( + &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine, + m_compiledData.data(), &m_importCache); + QQmlJS::DiagnosticMessage error = propertyCacheCreator.buildMetaObjects(); + if (error.isValid()) { + setError(error); + return; + } + } + + QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator( + &m_compiledData->propertyCaches, m_compiledData.data()); + aliasCreator.appendAliasPropertiesToMetaObjects(); + + pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches); +} + +static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine) +{ + for (const auto &typeRef: typeRefs) { + if (typeRef.typeData) { + const auto unit = typeRef.typeData->compilationUnit()->unitData(); + hash->addData(unit->md5Checksum, sizeof(unit->md5Checksum)); + } else if (typeRef.type.isValid()) { + const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject()); + bool ok = false; + hash->addData(propertyCache->checksum(&ok)); + if (!ok) + return false; + } + } + return true; +} + +void QQmlTypeData::done() +{ + auto cleanup = qScopeGuard([this]{ + m_document.reset(); + m_typeReferences.clear(); + if (isError()) + m_compiledData = nullptr; + }); + + if (isError()) + return; + + // Check all script dependencies for errors + for (int ii = 0; ii < m_scripts.count(); ++ii) { + const ScriptReference &script = m_scripts.at(ii); + Q_ASSERT(script.script->isCompleteOrError()); + if (script.script->isError()) { + QList<QQmlError> errors = script.script->errors(); + QQmlError error; + error.setUrl(url()); + error.setLine(script.location.line); + error.setColumn(script.location.column); + error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); + errors.prepend(error); + setError(errors); + return; + } + } + + // Check all type dependencies for errors + for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; + ++it) { + const TypeReference &type = *it; + Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); + if (type.typeData && type.typeData->isError()) { + const QString typeName = stringAt(it.key()); + + QList<QQmlError> errors = type.typeData->errors(); + QQmlError error; + error.setUrl(url()); + error.setLine(type.location.line); + error.setColumn(type.location.column); + error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); + errors.prepend(error); + setError(errors); + return; + } + } + + // Check all composite singleton type dependencies for errors + for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) { + const TypeReference &type = m_compositeSingletons.at(ii); + Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); + if (type.typeData && type.typeData->isError()) { + QString typeName = type.type.qmlTypeName(); + + QList<QQmlError> errors = type.typeData->errors(); + QQmlError error; + error.setUrl(url()); + error.setLine(type.location.line); + error.setColumn(type.location.column); + error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); + errors.prepend(error); + setError(errors); + return; + } + } + + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; + QV4::ResolvedTypeReferenceMap resolvedTypeCache; + { + QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); + if (error.isValid()) { + setError(error); + return; + } + } + + QQmlEngine *const engine = typeLoader()->engine(); + + const auto dependencyHasher = [engine, &resolvedTypeCache, this]() { + QCryptographicHash hash(QCryptographicHash::Md5); + return (resolvedTypeCache.addToHash(&hash, engine) + && ::addTypeReferenceChecksumsToHash(m_compositeSingletons, &hash, engine)) + ? hash.result() + : QByteArray(); + }; + + // verify if any dependencies changed if we're using a cache + if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) { + qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName(); + if (!loadFromSource()) + return; + m_backupSourceCode = SourceCodeData(); + m_compiledData = nullptr; + } + + if (!m_document.isNull()) { + // Compile component + compile(typeNameCache, &resolvedTypeCache, dependencyHasher); + } else { + createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); + } + + if (isError()) + return; + + { + QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine); + { + // Sanity check property bindings + QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData); + QVector<QQmlJS::DiagnosticMessage> errors = validator.validate(); + if (!errors.isEmpty()) { + setError(errors); + return; + } + } + + m_compiledData->finalizeCompositeType(enginePrivate); + } + + { + QQmlType type = QQmlMetaType::qmlType(finalUrl(), true); + if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) { + if (!type.isValid()) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); + setError(error); + return; + } else if (!type.isCompositeSingleton()) { + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName())); + setError(error); + return; + } + } else { + // If the type is CompositeSingleton but there was no pragma Singleton in the + // QML file, lets report an error. + if (type.isValid() && type.isCompositeSingleton()) { + QString typeName = type.qmlTypeName(); + setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); + return; + } + } + } + + { + // Collect imported scripts + m_compiledData->dependentScripts.reserve(m_scripts.count()); + for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { + const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex); + + QStringRef qualifier(&script.qualifier); + QString enclosingNamespace; + + const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); + if (lastDotIndex != -1) { + enclosingNamespace = qualifier.left(lastDotIndex).toString(); + qualifier = qualifier.mid(lastDotIndex+1); + } + + m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData(); + m_compiledData->dependentScripts << scriptData; + } + } +} + +void QQmlTypeData::completed() +{ + // Notify callbacks + while (!m_callbacks.isEmpty()) { + TypeDataCallback *callback = m_callbacks.takeFirst(); + callback->typeDataReady(this); + } +} + +bool QQmlTypeData::loadImplicitImport() +{ + m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error) + + m_importCache.setBaseUrl(finalUrl(), finalUrlString()); + + QQmlImportDatabase *importDatabase = typeLoader()->importDatabase(); + // 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; + m_importCache.addImplicitImport(importDatabase, &implicitImportErrors); + + if (!implicitImportErrors.isEmpty()) { + setError(implicitImportErrors); + return false; + } + + return true; +} + +void QQmlTypeData::dataReceived(const SourceCodeData &data) +{ + m_backupSourceCode = data; + + if (tryLoadFromDiskCache()) + return; + + if (isError()) + return; + + if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else if (!m_backupSourceCode.exists()) + setError(QQmlTypeLoader::tr("No such file or directory")); + else + setError(QQmlTypeLoader::tr("File is empty")); + return; + } + + if (!loadFromSource()) + return; + + continueLoadFromIR(); +} + +void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) +{ + m_document.reset(new QmlIR::Document(isDebugging())); + QQmlIRLoader loader(unit, m_document.data()); + loader.load(); + m_document->jsModule.fileName = urlString(); + m_document->jsModule.finalUrl = finalUrlString(); + m_document->javaScriptCompilationUnit = QV4::CompiledData::CompilationUnit(unit); + continueLoadFromIR(); +} + +bool QQmlTypeData::loadFromSource() +{ + m_document.reset(new QmlIR::Document(isDebugging())); + m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp(); + QQmlEngine *qmlEngine = typeLoader()->engine(); + QmlIR::IRBuilder compiler(qmlEngine->handle()->illegalNames()); + + QString sourceError; + const QString source = m_backupSourceCode.readAll(&sourceError); + if (!sourceError.isEmpty()) { + setError(sourceError); + return false; + } + + if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) { + QList<QQmlError> errors; + errors.reserve(compiler.errors.count()); + for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) { + QQmlError e; + e.setUrl(url()); + e.setLine(msg.line); + e.setColumn(msg.column); + e.setDescription(msg.message); + errors << e; + } + setError(errors); + return false; + } + return true; +} + +void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit) +{ + m_document.reset(new QmlIR::Document(isDebugging())); + QQmlIRLoader loader(unit.unitData(), m_document.data()); + loader.load(); + m_document->jsModule.fileName = urlString(); + m_document->jsModule.finalUrl = finalUrlString(); + m_document->javaScriptCompilationUnit = std::move(unit); + continueLoadFromIR(); +} + +void QQmlTypeData::continueLoadFromIR() +{ + m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); + m_importCache.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 + QQmlJS::MemoryPool *pool = m_document->jsParserEngine.pool(); + auto implicitImport = pool->New<QV4::CompiledData::Import>(); + implicitImport->uriIndex = m_document->registerString(QLatin1String(".")); + implicitImport->qualifierIndex = 0; // empty string + implicitImport->majorVersion = -1; + implicitImport->minorVersion = -1; + QList<QQmlError> errors; + + if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) { + setError(errors); + return; + } + } + } + + QList<QQmlError> errors; + + for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) { + if (!addImport(import, &errors)) { + Q_ASSERT(errors.size()); + QQmlError error(errors.takeFirst()); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); // put it back on the list after filling out information. + setError(errors); + return; + } + } +} + +void QQmlTypeData::allDependenciesDone() +{ + QQmlTypeLoader::Blob::allDependenciesDone(); + + if (!m_typesResolved) { + // Check that all imports were resolved + QList<QQmlError> errors; + QHash<const QV4::CompiledData::Import *, int>::const_iterator it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd(); + for ( ; it != end; ++it) { + if (*it == 0) { + // This import was not resolved + for (auto keyIt = m_unresolvedImports.keyBegin(), + keyEnd = m_unresolvedImports.keyEnd(); + keyIt != keyEnd; ++keyIt) { + const QV4::CompiledData::Import *import = *keyIt; + QQmlError error; + error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(stringAt(import->uriIndex))); + error.setUrl(m_importCache.baseUrl()); + error.setLine(import->location.line); + error.setColumn(import->location.column); + errors.prepend(error); + } + } + } + if (errors.size()) { + setError(errors); + return; + } + + resolveTypes(); + m_typesResolved = true; + } +} + +void QQmlTypeData::downloadProgressChanged(qreal p) +{ + for (int ii = 0; ii < m_callbacks.count(); ++ii) { + TypeDataCallback *callback = m_callbacks.at(ii); + callback->typeDataProgress(this, p); + } +} + +QString QQmlTypeData::stringAt(int index) const +{ + if (m_compiledData) + return m_compiledData->stringAt(index); + return m_document->jsGenerator.stringTable.stringForIndex(index); +} + +void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, + const QV4::CompiledData::DependentTypesHasher &dependencyHasher) +{ + Q_ASSERT(m_compiledData.isNull()); + + const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit.unitData() + && (m_document->javaScriptCompilationUnit.unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation); + + QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); + QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher); + m_compiledData = compiler.compile(); + if (!m_compiledData) { + setError(compiler.compilationErrors()); + return; + } + + const bool trySaveToDisk = (!diskCacheDisabled() || diskCacheForced()) + && !m_document->jsModule.debugMode && !typeRecompilation; + if (trySaveToDisk) { + QString errorString; + if (m_compiledData->saveToDisk(url(), &errorString)) { + QString error; + if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { + // ignore error, keep using the in-memory compilation unit. + } + } else { + qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->fileName() << "to disk:" << errorString; + } + } +} + +void QQmlTypeData::resolveTypes() +{ + // Add any imported scripts to our resolved set + const auto resolvedScripts = m_importCache.resolvedScripts(); + for (const QQmlImports::ScriptReference &script : resolvedScripts) { + QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location); + addDependency(blob.data()); + + ScriptReference ref; + //ref.location = ... + if (!script.qualifier.isEmpty()) + { + ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace; + // Add a reference to the enclosing namespace + m_namespaces.insert(script.qualifier); + } else { + ref.qualifier = script.nameSpace; + } + + ref.script = blob; + m_scripts << ref; + } + + // Lets handle resolved composite singleton types + const auto resolvedCompositeSingletons = m_importCache.resolvedCompositeSingletons(); + for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) { + TypeReference ref; + QString typeName; + if (!csRef.prefix.isEmpty()) { + typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName; + // Add a reference to the enclosing namespace + m_namespaces.insert(csRef.prefix); + } else { + typeName = csRef.typeName; + } + + int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1; + int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1; + + if (!resolveType(typeName, majorVersion, minorVersion, ref, -1, -1, true, + QQmlType::CompositeSingletonType)) + return; + + if (ref.type.isCompositeSingleton()) { + ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); + if (ref.typeData->status() == QQmlDataBlob::ResolvingDependencies) { + // TODO: give an error message? If so, we should record and show the path of the cycle. + continue; + } + addDependency(ref.typeData.data()); + ref.prefix = csRef.prefix; + + m_compositeSingletons << ref; + } + } + + for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd(); + unresolvedRef != end; ++unresolvedRef) { + + TypeReference ref; // resolved reference + + const bool reportErrors = unresolvedRef->errorWhenNotFound; + + int majorVersion = -1; + int minorVersion = -1; + + const QString name = stringAt(unresolvedRef.key()); + + if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line, + unresolvedRef->location.column, reportErrors, + QQmlType::AnyRegistrationType) && reportErrors) + return; + + if (ref.type.isComposite()) { + ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); + addDependency(ref.typeData.data()); + } + ref.majorVersion = majorVersion; + ref.minorVersion = minorVersion; + + ref.location.line = unresolvedRef->location.line; + ref.location.column = unresolvedRef->location.column; + + ref.needsCreation = unresolvedRef->needsCreation; + + m_resolvedTypes.insert(unresolvedRef.key(), ref); + } + + // ### this allows enums to work without explicit import or instantiation of the type + if (!m_implicitImportLoaded) + loadImplicitImport(); +} + +QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache + ) const +{ + typeNameCache->adopt(new QQmlTypeNameCache(m_importCache)); + + for (const QString &ns: m_namespaces) + (*typeNameCache)->add(ns); + + // Add any Composite Singletons that were used to the import cache + for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) + (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix); + + m_importCache.populateCache(typeNameCache->data()); + + QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); + + for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { + QScopedPointer<QV4::ResolvedTypeReference> ref(new QV4::ResolvedTypeReference); + QQmlType qmlType = resolvedType->type; + if (resolvedType->typeData) { + if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) { + return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName())); + } + ref->compilationUnit = resolvedType->typeData->compilationUnit(); + } else if (qmlType.isValid()) { + ref->type = qmlType; + Q_ASSERT(ref->type.isValid()); + + if (resolvedType->needsCreation && !ref->type.isCreatable()) { + QString reason = ref->type.noCreationReason(); + if (reason.isEmpty()) + reason = tr("Element is not creatable."); + return qQmlCompileError(resolvedType->location, reason); + } + + if (ref->type.containsRevisionedAttributes()) { + ref->typePropertyCache = engine->cache(ref->type, + resolvedType->minorVersion); + } + } + ref->majorVersion = resolvedType->majorVersion; + ref->minorVersion = resolvedType->minorVersion; + ref->doDynamicTypeCheck(); + resolvedTypeCache->insert(resolvedType.key(), ref.take()); + } + QQmlJS::DiagnosticMessage noError; + return noError; +} + +bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, + TypeReference &ref, int lineNumber, int columnNumber, + bool reportErrors, QQmlType::RegistrationType registrationType) +{ + QQmlImportNamespace *typeNamespace = nullptr; + QList<QQmlError> errors; + + bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion, + &typeNamespace, &errors, registrationType); + if (!typeNamespace && !typeFound && !m_implicitImportLoaded) { + // Lazy loading of implicit import + if (loadImplicitImport()) { + // Try again to find the type + errors.clear(); + typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion, + &typeNamespace, &errors, registrationType); + } else { + return false; //loadImplicitImport() hit an error, and called setError already + } + } + + if ((!typeFound || typeNamespace) && reportErrors) { + // Known to not be a type: + // - known to be a namespace (Namespace {}) + // - type with unknown namespace (UnknownNamespace.SomeType {}) + QQmlError error; + if (typeNamespace) { + error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName)); + } else { + if (errors.size()) { + error = errors.takeFirst(); + } else { + // this should not be possible! + // Description should come from error provided by addImport() function. + error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database")); + } + error.setUrl(m_importCache.baseUrl()); + error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description())); + } + + if (lineNumber != -1) + error.setLine(lineNumber); + if (columnNumber != -1) + error.setColumn(columnNumber); + + errors.prepend(error); + setError(errors); + return false; + } + + return true; +} + +void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/) +{ + ScriptReference ref; + ref.script = blob; + ref.location = location; + ref.qualifier = qualifier; + + m_scripts << ref; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypedata_p.h b/src/qml/qml/qqmltypedata_p.h new file mode 100644 index 0000000000..e1d0c900ea --- /dev/null +++ b/src/qml/qml/qqmltypedata_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPEDATA_P_H +#define QQMLTYPEDATA_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 <private/qqmltypeloader_p.h> +#include <private/qv4executablecompilationunit_p.h> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob +{ + Q_DECLARE_TR_FUNCTIONS(QQmlTypeData) +public: + struct TypeReference + { + TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {} + + QV4::CompiledData::Location location; + QQmlType type; + int majorVersion; + int minorVersion; + QQmlRefPointer<QQmlTypeData> typeData; + QString prefix; // used by CompositeSingleton types + QString qualifiedName() const; + bool needsCreation; + }; + + struct ScriptReference + { + QV4::CompiledData::Location location; + QString qualifier; + QQmlRefPointer<QQmlScriptBlob> script; + }; + +private: + friend class QQmlTypeLoader; + + QQmlTypeData(const QUrl &, QQmlTypeLoader *); + +public: + ~QQmlTypeData() override; + + const QList<ScriptReference> &resolvedScripts() const; + + QV4::ExecutableCompilationUnit *compilationUnit() const; + + // Used by QQmlComponent to get notifications + struct TypeDataCallback { + virtual ~TypeDataCallback(); + virtual void typeDataProgress(QQmlTypeData *, qreal) {} + virtual void typeDataReady(QQmlTypeData *) {} + }; + void registerCallback(TypeDataCallback *); + void unregisterCallback(TypeDataCallback *); + +protected: + void done() override; + void completed() override; + void dataReceived(const SourceCodeData &) override; + void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override; + void allDependenciesDone() override; + void downloadProgressChanged(qreal) override; + + QString stringAt(int index) const override; + +private: + bool tryLoadFromDiskCache(); + bool loadFromSource(); + void restoreIR(QV4::CompiledData::CompilationUnit &&unit); + void continueLoadFromIR(); + void resolveTypes(); + QQmlJS::DiagnosticMessage buildTypeResolutionCaches( + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache + ) const; + void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, + const QV4::CompiledData::DependentTypesHasher &dependencyHasher); + void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, + const QV4::ResolvedTypeReferenceMap &resolvedTypeCache); + bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, + TypeReference &ref, int lineNumber = -1, int columnNumber = -1, + bool reportErrors = true, + QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType); + + void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; + + + SourceCodeData m_backupSourceCode; // used when cache verification fails. + QScopedPointer<QmlIR::Document> m_document; + QV4::CompiledData::TypeReferenceMap m_typeReferences; + + QList<ScriptReference> m_scripts; + + QSet<QString> m_namespaces; + QList<TypeReference> m_compositeSingletons; + + // map from name index to resolved type + // While this could be a hash, a map is chosen here to provide a stable + // order, which is used to calculating a check-sum on dependent meta-objects. + QMap<int, TypeReference> m_resolvedTypes; + bool m_typesResolved:1; + + QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compiledData; + + QList<TypeDataCallback *> m_callbacks; + + bool m_implicitImportLoaded; + bool loadImplicitImport(); +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPEDATA_P_H diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 0bfdbdd2fa..46207e6b57 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -37,77 +37,37 @@ ** ****************************************************************************/ -#include "qqmltypeloader_p.h" -#include "qqmlabstracturlinterceptor.h" -#include "qqmlexpression_p.h" - -#include <private/qqmlengine_p.h> -#include <private/qqmlglobal_p.h> -#include <private/qqmlthread_p.h> -#include <private/qv4codegen_p.h> -#include <private/qqmlcomponent_p.h> +#include <private/qqmltypeloader_p.h> + +#include <private/qqmldirdata_p.h> #include <private/qqmlprofiler_p.h> -#include <private/qqmltypecompiler_p.h> -#include <private/qqmlpropertyvalidator_p.h> -#include <private/qqmlpropertycachecreator_p.h> -#include <private/qv4module_p.h> -#include <private/qqmlirloader_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qqmltypedata_p.h> +#include <private/qqmltypeloaderqmldircontent_p.h> +#include <private/qqmltypeloaderthread_p.h> + +#include <QtQml/qqmlabstracturlinterceptor.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlextensioninterface.h> +#include <QtQml/qqmlfile.h> #include <QtCore/qdir.h> +#include <QtCore/qdiriterator.h> #include <QtCore/qfile.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmutex.h> #include <QtCore/qthread.h> -#include <QtQml/qqmlfile.h> -#include <QtCore/qdiriterator.h> -#include <QtQml/qqmlcomponent.h> -#include <QtCore/qwaitcondition.h> -#include <QtCore/qloggingcategory.h> -#include <QtQml/qqmlextensioninterface.h> -#include <QtCore/qcryptographichash.h> -#include <QtCore/qscopeguard.h> #include <functional> -#if defined (Q_OS_UNIX) -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#endif - -#if defined (QT_LINUXBASE) -// LSB doesn't declare NAME_MAX. Use SYMLINK_MAX instead, which seems to -// always be identical to NAME_MAX -#ifndef NAME_MAX -# define NAME_MAX _POSIX_SYMLINK_MAX -#endif - -#endif - // #define DATABLOB_DEBUG - #ifdef DATABLOB_DEBUG - -#define ASSERT_MAINTHREAD() do { if (m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in main thread"); } while (false) #define ASSERT_LOADTHREAD() do { if (!m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in load thread"); } while (false) -#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false) - #else - -#define ASSERT_MAINTHREAD() #define ASSERT_LOADTHREAD() -#define ASSERT_CALLBACK() - #endif -DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS); DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE); DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE); -Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) -Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache") - QT_BEGIN_NAMESPACE namespace { @@ -121,829 +81,6 @@ namespace { }; } -#if QT_CONFIG(qml_network) -// 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 QQmlTypeLoader 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 QQmlTypeLoaderNetworkReplyProxy : public QObject -{ - Q_OBJECT -public: - QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l); - -public slots: - void finished(); - void downloadProgress(qint64, qint64); - void manualFinished(QNetworkReply*); - -private: - QQmlTypeLoader *l; -}; -#endif // qml_network - -class QQmlTypeLoaderThread : public QQmlThread -{ - typedef QQmlTypeLoaderThread This; - -public: - QQmlTypeLoaderThread(QQmlTypeLoader *loader); -#if QT_CONFIG(qml_network) - QNetworkAccessManager *networkAccessManager() const; - QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const; -#endif // qml_network - void load(QQmlDataBlob *b); - void loadAsync(QQmlDataBlob *b); - void loadWithStaticData(QQmlDataBlob *b, const QByteArray &); - void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &); - void loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); - void loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); - void callCompleted(QQmlDataBlob *b); - void callDownloadProgressChanged(QQmlDataBlob *b, qreal p); - void initializeEngine(QQmlExtensionInterface *, const char *); - -protected: - void shutdownThread() override; - -private: - void loadThread(QQmlDataBlob *b); - void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &); - void loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); - void callCompletedMain(QQmlDataBlob *b); - void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p); - void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri); - - QQmlTypeLoader *m_loader; -#if QT_CONFIG(qml_network) - mutable QNetworkAccessManager *m_networkAccessManager; - mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy; -#endif // qml_network -}; - -#if QT_CONFIG(qml_network) -QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l) -: l(l) -{ -} - -void QQmlTypeLoaderNetworkReplyProxy::finished() -{ - Q_ASSERT(sender()); - Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); - QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); - l->networkReplyFinished(reply); -} - -void QQmlTypeLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - Q_ASSERT(sender()); - Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); - QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); - l->networkReplyProgress(reply, bytesReceived, bytesTotal); -} - -// This function is for when you want to shortcut the signals and call directly -void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply) -{ - qint64 replySize = reply->size(); - l->networkReplyProgress(reply, replySize, replySize); - l->networkReplyFinished(reply); -} -#endif // qml_network - -/*! -\class QQmlDataBlob -\brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlTypeLoader. -\internal - -QQmlDataBlob's are loaded by a QQmlTypeLoader. The user creates the QQmlDataBlob -and then calls QQmlTypeLoader::load() or QQmlTypeLoader::loadWithStaticData() to load it. -The QQmlTypeLoader invokes callbacks on the QQmlDataBlob as data becomes available. -*/ - -/*! -\enum QQmlDataBlob::Status - -This enum describes the status of the data blob. - -\list -\li Null The blob has not yet been loaded by a QQmlTypeLoader -\li Loading The blob is loading network data. The QQmlDataBlob::setData() callback has not yet been -invoked or has not yet returned. -\li WaitingForDependencies The blob is waiting for dependencies to be done before continueing. This status -only occurs after the QQmlDataBlob::setData() callback has been made, and when the blob has outstanding -dependencies. -\li Complete The blob's data has been loaded and all dependencies are done. -\li Error An error has been set on this blob. -\endlist -*/ - -/*! -\enum QQmlDataBlob::Type - -This enum describes the type of the data blob. - -\list -\li QmlFile This is a QQmlTypeData -\li JavaScriptFile This is a QQmlScriptData -\li QmldirFile This is a QQmlQmldirData -\endlist -*/ - -/*! -Create a new QQmlDataBlob for \a url and of the provided \a type. -*/ -QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type, QQmlTypeLoader *manager) -: m_typeLoader(manager), m_type(type), m_url(url), m_finalUrl(url), m_redirectCount(0), - m_inCallback(false), m_isDone(false) -{ - //Set here because we need to get the engine from the manager - if (m_typeLoader->engine() && m_typeLoader->engine()->urlInterceptor()) - m_url = m_typeLoader->engine()->urlInterceptor()->intercept(m_url, - (QQmlAbstractUrlInterceptor::DataType)m_type); -} - -/*! \internal */ -QQmlDataBlob::~QQmlDataBlob() -{ - Q_ASSERT(m_waitingOnMe.isEmpty()); - - cancelAllWaitingFor(); -} - -/*! - Must be called before loading can occur. -*/ -void QQmlDataBlob::startLoading() -{ - Q_ASSERT(status() == QQmlDataBlob::Null); - m_data.setStatus(QQmlDataBlob::Loading); -} - -/*! -Returns the type provided to the constructor. -*/ -QQmlDataBlob::Type QQmlDataBlob::type() const -{ - return m_type; -} - -/*! -Returns the blob's status. -*/ -QQmlDataBlob::Status QQmlDataBlob::status() const -{ - return m_data.status(); -} - -/*! -Returns true if the status is Null. -*/ -bool QQmlDataBlob::isNull() const -{ - return status() == Null; -} - -/*! -Returns true if the status is Loading. -*/ -bool QQmlDataBlob::isLoading() const -{ - return status() == Loading; -} - -/*! -Returns true if the status is WaitingForDependencies. -*/ -bool QQmlDataBlob::isWaiting() const -{ - return status() == WaitingForDependencies || - status() == ResolvingDependencies; -} - -/*! -Returns true if the status is Complete. -*/ -bool QQmlDataBlob::isComplete() const -{ - return status() == Complete; -} - -/*! -Returns true if the status is Error. -*/ -bool QQmlDataBlob::isError() const -{ - return status() == Error; -} - -/*! -Returns true if the status is Complete or Error. -*/ -bool QQmlDataBlob::isCompleteOrError() const -{ - Status s = status(); - return s == Error || s == Complete; -} - -/*! -Returns the data download progress from 0 to 1. -*/ -qreal QQmlDataBlob::progress() const -{ - quint8 p = m_data.progress(); - if (p == 0xFF) return 1.; - else return qreal(p) / qreal(0xFF); -} - -/*! -Returns the physical url of the data. Initially this is the same as -finalUrl(), but if a URL interceptor is set, it will work on this URL -and leave finalUrl() alone. - -\sa finalUrl() -*/ -QUrl QQmlDataBlob::url() const -{ - return m_url; -} - -QString QQmlDataBlob::urlString() const -{ - if (m_urlString.isEmpty()) - m_urlString = m_url.toString(); - - return m_urlString; -} - -/*! -Returns the logical URL to be used for resolving further URLs referred to in -the code. - -This is the blob url passed to the constructor. If a URL interceptor rewrites -the URL, this one stays the same. If a network redirect happens while fetching -the data, this url is updated to reflect the new location. Therefore, if both -an interception and a redirection happen, the final url will indirectly -incorporate the result of the interception, potentially breaking further -lookups. - -\sa url() -*/ -QUrl QQmlDataBlob::finalUrl() const -{ - return m_finalUrl; -} - -/*! -Returns the finalUrl() as a string. -*/ -QString QQmlDataBlob::finalUrlString() const -{ - if (m_finalUrlString.isEmpty()) - m_finalUrlString = m_finalUrl.toString(); - - return m_finalUrlString; -} - -/*! -Return the errors on this blob. - -May only be called from the load thread, or after the blob isCompleteOrError(). -*/ -QList<QQmlError> QQmlDataBlob::errors() const -{ - Q_ASSERT(isCompleteOrError() || (m_typeLoader && m_typeLoader->m_thread->isThisThread())); - return m_errors; -} - -/*! -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. - -The setError() method may only be called from within a QQmlDataBlob callback. -*/ -void QQmlDataBlob::setError(const QQmlError &errors) -{ - ASSERT_CALLBACK(); - - QList<QQmlError> l; - l << errors; - setError(l); -} - -/*! -\overload -*/ -void QQmlDataBlob::setError(const QList<QQmlError> &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); - - if (dumpErrors()) { - qWarning().nospace() << "Errors for " << urlString(); - for (int ii = 0; ii < errors.count(); ++ii) - qWarning().nospace() << " " << qPrintable(errors.at(ii).toString()); - } - cancelAllWaitingFor(); - - if (!m_inCallback) - tryDone(); -} - -void QQmlDataBlob::setError(const QQmlJS::DiagnosticMessage &error) -{ - QQmlError e; - e.setColumn(error.column); - e.setLine(error.line); - e.setDescription(error.message); - e.setUrl(url()); - setError(e); -} - -void QQmlDataBlob::setError(const QVector<QQmlJS::DiagnosticMessage> &errors) -{ - QList<QQmlError> finalErrors; - finalErrors.reserve(errors.count()); - for (const auto &error : errors) { - QQmlError e; - e.setColumn(error.column); - e.setLine(error.line); - e.setDescription(error.message); - e.setUrl(url()); - finalErrors << e; - } - setError(finalErrors); -} - -void QQmlDataBlob::setError(const QString &description) -{ - QQmlError e; - e.setDescription(description); - e.setUrl(url()); - setError(e); -} - -/*! -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 QQmlDataBlob callback. -*/ -void QQmlDataBlob::addDependency(QQmlDataBlob *blob) -{ - ASSERT_CALLBACK(); - - Q_ASSERT(status() != Null); - - if (!blob || - blob->status() == Error || blob->status() == Complete || - status() == Error || status() == Complete || m_isDone) - return; - - for (auto existingDep: qAsConst(m_waitingFor)) - if (existingDep.data() == blob) - return; - - m_data.setStatus(WaitingForDependencies); - - m_waitingFor.append(blob); - blob->m_waitingOnMe.append(this); -} - -/*! -\fn void QQmlDataBlob::dataReceived(const Data &data) - -Invoked when data for the blob is received. Implementors should use this callback -to determine a blob's dependencies. Within this callback you may call setError() -or addDependency(). -*/ - -/*! -Invoked once data has either been received or a network error occurred, and all -dependencies are complete. - -You can set an error in this method, but you cannot add new dependencies. Implementors -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 QQmlDataBlob::done() -{ -} - -#if QT_CONFIG(qml_network) -/*! -Invoked if there is a network error while fetching this blob. - -The default implementation sets an appropriate QQmlError. -*/ -void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError) -{ - Q_UNUSED(networkError); - - QQmlError error; - error.setUrl(m_url); - - const char *errorString = nullptr; - switch (networkError) { - default: - errorString = "Network error"; - break; - case QNetworkReply::ConnectionRefusedError: - errorString = "Connection refused"; - break; - case QNetworkReply::RemoteHostClosedError: - errorString = "Remote host closed the connection"; - break; - case QNetworkReply::HostNotFoundError: - errorString = "Host not found"; - break; - case QNetworkReply::TimeoutError: - errorString = "Timeout"; - break; - case QNetworkReply::ProxyConnectionRefusedError: - case QNetworkReply::ProxyConnectionClosedError: - case QNetworkReply::ProxyNotFoundError: - case QNetworkReply::ProxyTimeoutError: - case QNetworkReply::ProxyAuthenticationRequiredError: - case QNetworkReply::UnknownProxyError: - errorString = "Proxy error"; - break; - case QNetworkReply::ContentAccessDenied: - errorString = "Access denied"; - break; - case QNetworkReply::ContentNotFoundError: - errorString = "File not found"; - break; - case QNetworkReply::AuthenticationRequiredError: - errorString = "Authentication required"; - break; - }; - - error.setDescription(QLatin1String(errorString)); - - setError(error); -} -#endif // qml_network - -/*! -Called if \a blob, which was previously waited for, has an error. - -The default implementation does nothing. -*/ -void QQmlDataBlob::dependencyError(QQmlDataBlob *blob) -{ - Q_UNUSED(blob); -} - -/*! -Called if \a blob, which was previously waited for, has completed. - -The default implementation does nothing. -*/ -void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob) -{ - Q_UNUSED(blob); -} - -/*! -Called when all blobs waited for have completed. This occurs regardless of -whether they are in error, or complete state. - -The default implementation does nothing. -*/ -void QQmlDataBlob::allDependenciesDone() -{ - m_data.setStatus(QQmlDataBlob::ResolvingDependencies); -} - -/*! -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 QQmlDataBlob::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 QQmlDataBlob::completed() -{ -} - - -void QQmlDataBlob::tryDone() -{ - if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) { - m_isDone = true; - addref(); - -#ifdef DATABLOB_DEBUG - qWarning("QQmlDataBlob::done() %s", qPrintable(urlString())); -#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); -#ifdef DATABLOB_DEBUG - qWarning("QQmlDataBlob: Dispatching completed"); -#endif - m_typeLoader->m_thread->callCompleted(this); - - release(); - } -} - -void QQmlDataBlob::cancelAllWaitingFor() -{ - while (m_waitingFor.count()) { - QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast(); - - Q_ASSERT(blob->m_waitingOnMe.contains(this)); - - blob->m_waitingOnMe.removeOne(this); - } -} - -void QQmlDataBlob::notifyAllWaitingOnMe() -{ - while (m_waitingOnMe.count()) { - QQmlDataBlob *blob = m_waitingOnMe.takeLast(); - - Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(), - [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; })); - - blob->notifyComplete(this); - } -} - -void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) -{ - Q_ASSERT(blob->status() == Error || blob->status() == Complete); - QQmlCompilingProfiler prof(typeLoader()->profiler(), blob); - - m_inCallback = true; - - QQmlRefPointer<QQmlDataBlob> blobRef; - for (int i = 0; i < m_waitingFor.count(); ++i) { - if (m_waitingFor.at(i).data() == blob) { - blobRef = m_waitingFor.takeAt(i); - break; - } - } - Q_ASSERT(blobRef); - - if (blob->status() == Error) { - dependencyError(blob); - } else if (blob->status() == Complete) { - dependencyComplete(blob); - } - - if (!isError() && m_waitingFor.isEmpty()) - allDependenciesDone(); - - m_inCallback = false; - - 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 - -QQmlDataBlob::ThreadData::ThreadData() -: _p(0) -{ -} - -QQmlDataBlob::Status QQmlDataBlob::ThreadData::status() const -{ - return QQmlDataBlob::Status((_p.loadRelaxed() & TD_STATUS_MASK) >> TD_STATUS_SHIFT); -} - -void QQmlDataBlob::ThreadData::setStatus(QQmlDataBlob::Status status) -{ - while (true) { - int d = _p.loadRelaxed(); - int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK); - if (d == nd || _p.testAndSetOrdered(d, nd)) return; - } -} - -bool QQmlDataBlob::ThreadData::isAsync() const -{ - return _p.loadRelaxed() & TD_ASYNC_MASK; -} - -void QQmlDataBlob::ThreadData::setIsAsync(bool v) -{ - while (true) { - int d = _p.loadRelaxed(); - int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0); - if (d == nd || _p.testAndSetOrdered(d, nd)) return; - } -} - -quint8 QQmlDataBlob::ThreadData::progress() const -{ - return quint8((_p.loadRelaxed() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT); -} - -void QQmlDataBlob::ThreadData::setProgress(quint8 v) -{ - while (true) { - int d = _p.loadRelaxed(); - int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK); - if (d == nd || _p.testAndSetOrdered(d, nd)) return; - } -} - -QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader) -: m_loader(loader) -#if QT_CONFIG(qml_network) -, m_networkAccessManager(nullptr), m_networkReplyProxy(nullptr) -#endif // qml_network -{ - // Do that after initializing all the members. - startup(); -} - -#if QT_CONFIG(qml_network) -QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const -{ - Q_ASSERT(isThisThread()); - if (!m_networkAccessManager) { - m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(nullptr); - m_networkReplyProxy = new QQmlTypeLoaderNetworkReplyProxy(m_loader); - } - - return m_networkAccessManager; -} - -QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const -{ - Q_ASSERT(isThisThread()); - Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first - return m_networkReplyProxy; -} -#endif // qml_network - -void QQmlTypeLoaderThread::load(QQmlDataBlob *b) -{ - b->addref(); - callMethodInThread(&This::loadThread, b); -} - -void QQmlTypeLoaderThread::loadAsync(QQmlDataBlob *b) -{ - b->addref(); - postMethodToThread(&This::loadThread, b); -} - -void QQmlTypeLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d) -{ - b->addref(); - callMethodInThread(&This::loadWithStaticDataThread, b, d); -} - -void QQmlTypeLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d) -{ - b->addref(); - postMethodToThread(&This::loadWithStaticDataThread, b, d); -} - -void QQmlTypeLoaderThread::loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) -{ - b->addref(); - callMethodInThread(&This::loadWithCachedUnitThread, b, unit); -} - -void QQmlTypeLoaderThread::loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) -{ - b->addref(); - postMethodToThread(&This::loadWithCachedUnitThread, b, unit); -} - -void QQmlTypeLoaderThread::callCompleted(QQmlDataBlob *b) -{ - b->addref(); -#if !QT_CONFIG(thread) - if (!isThisThread()) - postMethodToThread(&This::callCompletedMain, b); -#else - postMethodToMain(&This::callCompletedMain, b); -#endif -} - -void QQmlTypeLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p) -{ - b->addref(); -#if !QT_CONFIG(thread) - if (!isThisThread()) - postMethodToThread(&This::callDownloadProgressChangedMain, b, p); -#else - postMethodToMain(&This::callDownloadProgressChangedMain, b, p); -#endif -} - -void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface, - const char *uri) -{ - callMethodInMain(&This::initializeEngineMain, iface, uri); -} - -void QQmlTypeLoaderThread::shutdownThread() -{ -#if QT_CONFIG(qml_network) - delete m_networkAccessManager; - m_networkAccessManager = nullptr; - delete m_networkReplyProxy; - m_networkReplyProxy = nullptr; -#endif // qml_network -} - -void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b) -{ - m_loader->loadThread(b); - b->release(); -} - -void QQmlTypeLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d) -{ - m_loader->loadWithStaticDataThread(b, d); - b->release(); -} - -void QQmlTypeLoaderThread::loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) -{ - m_loader->loadWithCachedUnitThread(b, unit); - b->release(); -} - -void QQmlTypeLoaderThread::callCompletedMain(QQmlDataBlob *b) -{ -#ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoaderThread: %s completed() callback", qPrintable(b->urlString())); -#endif - b->completed(); - b->release(); -} - -void QQmlTypeLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p) -{ -#ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoaderThread: %s downloadProgressChanged(%f) callback", - qPrintable(b->urlString()), p); -#endif - b->downloadProgressChanged(p); - b->release(); -} - -void QQmlTypeLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface, - const char *uri) -{ - Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread()); - iface->initializeEngine(m_loader->engine(), uri); -} - /*! \class QQmlTypeLoader \brief The QQmlTypeLoader class abstracts loading files and their dependencies over the network. @@ -1532,6 +669,16 @@ bool QQmlTypeLoader::Blob::isDebugging() const return typeLoader()->engine()->handle()->debugger() != nullptr; } +bool QQmlTypeLoader::Blob::diskCacheDisabled() +{ + return disableDiskCache(); +} + +bool QQmlTypeLoader::Blob::diskCacheForced() +{ + return forceDiskCache(); +} + bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) { bool resolve = true; @@ -1564,77 +711,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirDa return true; } - -QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent() -{ -} - -bool QQmlTypeLoaderQmldirContent::hasError() const -{ - return m_parser.hasError(); -} - -QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const -{ - QList<QQmlError> errors; - const QUrl url(uri); - for (const auto parseError : m_parser.errors(uri)) { - QQmlError error; - error.setUrl(url); - error.setLine(parseError.line); - error.setColumn(parseError.column); - error.setDescription(parseError.message); - errors.append(error); - } - return errors; -} - -QString QQmlTypeLoaderQmldirContent::typeNamespace() const -{ - return m_parser.typeNamespace(); -} - -void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) -{ - m_hasContent = true; - m_location = location; - m_parser.parse(content); -} - -void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error) -{ - QQmlJS::DiagnosticMessage parseError; - parseError.line = error.line(); - parseError.column = error.column(); - parseError.message = error.description(); - m_parser.setError(parseError); -} - -QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const -{ - return m_parser.components(); -} - -QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const -{ - return m_parser.scripts(); -} - -QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const -{ - return m_parser.plugins(); -} - -QString QQmlTypeLoaderQmldirContent::pluginLocation() const -{ - return m_location; -} - -bool QQmlTypeLoaderQmldirContent::designerSupported() const -{ - return m_parser.designerSupported(); -} - /*! Constructs a new type loader that uses the given \a engine. */ @@ -1788,17 +864,6 @@ QQmlRefPointer<QQmlQmldirData> QQmlTypeLoader::getQmldir(const QUrl &url) return qmldirData; } -// #### Qt 6: Remove this function, it exists only for binary compatibility. -/*! - * \internal - */ -bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName) -{ - Q_UNUSED(name) - Q_UNUSED(fileName) - return false; -} - /*! Returns the absolute filename of path via a directory cache. Returns a empty string if the path does not exist. @@ -2109,1221 +1174,4 @@ bool QQmlTypeLoader::isScriptLoaded(const QUrl &url) const return m_scriptCache.contains(url); } -QQmlTypeData::TypeDataCallback::~TypeDataCallback() -{ -} - -QString QQmlTypeData::TypeReference::qualifiedName() const -{ - QString result; - if (!prefix.isEmpty()) { - result = prefix + QLatin1Char('.'); - } - result.append(type.qmlTypeName()); - return result; -} - -QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) -: QQmlTypeLoader::Blob(url, QmlFile, manager), - m_typesResolved(false), m_implicitImportLoaded(false) -{ - -} - -QQmlTypeData::~QQmlTypeData() -{ - m_scripts.clear(); - m_compositeSingletons.clear(); - m_resolvedTypes.clear(); -} - -const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const -{ - return m_scripts; -} - -QV4::ExecutableCompilationUnit *QQmlTypeData::compilationUnit() const -{ - return m_compiledData.data(); -} - -void QQmlTypeData::registerCallback(TypeDataCallback *callback) -{ - Q_ASSERT(!m_callbacks.contains(callback)); - m_callbacks.append(callback); -} - -void QQmlTypeData::unregisterCallback(TypeDataCallback *callback) -{ - Q_ASSERT(m_callbacks.contains(callback)); - m_callbacks.removeOne(callback); - Q_ASSERT(!m_callbacks.contains(callback)); -} - -bool QQmlTypeData::tryLoadFromDiskCache() -{ - if (disableDiskCache() && !forceDiskCache()) - return false; - - if (isDebugging()) - return false; - - QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle(); - if (!v4) - return false; - - QQmlRefPointer<QV4::ExecutableCompilationUnit> unit = QV4::ExecutableCompilationUnit::create(); - { - QString error; - if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { - qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error; - return false; - } - } - - if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) { - restoreIR(std::move(*unit)); - return true; - } - - m_compiledData = unit; - - for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) - m_typeReferences.collectFromObject(m_compiledData->objectAt(i)); - - m_importCache.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 false; - - // find the implicit import - for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) { - const QV4::CompiledData::Import *import = m_compiledData->importAt(i); - if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".") - && import->qualifierIndex == 0 - && import->majorVersion == -1 - && import->minorVersion == -1) { - QList<QQmlError> errors; - if (!fetchQmldir(qmldirUrl, import, 1, &errors)) { - setError(errors); - return false; - } - break; - } - } - } - } - - for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) { - const QV4::CompiledData::Import *import = m_compiledData->importAt(i); - QList<QQmlError> errors; - if (!addImport(import, &errors)) { - Q_ASSERT(errors.size()); - QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); // put it back on the list after filling out information. - setError(errors); - return false; - } - } - - return true; -} - -void QQmlTypeData::createTypeAndPropertyCaches( - const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - const QV4::ResolvedTypeReferenceMap &resolvedTypeCache) -{ - Q_ASSERT(m_compiledData); - m_compiledData->typeNameCache = typeNameCache; - m_compiledData->resolvedTypes = resolvedTypeCache; - - QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); - - QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; - - { - QQmlPropertyCacheCreator<QV4::ExecutableCompilationUnit> propertyCacheCreator( - &m_compiledData->propertyCaches, &pendingGroupPropertyBindings, engine, - m_compiledData.data(), &m_importCache); - QQmlJS::DiagnosticMessage error = propertyCacheCreator.buildMetaObjects(); - if (error.isValid()) { - setError(error); - return; - } - } - - QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator( - &m_compiledData->propertyCaches, m_compiledData.data()); - aliasCreator.appendAliasPropertiesToMetaObjects(); - - pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches); -} - -static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine) -{ - for (const auto &typeRef: typeRefs) { - if (typeRef.typeData) { - const auto unit = typeRef.typeData->compilationUnit()->unitData(); - hash->addData(unit->md5Checksum, sizeof(unit->md5Checksum)); - } else if (typeRef.type.isValid()) { - const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject()); - bool ok = false; - hash->addData(propertyCache->checksum(&ok)); - if (!ok) - return false; - } - } - return true; -} - -void QQmlTypeData::done() -{ - auto cleanup = qScopeGuard([this]{ - m_document.reset(); - m_typeReferences.clear(); - if (isError()) - m_compiledData = nullptr; - }); - - if (isError()) - return; - - // Check all script dependencies for errors - for (int ii = 0; ii < m_scripts.count(); ++ii) { - const ScriptReference &script = m_scripts.at(ii); - Q_ASSERT(script.script->isCompleteOrError()); - if (script.script->isError()) { - QList<QQmlError> errors = script.script->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(script.location.line); - error.setColumn(script.location.column); - error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); - errors.prepend(error); - setError(errors); - return; - } - } - - // Check all type dependencies for errors - for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end; - ++it) { - const TypeReference &type = *it; - Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); - if (type.typeData && type.typeData->isError()) { - const QString typeName = stringAt(it.key()); - - QList<QQmlError> errors = type.typeData->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(type.location.line); - error.setColumn(type.location.column); - error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); - errors.prepend(error); - setError(errors); - return; - } - } - - // Check all composite singleton type dependencies for errors - for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) { - const TypeReference &type = m_compositeSingletons.at(ii); - Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); - if (type.typeData && type.typeData->isError()) { - QString typeName = type.type.qmlTypeName(); - - QList<QQmlError> errors = type.typeData->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(type.location.line); - error.setColumn(type.location.column); - error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); - errors.prepend(error); - setError(errors); - return; - } - } - - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; - QV4::ResolvedTypeReferenceMap resolvedTypeCache; - { - QQmlJS::DiagnosticMessage error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); - if (error.isValid()) { - setError(error); - return; - } - } - - QQmlEngine *const engine = typeLoader()->engine(); - - const auto dependencyHasher = [engine, &resolvedTypeCache, this]() { - QCryptographicHash hash(QCryptographicHash::Md5); - return (resolvedTypeCache.addToHash(&hash, engine) - && ::addTypeReferenceChecksumsToHash(m_compositeSingletons, &hash, engine)) - ? hash.result() - : QByteArray(); - }; - - // verify if any dependencies changed if we're using a cache - if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) { - qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName(); - if (!loadFromSource()) - return; - m_backupSourceCode = SourceCodeData(); - m_compiledData = nullptr; - } - - if (!m_document.isNull()) { - // Compile component - compile(typeNameCache, &resolvedTypeCache, dependencyHasher); - } else { - createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); - } - - if (isError()) - return; - - { - QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine); - { - // Sanity check property bindings - QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData); - QVector<QQmlJS::DiagnosticMessage> errors = validator.validate(); - if (!errors.isEmpty()) { - setError(errors); - return; - } - } - - m_compiledData->finalizeCompositeType(enginePrivate); - } - - { - QQmlType type = QQmlMetaType::qmlType(finalUrl(), true); - if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) { - if (!type.isValid()) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent.")); - setError(error); - return; - } else if (!type.isCompositeSingleton()) { - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type.qmlTypeName())); - setError(error); - return; - } - } else { - // If the type is CompositeSingleton but there was no pragma Singleton in the - // QML file, lets report an error. - if (type.isValid() && type.isCompositeSingleton()) { - QString typeName = type.qmlTypeName(); - setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName)); - return; - } - } - } - - { - // Collect imported scripts - m_compiledData->dependentScripts.reserve(m_scripts.count()); - for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { - const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex); - - QStringRef qualifier(&script.qualifier); - QString enclosingNamespace; - - const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); - if (lastDotIndex != -1) { - enclosingNamespace = qualifier.left(lastDotIndex).toString(); - qualifier = qualifier.mid(lastDotIndex+1); - } - - m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); - QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData(); - m_compiledData->dependentScripts << scriptData; - } - } -} - -void QQmlTypeData::completed() -{ - // Notify callbacks - while (!m_callbacks.isEmpty()) { - TypeDataCallback *callback = m_callbacks.takeFirst(); - callback->typeDataReady(this); - } -} - -bool QQmlTypeData::loadImplicitImport() -{ - m_implicitImportLoaded = true; // Even if we hit an error, count as loaded (we'd just keep hitting the error) - - m_importCache.setBaseUrl(finalUrl(), finalUrlString()); - - QQmlImportDatabase *importDatabase = typeLoader()->importDatabase(); - // 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; - m_importCache.addImplicitImport(importDatabase, &implicitImportErrors); - - if (!implicitImportErrors.isEmpty()) { - setError(implicitImportErrors); - return false; - } - - return true; -} - -void QQmlTypeData::dataReceived(const SourceCodeData &data) -{ - m_backupSourceCode = data; - - if (tryLoadFromDiskCache()) - return; - - if (isError()) - return; - - if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) { - if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) - setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); - else if (!m_backupSourceCode.exists()) - setError(QQmlTypeLoader::tr("No such file or directory")); - else - setError(QQmlTypeLoader::tr("File is empty")); - return; - } - - if (!loadFromSource()) - return; - - continueLoadFromIR(); -} - -void QQmlTypeData::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) -{ - m_document.reset(new QmlIR::Document(isDebugging())); - QQmlIRLoader loader(unit, m_document.data()); - loader.load(); - m_document->jsModule.fileName = urlString(); - m_document->jsModule.finalUrl = finalUrlString(); - m_document->javaScriptCompilationUnit = QV4::CompiledData::CompilationUnit(unit); - continueLoadFromIR(); -} - -bool QQmlTypeData::loadFromSource() -{ - m_document.reset(new QmlIR::Document(isDebugging())); - m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp(); - QQmlEngine *qmlEngine = typeLoader()->engine(); - QmlIR::IRBuilder compiler(qmlEngine->handle()->illegalNames()); - - QString sourceError; - const QString source = m_backupSourceCode.readAll(&sourceError); - if (!sourceError.isEmpty()) { - setError(sourceError); - return false; - } - - if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) { - QList<QQmlError> errors; - errors.reserve(compiler.errors.count()); - for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) { - QQmlError e; - e.setUrl(url()); - e.setLine(msg.line); - e.setColumn(msg.column); - e.setDescription(msg.message); - errors << e; - } - setError(errors); - return false; - } - return true; -} - -void QQmlTypeData::restoreIR(QV4::CompiledData::CompilationUnit &&unit) -{ - m_document.reset(new QmlIR::Document(isDebugging())); - QQmlIRLoader loader(unit.unitData(), m_document.data()); - loader.load(); - m_document->jsModule.fileName = urlString(); - m_document->jsModule.finalUrl = finalUrlString(); - m_document->javaScriptCompilationUnit = std::move(unit); - continueLoadFromIR(); -} - -void QQmlTypeData::continueLoadFromIR() -{ - m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); - m_importCache.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 - QQmlJS::MemoryPool *pool = m_document->jsParserEngine.pool(); - auto implicitImport = pool->New<QV4::CompiledData::Import>(); - implicitImport->uriIndex = m_document->registerString(QLatin1String(".")); - implicitImport->qualifierIndex = 0; // empty string - implicitImport->majorVersion = -1; - implicitImport->minorVersion = -1; - QList<QQmlError> errors; - - if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) { - setError(errors); - return; - } - } - } - - QList<QQmlError> errors; - - for (const QV4::CompiledData::Import *import : qAsConst(m_document->imports)) { - if (!addImport(import, &errors)) { - Q_ASSERT(errors.size()); - QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); // put it back on the list after filling out information. - setError(errors); - return; - } - } -} - -void QQmlTypeData::allDependenciesDone() -{ - QQmlTypeLoader::Blob::allDependenciesDone(); - - if (!m_typesResolved) { - // Check that all imports were resolved - QList<QQmlError> errors; - QHash<const QV4::CompiledData::Import *, int>::const_iterator it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd(); - for ( ; it != end; ++it) { - if (*it == 0) { - // This import was not resolved - for (auto keyIt = m_unresolvedImports.keyBegin(), - keyEnd = m_unresolvedImports.keyEnd(); - keyIt != keyEnd; ++keyIt) { - const QV4::CompiledData::Import *import = *keyIt; - QQmlError error; - error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(stringAt(import->uriIndex))); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); - } - } - } - if (errors.size()) { - setError(errors); - return; - } - - resolveTypes(); - m_typesResolved = true; - } -} - -void QQmlTypeData::downloadProgressChanged(qreal p) -{ - for (int ii = 0; ii < m_callbacks.count(); ++ii) { - TypeDataCallback *callback = m_callbacks.at(ii); - callback->typeDataProgress(this, p); - } -} - -QString QQmlTypeData::stringAt(int index) const -{ - if (m_compiledData) - return m_compiledData->stringAt(index); - return m_document->jsGenerator.stringTable.stringForIndex(index); -} - -void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache, - const QV4::CompiledData::DependentTypesHasher &dependencyHasher) -{ - Q_ASSERT(m_compiledData.isNull()); - - const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit.unitData() - && (m_document->javaScriptCompilationUnit.unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation); - - QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); - QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher); - m_compiledData = compiler.compile(); - if (!m_compiledData) { - setError(compiler.compilationErrors()); - return; - } - - const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode && !typeRecompilation; - if (trySaveToDisk) { - QString errorString; - if (m_compiledData->saveToDisk(url(), &errorString)) { - QString error; - if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), &error)) { - // ignore error, keep using the in-memory compilation unit. - } - } else { - qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->fileName() << "to disk:" << errorString; - } - } -} - -void QQmlTypeData::resolveTypes() -{ - // Add any imported scripts to our resolved set - const auto resolvedScripts = m_importCache.resolvedScripts(); - for (const QQmlImports::ScriptReference &script : resolvedScripts) { - QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location); - addDependency(blob.data()); - - ScriptReference ref; - //ref.location = ... - if (!script.qualifier.isEmpty()) - { - ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace; - // Add a reference to the enclosing namespace - m_namespaces.insert(script.qualifier); - } else { - ref.qualifier = script.nameSpace; - } - - ref.script = blob; - m_scripts << ref; - } - - // Lets handle resolved composite singleton types - const auto resolvedCompositeSingletons = m_importCache.resolvedCompositeSingletons(); - for (const QQmlImports::CompositeSingletonReference &csRef : resolvedCompositeSingletons) { - TypeReference ref; - QString typeName; - if (!csRef.prefix.isEmpty()) { - typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName; - // Add a reference to the enclosing namespace - m_namespaces.insert(csRef.prefix); - } else { - typeName = csRef.typeName; - } - - int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1; - int minorVersion = csRef.minorVersion > -1 ? csRef.minorVersion : -1; - - if (!resolveType(typeName, majorVersion, minorVersion, ref, -1, -1, true, - QQmlType::CompositeSingletonType)) - return; - - if (ref.type.isCompositeSingleton()) { - ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); - if (ref.typeData->status() == QQmlDataBlob::ResolvingDependencies) { - // TODO: give an error message? If so, we should record and show the path of the cycle. - continue; - } - addDependency(ref.typeData.data()); - ref.prefix = csRef.prefix; - - m_compositeSingletons << ref; - } - } - - for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd(); - unresolvedRef != end; ++unresolvedRef) { - - TypeReference ref; // resolved reference - - const bool reportErrors = unresolvedRef->errorWhenNotFound; - - int majorVersion = -1; - int minorVersion = -1; - - const QString name = stringAt(unresolvedRef.key()); - - if (!resolveType(name, majorVersion, minorVersion, ref, unresolvedRef->location.line, - unresolvedRef->location.column, reportErrors, - QQmlType::AnyRegistrationType) && reportErrors) - return; - - if (ref.type.isComposite()) { - ref.typeData = typeLoader()->getType(ref.type.sourceUrl()); - addDependency(ref.typeData.data()); - } - ref.majorVersion = majorVersion; - ref.minorVersion = minorVersion; - - ref.location.line = unresolvedRef->location.line; - ref.location.column = unresolvedRef->location.column; - - ref.needsCreation = unresolvedRef->needsCreation; - - m_resolvedTypes.insert(unresolvedRef.key(), ref); - } - - // ### this allows enums to work without explicit import or instantiation of the type - if (!m_implicitImportLoaded) - loadImplicitImport(); -} - -QQmlJS::DiagnosticMessage QQmlTypeData::buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache - ) const -{ - typeNameCache->adopt(new QQmlTypeNameCache(m_importCache)); - - for (const QString &ns: m_namespaces) - (*typeNameCache)->add(ns); - - // Add any Composite Singletons that were used to the import cache - for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) - (*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix); - - m_importCache.populateCache(typeNameCache->data()); - - QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); - - for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) { - QScopedPointer<QV4::ResolvedTypeReference> ref(new QV4::ResolvedTypeReference); - QQmlType qmlType = resolvedType->type; - if (resolvedType->typeData) { - if (resolvedType->needsCreation && qmlType.isCompositeSingleton()) { - return qQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType.qmlTypeName())); - } - ref->compilationUnit = resolvedType->typeData->compilationUnit(); - } else if (qmlType.isValid()) { - ref->type = qmlType; - Q_ASSERT(ref->type.isValid()); - - if (resolvedType->needsCreation && !ref->type.isCreatable()) { - QString reason = ref->type.noCreationReason(); - if (reason.isEmpty()) - reason = tr("Element is not creatable."); - return qQmlCompileError(resolvedType->location, reason); - } - - if (ref->type.containsRevisionedAttributes()) { - ref->typePropertyCache = engine->cache(ref->type, - resolvedType->minorVersion); - } - } - ref->majorVersion = resolvedType->majorVersion; - ref->minorVersion = resolvedType->minorVersion; - ref->doDynamicTypeCheck(); - resolvedTypeCache->insert(resolvedType.key(), ref.take()); - } - QQmlJS::DiagnosticMessage noError; - return noError; -} - -bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, - TypeReference &ref, int lineNumber, int columnNumber, - bool reportErrors, QQmlType::RegistrationType registrationType) -{ - QQmlImportNamespace *typeNamespace = nullptr; - QList<QQmlError> errors; - - bool typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion, - &typeNamespace, &errors, registrationType); - if (!typeNamespace && !typeFound && !m_implicitImportLoaded) { - // Lazy loading of implicit import - if (loadImplicitImport()) { - // Try again to find the type - errors.clear(); - typeFound = m_importCache.resolveType(typeName, &ref.type, &majorVersion, &minorVersion, - &typeNamespace, &errors, registrationType); - } else { - return false; //loadImplicitImport() hit an error, and called setError already - } - } - - if ((!typeFound || typeNamespace) && reportErrors) { - // Known to not be a type: - // - known to be a namespace (Namespace {}) - // - type with unknown namespace (UnknownNamespace.SomeType {}) - QQmlError error; - if (typeNamespace) { - error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(typeName)); - } else { - if (errors.size()) { - error = errors.takeFirst(); - } else { - // this should not be possible! - // Description should come from error provided by addImport() function. - error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database")); - } - error.setUrl(m_importCache.baseUrl()); - error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(typeName).arg(error.description())); - } - - if (lineNumber != -1) - error.setLine(lineNumber); - if (columnNumber != -1) - error.setColumn(columnNumber); - - errors.prepend(error); - setError(errors); - return false; - } - - return true; -} - -void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/) -{ - ScriptReference ref; - ref.script = blob; - ref.location = location; - ref.qualifier = qualifier; - - m_scripts << ref; -} - -QQmlScriptData::QQmlScriptData() - : typeNameCache(nullptr) - , m_loaded(false) -{ -} - -QQmlContextData *QQmlScriptData::qmlContextDataForContext(QQmlContextData *parentQmlContextData) -{ - Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); - - if (m_precompiledScript->isESModule()) - return nullptr; - - auto qmlContextData = new QQmlContextData(); - - qmlContextData->isInternal = true; - qmlContextData->isJSContext = true; - if (m_precompiledScript->isSharedLibrary()) - qmlContextData->isPragmaLibraryContext = true; - else - qmlContextData->isPragmaLibraryContext = parentQmlContextData->isPragmaLibraryContext; - qmlContextData->baseUrl = url; - qmlContextData->baseUrlString = urlString; - - // For backward compatibility, if there are no imports, we need to use the - // imports from the parent context. See QTBUG-17518. - if (!typeNameCache->isEmpty()) { - qmlContextData->imports = typeNameCache; - } else if (!m_precompiledScript->isSharedLibrary()) { - qmlContextData->imports = parentQmlContextData->imports; - qmlContextData->importedScripts = parentQmlContextData->importedScripts; - } - - if (!m_precompiledScript->isSharedLibrary()) { - qmlContextData->setParent(parentQmlContextData); - } else { - qmlContextData->engine = parentQmlContextData->engine; // Fix for QTBUG-21620 - } - - QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); - QV4::Scope scope(v4); - QV4::ScopedObject scriptsArray(scope); - if (qmlContextData->importedScripts.isNullOrUndefined()) { - scriptsArray = v4->newArrayObject(scripts.count()); - qmlContextData->importedScripts.set(v4, scriptsArray); - } else { - scriptsArray = qmlContextData->importedScripts.valueRef(); - } - QV4::ScopedValue v(scope); - for (int ii = 0; ii < scripts.count(); ++ii) - scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(qmlContextData))); - - return qmlContextData; -} - -QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parentQmlContextData) -{ - if (m_loaded) - return m_value.value(); - - Q_ASSERT(parentQmlContextData && parentQmlContextData->engine); - QV4::ExecutionEngine *v4 = parentQmlContextData->engine->handle(); - QV4::Scope scope(v4); - - if (!hasEngine()) { - addToEngine(parentQmlContextData->engine); - addref(); - } - - QQmlContextDataRef qmlContextData = qmlContextDataForContext(parentQmlContextData); - QV4::Scoped<QV4::QmlContext> qmlExecutionContext(scope); - if (qmlContextData) - qmlExecutionContext = - QV4::QmlContext::create(v4->rootContext(), qmlContextData, /* scopeObject: */ nullptr); - - QV4::Scoped<QV4::Module> module(scope, m_precompiledScript->instantiate(v4)); - if (module) { - if (qmlContextData) { - module->d()->scope->outer.set(v4, qmlExecutionContext->d()); - qmlExecutionContext->d()->qml()->module.set(v4, module->d()); - } - - module->evaluate(); - } - - if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - if (error.isValid()) - QQmlEnginePrivate::get(v4)->warning(error); - } - - QV4::ScopedValue value(scope); - if (qmlContextData) - value = qmlExecutionContext->d()->qml(); - else if (module) - value = module->d(); - - if (m_precompiledScript->isSharedLibrary() || m_precompiledScript->isESModule()) { - m_loaded = true; - m_value.set(v4, value); - } - - return value->asReturnedValue(); -} - -void QQmlScriptData::clear() -{ - if (typeNameCache) { - typeNameCache->release(); - typeNameCache = nullptr; - } - - scripts.clear(); - - // An addref() was made when the QQmlCleanup was added to the engine. - release(); -} - -QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader) - : QQmlTypeLoader::Blob(url, JavaScriptFile, loader) - , m_isModule(url.path().endsWith(QLatin1String(".mjs"))) -{ -} - -QQmlScriptBlob::~QQmlScriptBlob() -{ -} - -QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const -{ - return m_scriptData; -} - -void QQmlScriptBlob::dataReceived(const SourceCodeData &data) -{ - if (!disableDiskCache() || forceDiskCache()) { - QQmlRefPointer<QV4::ExecutableCompilationUnit> unit - = QV4::ExecutableCompilationUnit::create(); - QString error; - if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { - initializeFromCompilationUnit(unit); - return; - } else { - qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error; - } - } - - if (!data.exists()) { - if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) - setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); - else - setError(QQmlTypeLoader::tr("No such file or directory")); - return; - } - - QString error; - QString source = data.readAll(&error); - if (!error.isEmpty()) { - setError(error); - return; - } - - QV4::CompiledData::CompilationUnit unit; - - if (m_isModule) { - QList<QQmlJS::DiagnosticMessage> diagnostics; - unit = QV4::Compiler::Codegen::compileModule(isDebugging(), urlString(), source, - data.sourceTimeStamp(), &diagnostics); - QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics); - if (!errors.isEmpty()) { - setError(errors); - return; - } - } else { - QmlIR::Document irUnit(isDebugging()); - - irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp(); - - QmlIR::ScriptDirectivesCollector collector(&irUnit); - irUnit.jsParserEngine.setDirectives(&collector); - - QList<QQmlError> errors; - irUnit.javaScriptCompilationUnit = QV4::Script::precompile( - &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(), - source, &errors, QV4::Compiler::ContextType::ScriptImportedByQML); - - source.clear(); - if (!errors.isEmpty()) { - setError(errors); - return; - } - - QmlIR::QmlUnitGenerator qmlGenerator; - qmlGenerator.generate(irUnit); - unit = std::move(irUnit.javaScriptCompilationUnit); - } - - auto executableUnit = QV4::ExecutableCompilationUnit::create(std::move(unit)); - - if ((!disableDiskCache() || forceDiskCache()) && !isDebugging()) { - QString errorString; - if (executableUnit->saveToDisk(url(), &errorString)) { - QString error; - if (!executableUnit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { - // ignore error, keep using the in-memory compilation unit. - } - } else { - qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" - << executableUnit->fileName() << "to disk:" << errorString; - } - } - - initializeFromCompilationUnit(executableUnit); -} - -void QQmlScriptBlob::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) -{ - initializeFromCompilationUnit(QV4::ExecutableCompilationUnit::create( - QV4::CompiledData::CompilationUnit(unit, urlString(), finalUrlString()))); -} - -void QQmlScriptBlob::done() -{ - if (isError()) - return; - - // Check all script dependencies for errors - for (int ii = 0; ii < m_scripts.count(); ++ii) { - const ScriptReference &script = m_scripts.at(ii); - Q_ASSERT(script.script->isCompleteOrError()); - if (script.script->isError()) { - QList<QQmlError> errors = script.script->errors(); - QQmlError error; - error.setUrl(url()); - error.setLine(script.location.line); - error.setColumn(script.location.column); - error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); - errors.prepend(error); - setError(errors); - return; - } - } - - if (!m_isModule) { - m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache); - - QSet<QString> ns; - - for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) { - const ScriptReference &script = m_scripts.at(scriptIndex); - - m_scriptData->scripts.append(script.script); - - if (!script.nameSpace.isNull()) { - if (!ns.contains(script.nameSpace)) { - ns.insert(script.nameSpace); - m_scriptData->typeNameCache->add(script.nameSpace); - } - } - m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace); - } - - m_importCache.populateCache(m_scriptData->typeNameCache); - } - m_scripts.clear(); -} - -QString QQmlScriptBlob::stringAt(int index) const -{ - return m_scriptData->m_precompiledScript->stringAt(index); -} - -void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) -{ - ScriptReference ref; - ref.script = blob; - ref.location = location; - ref.qualifier = qualifier; - ref.nameSpace = nameSpace; - - m_scripts << ref; -} - -void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit) -{ - Q_ASSERT(!m_scriptData); - m_scriptData.adopt(new QQmlScriptData()); - m_scriptData->url = finalUrl(); - m_scriptData->urlString = finalUrlString(); - m_scriptData->m_precompiledScript = unit; - - m_importCache.setBaseUrl(finalUrl(), finalUrlString()); - - QQmlRefPointer<QV4::ExecutableCompilationUnit> script = m_scriptData->m_precompiledScript; - - if (!m_isModule) { - QList<QQmlError> errors; - for (quint32 i = 0, count = script->importCount(); i < count; ++i) { - const QV4::CompiledData::Import *import = script->importAt(i); - if (!addImport(import, &errors)) { - Q_ASSERT(errors.size()); - QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(import->location.line); - error.setColumn(import->location.column); - errors.prepend(error); // put it back on the list after filling out information. - setError(errors); - return; - } - } - } - - auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine()); - - v4->injectModule(unit); - - for (const QString &request: unit->moduleRequests()) { - if (v4->moduleForUrl(QUrl(request), unit.data())) - continue; - - const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request)); - QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(absoluteRequest); - addDependency(blob.data()); - scriptImported(blob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(), /*namespace*/QString()); - } -} - -QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader) -: QQmlTypeLoader::Blob(url, QmldirFile, loader) -{ -} - -const QString &QQmlQmldirData::content() const -{ - return m_content; -} - -const QV4::CompiledData::Import *QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const -{ - QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *>::const_iterator it = - m_imports.find(blob); - if (it == m_imports.end()) - return nullptr; - return *it; -} - -void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import) -{ - m_imports[blob] = import; -} - -int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const -{ - QHash<QQmlTypeLoader::Blob *, int>::const_iterator it = m_priorities.find(blob); - if (it == m_priorities.end()) - return 0; - return *it; -} - -void QQmlQmldirData::setPriority(QQmlTypeLoader::Blob *blob, int priority) -{ - m_priorities[blob] = priority; -} - -void QQmlQmldirData::dataReceived(const SourceCodeData &data) -{ - QString error; - m_content = data.readAll(&error); - if (!error.isEmpty()) { - setError(error); - return; - } -} - -void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *) -{ - Q_UNIMPLEMENTED(); -} - -QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const -{ - error->clear(); - if (hasInlineSourceCode) - return inlineSourceCode; - - QFile f(fileInfo.absoluteFilePath()); - if (!f.open(QIODevice::ReadOnly)) { - *error = f.errorString(); - return QString(); - } - - const qint64 fileSize = fileInfo.size(); - - if (uchar *mappedData = f.map(0, fileSize)) { - QString source = QString::fromUtf8(reinterpret_cast<const char *>(mappedData), fileSize); - f.unmap(mappedData); - return source; - } - - QByteArray data(fileSize, Qt::Uninitialized); - if (f.read(data.data(), data.length()) != data.length()) { - *error = f.errorString(); - return QString(); - } - return QString::fromUtf8(data); -} - -QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const -{ - if (hasInlineSourceCode) - return QDateTime(); - - return fileInfo.lastModified(); -} - -bool QQmlDataBlob::SourceCodeData::exists() const -{ - if (hasInlineSourceCode) - return true; - return fileInfo.exists(); -} - -bool QQmlDataBlob::SourceCodeData::isEmpty() const -{ - if (hasInlineSourceCode) - return inlineSourceCode.isEmpty(); - return fileInfo.size() == 0; -} - QT_END_NAMESPACE - -#include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 3330d52e56..5710bdba56 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -51,213 +51,25 @@ // We mean it. // +#include <private/qqmldatablob_p.h> +#include <private/qqmlimport_p.h> +#include <private/qqmlmetatype_p.h> + #include <QtQml/qtqmlglobal.h> -#include <QtCore/qobject.h> -#include <QtCore/qatomic.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qcache.h> -#if QT_CONFIG(qml_network) -#include <QtNetwork/qnetworkreply.h> -#endif #include <QtQml/qqmlerror.h> -#include <QtQml/qqmlengine.h> -#include <QtQml/qqmlfile.h> -#include <QtQml/qqmlabstracturlinterceptor.h> - -#include <private/qhashedstring_p.h> -#include <private/qqmlimport_p.h> -#include <private/qqmlcleanup_p.h> -#include <private/qqmldirparser_p.h> -#include <private/qflagpointer_p.h> -#include <private/qqmlirbuilder_p.h> -#include <private/qv4executablecompilationunit_p.h> -#include <private/qv4value_p.h> -#include <private/qv4script_p.h> +#include <QtCore/qcache.h> +#include <QtCore/qmutex.h> QT_BEGIN_NAMESPACE -class QQmlScriptData; class QQmlScriptBlob; class QQmlQmldirData; -class QQmlTypeLoader; -class QQmlComponentPrivate; class QQmlTypeData; -class QQmlTypeLoader; class QQmlExtensionInterface; class QQmlProfiler; - -namespace QmlIR { -struct Document; -} - -class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount -{ -public: - enum Status { - Null, // Prior to QQmlTypeLoader::load() - Loading, // Prior to data being received and dataReceived() being called - WaitingForDependencies, // While there are outstanding addDependency()s - ResolvingDependencies, // While resolving outstanding dependencies, to detect cycles - Complete, // Finished - Error // Error - }; - - enum Type { //Matched in QQmlAbstractUrlInterceptor - QmlFile = QQmlAbstractUrlInterceptor::QmlFile, - JavaScriptFile = QQmlAbstractUrlInterceptor::JavaScriptFile, - QmldirFile = QQmlAbstractUrlInterceptor::QmldirFile - }; - - QQmlDataBlob(const QUrl &, Type, QQmlTypeLoader* manager); - ~QQmlDataBlob() override; - - void startLoading(); - - QQmlTypeLoader *typeLoader() const { return m_typeLoader; } - - Type type() const; - - Status status() const; - bool isNull() const; - bool isLoading() const; - bool isWaiting() const; - bool isComplete() const; - bool isError() const; - bool isCompleteOrError() const; - - qreal progress() const; - - QUrl url() const; - QString urlString() const; - QUrl finalUrl() const; - QString finalUrlString() const; - - QList<QQmlError> errors() const; - - class SourceCodeData { - public: - QString readAll(QString *error) const; - QDateTime sourceTimeStamp() const; - bool exists() const; - bool isEmpty() const; - private: - friend class QQmlDataBlob; - friend class QQmlTypeLoader; - QString inlineSourceCode; - QFileInfo fileInfo; - bool hasInlineSourceCode = false; - }; - -protected: - // Can be called from within callbacks - void setError(const QQmlError &); - void setError(const QList<QQmlError> &errors); - void setError(const QQmlJS::DiagnosticMessage &error); - void setError(const QVector<QQmlJS::DiagnosticMessage> &errors); - void setError(const QString &description); - void addDependency(QQmlDataBlob *); - - // Callbacks made in load thread - virtual void dataReceived(const SourceCodeData &) = 0; - virtual void initializeFromCachedUnit(const QV4::CompiledData::Unit*) = 0; - virtual void done(); -#if QT_CONFIG(qml_network) - virtual void networkError(QNetworkReply::NetworkError); -#endif - virtual void dependencyError(QQmlDataBlob *); - virtual void dependencyComplete(QQmlDataBlob *); - virtual void allDependenciesDone(); - - // Callbacks made in main thread - virtual void downloadProgressChanged(qreal); - virtual void completed(); - -protected: - // Manager that is currently fetching data for me - QQmlTypeLoader *m_typeLoader; - -private: - friend class QQmlTypeLoader; - friend class QQmlTypeLoaderThread; - - void tryDone(); - void cancelAllWaitingFor(); - void notifyAllWaitingOnMe(); - void notifyComplete(QQmlDataBlob *); - - struct ThreadData { - inline ThreadData(); - inline QQmlDataBlob::Status status() const; - inline void setStatus(QQmlDataBlob::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<QQmlError> m_errors; - - Type m_type; - - QUrl m_url; - QUrl m_finalUrl; - mutable QString m_urlString; - mutable QString m_finalUrlString; - - // List of QQmlDataBlob's that are waiting for me to complete. - QList<QQmlDataBlob *> m_waitingOnMe; - - // List of QQmlDataBlob's that I am waiting for to complete. - QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor; - - int m_redirectCount:30; - bool m_inCallback:1; - bool m_isDone:1; -}; - class QQmlTypeLoaderThread; - -class QQmlTypeLoaderQmldirContent -{ -private: - friend class QQmlTypeLoader; - - void setContent(const QString &location, const QString &content); - void setError(const QQmlError &); - -public: - QQmlTypeLoaderQmldirContent(); - QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default; - QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default; - - bool hasContent() const { return m_hasContent; } - bool hasError() const; - QList<QQmlError> errors(const QString &uri) const; - - QString typeNamespace() const; - - QQmlDirComponents components() const; - QQmlDirScripts scripts() const; - QQmlDirPlugins plugins() const; - - QString pluginLocation() const; - - bool designerSupported() const; - -private: - QQmlDirParser m_parser; - QString m_location; - bool m_hasContent = false; -}; +class QQmlEngine; class Q_QML_PRIVATE_EXPORT QQmlTypeLoader { @@ -293,6 +105,9 @@ public: bool isDebugging() const; + static bool diskCacheDisabled(); + static bool diskCacheForced(); + QQmlImports m_importCache; QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports; QVector<QQmlRefPointer<QQmlQmldirData>> m_qmldirs; @@ -418,209 +233,6 @@ private: friend struct StaticLoader; }; -class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob -{ - Q_DECLARE_TR_FUNCTIONS(QQmlTypeData) -public: - struct TypeReference - { - TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {} - - QV4::CompiledData::Location location; - QQmlType type; - int majorVersion; - int minorVersion; - QQmlRefPointer<QQmlTypeData> typeData; - QString prefix; // used by CompositeSingleton types - QString qualifiedName() const; - bool needsCreation; - }; - - struct ScriptReference - { - QV4::CompiledData::Location location; - QString qualifier; - QQmlRefPointer<QQmlScriptBlob> script; - }; - -private: - friend class QQmlTypeLoader; - - QQmlTypeData(const QUrl &, QQmlTypeLoader *); - -public: - ~QQmlTypeData() override; - - const QList<ScriptReference> &resolvedScripts() const; - - QV4::ExecutableCompilationUnit *compilationUnit() const; - - // Used by QQmlComponent to get notifications - struct TypeDataCallback { - virtual ~TypeDataCallback(); - virtual void typeDataProgress(QQmlTypeData *, qreal) {} - virtual void typeDataReady(QQmlTypeData *) {} - }; - void registerCallback(TypeDataCallback *); - void unregisterCallback(TypeDataCallback *); - -protected: - void done() override; - void completed() override; - void dataReceived(const SourceCodeData &) override; - void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override; - void allDependenciesDone() override; - void downloadProgressChanged(qreal) override; - - QString stringAt(int index) const override; - -private: - bool tryLoadFromDiskCache(); - bool loadFromSource(); - void restoreIR(QV4::CompiledData::CompilationUnit &&unit); - void continueLoadFromIR(); - void resolveTypes(); - QQmlJS::DiagnosticMessage buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache - ) const; - void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache, - const QV4::CompiledData::DependentTypesHasher &dependencyHasher); - void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - const QV4::ResolvedTypeReferenceMap &resolvedTypeCache); - bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, - TypeReference &ref, int lineNumber = -1, int columnNumber = -1, - bool reportErrors = true, - QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType); - - void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; - - - SourceCodeData m_backupSourceCode; // used when cache verification fails. - QScopedPointer<QmlIR::Document> m_document; - QV4::CompiledData::TypeReferenceMap m_typeReferences; - - QList<ScriptReference> m_scripts; - - QSet<QString> m_namespaces; - QList<TypeReference> m_compositeSingletons; - - // map from name index to resolved type - // While this could be a hash, a map is chosen here to provide a stable - // order, which is used to calculating a check-sum on dependent meta-objects. - QMap<int, TypeReference> m_resolvedTypes; - bool m_typesResolved:1; - - QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compiledData; - - QList<TypeDataCallback *> m_callbacks; - - bool m_implicitImportLoaded; - bool loadImplicitImport(); -}; - -// QQmlScriptData 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 QQmlCleanup::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 QQmlScriptData : public QQmlCleanup, public QQmlRefCount -{ -private: - friend class QQmlTypeLoader; - - QQmlScriptData(); - -public: - QUrl url; - QString urlString; - QQmlTypeNameCache *typeNameCache; - QVector<QQmlRefPointer<QQmlScriptBlob>> scripts; - - QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); - - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit() const { return m_precompiledScript; } - -protected: - void clear() override; // From QQmlCleanup - -private: - friend class QQmlScriptBlob; - - void initialize(QQmlEngine *); - QQmlContextData *qmlContextDataForContext(QQmlContextData *parentQmlContextData); - - bool m_loaded; - QQmlRefPointer<QV4::ExecutableCompilationUnit> m_precompiledScript; - QV4::PersistentValue m_value; -}; - -class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlTypeLoader::Blob -{ -private: - friend class QQmlTypeLoader; - - QQmlScriptBlob(const QUrl &, QQmlTypeLoader *); - -public: - ~QQmlScriptBlob() override; - - struct ScriptReference - { - QV4::CompiledData::Location location; - QString qualifier; - QString nameSpace; - QQmlRefPointer<QQmlScriptBlob> script; - }; - - QQmlRefPointer<QQmlScriptData> scriptData() const; - -protected: - void dataReceived(const SourceCodeData &) override; - void initializeFromCachedUnit(const QV4::CompiledData::Unit *unit) override; - void done() override; - - QString stringAt(int index) const override; - -private: - void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; - void initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit); - - QList<ScriptReference> m_scripts; - QQmlRefPointer<QQmlScriptData> m_scriptData; - const bool m_isModule; -}; - -class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob -{ -private: - friend class QQmlTypeLoader; - - QQmlQmldirData(const QUrl &, QQmlTypeLoader *); - -public: - const QString &content() const; - - const QV4::CompiledData::Import *import(QQmlTypeLoader::Blob *) const; - void setImport(QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *); - - int priority(QQmlTypeLoader::Blob *) const; - void setPriority(QQmlTypeLoader::Blob *, int); - -protected: - void dataReceived(const SourceCodeData &) override; - void initializeFromCachedUnit(const QV4::CompiledData::Unit *) override; - -private: - QString m_content; - QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *> m_imports; - QHash<QQmlTypeLoader::Blob *, int> m_priorities; -}; - - QT_END_NAMESPACE #endif // QQMLTYPELOADER_P_H diff --git a/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp new file mode 100644 index 0000000000..af97643163 --- /dev/null +++ b/src/qml/qml/qqmltypeloadernetworkreplyproxy.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmltypeloadernetworkreplyproxy_p.h> +#include <private/qqmltypeloader_p.h> + +QT_BEGIN_NAMESPACE + +QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l) + : l(l) +{ +} + +void QQmlTypeLoaderNetworkReplyProxy::finished() +{ + Q_ASSERT(sender()); + Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + l->networkReplyFinished(reply); +} + +void QQmlTypeLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + Q_ASSERT(sender()); + Q_ASSERT(qobject_cast<QNetworkReply *>(sender())); + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + l->networkReplyProgress(reply, bytesReceived, bytesTotal); +} + +// This function is for when you want to shortcut the signals and call directly +void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply) +{ + qint64 replySize = reply->size(); + l->networkReplyProgress(reply, replySize, replySize); + l->networkReplyFinished(reply); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h b/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h new file mode 100644 index 0000000000..ed87a2b508 --- /dev/null +++ b/src/qml/qml/qqmltypeloadernetworkreplyproxy_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPELOADERNETWORKREPLYPROXY_P_H +#define QQMLTYPELOADERNETWORKREPLYPROXY_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 <QtQml/qtqmlglobal.h> +#include <QtCore/qobject.h> + +QT_REQUIRE_CONFIG(qml_network); + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QQmlTypeLoader; + +// 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 QQmlTypeLoader 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 QQmlTypeLoaderNetworkReplyProxy : public QObject +{ + Q_OBJECT +public: + QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l); + +public slots: + void finished(); + void downloadProgress(qint64, qint64); + void manualFinished(QNetworkReply*); + +private: + QQmlTypeLoader *l; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPELOADERNETWORKREPLYPROXY_P_H diff --git a/src/qml/qml/qqmltypeloaderqmldircontent.cpp b/src/qml/qml/qqmltypeloaderqmldircontent.cpp new file mode 100644 index 0000000000..4aaa60f496 --- /dev/null +++ b/src/qml/qml/qqmltypeloaderqmldircontent.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmltypeloaderqmldircontent_p.h> +#include <QtQml/qqmlerror.h> + +QT_BEGIN_NAMESPACE + +QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent() +{ +} + +bool QQmlTypeLoaderQmldirContent::hasError() const +{ + return m_parser.hasError(); +} + +QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const +{ + QList<QQmlError> errors; + const QUrl url(uri); + for (const auto parseError : m_parser.errors(uri)) { + QQmlError error; + error.setUrl(url); + error.setLine(parseError.line); + error.setColumn(parseError.column); + error.setDescription(parseError.message); + errors.append(error); + } + return errors; +} + +QString QQmlTypeLoaderQmldirContent::typeNamespace() const +{ + return m_parser.typeNamespace(); +} + +void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) +{ + m_hasContent = true; + m_location = location; + m_parser.parse(content); +} + +void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error) +{ + QQmlJS::DiagnosticMessage parseError; + parseError.line = error.line(); + parseError.column = error.column(); + parseError.message = error.description(); + m_parser.setError(parseError); +} + +QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const +{ + return m_parser.components(); +} + +QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const +{ + return m_parser.scripts(); +} + +QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const +{ + return m_parser.plugins(); +} + +QString QQmlTypeLoaderQmldirContent::pluginLocation() const +{ + return m_location; +} + +bool QQmlTypeLoaderQmldirContent::designerSupported() const +{ + return m_parser.designerSupported(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypeloaderqmldircontent_p.h b/src/qml/qml/qqmltypeloaderqmldircontent_p.h new file mode 100644 index 0000000000..9e0a80cea8 --- /dev/null +++ b/src/qml/qml/qqmltypeloaderqmldircontent_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPELOADERQMLDIRCONTENT_P_H +#define QQMLTYPELOADERQMLDIRCONTENT_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 <private/qqmldirparser_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlError; +class QQmlTypeLoaderQmldirContent +{ +private: + friend class QQmlTypeLoader; + + void setContent(const QString &location, const QString &content); + void setError(const QQmlError &); + +public: + QQmlTypeLoaderQmldirContent(); + QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default; + QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default; + + bool hasContent() const { return m_hasContent; } + bool hasError() const; + QList<QQmlError> errors(const QString &uri) const; + + QString typeNamespace() const; + + QQmlDirComponents components() const; + QQmlDirScripts scripts() const; + QQmlDirPlugins plugins() const; + + QString pluginLocation() const; + + bool designerSupported() const; + +private: + QQmlDirParser m_parser; + QString m_location; + bool m_hasContent = false; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPELOADERQMLDIRCONTENT_P_H diff --git a/src/qml/qml/qqmltypeloaderthread.cpp b/src/qml/qml/qqmltypeloaderthread.cpp new file mode 100644 index 0000000000..0e1cecd1e5 --- /dev/null +++ b/src/qml/qml/qqmltypeloaderthread.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qqmlengine_p.h> +#include <private/qqmlextensionplugin_p.h> +#include <private/qqmltypeloaderthread_p.h> + +#if QT_CONFIG(qml_network) +#include <private/qqmltypeloadernetworkreplyproxy_p.h> +#endif + +QT_BEGIN_NAMESPACE + +QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader) + : m_loader(loader) +#if QT_CONFIG(qml_network) + , m_networkAccessManager(nullptr), m_networkReplyProxy(nullptr) +#endif // qml_network +{ + // Do that after initializing all the members. + startup(); +} + +#if QT_CONFIG(qml_network) +QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const +{ + Q_ASSERT(isThisThread()); + if (!m_networkAccessManager) { + m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(nullptr); + m_networkReplyProxy = new QQmlTypeLoaderNetworkReplyProxy(m_loader); + } + + return m_networkAccessManager; +} + +QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const +{ + Q_ASSERT(isThisThread()); + Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first + return m_networkReplyProxy; +} +#endif // qml_network + +void QQmlTypeLoaderThread::load(QQmlDataBlob *b) +{ + b->addref(); + callMethodInThread(&This::loadThread, b); +} + +void QQmlTypeLoaderThread::loadAsync(QQmlDataBlob *b) +{ + b->addref(); + postMethodToThread(&This::loadThread, b); +} + +void QQmlTypeLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d) +{ + b->addref(); + callMethodInThread(&This::loadWithStaticDataThread, b, d); +} + +void QQmlTypeLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d) +{ + b->addref(); + postMethodToThread(&This::loadWithStaticDataThread, b, d); +} + +void QQmlTypeLoaderThread::loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) +{ + b->addref(); + callMethodInThread(&This::loadWithCachedUnitThread, b, unit); +} + +void QQmlTypeLoaderThread::loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) +{ + b->addref(); + postMethodToThread(&This::loadWithCachedUnitThread, b, unit); +} + +void QQmlTypeLoaderThread::callCompleted(QQmlDataBlob *b) +{ + b->addref(); +#if !QT_CONFIG(thread) + if (!isThisThread()) + postMethodToThread(&This::callCompletedMain, b); +#else + postMethodToMain(&This::callCompletedMain, b); +#endif +} + +void QQmlTypeLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p) +{ + b->addref(); +#if !QT_CONFIG(thread) + if (!isThisThread()) + postMethodToThread(&This::callDownloadProgressChangedMain, b, p); +#else + postMethodToMain(&This::callDownloadProgressChangedMain, b, p); +#endif +} + +void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface, + const char *uri) +{ + callMethodInMain(&This::initializeEngineMain, iface, uri); +} + +void QQmlTypeLoaderThread::shutdownThread() +{ +#if QT_CONFIG(qml_network) + delete m_networkAccessManager; + m_networkAccessManager = nullptr; + delete m_networkReplyProxy; + m_networkReplyProxy = nullptr; +#endif // qml_network +} + +void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b) +{ + m_loader->loadThread(b); + b->release(); +} + +void QQmlTypeLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d) +{ + m_loader->loadWithStaticDataThread(b, d); + b->release(); +} + +void QQmlTypeLoaderThread::loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit) +{ + m_loader->loadWithCachedUnitThread(b, unit); + b->release(); +} + +void QQmlTypeLoaderThread::callCompletedMain(QQmlDataBlob *b) +{ +#ifdef DATABLOB_DEBUG + qWarning("QQmlTypeLoaderThread: %s completed() callback", qPrintable(b->urlString())); +#endif + b->completed(); + b->release(); +} + +void QQmlTypeLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p) +{ +#ifdef DATABLOB_DEBUG + qWarning("QQmlTypeLoaderThread: %s downloadProgressChanged(%f) callback", + qPrintable(b->urlString()), p); +#endif + b->downloadProgressChanged(p); + b->release(); +} + +void QQmlTypeLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface, + const char *uri) +{ + Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread()); + iface->initializeEngine(m_loader->engine(), uri); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypeloaderthread_p.h b/src/qml/qml/qqmltypeloaderthread_p.h new file mode 100644 index 0000000000..67e47e86de --- /dev/null +++ b/src/qml/qml/qqmltypeloaderthread_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTYPELOADERTHREAD_P_H +#define QQMLTYPELOADERTHREAD_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 <private/qqmlthread_p.h> +#include <private/qv4compileddata_p.h> + +#include <QtQml/qtqmlglobal.h> + +#if QT_CONFIG(qml_network) +#include <private/qqmltypeloadernetworkreplyproxy_p.h> +#include <QtNetwork/qnetworkaccessmanager.h> +#endif + +QT_BEGIN_NAMESPACE + +class QQmlDataBlob; +class QQmlTypeLoader; +class QQmlExtensionInterface; + +class QQmlTypeLoaderThread : public QQmlThread +{ + typedef QQmlTypeLoaderThread This; + +public: + QQmlTypeLoaderThread(QQmlTypeLoader *loader); +#if QT_CONFIG(qml_network) + QNetworkAccessManager *networkAccessManager() const; + QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const; +#endif // qml_network + void load(QQmlDataBlob *b); + void loadAsync(QQmlDataBlob *b); + void loadWithStaticData(QQmlDataBlob *b, const QByteArray &); + void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &); + void loadWithCachedUnit(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); + void loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); + void callCompleted(QQmlDataBlob *b); + void callDownloadProgressChanged(QQmlDataBlob *b, qreal p); + void initializeEngine(QQmlExtensionInterface *, const char *); + +protected: + void shutdownThread() override; + +private: + void loadThread(QQmlDataBlob *b); + void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &); + void loadWithCachedUnitThread(QQmlDataBlob *b, const QV4::CompiledData::Unit *unit); + void callCompletedMain(QQmlDataBlob *b); + void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p); + void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri); + + QQmlTypeLoader *m_loader; +#if QT_CONFIG(qml_network) + mutable QNetworkAccessManager *m_networkAccessManager; + mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy; +#endif // qml_network +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPELOADERTHREAD_P_H diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 57c4eec879..931f37b35a 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -42,6 +42,7 @@ #include <private/qqmlengine_p.h> #include <private/qqmlcontext_p.h> #include <private/qqmlmetaobject_p.h> +#include <private/qqmltypedata_p.h> #include <private/qjsvalue_p.h> #include <private/qv4functionobject_p.h> diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 4838ef3814..2e213e7dc3 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -40,6 +40,7 @@ #include "qqmlbuiltinfunctions_p.h" #include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlfile.h> #include <private/qqmlengine_p.h> #include <private/qqmlcomponent_p.h> #include <private/qqmlloggingcategory_p.h> diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp index c1684d955c..1cd089f454 100644 --- a/src/qmlmodels/qqmllistmodel.cpp +++ b/src/qmlmodels/qqmllistmodel.cpp @@ -53,6 +53,7 @@ #include <private/qv4objectiterator_p.h> #include <private/qv4alloca_p.h> #include <private/qv4lookup_p.h> +#include <private/qv4qmlcontext_p.h> #include <qqmlcontext.h> #include <qqmlinfo.h> diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index d7c0157eee..1cb30f5a8d 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -51,6 +51,7 @@ #include <private/qqmlcontext_p.h> #include <private/qquickstate_p_p.h> #include <private/qqmlboundsignal_p.h> +#include <private/qv4qmlcontext_p.h> #include <QtCore/qdebug.h> diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 5462e6c8ae..102acf73d6 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -36,6 +36,7 @@ #include <QSysInfo> #include <QLoggingCategory> #include <private/qqmlcomponent_p.h> +#include <private/qqmlscriptdata_p.h> #include <qtranslator.h> #include "../../shared/util.h" diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 23be541a72..1f0115b926 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -34,6 +34,7 @@ #include <private/qv4codegen_p.h> #include <private/qqmlcomponent_p.h> #include <private/qv4executablecompilationunit_p.h> +#include <private/qqmlscriptdata_p.h> #include <QQmlComponent> #include <QQmlEngine> #include <QQmlFileSelector> diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index b1a1ed4dec..d603ca6907 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -47,6 +47,7 @@ #include <private/qv4alloca_p.h> #include <private/qv4runtime_p.h> #include <private/qv4object_p.h> +#include <private/qv4script_p.h> #include <private/qqmlcomponentattached_p.h> #include <private/qv4objectiterator_p.h> #include <private/qqmlabstractbinding_p.h> diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index e55cd6f7a0..66d50cfe39 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -43,6 +43,7 @@ #include <QQmlIncubationController> #include <QTemporaryDir> #include <private/qqmlengine_p.h> +#include <private/qqmltypedata_p.h> #include <QQmlAbstractUrlInterceptor> class tst_qqmlengine : public QQmlDataTest diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index d6215307bf..ffb1d51971 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -27,6 +27,8 @@ ****************************************************************************/ #include "testtypes.h" +#include <private/qv4qmlcontext_p.h> + static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index 3b17df9872..dcfe914af6 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -32,6 +32,7 @@ #include <QTranslator> #include <QQmlContext> #include <private/qqmlengine_p.h> +#include <private/qqmltypedata_p.h> #include "../../shared/util.h" class tst_qqmltranslation : public QQmlDataTest diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index 0c4abf19f4..0357f121bb 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -28,6 +28,7 @@ #include <QtTest/QtTest> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlfile.h> #include <QtQml/qqmlnetworkaccessmanagerfactory.h> #include <QtQuick/qquickview.h> #include <QtQuick/qquickitem.h> @@ -35,6 +36,7 @@ #include <QtCore/qprocess.h> #endif #include <QtQml/private/qqmlengine_p.h> +#include <QtQml/private/qqmltypedata_p.h> #include <QtQml/private/qqmltypeloader_p.h> #include "../../shared/testhttpserver.h" #include "../../shared/util.h" |