diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2019-07-12 20:36:14 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2019-07-12 20:36:48 +0200 |
commit | 880a143eb572de3e8a6734cc663ed4b6e309b99d (patch) | |
tree | 4d1ad149dff7d9708e5981ea7b0ec7f8aee24bbf /src | |
parent | c433b78660923b6268653b437a2a04078de0f058 (diff) | |
parent | a6057b46eebc71772d584ea5d318d130e2f40a19 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/qt6
Change-Id: I4476733af61fcf3a2af1d121a4585c3fae1c240e
Diffstat (limited to 'src')
140 files changed, 5174 insertions, 3527 deletions
diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 2ed839a6e0..a87b0a59f6 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -5,9 +5,9 @@ SUBDIRS += \ builtins \ qtqml \ models \ - labsmodels \ - folderlistmodel + labsmodels +qtConfig(qml-itemmodel): SUBDIRS += folderlistmodel qtConfig(qml-worker-script): SUBDIRS += workerscript qtHaveModule(sql): SUBDIRS += localstorage qtConfig(settings): SUBDIRS += settings 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/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h index 4a9fecb504..1e34086b0c 100644 --- a/src/particles/qquickparticlesystem_p.h +++ b/src/particles/qquickparticlesystem_p.h @@ -61,6 +61,7 @@ #include <QtQml/qqml.h> #include <private/qv4util_p.h> #include <private/qv4global_p.h> +#include <private/qv4staticvalue_p.h> #include "qtquickparticlesglobal_p.h" QT_BEGIN_NAMESPACE 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/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp index 5d2684b510..8bb3b95e48 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp @@ -93,7 +93,9 @@ QQmlPreviewHandler::QQmlPreviewHandler(QObject *parent) : QObject(parent) QQmlPreviewHandler::~QQmlPreviewHandler() { +#if QT_CONFIG(translation) removeTranslators(); +#endif clear(); } @@ -223,6 +225,7 @@ void QQmlPreviewHandler::doZoom() m_lastPosition.initLastSavedWindowPosition(m_currentWindow); } +#if QT_CONFIG(translation) void QQmlPreviewHandler::removeTranslators() { if (!m_qtTranslator.isNull()) { @@ -255,6 +258,7 @@ void QQmlPreviewHandler::language(const QUrl &context, const QLocale &locale) for (QQmlEngine *engine : qAsConst(m_engines)) engine->retranslate(); } +#endif void QQmlPreviewHandler::clear() { diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h index 47491b9d8f..f7f343a4ee 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h @@ -81,7 +81,9 @@ public: void loadUrl(const QUrl &url); void rerun(); void zoom(qreal newFactor); +#if QT_CONFIG(translation) void language(const QUrl &context, const QLocale &locale); +#endif void clear(); @@ -115,7 +117,9 @@ private: void frameSwapped(); void fpsTimerHit(); +#if QT_CONFIG(translation) void removeTranslators(); +#endif QScopedPointer<QQuickItem> m_dummyItem; QList<QQmlEngine *> m_engines; @@ -145,8 +149,10 @@ private: FrameTime m_rendering; FrameTime m_synchronizing; +#if QT_CONFIG(translation) QScopedPointer<QTranslator> m_qtTranslator; QScopedPointer<QTranslator> m_qmlTranslator; +#endif }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp index 2e2224df47..2e6aaa5858 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp @@ -64,7 +64,9 @@ QQmlPreviewServiceImpl::QQmlPreviewServiceImpl(QObject *parent) : connect(this, &QQmlPreviewServiceImpl::load, &m_handler, &QQmlPreviewHandler::loadUrl); connect(this, &QQmlPreviewServiceImpl::rerun, &m_handler, &QQmlPreviewHandler::rerun); connect(this, &QQmlPreviewServiceImpl::zoom, &m_handler, &QQmlPreviewHandler::zoom); +#if QT_CONFIG(translation) connect(this, &QQmlPreviewServiceImpl::language, &m_handler, &QQmlPreviewHandler::language); +#endif connect(&m_handler, &QQmlPreviewHandler::error, this, &QQmlPreviewServiceImpl::forwardError, Qt::DirectConnection); connect(&m_handler, &QQmlPreviewHandler::fps, this, &QQmlPreviewServiceImpl::forwardFps, @@ -135,6 +137,7 @@ void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data) emit zoom(static_cast<qreal>(factor)); break; } +#if QT_CONFIG(translation) case Language: { QUrl context; QString locale; @@ -143,6 +146,7 @@ void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data) locale.isEmpty() ? QLocale() : QLocale(locale)); break; } +#endif default: forwardError(QString::fromLatin1("Invalid command: %1").arg(command)); break; diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h index 7bdc87ec59..de50e6fc61 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h @@ -99,7 +99,9 @@ signals: void rerun(); void clearCache(); void zoom(qreal factor); +#if QT_CONFIG(translation) void language(const QUrl &context, const QLocale &locale); +#endif private: QScopedPointer<QQmlPreviewFileEngineHandler> m_fileEngine; diff --git a/src/qml/common/common.pri b/src/qml/common/common.pri index de95772120..bcc3ea0fa0 100644 --- a/src/qml/common/common.pri +++ b/src/qml/common/common.pri @@ -1,3 +1,35 @@ +!build_pass { + # Create a header containing a hash that describes this library. For a + # released version of Qt, we'll use the .tag file that is updated by git + # archive with the commit hash. For unreleased versions, we'll ask git + # describe. Note that it won't update unless qmake is run again, even if + # the commit change also changed something in this library. + tagFile = $$PWD/../../.tag + tag = + exists($$tagFile) { + tag = $$cat($$tagFile, singleline) + QMAKE_INTERNAL_INCLUDED_FILES += $$tagFile + } + !equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") { + QML_COMPILE_HASH = $$tag + } else:exists($$PWD/../../.git) { + commit = $$system(git rev-parse HEAD) + QML_COMPILE_HASH = $$commit + } + compile_hash_contents = \ + "// Generated file, DO NOT EDIT" \ + "$${LITERAL_HASH}define QML_COMPILE_HASH \"$$QML_COMPILE_HASH\"" \ + "$${LITERAL_HASH}define QML_COMPILE_HASH_LENGTH $$str_size($$QML_COMPILE_HASH)" + write_file("$$OUT_PWD/qml_compile_hash_p.h", compile_hash_contents)|error() +} + HEADERS += \ $$PWD/qqmlapiversion_p.h \ - $$PWD/qqmljsdiagnosticmessage_p.h + $$PWD/qqmljsdiagnosticmessage_p.h \ + $$PWD/qqmljsfixedpoolarray_p.h \ + $$PWD/qqmljsmemorypool_p.h \ + $$PWD/qv4alloca_p.h \ + $$PWD/qv4calldata_p.h \ + $$PWD/qv4compileddata_p.h \ + $$PWD/qv4staticvalue_p.h \ + $$PWD/qv4stringtoarrayindex_p.h diff --git a/src/qml/common/qqmljsfixedpoolarray_p.h b/src/qml/common/qqmljsfixedpoolarray_p.h new file mode 100644 index 0000000000..b65b994d6c --- /dev/null +++ b/src/qml/common/qqmljsfixedpoolarray_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** 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 QQMLJSFIXEDPOOLARRAY_P_H +#define QQMLJSFIXEDPOOLARRAY_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 <QtCore/qglobal.h> +#include <private/qqmljsmemorypool_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +template <typename T> +class FixedPoolArray +{ + T *data; + int count = 0; + +public: + FixedPoolArray() + : data(nullptr) + {} + + FixedPoolArray(MemoryPool *pool, int size) + { allocate(pool, size); } + + void allocate(MemoryPool *pool, int size) + { + count = size; + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + } + + void allocate(MemoryPool *pool, const QVector<T> &vector) + { + count = vector.count(); + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + + if (QTypeInfo<T>::isComplex) { + for (int i = 0; i < count; ++i) + new (data + i) T(vector.at(i)); + } else { + memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T)); + } + } + + template <typename Container> + void allocate(MemoryPool *pool, const Container &container) + { + count = container.count(); + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + typename Container::ConstIterator it = container.constBegin(); + for (int i = 0; i < count; ++i) + new (data + i) T(*it++); + } + + int size() const + { return count; } + + const T &at(int index) const { + Q_ASSERT(index >= 0 && index < count); + return data[index]; + } + + T &at(int index) { + Q_ASSERT(index >= 0 && index < count); + return data[index]; + } + + T &operator[](int index) { + return at(index); + } + + + int indexOf(const T &value) const { + for (int i = 0; i < count; ++i) + if (data[i] == value) + return i; + return -1; + } + + const T *begin() const { return data; } + const T *end() const { return data + count; } + + T *begin() { return data; } + T *end() { return data + count; } +}; + +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QQMLJSFIXEDPOOLARRAY_P_H diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/common/qqmljsmemorypool_p.h index e7b1f46414..0cf7ea84e6 100644 --- a/src/qml/parser/qqmljsmemorypool_p.h +++ b/src/qml/common/qqmljsmemorypool_p.h @@ -51,8 +51,6 @@ // We mean it. // -#include "qqmljsglobal_p.h" - #include <QtCore/qglobal.h> #include <QtCore/qshareddata.h> #include <QtCore/qdebug.h> @@ -65,7 +63,7 @@ namespace QQmlJS { class Managed; -class QML_PARSER_EXPORT MemoryPool : public QSharedData +class MemoryPool : public QSharedData { MemoryPool(const MemoryPool &other); void operator =(const MemoryPool &other); @@ -162,7 +160,7 @@ private: }; }; -class QML_PARSER_EXPORT Managed +class Managed { Q_DISABLE_COPY(Managed) public: @@ -174,81 +172,6 @@ public: void operator delete(void *, MemoryPool *) {} }; -template <typename T> -class FixedPoolArray -{ - T *data; - int count = 0; - -public: - FixedPoolArray() - : data(nullptr) - {} - - FixedPoolArray(MemoryPool *pool, int size) - { allocate(pool, size); } - - void allocate(MemoryPool *pool, int size) - { - count = size; - data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); - } - - void allocate(MemoryPool *pool, const QVector<T> &vector) - { - count = vector.count(); - data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); - - if (QTypeInfo<T>::isComplex) { - for (int i = 0; i < count; ++i) - new (data + i) T(vector.at(i)); - } else { - memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T)); - } - } - - template <typename Container> - void allocate(MemoryPool *pool, const Container &container) - { - count = container.count(); - data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); - typename Container::ConstIterator it = container.constBegin(); - for (int i = 0; i < count; ++i) - new (data + i) T(*it++); - } - - int size() const - { return count; } - - const T &at(int index) const { - Q_ASSERT(index >= 0 && index < count); - return data[index]; - } - - T &at(int index) { - Q_ASSERT(index >= 0 && index < count); - return data[index]; - } - - T &operator[](int index) { - return at(index); - } - - - int indexOf(const T &value) const { - for (int i = 0; i < count; ++i) - if (data[i] == value) - return i; - return -1; - } - - const T *begin() const { return data; } - const T *end() const { return data + count; } - - T *begin() { return data; } - T *end() { return data + count; } -}; - } // namespace QQmlJS QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4alloca_p.h b/src/qml/common/qv4alloca_p.h index 65c3e4d65a..65c3e4d65a 100644 --- a/src/qml/compiler/qv4alloca_p.h +++ b/src/qml/common/qv4alloca_p.h diff --git a/src/qml/compiler/qv4calldata_p.h b/src/qml/common/qv4calldata_p.h index 5a5280cb86..5a5280cb86 100644 --- a/src/qml/compiler/qv4calldata_p.h +++ b/src/qml/common/qv4calldata_p.h diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index f3d5de3db1..c3ddce5884 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -50,6 +50,8 @@ // We mean it. // +#include <functional> + #include <QtCore/qstring.h> #include <QtCore/qscopeguard.h> #include <QtCore/qvector.h> @@ -62,6 +64,7 @@ #include <private/qendian_p.h> #include <private/qv4staticvalue_p.h> +#include <functional> QT_BEGIN_NAMESPACE @@ -72,12 +75,10 @@ QT_BEGIN_NAMESPACE // Also change the comment behind the number to describe the latest change. This has the added // benefit that if another patch changes the version too, it will result in a merge conflict, and // not get removed silently. -#define QV4_DATA_STRUCTURE_VERSION 0x23 // Remove trace slots +#define QV4_DATA_STRUCTURE_VERSION 0x24 // Collect function parameter types class QIODevice; -class QQmlPropertyData; class QQmlTypeNameCache; -class QQmlScriptData; class QQmlType; class QQmlEngine; @@ -255,6 +256,29 @@ struct Block }; static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +enum class BuiltinType : unsigned int { + Var = 0, Variant, Int, Bool, Real, String, Url, Color, + Font, Time, Date, DateTime, Rect, Point, Size, + Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, InvalidBuiltin +}; + +struct ParameterType +{ + union { + quint32 _dummy; + quint32_le_bitfield<0, 1> indexIsBuiltinType; + quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType; + }; +}; +static_assert(sizeof(ParameterType) == 4, "ParameterType structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + +struct Parameter +{ + quint32_le nameIndex; + ParameterType type; +}; +static_assert(sizeof(Parameter) == 8, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + // Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties // for unaligned access. The ordering of the fields is also from largest to smallest. struct Function @@ -273,6 +297,7 @@ struct Function quint16_le length; quint16_le nFormals; quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData. + ParameterType returnType; quint32_le localsOffset; quint16_le nLocals; quint16_le nLineNumbers; @@ -294,13 +319,13 @@ struct Function // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] - const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); } + const Parameter *formalsTable() const { return reinterpret_cast<const Parameter *>(reinterpret_cast<const char *>(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); } const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); } // --- QQmlPropertyCacheCreator interface - const quint32_le *formalsBegin() const { return formalsTable(); } - const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } + const Parameter *formalsBegin() const { return formalsTable(); } + const Parameter *formalsEnd() const { return formalsTable() + nFormals; } // --- const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); } @@ -308,7 +333,7 @@ struct Function const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; } static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) { - int trailingData = (nFormals + nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) + int trailingData = nFormals * sizeof(Parameter) + (nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine); size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); Q_ASSERT(size < INT_MAX); @@ -319,7 +344,7 @@ struct Function return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 56, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Method { enum Type { @@ -602,23 +627,6 @@ struct Enum }; static_assert(sizeof(Enum) == 12, "Enum structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); -enum class BuiltinType : unsigned int { - Var = 0, Variant, Int, Bool, Real, String, Url, Color, - Font, Time, Date, DateTime, Rect, Point, Size, - Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, InvalidBuiltin -}; - -struct Parameter -{ - quint32_le nameIndex; - union { - quint32 _dummy; - quint32_le_bitfield<0, 1> indexIsBuiltinType; - quint32_le_bitfield<1, 31> typeNameIndexOrBuiltinType; - }; -}; -static_assert(sizeof(Parameter) == 8, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); - struct Signal { quint32_le nameIndex; @@ -1087,9 +1095,6 @@ struct TypeReferenceMap : QHash<int, TypeReference> using DependentTypesHasher = std::function<QByteArray()>; -// index is per-object binding index -typedef QVector<QQmlPropertyData*> BindingPropertyData; - // This is how this hooks into the existing structures: struct CompilationUnitBase @@ -1119,10 +1124,10 @@ struct CompilationUnitBase } // pointers either to data->constants() or little-endian memory copy. - QV4::Heap::String **runtimeStrings = nullptr; // Array + Heap::String **runtimeStrings = nullptr; // Array const StaticValue* constants = nullptr; QV4::StaticValue *runtimeRegularExpressions = nullptr; - QV4::Heap::InternalClass **runtimeClasses = nullptr; + Heap::InternalClass **runtimeClasses = nullptr; const StaticValue** imports = nullptr; }; diff --git a/src/qml/compiler/qv4staticvalue_p.h b/src/qml/common/qv4staticvalue_p.h index c6b4bdb158..8160bbb748 100644 --- a/src/qml/compiler/qv4staticvalue_p.h +++ b/src/qml/common/qv4staticvalue_p.h @@ -50,14 +50,24 @@ // We mean it. // -#include <QtQml/private/qtqmlglobal_p.h> -#include <QtQml/private/qv4global_p.h> #include <QtCore/private/qnumeric_p.h> +#ifdef QT_NO_DEBUG +#define QV4_NEARLY_ALWAYS_INLINE Q_ALWAYS_INLINE +#else +#define QV4_NEARLY_ALWAYS_INLINE inline +#endif + QT_BEGIN_NAMESPACE namespace QV4 { +// ReturnedValue is used to return values from runtime methods +// the type has to be a primitive type (no struct or union), so that the compiler +// will return it in a register on all platforms. +// It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm +typedef quint64 ReturnedValue; + struct Double { quint64 d; @@ -105,7 +115,7 @@ struct Double { } }; -struct Q_QML_PRIVATE_EXPORT StaticValue +struct StaticValue { StaticValue() = default; constexpr StaticValue(quint64 val) : _val(val) {} @@ -180,9 +190,9 @@ struct Q_QML_PRIVATE_EXPORT StaticValue quint64 _val; - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; } + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; } + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; } + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; } #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN static inline int valueOffset() { return 0; } @@ -192,22 +202,22 @@ struct Q_QML_PRIVATE_EXPORT StaticValue static inline int tagOffset() { return 0; } #endif static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } - QML_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); } - QML_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); } + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } + QV4_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); } + QV4_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; } + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); } - QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const + QV4_NEARLY_ALWAYS_INLINE constexpr int int_32() const { return int(value()); } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i) + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i) { setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); } - QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } + QV4_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() + QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() { setTagValue(quint32(ValueTypeInternal::Empty), 0); } @@ -353,7 +363,7 @@ struct Q_QML_PRIVATE_EXPORT StaticValue #endif } - QML_NEARLY_ALWAYS_INLINE double doubleValue() const { + QV4_NEARLY_ALWAYS_INLINE double doubleValue() const { Q_ASSERT(isDouble()); double d; StaticValue v = *this; @@ -362,7 +372,7 @@ struct Q_QML_PRIVATE_EXPORT StaticValue return d; } - QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { + QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) { if (qt_is_nan(d)) d = qt_qnan(); memcpy(&_val, &d, 8); @@ -383,7 +393,7 @@ struct Q_QML_PRIVATE_EXPORT StaticValue return false; } - QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { + QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { int i = int(d); return (i == d && !(d == 0 && std::signbit(d))); } diff --git a/src/qml/compiler/qv4stringtoarrayindex_p.h b/src/qml/common/qv4stringtoarrayindex_p.h index 61bd988d1e..61bd988d1e 100644 --- a/src/qml/compiler/qv4stringtoarrayindex_p.h +++ b/src/qml/common/qv4stringtoarrayindex_p.h diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index c6ae8c6b69..4d6926d420 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -2,21 +2,17 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD HEADERS += \ - $$PWD/qv4alloca_p.h \ $$PWD/qv4bytecodegenerator_p.h \ - $$PWD/qv4compileddata_p.h \ $$PWD/qv4compiler_p.h \ $$PWD/qv4compilercontext_p.h \ $$PWD/qv4compilercontrolflow_p.h \ + $$PWD/qv4compilerglobal_p.h \ $$PWD/qv4compilerscanfunctions_p.h \ $$PWD/qv4codegen_p.h \ $$PWD/qqmlirbuilder_p.h \ $$PWD/qv4instr_moth_p.h \ $$PWD/qv4bytecodehandler_p.h \ - $$PWD/qv4calldata_p.h \ - $$PWD/qv4util_p.h \ - $$PWD/qv4staticvalue_p.h \ - $$PWD/qv4stringtoarrayindex_p.h + $$PWD/qv4util_p.h SOURCES += \ $$PWD/qv4bytecodegenerator.cpp \ diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 1891f31f13..aab3f8e9d6 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -56,6 +56,7 @@ QT_USE_NAMESPACE static const quint32 emptyStringIndex = 0; using namespace QmlIR; +using namespace QQmlJS; #define COMPILE_EXCEPTION(location, desc) \ { \ @@ -63,6 +64,84 @@ using namespace QmlIR; return false; \ } +bool Parameter::init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString ¶meterName, + const QString &typeName) +{ + return init(this, stringGenerator, stringGenerator->registerString(parameterName), stringGenerator->registerString(typeName)); +} + +bool Parameter::init(QV4::CompiledData::Parameter *param, const QV4::Compiler::JSUnitGenerator *stringGenerator, + int parameterNameIndex, int typeNameIndex) +{ + param->nameIndex = parameterNameIndex; + return initType(¶m->type, stringGenerator, typeNameIndex); +} + +bool Parameter::initType(QV4::CompiledData::ParameterType *paramType, const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex) +{ + paramType->indexIsBuiltinType = false; + paramType->typeNameIndexOrBuiltinType = 0; + const QString typeName = stringGenerator->stringForIndex(typeNameIndex); + auto builtinType = stringToBuiltinType(typeName); + if (builtinType == QV4::CompiledData::BuiltinType::InvalidBuiltin) { + if (typeName.isEmpty() || !typeName.at(0).isUpper()) + return false; + paramType->indexIsBuiltinType = false; + paramType->typeNameIndexOrBuiltinType = typeNameIndex; + Q_ASSERT(quint32(typeNameIndex) < (1u << 31)); + } else { + paramType->indexIsBuiltinType = true; + paramType->typeNameIndexOrBuiltinType = static_cast<quint32>(builtinType); + Q_ASSERT(quint32(builtinType) < (1u << 31)); + } + return true; +} + +QV4::CompiledData::BuiltinType Parameter::stringToBuiltinType(const QString &typeName) +{ + static const struct TypeNameToType { + const char *name; + size_t nameLength; + QV4::CompiledData::BuiltinType type; + } propTypeNameToTypes[] = { + { "int", strlen("int"), QV4::CompiledData::BuiltinType::Int }, + { "bool", strlen("bool"), QV4::CompiledData::BuiltinType::Bool }, + { "double", strlen("double"), QV4::CompiledData::BuiltinType::Real }, + { "real", strlen("real"), QV4::CompiledData::BuiltinType::Real }, + { "string", strlen("string"), QV4::CompiledData::BuiltinType::String }, + { "url", strlen("url"), QV4::CompiledData::BuiltinType::Url }, + { "color", strlen("color"), QV4::CompiledData::BuiltinType::Color }, + // Internally QTime, QDate and QDateTime are all supported. + // To be more consistent with JavaScript we expose only + // QDateTime as it matches closely with the Date JS type. + // We also call it "date" to match. + // { "time", strlen("time"), Property::Time }, + // { "date", strlen("date"), Property::Date }, + { "date", strlen("date"), QV4::CompiledData::BuiltinType::DateTime }, + { "rect", strlen("rect"), QV4::CompiledData::BuiltinType::Rect }, + { "point", strlen("point"), QV4::CompiledData::BuiltinType::Point }, + { "size", strlen("size"), QV4::CompiledData::BuiltinType::Size }, + { "font", strlen("font"), QV4::CompiledData::BuiltinType::Font }, + { "vector2d", strlen("vector2d"), QV4::CompiledData::BuiltinType::Vector2D }, + { "vector3d", strlen("vector3d"), QV4::CompiledData::BuiltinType::Vector3D }, + { "vector4d", strlen("vector4d"), QV4::CompiledData::BuiltinType::Vector4D }, + { "quaternion", strlen("quaternion"), QV4::CompiledData::BuiltinType::Quaternion }, + { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::BuiltinType::Matrix4x4 }, + { "variant", strlen("variant"), QV4::CompiledData::BuiltinType::Variant }, + { "var", strlen("var"), QV4::CompiledData::BuiltinType::Var } + }; + static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / + sizeof(propTypeNameToTypes[0]); + + for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { + const TypeNameToType *t = propTypeNameToTypes + typeIndex; + if (typeName == QLatin1String(t->name, static_cast<int>(t->nameLength))) { + return t->type; + } + } + return QV4::CompiledData::BuiltinType::InvalidBuiltin; +} + void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &loc) { inheritedTypeNameIndex = typeNameIndex; @@ -247,64 +326,11 @@ QStringList Signal::parameterStringList(const QV4::Compiler::StringTableGenerato { QStringList result; result.reserve(parameters->count); - for (SignalParameter *param = parameters->first; param; param = param->next) + for (Parameter *param = parameters->first; param; param = param->next) result << stringPool->stringForIndex(param->nameIndex); return result; } -static void replaceWithSpace(QString &str, int idx, int n) -{ - QChar *data = str.data() + idx; - const QChar space(QLatin1Char(' ')); - for (int ii = 0; ii < n; ++ii) - *data++ = space; -} - -void Document::removeScriptPragmas(QString &script) -{ - const QLatin1String pragma("pragma"); - const QLatin1String library("library"); - - QQmlJS::Lexer l(nullptr); - l.setCode(script, 0); - - int token = l.lex(); - - while (true) { - if (token != QQmlJSGrammar::T_DOT) - return; - - int startOffset = l.tokenOffset(); - int startLine = l.tokenStartLine(); - - token = l.lex(); - - if (token != QQmlJSGrammar::T_PRAGMA || - l.tokenStartLine() != startLine || - script.midRef(l.tokenOffset(), l.tokenLength()) != pragma) - return; - - token = l.lex(); - - if (token != QQmlJSGrammar::T_IDENTIFIER || - l.tokenStartLine() != startLine) - return; - - const QStringRef pragmaValue = script.midRef(l.tokenOffset(), l.tokenLength()); - int endOffset = l.tokenLength() + l.tokenOffset(); - - token = l.lex(); - if (l.tokenStartLine() == startLine) - return; - - if (pragmaValue == library) { - replaceWithSpace(script, startOffset, endOffset - startOffset); - } else { - return; - } - } -} - Document::Document(bool debugMode) : jsModule(debugMode) , program(nullptr) @@ -764,40 +790,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) { - static const struct TypeNameToType { - const char *name; - size_t nameLength; - QV4::CompiledData::BuiltinType type; - } propTypeNameToTypes[] = { - { "int", strlen("int"), QV4::CompiledData::BuiltinType::Int }, - { "bool", strlen("bool"), QV4::CompiledData::BuiltinType::Bool }, - { "double", strlen("double"), QV4::CompiledData::BuiltinType::Real }, - { "real", strlen("real"), QV4::CompiledData::BuiltinType::Real }, - { "string", strlen("string"), QV4::CompiledData::BuiltinType::String }, - { "url", strlen("url"), QV4::CompiledData::BuiltinType::Url }, - { "color", strlen("color"), QV4::CompiledData::BuiltinType::Color }, - // Internally QTime, QDate and QDateTime are all supported. - // To be more consistent with JavaScript we expose only - // QDateTime as it matches closely with the Date JS type. - // We also call it "date" to match. - // { "time", strlen("time"), Property::Time }, - // { "date", strlen("date"), Property::Date }, - { "date", strlen("date"), QV4::CompiledData::BuiltinType::DateTime }, - { "rect", strlen("rect"), QV4::CompiledData::BuiltinType::Rect }, - { "point", strlen("point"), QV4::CompiledData::BuiltinType::Point }, - { "size", strlen("size"), QV4::CompiledData::BuiltinType::Size }, - { "font", strlen("font"), QV4::CompiledData::BuiltinType::Font }, - { "vector2d", strlen("vector2d"), QV4::CompiledData::BuiltinType::Vector2D }, - { "vector3d", strlen("vector3d"), QV4::CompiledData::BuiltinType::Vector3D }, - { "vector4d", strlen("vector4d"), QV4::CompiledData::BuiltinType::Vector4D }, - { "quaternion", strlen("quaternion"), QV4::CompiledData::BuiltinType::Quaternion }, - { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::BuiltinType::Matrix4x4 }, - { "variant", strlen("variant"), QV4::CompiledData::BuiltinType::Variant }, - { "var", strlen("var"), QV4::CompiledData::BuiltinType::Var } - }; - static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / - sizeof(propTypeNameToTypes[0]); - if (node->type == QQmlJS::AST::UiPublicMember::Signal) { Signal *signal = New<Signal>(); QString signalName = node->name.toString(); @@ -807,7 +799,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) signal->location.line = loc.startLine; signal->location.column = loc.startColumn; - signal->parameters = New<PoolList<SignalParameter> >(); + signal->parameters = New<PoolList<Parameter> >(); QQmlJS::AST::UiParameterList *p = node->parameters; while (p) { @@ -818,38 +810,13 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) return false; } - const TypeNameToType *type = nullptr; - for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { - const TypeNameToType *t = propTypeNameToTypes + typeIndex; - if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { - type = t; - break; - } - } - - SignalParameter *param = New<SignalParameter>(); - - if (!type) { - if (memberType.at(0).isUpper()) { - // Must be a QML object type. - // Lazily determine type during compilation. - param->indexIsBuiltinType = false; - param->typeNameIndexOrBuiltinType = registerString(memberType); - Q_ASSERT(quint32(jsGenerator->getStringId(memberType)) < (1u << 31)); - } else { - QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); - errStr.append(memberType); - recordError(node->typeToken, errStr); - return false; - } - } else { - // the parameter is a known basic type - param->indexIsBuiltinType = true; - param->typeNameIndexOrBuiltinType = static_cast<quint32>(type->type); - Q_ASSERT(quint32(type->type) < (1u << 31)); + Parameter *param = New<Parameter>(); + if (!param->init(jsGenerator, p->name.toString(), memberType)) { + QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); + errStr.append(memberType); + recordError(node->typeToken, errStr); + return false; } - - param->nameIndex = registerString(p->name.toString()); signal->parameters->append(param); p = p->next; } @@ -875,15 +842,10 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) Property *property = New<Property>(); property->isReadOnly = node->isReadonlyMember; - bool typeFound = false; - - for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { - const TypeNameToType *t = propTypeNameToTypes + ii; - if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { - property->setBuiltinType(t->type); - typeFound = true; - } - } + QV4::CompiledData::BuiltinType builtinPropertyType = Parameter::stringToBuiltinType(memberType); + bool typeFound = builtinPropertyType != QV4::CompiledData::BuiltinType::InvalidBuiltin; + if (typeFound) + property->setBuiltinType(builtinPropertyType); if (!typeFound && memberType.at(0).isUpper()) { const QStringRef &typeModifier = node->typeModifier; @@ -960,13 +922,16 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) f->index = index; f->nameIndex = registerString(funDecl->name.toString()); - const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList(); + QString returnTypeName = funDecl->typeAnnotation ? funDecl->typeAnnotation->type->toString() : QString(); + Parameter::initType(&f->returnType, jsGenerator, registerString(returnTypeName)); + + const QQmlJS::AST::BoundNames formals = funDecl->formals ? funDecl->formals->formals() : QQmlJS::AST::BoundNames(); int formalsCount = formals.size(); f->formals.allocate(pool, formalsCount); int i = 0; - for (const QString &arg : formals) { - f->formals[i] = registerString(arg); + for (const auto &arg : formals) { + f->formals[i].init(jsGenerator, arg.id, arg.typeName()); ++i; } @@ -1696,7 +1661,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen signalToWrite->nParameters = s->parameters->count; QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite)); - for (SignalParameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite) + for (Parameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite) *parameterToWrite = *param; int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count); @@ -1782,19 +1747,11 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding return bindingPtr; } -JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, - QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, - QQmlJS::AST::UiProgram *qmlRoot, - const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames) - : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false) - , sourceCode(sourceCode) - , jsEngine(jsEngine) - , qmlRoot(qmlRoot) - , stringPool(stringPool) +JSCodeGen::JSCodeGen(Document *document, const QSet<QString> &globalNames) + : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/false), document(document) { m_globalNames = globalNames; - - _module = jsModule; + _module = &document->jsModule; _fileNameIsUrl = true; } @@ -1802,17 +1759,17 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil { auto qmlName = [&](const CompiledFunctionOrExpression &c) { if (c.nameIndex != 0) - return stringPool->stringForIndex(c.nameIndex); + return document->stringAt(c.nameIndex); else return QStringLiteral("%qml-expression-entry"); }; QVector<int> runtimeFunctionIndices(functions.size()); - QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global); + QV4::Compiler::ScanFunctions scan(this, document->code, QV4::Compiler::ContextType::Global); scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding); for (const CompiledFunctionOrExpression &f : functions) { - Q_ASSERT(f.node != qmlRoot); - Q_ASSERT(f.parentNode && f.parentNode != qmlRoot); + Q_ASSERT(f.node != document->program); + Q_ASSERT(f.parentNode && f.parentNode != document->program); QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node); if (function) { @@ -1835,7 +1792,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil for (int i = 0; i < functions.count(); ++i) { const CompiledFunctionOrExpression &qmlFunction = functions.at(i); QQmlJS::AST::Node *node = qmlFunction.node; - Q_ASSERT(node != qmlRoot); + Q_ASSERT(node != document->program); QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(node); @@ -1850,7 +1807,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil body = function->body; } else { // Synthesize source elements. - QQmlJS::MemoryPool *pool = jsEngine->pool(); + QQmlJS::MemoryPool *pool = document->jsParserEngine.pool(); QQmlJS::AST::Statement *stmt = node->statementCast(); if (!stmt) { @@ -1870,3 +1827,58 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil return runtimeFunctionIndices; } + +bool JSCodeGen::generateCodeForComponents(const QVector<quint32> &componentRoots) +{ + for (int i = 0; i < componentRoots.count(); ++i) { + if (!compileComponent(componentRoots.at(i))) + return false; + } + + return compileComponent(/*root object*/0); +} + +bool JSCodeGen::compileComponent(int contextObject) +{ + const QmlIR::Object *obj = document->objects.at(contextObject); + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->bindingCount() == 1); + const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); + Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + contextObject = componentBinding->value.objectIndex; + } + + return compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject); +} + +bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) +{ + QmlIR::Object *object = document->objects.at(objectIndex); + if (object->flags & QV4::CompiledData::Object::IsComponent) + return true; + + if (object->functionsAndExpressions->count > 0) { + QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; + for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) + functionsToCompile << *foe; + const QVector<int> runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile); + if (hasError()) + return false; + + object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(), + runtimeFunctionIndices); + } + + for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { + if (binding->type < QV4::CompiledData::Binding::Type_Object) + continue; + + int target = binding->value.objectIndex; + int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex; + + if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope)) + return false; + } + + return true; +} diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 64298466e0..c366c8e459 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -55,6 +55,7 @@ #include <private/qv4compiler_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmljsmemorypool_p.h> +#include <private/qqmljsfixedpoolarray_p.h> #include <private/qv4codegen_p.h> #include <private/qv4compiler_p.h> #include <QTextStream> @@ -215,22 +216,30 @@ struct Enum }; -struct SignalParameter : public QV4::CompiledData::Parameter +struct Parameter : public QV4::CompiledData::Parameter { - SignalParameter *next; + Parameter *next; + + bool init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString ¶meterName, const QString &typeName); + static bool init(QV4::CompiledData::Parameter *param, const QV4::Compiler::JSUnitGenerator *stringGenerator, + int parameterNameIndex, int typeNameIndex); + static bool initType(QV4::CompiledData::ParameterType *paramType, + const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex); + + static QV4::CompiledData::BuiltinType stringToBuiltinType(const QString &typeName); }; struct Signal { int nameIndex; QV4::CompiledData::Location location; - PoolList<SignalParameter> *parameters; + PoolList<Parameter> *parameters; QStringList parameterStringList(const QV4::Compiler::StringTableGenerator *stringPool) const; int parameterCount() const { return parameters->count; } - PoolList<SignalParameter>::Iterator parametersBegin() const { return parameters->begin(); } - PoolList<SignalParameter>::Iterator parametersEnd() const { return parameters->end(); } + PoolList<Parameter>::Iterator parametersBegin() const { return parameters->begin(); } + PoolList<Parameter>::Iterator parametersEnd() const { return parameters->end(); } Signal *next; }; @@ -260,17 +269,18 @@ struct Function QV4::CompiledData::Location location; int nameIndex; quint32 index; // index in parsedQML::functions - FixedPoolArray<int> formals; + QQmlJS::FixedPoolArray<Parameter> formals; + QV4::CompiledData::ParameterType returnType; // --- QQmlPropertyCacheCreator interface - const int *formalsBegin() const { return formals.begin(); } - const int *formalsEnd() const { return formals.end(); } + const Parameter *formalsBegin() const { return formals.begin(); } + const Parameter *formalsEnd() const { return formals.end(); } // --- Function *next; }; -struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression +struct Q_QMLCOMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression { CompiledFunctionOrExpression() {} @@ -281,7 +291,7 @@ struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression CompiledFunctionOrExpression *next = nullptr; }; -struct Q_QML_PRIVATE_EXPORT Object +struct Q_QMLCOMPILER_PRIVATE_EXPORT Object { Q_DECLARE_TR_FUNCTIONS(Object) public: @@ -340,9 +350,9 @@ public: QString bindingAsString(Document *doc, int scriptIndex) const; PoolList<CompiledFunctionOrExpression> *functionsAndExpressions; - FixedPoolArray<int> runtimeFunctionIndices; + QQmlJS::FixedPoolArray<int> runtimeFunctionIndices; - FixedPoolArray<quint32> namedObjectsInComponent; + QQmlJS::FixedPoolArray<quint32> namedObjectsInComponent; int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); } const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); } @@ -357,7 +367,7 @@ private: PoolList<Function> *functions; }; -struct Q_QML_PRIVATE_EXPORT Pragma +struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma { enum PragmaType { PragmaSingleton = 0x1 @@ -367,7 +377,7 @@ struct Q_QML_PRIVATE_EXPORT Pragma QV4::CompiledData::Location location; }; -struct Q_QML_PRIVATE_EXPORT Document +struct Q_QMLCOMPILER_PRIVATE_EXPORT Document { Document(bool debugMode); QString code; @@ -383,11 +393,9 @@ struct Q_QML_PRIVATE_EXPORT Document int registerString(const QString &str) { return jsGenerator.registerString(str); } QString stringAt(int index) const { return jsGenerator.stringForIndex(index); } - - static void removeScriptPragmas(QString &script); }; -class Q_QML_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives +class Q_QMLCOMPILER_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives { QmlIR::Document *document; QQmlJS::Engine *engine; @@ -401,7 +409,7 @@ public: void importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column) override; }; -struct Q_QML_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor +struct Q_QMLCOMPILER_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor { Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator) public: @@ -432,7 +440,7 @@ public: void throwRecursionDepthError() override { - recordError(AST::SourceLocation(), + recordError(QQmlJS::AST::SourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); } @@ -451,13 +459,19 @@ public: QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first, const QQmlJS::AST::SourceLocation &last) const; - void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, AST::Node *parentNode); + void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, + QQmlJS::AST::Node *parentNode); void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding); - void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, AST::Node *parentNode); + void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, + QQmlJS::AST::Node *parentNode); void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false); - void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, AST::Node *parentNode); - void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); + void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, + const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, + QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode); + void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, + const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, + int objectIndex, bool isListItem = false, bool isOnAssignment = false); bool appendAlias(QQmlJS::AST::UiPublicMember *node); @@ -499,7 +513,7 @@ public: QV4::Compiler::JSUnitGenerator *jsGenerator; }; -struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator +struct Q_QMLCOMPILER_PRIVATE_EXPORT QmlUnitGenerator { void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher()); @@ -508,34 +522,23 @@ private: char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const; }; -struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen +struct Q_QMLCOMPILER_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen { - JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule, - QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, - const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames); + JSCodeGen(Document *document, const QSet<QString> &globalNames); // Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions); + bool generateCodeForComponents(const QVector<quint32> &componentRoots); + bool compileComponent(int contextObject); + bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); + private: - QString sourceCode; - QQmlJS::Engine *jsEngine; // needed for memory pool - QQmlJS::AST::UiProgram *qmlRoot; - const QV4::Compiler::StringTableGenerator *stringPool; + Document *document; }; } // namespace QmlIR -inline QQmlJS::DiagnosticMessage qQmlCompileError(const QV4::CompiledData::Location &location, - const QString &description) -{ - QQmlJS::DiagnosticMessage error; - error.line = location.line; - error.column = location.column; - error.message = description; - return error; -} - QT_END_NAMESPACE #endif // QQMLIRBUILDER_P_H diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp index ea252a6013..7df1614ffe 100644 --- a/src/qml/compiler/qv4bytecodegenerator.cpp +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -206,7 +206,6 @@ int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, in lastInstrType = int(type); lastInstr = i; -#if QT_CONFIG(qml_debug) if (debugMode && type != Instr::Type::Debug) { QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug() @@ -219,9 +218,6 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instru } QT_WARNING_POP } -#else - Q_UNUSED(debugMode); -#endif const int pos = instructions.size(); diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index acd4aa62ea..8c509dd9f1 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -62,6 +62,11 @@ class SourceLocation; } namespace QV4 { + +namespace Compiler { +struct Context; +} + namespace Moth { class BytecodeGenerator { diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 453963c1dd..c43ea64e2e 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -48,7 +48,6 @@ #include <private/qqmljsast_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> -#include <private/qv4stringtoarrayindex_p.h> #include <private/qv4staticvalue_p.h> #include <private/qv4compilercontext_p.h> #include <private/qv4compilercontrolflow_p.h> @@ -69,6 +68,7 @@ static const bool disable_lookups = false; QT_USE_NAMESPACE using namespace QV4; using namespace QV4::Compiler; +using namespace QQmlJS; using namespace QQmlJS::AST; static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator, @@ -610,6 +610,8 @@ void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, con if (hasError()) return; + accept(e->typeAnnotation); + if (e->initializer) { if (!baseRef.isValid()) { // assignment @@ -885,6 +887,12 @@ bool Codegen::visit(ExportDeclaration *ast) return false; } +bool Codegen::visit(TypeAnnotation *ast) +{ + throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Type annotations are not supported (yet).")); + return false; +} + bool Codegen::visit(StatementList *) { Q_UNREACHABLE(); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 51b821aafe..82a4fc3289 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -50,7 +50,6 @@ // We mean it. // -#include "private/qv4global_p.h" #include <private/qqmljsastvisitor_p.h> #include <private/qqmljsengine_p.h> #include <private/qqmljsast_p.h> @@ -63,8 +62,6 @@ QT_BEGIN_NAMESPACE -using namespace QQmlJS; - namespace QV4 { namespace Moth { @@ -81,7 +78,7 @@ struct ControlFlow; struct ControlFlowCatch; struct ControlFlowFinally; -class Q_QML_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor +class Q_QMLCOMPILER_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor { protected: using BytecodeGenerator = QV4::Moth::BytecodeGenerator; @@ -93,14 +90,14 @@ public: void generateFromProgram(const QString &fileName, const QString &finalUrl, const QString &sourceCode, - AST::Program *ast, + QQmlJS::AST::Program *ast, Module *module, ContextType contextType = ContextType::Global); void generateFromModule(const QString &fileName, const QString &finalUrl, const QString &sourceCode, - AST::ESModule *ast, + QQmlJS::AST::ESModule *ast, Module *module); public: @@ -487,10 +484,10 @@ protected: } }; - void enterContext(AST::Node *node); + void enterContext(QQmlJS::AST::Node *node); int leaveContext(); public: - Context *enterBlock(AST::Node *node); + Context *enterBlock(QQmlJS::AST::Node *node); int leaveBlock() { return leaveContext(); } protected: void leaveLoop(); @@ -521,18 +518,18 @@ public: int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); } // Returns index in _module->functions - virtual int defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::StatementList *body); + virtual int defineFunction(const QString &name, QQmlJS::AST::Node *ast, + QQmlJS::AST::FormalParameterList *formals, + QQmlJS::AST::StatementList *body); protected: - void statement(AST::Statement *ast); - void statement(AST::ExpressionNode *ast); - void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, + void statement(QQmlJS::AST::Statement *ast); + void statement(QQmlJS::AST::ExpressionNode *ast); + void condition(QQmlJS::AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue, const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition); - inline Reference expression(AST::ExpressionNode *ast, const QString &name = QString()) + inline Reference expression(QQmlJS::AST::ExpressionNode *ast, const QString &name = QString()) { if (!ast || hasError()) return Reference(); @@ -542,131 +539,134 @@ protected: return popResult(); } - inline void accept(AST::Node *node) + inline void accept(QQmlJS::AST::Node *node) { if (!hasError() && node) node->accept(this); } - void program(AST::Program *ast); - void statementList(AST::StatementList *ast); - void variableDeclaration(AST::PatternElement *ast); - void variableDeclarationList(AST::VariableDeclarationList *ast); + void program(QQmlJS::AST::Program *ast); + void statementList(QQmlJS::AST::StatementList *ast); + void variableDeclaration(QQmlJS::AST::PatternElement *ast); + void variableDeclarationList(QQmlJS::AST::VariableDeclarationList *ast); - Reference targetForPatternElement(AST::PatternElement *p); - void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false); - void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false); - void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false); - void destructurePattern(AST::Pattern *p, const Reference &rhs); + Reference targetForPatternElement(QQmlJS::AST::PatternElement *p); + void initializeAndDestructureBindingElement(QQmlJS::AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false); + void destructurePropertyList(const Reference &object, QQmlJS::AST::PatternPropertyList *bindingList, bool isDefinition = false); + void destructureElementList(const Reference &array, QQmlJS::AST::PatternElementList *bindingList, bool isDefinition = false); + void destructurePattern(QQmlJS::AST::Pattern *p, const Reference &rhs); - Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name); + Reference referenceForPropertyName(const Codegen::Reference &object, QQmlJS::AST::PropertyName *name); void emitReturn(const Reference &expr); // nodes - bool visit(AST::ArgumentList *ast) override; - bool visit(AST::CaseBlock *ast) override; - bool visit(AST::CaseClause *ast) override; - bool visit(AST::CaseClauses *ast) override; - bool visit(AST::Catch *ast) override; - bool visit(AST::DefaultClause *ast) override; - bool visit(AST::Elision *ast) override; - bool visit(AST::Finally *ast) override; - bool visit(AST::FormalParameterList *ast) override; - bool visit(AST::Program *ast) override; - bool visit(AST::StatementList *ast) override; - bool visit(AST::UiArrayMemberList *ast) override; - bool visit(AST::UiImport *ast) override; - bool visit(AST::UiHeaderItemList *ast) override; - bool visit(AST::UiPragma *ast) override; - bool visit(AST::UiObjectInitializer *ast) override; - bool visit(AST::UiObjectMemberList *ast) override; - bool visit(AST::UiParameterList *ast) override; - bool visit(AST::UiProgram *ast) override; - bool visit(AST::UiQualifiedId *ast) override; - bool visit(AST::VariableDeclarationList *ast) override; - - bool visit(AST::PatternElement *ast) override; - bool visit(AST::PatternElementList *ast) override; - bool visit(AST::PatternProperty *ast) override; - bool visit(AST::PatternPropertyList *ast) override; - - bool visit(AST::ExportDeclaration *ast) override; + bool visit(QQmlJS::AST::ArgumentList *ast) override; + bool visit(QQmlJS::AST::CaseBlock *ast) override; + bool visit(QQmlJS::AST::CaseClause *ast) override; + bool visit(QQmlJS::AST::CaseClauses *ast) override; + bool visit(QQmlJS::AST::Catch *ast) override; + bool visit(QQmlJS::AST::DefaultClause *ast) override; + bool visit(QQmlJS::AST::Elision *ast) override; + bool visit(QQmlJS::AST::Finally *ast) override; + bool visit(QQmlJS::AST::FormalParameterList *ast) override; + bool visit(QQmlJS::AST::Program *ast) override; + bool visit(QQmlJS::AST::StatementList *ast) override; + bool visit(QQmlJS::AST::UiArrayMemberList *ast) override; + bool visit(QQmlJS::AST::UiImport *ast) override; + bool visit(QQmlJS::AST::UiHeaderItemList *ast) override; + bool visit(QQmlJS::AST::UiPragma *ast) override; + bool visit(QQmlJS::AST::UiObjectInitializer *ast) override; + bool visit(QQmlJS::AST::UiObjectMemberList *ast) override; + bool visit(QQmlJS::AST::UiParameterList *ast) override; + bool visit(QQmlJS::AST::UiProgram *ast) override; + bool visit(QQmlJS::AST::UiQualifiedId *ast) override; + bool visit(QQmlJS::AST::VariableDeclarationList *ast) override; + + bool visit(QQmlJS::AST::PatternElement *ast) override; + bool visit(QQmlJS::AST::PatternElementList *ast) override; + bool visit(QQmlJS::AST::PatternProperty *ast) override; + bool visit(QQmlJS::AST::PatternPropertyList *ast) override; + + bool visit(QQmlJS::AST::ExportDeclaration *ast) override; + + bool visit(QQmlJS::AST::TypeAnnotation *ast) override; // expressions - bool visit(AST::Expression *ast) override; - bool visit(AST::ArrayPattern *ast) override; - bool visit(AST::ArrayMemberExpression *ast) override; - bool visit(AST::BinaryExpression *ast) override; - bool visit(AST::CallExpression *ast) override; - bool visit(AST::ConditionalExpression *ast) override; - bool visit(AST::DeleteExpression *ast) override; - bool visit(AST::FalseLiteral *ast) override; - bool visit(AST::SuperLiteral *ast) override; - bool visit(AST::FieldMemberExpression *ast) override; - bool visit(AST::TaggedTemplate *ast) override; - bool visit(AST::FunctionExpression *ast) override; - bool visit(AST::IdentifierExpression *ast) override; - bool visit(AST::NestedExpression *ast) override; - bool visit(AST::NewExpression *ast) override; - bool visit(AST::NewMemberExpression *ast) override; - bool visit(AST::NotExpression *ast) override; - bool visit(AST::NullExpression *ast) override; - bool visit(AST::NumericLiteral *ast) override; - bool visit(AST::ObjectPattern *ast) override; - bool visit(AST::PostDecrementExpression *ast) override; - bool visit(AST::PostIncrementExpression *ast) override; - bool visit(AST::PreDecrementExpression *ast) override; - bool visit(AST::PreIncrementExpression *ast) override; - bool visit(AST::RegExpLiteral *ast) override; - bool visit(AST::StringLiteral *ast) override; - bool visit(AST::TemplateLiteral *ast) override; - bool visit(AST::ThisExpression *ast) override; - bool visit(AST::TildeExpression *ast) override; - bool visit(AST::TrueLiteral *ast) override; - bool visit(AST::TypeOfExpression *ast) override; - bool visit(AST::UnaryMinusExpression *ast) override; - bool visit(AST::UnaryPlusExpression *ast) override; - bool visit(AST::VoidExpression *ast) override; - bool visit(AST::FunctionDeclaration *ast) override; - bool visit(AST::YieldExpression *ast) override; - bool visit(AST::ClassExpression *ast) override; - bool visit(AST::ClassDeclaration *ast) override; + bool visit(QQmlJS::AST::Expression *ast) override; + bool visit(QQmlJS::AST::ArrayPattern *ast) override; + bool visit(QQmlJS::AST::ArrayMemberExpression *ast) override; + bool visit(QQmlJS::AST::BinaryExpression *ast) override; + bool visit(QQmlJS::AST::CallExpression *ast) override; + bool visit(QQmlJS::AST::ConditionalExpression *ast) override; + bool visit(QQmlJS::AST::DeleteExpression *ast) override; + bool visit(QQmlJS::AST::FalseLiteral *ast) override; + bool visit(QQmlJS::AST::SuperLiteral *ast) override; + bool visit(QQmlJS::AST::FieldMemberExpression *ast) override; + bool visit(QQmlJS::AST::TaggedTemplate *ast) override; + bool visit(QQmlJS::AST::FunctionExpression *ast) override; + bool visit(QQmlJS::AST::IdentifierExpression *ast) override; + bool visit(QQmlJS::AST::NestedExpression *ast) override; + bool visit(QQmlJS::AST::NewExpression *ast) override; + bool visit(QQmlJS::AST::NewMemberExpression *ast) override; + bool visit(QQmlJS::AST::NotExpression *ast) override; + bool visit(QQmlJS::AST::NullExpression *ast) override; + bool visit(QQmlJS::AST::NumericLiteral *ast) override; + bool visit(QQmlJS::AST::ObjectPattern *ast) override; + bool visit(QQmlJS::AST::PostDecrementExpression *ast) override; + bool visit(QQmlJS::AST::PostIncrementExpression *ast) override; + bool visit(QQmlJS::AST::PreDecrementExpression *ast) override; + bool visit(QQmlJS::AST::PreIncrementExpression *ast) override; + bool visit(QQmlJS::AST::RegExpLiteral *ast) override; + bool visit(QQmlJS::AST::StringLiteral *ast) override; + bool visit(QQmlJS::AST::TemplateLiteral *ast) override; + bool visit(QQmlJS::AST::ThisExpression *ast) override; + bool visit(QQmlJS::AST::TildeExpression *ast) override; + bool visit(QQmlJS::AST::TrueLiteral *ast) override; + bool visit(QQmlJS::AST::TypeOfExpression *ast) override; + bool visit(QQmlJS::AST::UnaryMinusExpression *ast) override; + bool visit(QQmlJS::AST::UnaryPlusExpression *ast) override; + bool visit(QQmlJS::AST::VoidExpression *ast) override; + bool visit(QQmlJS::AST::FunctionDeclaration *ast) override; + bool visit(QQmlJS::AST::YieldExpression *ast) override; + bool visit(QQmlJS::AST::ClassExpression *ast) override; + bool visit(QQmlJS::AST::ClassDeclaration *ast) override; // statements - bool visit(AST::Block *ast) override; - bool visit(AST::BreakStatement *ast) override; - bool visit(AST::ContinueStatement *ast) override; - bool visit(AST::DebuggerStatement *ast) override; - bool visit(AST::DoWhileStatement *ast) override; - bool visit(AST::EmptyStatement *ast) override; - bool visit(AST::ExpressionStatement *ast) override; - bool visit(AST::ForEachStatement *ast) override; - bool visit(AST::ForStatement *ast) override; - bool visit(AST::IfStatement *ast) override; - bool visit(AST::LabelledStatement *ast) override; - bool visit(AST::ReturnStatement *ast) override; - bool visit(AST::SwitchStatement *ast) override; - bool visit(AST::ThrowStatement *ast) override; - bool visit(AST::TryStatement *ast) override; - bool visit(AST::VariableStatement *ast) override; - bool visit(AST::WhileStatement *ast) override; - bool visit(AST::WithStatement *ast) override; + bool visit(QQmlJS::AST::Block *ast) override; + bool visit(QQmlJS::AST::BreakStatement *ast) override; + bool visit(QQmlJS::AST::ContinueStatement *ast) override; + bool visit(QQmlJS::AST::DebuggerStatement *ast) override; + bool visit(QQmlJS::AST::DoWhileStatement *ast) override; + bool visit(QQmlJS::AST::EmptyStatement *ast) override; + bool visit(QQmlJS::AST::ExpressionStatement *ast) override; + bool visit(QQmlJS::AST::ForEachStatement *ast) override; + bool visit(QQmlJS::AST::ForStatement *ast) override; + bool visit(QQmlJS::AST::IfStatement *ast) override; + bool visit(QQmlJS::AST::LabelledStatement *ast) override; + bool visit(QQmlJS::AST::ReturnStatement *ast) override; + bool visit(QQmlJS::AST::SwitchStatement *ast) override; + bool visit(QQmlJS::AST::ThrowStatement *ast) override; + bool visit(QQmlJS::AST::TryStatement *ast) override; + bool visit(QQmlJS::AST::VariableStatement *ast) override; + bool visit(QQmlJS::AST::WhileStatement *ast) override; + bool visit(QQmlJS::AST::WithStatement *ast) override; // ui object members - bool visit(AST::UiArrayBinding *ast) override; - bool visit(AST::UiObjectBinding *ast) override; - bool visit(AST::UiObjectDefinition *ast) override; - bool visit(AST::UiPublicMember *ast) override; - bool visit(AST::UiScriptBinding *ast) override; - bool visit(AST::UiSourceElement *ast) override; - - bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc); - virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); - virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); + bool visit(QQmlJS::AST::UiArrayBinding *ast) override; + bool visit(QQmlJS::AST::UiObjectBinding *ast) override; + bool visit(QQmlJS::AST::UiObjectDefinition *ast) override; + bool visit(QQmlJS::AST::UiPublicMember *ast) override; + bool visit(QQmlJS::AST::UiScriptBinding *ast) override; + bool visit(QQmlJS::AST::UiSourceElement *ast) override; + + bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, + const QQmlJS::AST::SourceLocation &loc); + virtual void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail); + virtual void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail); void throwRecursionDepthError() override { - throwSyntaxError(AST::SourceLocation(), + throwSyntaxError(QQmlJS::AST::SourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); } @@ -679,31 +679,33 @@ public: ErrorType errorType() const { return _errorType; } bool hasError() const { return _errorType != NoError; } - DiagnosticMessage error() const; + QQmlJS::DiagnosticMessage error() const; QUrl url() const; Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right); Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right); struct Arguments { int argc; int argv; bool hasSpread; }; - Arguments pushArgs(AST::ArgumentList *args); + Arguments pushArgs(QQmlJS::AST::ArgumentList *args); void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject); - Arguments pushTemplateArgs(AST::TemplateLiteral *args); - bool handleTaggedTemplate(Reference base, AST::TaggedTemplate *ast); - void createTemplateObject(AST::TemplateLiteral *t); + Arguments pushTemplateArgs(QQmlJS::AST::TemplateLiteral *args); + bool handleTaggedTemplate(Reference base, QQmlJS::AST::TaggedTemplate *ast); + void createTemplateObject(QQmlJS::AST::TemplateLiteral *t); void setUseFastLookups(bool b) { useFastLookups = b; } - void handleTryCatch(AST::TryStatement *ast); - void handleTryFinally(AST::TryStatement *ast); + void handleTryCatch(QQmlJS::AST::TryStatement *ast); + void handleTryFinally(QQmlJS::AST::TryStatement *ast); - Reference referenceForName(const QString &name, bool lhs, const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation()); + Reference referenceForName( + const QString &name, bool lhs, + const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation()); QV4::CompiledData::CompilationUnit generateCompilationUnit(bool generateUnitData = true); static QV4::CompiledData::CompilationUnit compileModule( bool debugMode, const QString &url, const QString &sourceCode, - const QDateTime &sourceTimeStamp, QList<DiagnosticMessage> *diagnostics); + const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics); Context *currentContext() const { return _context; } BytecodeGenerator *generator() const { return bytecodeGenerator; } @@ -762,7 +764,7 @@ protected: int _returnAddress; Context *_context; Context *_functionContext = nullptr; - AST::LabelledStatement *_labelledStatement; + QQmlJS::AST::LabelledStatement *_labelledStatement; QV4::Compiler::JSUnitGenerator *jsUnitGenerator; BytecodeGenerator *bytecodeGenerator = nullptr; Moth::BytecodeGenerator::Label *_returnLabel = nullptr; @@ -806,9 +808,10 @@ protected: }; private: - VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast); - void handleConstruct(const Reference &base, AST::ArgumentList *args); - void throwError(ErrorType errorType, const AST::SourceLocation &loc, const QString &detail); + VolatileMemoryLocations scanVolatileMemoryLocations(QQmlJS::AST::Node *ast); + void handleConstruct(const Reference &base, QQmlJS::AST::ArgumentList *args); + void throwError(ErrorType errorType, const QQmlJS::AST::SourceLocation &loc, + const QString &detail); }; } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index b378c294b7..acc4b02e96 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -38,13 +38,14 @@ ****************************************************************************/ #include <qv4compiler_p.h> -#include <qv4compileddata_p.h> #include <qv4codegen_p.h> +#include <private/qv4compileddata_p.h> #include <private/qv4staticvalue_p.h> #include <private/qv4alloca_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsast_p.h> #include <private/qml_compile_hash_p.h> +#include <private/qqmlirbuilder_p.h> #include <QCryptographicHash> // Efficient implementation that takes advantage of powers of two. @@ -268,8 +269,11 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(module->finalUrl); for (Context *f : qAsConst(module->functions)) { registerString(f->name); - for (int i = 0; i < f->arguments.size(); ++i) - registerString(f->arguments.at(i)); + registerString(f->returnType); + for (int i = 0; i < f->arguments.size(); ++i) { + registerString(f->arguments.at(i).id); + registerString(f->arguments.at(i).typeName()); + } for (int i = 0; i < f->locals.size(); ++i) registerString(f->locals.at(i)); } @@ -436,7 +440,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->length = irFunction->formals ? irFunction->formals->length() : 0; function->nFormals = irFunction->arguments.size(); function->formalsOffset = currentOffset; - currentOffset += function->nFormals * sizeof(quint32); + currentOffset += function->nFormals * sizeof(CompiledData::Parameter); + + QmlIR::Parameter::initType(&function->returnType, this, getStringId(irFunction->returnType)); function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone; function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone; @@ -465,9 +471,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->codeSize = irFunction->code.size(); // write formals - quint32_le *formals = (quint32_le *)(f + function->formalsOffset); - for (int i = 0; i < irFunction->arguments.size(); ++i) - formals[i] = getStringId(irFunction->arguments.at(i)); + CompiledData::Parameter *formals = (CompiledData::Parameter *)(f + function->formalsOffset); + for (int i = 0; i < irFunction->arguments.size(); ++i) { + QmlIR::Parameter::init(&formals[i], this, getStringId(irFunction->arguments.at(i).id), + getStringId(irFunction->arguments.at(i).typeName())); + } // write locals quint32_le *locals = (quint32_le *)(f + function->localsOffset); diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index f5884f6478..4f3c718175 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -53,9 +53,10 @@ #include <QtCore/qstring.h> #include <QtCore/qhash.h> #include <QtCore/qstringlist.h> -#include <private/qv4global_p.h> +#include <private/qv4compilerglobal_p.h> #include <private/qqmljsastfwd_p.h> #include <private/qv4compileddata_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE @@ -72,10 +73,12 @@ struct JSClassMember; namespace Compiler { +struct Context; +struct Module; struct Class; struct TemplateObject; -struct Q_QML_PRIVATE_EXPORT StringTableGenerator { +struct Q_QMLCOMPILER_PRIVATE_EXPORT StringTableGenerator { StringTableGenerator(); int registerString(const QString &str); @@ -102,7 +105,7 @@ private: bool frozen = false; }; -struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { +struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator { static void generateUnitChecksum(CompiledData::Unit *unit); struct MemberInfo { diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index f56942fffa..8c124ac409 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -50,7 +50,6 @@ // We mean it. // -#include "private/qv4global_p.h" #include <private/qqmljsast_p.h> #include <private/qv4compileddata_p.h> #include <QtCore/QStringList> @@ -62,8 +61,13 @@ QT_BEGIN_NAMESPACE namespace QV4 { +namespace Moth { +class BytecodeGenerator; +} + namespace Compiler { +class Codegen; struct ControlFlow; enum class ContextType { @@ -189,7 +193,8 @@ struct Context { MemberMap members; QSet<QString> usedVariables; QQmlJS::AST::FormalParameterList *formals = nullptr; - QStringList arguments; + QQmlJS::AST::BoundNames arguments; + QString returnType; QStringList locals; QStringList moduleRequests; QVector<ImportEntry> importEntries; @@ -288,7 +293,7 @@ struct Context { { // search backwards to handle duplicate argument names correctly for (int i = arguments.size() - 1; i >= 0; --i) { - if (arguments.at(i) == name) + if (arguments.at(i).id == name) return i; } return -1; diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h index 5b622e81d8..5623473726 100644 --- a/src/qml/compiler/qv4compilercontrolflow_p.h +++ b/src/qml/compiler/qv4compilercontrolflow_p.h @@ -50,7 +50,6 @@ // We mean it. // -#include <private/qv4global_p.h> #include <private/qv4codegen_p.h> #include <private/qqmljsast_p.h> #include <private/qv4bytecodegenerator_p.h> @@ -279,7 +278,7 @@ struct ControlFlowWith : public ControlFlowUnwind struct ControlFlowBlock : public ControlFlowUnwind { - ControlFlowBlock(Codegen *cg, AST::Node *ast) + ControlFlowBlock(Codegen *cg, QQmlJS::AST::Node *ast) : ControlFlowUnwind(cg, Block) { block = cg->enterBlock(ast); @@ -314,11 +313,11 @@ struct ControlFlowBlock : public ControlFlowUnwind struct ControlFlowCatch : public ControlFlowUnwind { - AST::Catch *catchExpression; + QQmlJS::AST::Catch *catchExpression; bool insideCatch = false; BytecodeGenerator::ExceptionHandler exceptionLabel; - ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression) + ControlFlowCatch(Codegen *cg, QQmlJS::AST::Catch *catchExpression) : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression), exceptionLabel(generator()->newExceptionHandler()) { @@ -372,10 +371,10 @@ struct ControlFlowCatch : public ControlFlowUnwind struct ControlFlowFinally : public ControlFlowUnwind { - AST::Finally *finally; + QQmlJS::AST::Finally *finally; bool insideFinally = false; - ControlFlowFinally(Codegen *cg, AST::Finally *finally) + ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally) : ControlFlowUnwind(cg, Finally), finally(finally) { Q_ASSERT(finally != nullptr); diff --git a/src/qml/compiler/qv4compilerglobal_p.h b/src/qml/compiler/qv4compilerglobal_p.h new file mode 100644 index 0000000000..3478074827 --- /dev/null +++ b/src/qml/compiler/qv4compilerglobal_p.h @@ -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$ +** +****************************************************************************/ + +#ifndef QV4COMPILERGLOBAL_H +#define QV4COMPILERGLOBAL_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 <QtCore/qglobal.h> +#include <QString> + +#include <private/qtqmlcompilerglobal_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +enum class ObjectLiteralArgument { + Value, + Method, + Getter, + Setter +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4COMPILERGLOBAL_H diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 416a0edee0..ab0ebf3d4b 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -52,6 +52,7 @@ QT_USE_NAMESPACE using namespace QV4; using namespace QV4::Compiler; +using namespace QQmlJS; using namespace QQmlJS::AST; static CompiledData::Location location(const QQmlJS::AST::SourceLocation &astLocation) @@ -202,16 +203,16 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) _context->exportEntries << entry; } } else if (auto *vstmt = AST::cast<AST::VariableStatement*>(declaration->variableStatementOrDeclaration)) { - QStringList boundNames; + BoundNames boundNames; for (VariableDeclarationList *it = vstmt->declarations; it; it = it->next) { if (!it->declaration) continue; it->declaration->boundNames(&boundNames); } - for (const QString &name: boundNames) { + for (const auto &name: boundNames) { Compiler::ExportEntry entry; - entry.localName = name; - entry.exportName = name; + entry.localName = name.id; + entry.exportName = name.id; entry.location = location(vstmt->firstSourceLocation()); _context->exportEntries << entry; } @@ -326,26 +327,26 @@ bool ScanFunctions::visit(PatternElement *ast) if (!ast->isVariableDeclaration()) return true; - QStringList names; + BoundNames names; ast->boundNames(&names); QQmlJS::AST::SourceLocation lastInitializerLocation = ast->lastSourceLocation(); if (_context->lastBlockInitializerLocation.isValid()) lastInitializerLocation = _context->lastBlockInitializerLocation; - for (const QString &name : qAsConst(names)) { - if (_context->isStrict && (name == QLatin1String("eval") || name == QLatin1String("arguments"))) + for (const auto &name : qAsConst(names)) { + if (_context->isStrict && (name.id == QLatin1String("eval") || name.id == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - checkName(QStringRef(&name), ast->identifierToken); - if (name == QLatin1String("arguments")) + checkName(QStringRef(&name.id), ast->identifierToken); + if (name.id == QLatin1String("arguments")) _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; if (ast->scope == VariableScope::Const && !ast->initializer && !ast->isForDeclaration && !ast->destructuringPattern()) { _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); return false; } - if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope, + if (!_context->addLocalVar(name.id, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope, /*function*/nullptr, lastInitializerLocation)) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name.id)); return false; } } @@ -679,6 +680,9 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete _context->isArrowFunction = true; else if (expr->isGenerator) _context->isGenerator = true; + + if (expr->typeAnnotation) + _context->returnType = expr->typeAnnotation->type->toString(); } @@ -691,11 +695,11 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete bool isSimpleParameterList = formals && formals->isSimpleParameterList(); - _context->arguments = formals ? formals->formals() : QStringList(); + _context->arguments = formals ? formals->formals() : BoundNames(); - const QStringList boundNames = formals ? formals->boundNames() : QStringList(); + const BoundNames boundNames = formals ? formals->boundNames() : BoundNames(); for (int i = 0; i < boundNames.size(); ++i) { - const QString &arg = boundNames.at(i); + const QString &arg = boundNames.at(i).id; if (_context->isStrict || !isSimpleParameterList) { bool duplicate = (boundNames.indexOf(arg, i + 1) != -1); if (duplicate) { @@ -712,6 +716,7 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete if (!_context->arguments.contains(arg)) _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var); } + return true; } diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 0f7bf1818a..f67db030a2 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -50,7 +50,7 @@ // We mean it. // -#include "private/qv4global_p.h" +#include <private/qtqmlcompilerglobal_p.h> #include <private/qqmljsastvisitor_p.h> #include <private/qqmljsast_p.h> #include <private/qqmljsengine_p.h> @@ -62,8 +62,6 @@ QT_BEGIN_NAMESPACE -using namespace QQmlJS; - namespace QV4 { namespace Moth { @@ -83,84 +81,87 @@ class ScanFunctions: protected QQmlJS::AST::Visitor typedef QScopedValueRollback<bool> TemporaryBoolAssignment; public: ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType); - void operator()(AST::Node *node); + void operator()(QQmlJS::AST::Node *node); void enterGlobalEnvironment(ContextType compilationMode); - void enterEnvironment(AST::Node *node, ContextType compilationMode, const QString &name); + void enterEnvironment(QQmlJS::AST::Node *node, ContextType compilationMode, + const QString &name); void leaveEnvironment(); - void enterQmlFunction(AST::FunctionDeclaration *ast) + void enterQmlFunction(QQmlJS::AST::FunctionDeclaration *ast) { enterFunction(ast, false); } protected: using Visitor::visit; using Visitor::endVisit; - void checkDirectivePrologue(AST::StatementList *ast); + void checkDirectivePrologue(QQmlJS::AST::StatementList *ast); - void checkName(const QStringRef &name, const AST::SourceLocation &loc); + void checkName(const QStringRef &name, const QQmlJS::AST::SourceLocation &loc); - bool visit(AST::Program *ast) override; - void endVisit(AST::Program *) override; + bool visit(QQmlJS::AST::Program *ast) override; + void endVisit(QQmlJS::AST::Program *) override; - bool visit(AST::ESModule *ast) override; - void endVisit(AST::ESModule *) override; + bool visit(QQmlJS::AST::ESModule *ast) override; + void endVisit(QQmlJS::AST::ESModule *) override; - bool visit(AST::ExportDeclaration *declaration) override; - bool visit(AST::ImportDeclaration *declaration) override; + bool visit(QQmlJS::AST::ExportDeclaration *declaration) override; + bool visit(QQmlJS::AST::ImportDeclaration *declaration) override; - bool visit(AST::CallExpression *ast) override; - bool visit(AST::PatternElement *ast) override; - bool visit(AST::IdentifierExpression *ast) override; - bool visit(AST::ExpressionStatement *ast) override; - bool visit(AST::FunctionExpression *ast) override; - bool visit(AST::TemplateLiteral *ast) override; - bool visit(AST::SuperLiteral *) override; - bool visit(AST::FieldMemberExpression *) override; - bool visit(AST::ArrayPattern *) override; + bool visit(QQmlJS::AST::CallExpression *ast) override; + bool visit(QQmlJS::AST::PatternElement *ast) override; + bool visit(QQmlJS::AST::IdentifierExpression *ast) override; + bool visit(QQmlJS::AST::ExpressionStatement *ast) override; + bool visit(QQmlJS::AST::FunctionExpression *ast) override; + bool visit(QQmlJS::AST::TemplateLiteral *ast) override; + bool visit(QQmlJS::AST::SuperLiteral *) override; + bool visit(QQmlJS::AST::FieldMemberExpression *) override; + bool visit(QQmlJS::AST::ArrayPattern *) override; - bool enterFunction(AST::FunctionExpression *ast, bool enterName); + bool enterFunction(QQmlJS::AST::FunctionExpression *ast, bool enterName); - void endVisit(AST::FunctionExpression *) override; + void endVisit(QQmlJS::AST::FunctionExpression *) override; - bool visit(AST::ObjectPattern *ast) override; + bool visit(QQmlJS::AST::ObjectPattern *ast) override; - bool visit(AST::PatternProperty *ast) override; - void endVisit(AST::PatternProperty *) override; + bool visit(QQmlJS::AST::PatternProperty *ast) override; + void endVisit(QQmlJS::AST::PatternProperty *) override; - bool visit(AST::FunctionDeclaration *ast) override; - void endVisit(AST::FunctionDeclaration *) override; + bool visit(QQmlJS::AST::FunctionDeclaration *ast) override; + void endVisit(QQmlJS::AST::FunctionDeclaration *) override; - bool visit(AST::ClassExpression *ast) override; - void endVisit(AST::ClassExpression *) override; + bool visit(QQmlJS::AST::ClassExpression *ast) override; + void endVisit(QQmlJS::AST::ClassExpression *) override; - bool visit(AST::ClassDeclaration *ast) override; - void endVisit(AST::ClassDeclaration *) override; + bool visit(QQmlJS::AST::ClassDeclaration *ast) override; + void endVisit(QQmlJS::AST::ClassDeclaration *) override; - bool visit(AST::DoWhileStatement *ast) override; - bool visit(AST::ForStatement *ast) override; - void endVisit(AST::ForStatement *) override; - bool visit(AST::ForEachStatement *ast) override; - void endVisit(AST::ForEachStatement *) override; + bool visit(QQmlJS::AST::DoWhileStatement *ast) override; + bool visit(QQmlJS::AST::ForStatement *ast) override; + void endVisit(QQmlJS::AST::ForStatement *) override; + bool visit(QQmlJS::AST::ForEachStatement *ast) override; + void endVisit(QQmlJS::AST::ForEachStatement *) override; - bool visit(AST::ThisExpression *ast) override; + bool visit(QQmlJS::AST::ThisExpression *ast) override; - bool visit(AST::Block *ast) override; - void endVisit(AST::Block *ast) override; + bool visit(QQmlJS::AST::Block *ast) override; + void endVisit(QQmlJS::AST::Block *ast) override; - bool visit(AST::CaseBlock *ast) override; - void endVisit(AST::CaseBlock *ast) override; + bool visit(QQmlJS::AST::CaseBlock *ast) override; + void endVisit(QQmlJS::AST::CaseBlock *ast) override; - bool visit(AST::Catch *ast) override; - void endVisit(AST::Catch *ast) override; + bool visit(QQmlJS::AST::Catch *ast) override; + void endVisit(QQmlJS::AST::Catch *ast) override; - bool visit(AST::WithStatement *ast) override; - void endVisit(AST::WithStatement *ast) override; + bool visit(QQmlJS::AST::WithStatement *ast) override; + void endVisit(QQmlJS::AST::WithStatement *ast) override; void throwRecursionDepthError() override; protected: - bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName); + bool enterFunction(QQmlJS::AST::Node *ast, const QString &name, + QQmlJS::AST::FormalParameterList *formals, + QQmlJS::AST::StatementList *body, bool enterName); void calcEscapingVariables(); // fields: @@ -173,7 +174,7 @@ protected: ContextType defaultProgramType; private: - static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr; + static constexpr QQmlJS::AST::Node *astNodeForGlobalEnvironment = nullptr; }; } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index ec81701160..c0dd696b8a 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -50,7 +50,7 @@ // // We mean it. // -#include <private/qv4global_p.h> + #include <private/qv4staticvalue_p.h> #include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper #include <qendian.h> diff --git a/src/qml/configure.json b/src/qml/configure.json index 9313e4594b..3fc1fd528b 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -178,6 +178,13 @@ "section": "QML", "condition": "features.thread", "output": [ "privateFeature" ] + }, + "qml-itemmodel": { + "label": "QML Item Model", + "purpose": "Provides the item model for item views in QML", + "section": "QML", + "condition": "features.itemmodel", + "output": [ "privateFeature" ] } }, diff --git a/src/qml/doc/qtqml.qdocconf b/src/qml/doc/qtqml.qdocconf index ba4155c08b..cb9fb575b2 100644 --- a/src/qml/doc/qtqml.qdocconf +++ b/src/qml/doc/qtqml.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtdeclarative.qdocconf) project = QtQml description = Qt QML Reference Documentation diff --git a/src/qml/doc/snippets/qml/events.qml b/src/qml/doc/snippets/qml/events.qml index 90bf5d7b3d..f437e32890 100644 --- a/src/qml/doc/snippets/qml/events.qml +++ b/src/qml/doc/snippets/qml/events.qml @@ -59,8 +59,8 @@ Rectangle { //! [signal declaration] signal trigger - signal send (string notice) - signal perform (string task, variant object) + signal send(notice: string) + signal perform(task: string, object: variant) //! [signal declaration] //! [signal handler declaration] @@ -88,7 +88,7 @@ Rectangle { Rectangle { id: messenger - signal send( string person, string notice) + signal send(person: string, notice: string) onSend: { console.log("For " + person + ", the notice is: " + notice) @@ -102,7 +102,7 @@ Rectangle { Rectangle { id: relay - signal send( string person, string notice) + signal send(person: string, notice: string) onSend: console.log("Send signal to: " + person + ", " + notice) Component.onCompleted: { diff --git a/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml index 073a5dc361..59907c38e7 100644 --- a/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml +++ b/src/qml/doc/snippets/qml/qtbinding/functions-qml/MyItem.qml @@ -52,7 +52,7 @@ import QtQuick 2.0 Item { - function myQmlFunction(msg) { + function myQmlFunction(msg: string) : string { console.log("Got message:", msg) return "some return value" } diff --git a/src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp b/src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp index c82f71f749..a562eae2b4 100644 --- a/src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp +++ b/src/qml/doc/snippets/qml/qtbinding/functions-qml/main.cpp @@ -60,13 +60,13 @@ QQmlEngine engine; QQmlComponent component(&engine, "MyItem.qml"); QObject *object = component.create(); -QVariant returnedValue; -QVariant msg = "Hello from C++"; +QString returnedValue; +QString msg = "Hello from C++"; QMetaObject::invokeMethod(object, "myQmlFunction", - Q_RETURN_ARG(QVariant, returnedValue), - Q_ARG(QVariant, msg)); + Q_RETURN_ARG(QString, returnedValue), + Q_ARG(QString, msg)); -qDebug() << "QML function returned:" << returnedValue.toString(); +qDebug() << "QML function returned:" << returnedValue; delete object; //![0] } diff --git a/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml b/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml index eebf2db832..aadc89b72c 100644 --- a/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml +++ b/src/qml/doc/snippets/qml/qtbinding/signals-qml/MyItem.qml @@ -55,7 +55,7 @@ Item { id: item width: 100; height: 100 - signal qmlSignal(string msg) + signal qmlSignal(msg: string) MouseArea { anchors.fill: parent diff --git a/src/qml/doc/snippets/qml/statemachine/guardcondition.qml b/src/qml/doc/snippets/qml/statemachine/guardcondition.qml index 8388b96c21..f1ec89b6ba 100644 --- a/src/qml/doc/snippets/qml/statemachine/guardcondition.qml +++ b/src/qml/doc/snippets/qml/statemachine/guardcondition.qml @@ -70,7 +70,7 @@ Rectangle { } } // define the signal the SignalTransition is connected with - signal mysignal(string mystr) + signal mysignal(mystr: string) // on clicking the button emit the signal with a single string argument onClicked: button.mysignal("test") } diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc index 9c33979f40..0a824bb5b5 100644 --- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc +++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc @@ -166,9 +166,12 @@ updated, and any \c onButtonTextChanged handlers would not be called. \section2 Invoking QML Methods -All QML methods are exposed to the meta-object system and can be called from C++ -using QMetaObject::invokeMethod(). Method parameters and return values passed -from QML are always translated into QVariant values in C++. +All QML methods are exposed to the meta-object system and can be called from +C++ using QMetaObject::invokeMethod(). You can specify types for the parameters +and the return value after the colon character, as shown in the code snippet +below. This can be useful, for example, when you want to connect a signal in +C++ with a certain signature to a QML-defined method. If you omit the types, +the C++ signature will use QVariant. Here is a C++ application that calls a QML method using QMetaObject::invokeMethod(): @@ -182,9 +185,12 @@ QMetaObject::invokeMethod(): \li \snippet qml/qtbinding/functions-qml/main.cpp 0 \endtable -Notice the Q_RETURN_ARG() and Q_ARG() arguments for QMetaObject::invokeMethod() -must be specified as QVariant types, as this is the generic data type used for -QML method parameters and return values. +Notice the parameter and return type specified after the colon. You can use \l +{QML Basic Types}{basic types} and \l {QML Object Types}{object types} as type +names. + +If the type is omitted in QML, then you must specify QVariant as type with +Q_RETURN_ARG() and Q_ARG() when calling QMetaObject::invokeMethod. \section2 Connecting to QML Signals @@ -210,9 +216,8 @@ QObject::connect(), so that the \c cppSlot() method is called whenever the \snippet qml/qtbinding/signals-qml/main.cpp 0 \endtable -When a QML object type is used as a signal parameter, the parameter should -use \l var as the type, and the value should be received in C++ using the -QVariant type: +A QML object type in a signal parameter is translated to a pointer to the class +in C++: \table \row @@ -226,7 +231,7 @@ QVariant type: id: item width: 100; height: 100 - signal qmlSignal(var anObject) + signal qmlSignal(anObject: Item) MouseArea { anchors.fill: parent @@ -241,18 +246,16 @@ QVariant type: { Q_OBJECT public slots: - void cppSlot(const QVariant &v) { - qDebug() << "Called the C++ slot with value:" << v; + void cppSlot(QQuickItem *item) { + qDebug() << "Called the C++ slot with item:" << item; - QQuickItem *item = - qobject_cast<QQuickItem*>(v.value<QObject*>()); qDebug() << "Item dimensions:" << item->width() << item->height(); } }; int main(int argc, char *argv[]) { - QApplication app(argc, argv); + QGuiApplication app(argc, argv); QQuickView view(QUrl::fromLocalFile("MyItem.qml")); QObject *item = view.rootObject(); diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 98e0ef9e70..206e2b9aa4 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -38,13 +38,14 @@ ****************************************************************************/ #include <qv4argumentsobject_p.h> #include <qv4arrayobject_p.h> -#include <qv4alloca_p.h> #include <qv4scopedvalue_p.h> #include <qv4string_p.h> #include <qv4function_p.h> #include <qv4jscall_p.h> #include <qv4symbol_p.h> +#include <private/qv4alloca_p.h> + using namespace QV4; DEFINE_OBJECT_VTABLE(ArgumentsObject); diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp index 350f6f9485..74f34a284d 100644 --- a/src/qml/jsruntime/qv4compilationunitmapper.cpp +++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp @@ -39,7 +39,7 @@ #include "qv4compilationunitmapper_p.h" -#include "qv4compileddata_p.h" +#include <private/qv4compileddata_p.h> #include <QFileInfo> #include <QDateTime> #include <QCoreApplication> diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 21c6a5d06b..1b26608bf3 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -52,8 +52,6 @@ #include <time.h> -#include <private/qqmljsengine_p.h> - #include <wtf/MathExtras.h> #if defined(Q_OS_LINUX) && QT_CONFIG(timezone) diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index 9b41bb6e7a..52263105fa 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -52,6 +52,7 @@ // #include "qv4global_p.h" +#include <private/qv4staticvalue_p.h> #include <QtCore/qobject.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 0d3ae71b05..be0de09d79 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -39,8 +39,6 @@ #include <qv4engine_p.h> #include <private/qv4compileddata_p.h> -#include <private/qv4compiler_p.h> -#include <private/qv4compilercontext_p.h> #include <private/qv4codegen_p.h> #include <private/qqmljsdiagnosticmessage_p.h> diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index ce25ab16b1..d233347060 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -57,7 +57,6 @@ #include <private/qintrusivelist_p.h> #include "qv4enginebase_p.h" #include <private/qqmlrefcount_p.h> -#include <private/qqmljsengine_p.h> #include <private/qqmldelayedcallqueue_p.h> #include <QtCore/qelapsedtimer.h> #include <QtCore/qmutex.h> diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index c6d6c77d11..525d3458f4 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -47,7 +47,7 @@ #include "qv4string_p.h" #include <private/qv4mm_p.h> -#include <qv4codegen_p.h> +#include <private/qv4codegen_p.h> #ifndef Q_OS_WIN # include <time.h> diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 492d1f4d03..a9283cfa54 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -44,11 +44,11 @@ #include <private/qv4lookup_p.h> #include <private/qv4qmlcontext_p.h> #include <private/qv4identifiertable_p.h> -#include <private/qv4instr_moth_p.h> #include <private/qv4objectproto_p.h> #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> @@ -784,7 +784,7 @@ QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Bind case Binding::Type_Boolean: return binding->value.b ? QStringLiteral("true") : QStringLiteral("false"); case Binding::Type_Number: - return QString::number(bindingValueAsNumber(binding)); + return QString::number(bindingValueAsNumber(binding), 'g', QLocale::FloatingPointShortest); case Binding::Type_Invalid: return QString(); #if !QT_CONFIG(translation) diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h index 010b8a2fd0..6eef3b12c3 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit_p.h +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -61,9 +61,13 @@ QT_BEGIN_NAMESPACE +class QQmlScriptData; class QQmlEnginePrivate; namespace QV4 { +// index is per-object binding index +typedef QVector<QQmlPropertyData*> BindingPropertyData; + class CompilationUnitMapper; struct ResolvedTypeReference; // map from name index @@ -131,7 +135,7 @@ public: // index is object index. This allows fast access to the // property data when initializing bindings, avoiding expensive // lookups by string (property name). - QVector<CompiledData::BindingPropertyData> bindingPropertyDataPerObject; + QVector<BindingPropertyData> bindingPropertyDataPerObject; // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects // this is initialized on-demand by QQmlContextData diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index d870cec68a..aeb4835c40 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -100,9 +100,9 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, for (quint32 i = 0; i < compiledFunction->nLocals; ++i) ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); - const quint32_le *formalsIndices = compiledFunction->formalsTable(); + const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable(); for (quint32 i = 0; i < compiledFunction->nFormals; ++i) - ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); + ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable); internalClass = ic->d(); nFormals = compiledFunction->nFormals; diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index cbbb61c68c..51960863c4 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -53,6 +53,7 @@ #include "qv4global_p.h" #include <private/qv4executablecompilationunit_p.h> #include <private/qv4context_p.h> +#include <private/qv4string_p.h> namespace JSC { class MacroAssemblerCodeRef; @@ -93,7 +94,7 @@ public: return static_cast<QV4::ExecutableCompilationUnit *>(compilationUnit); } - QV4::Heap::String *runtimeString(uint i) + QV4::Heap::String *runtimeString(uint i) const { return compilationUnit->runtimeStrings[i]; } @@ -120,7 +121,7 @@ public: void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters); inline Heap::String *name() const { - return compilationUnit->runtimeStrings[compiledFunction->nameIndex]; + return runtimeString(compiledFunction->nameIndex); } static QString prettyName(const Function *function, const void *address); diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index b1b0d67e64..6fb7946023 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -59,10 +59,10 @@ #include "private/qqmlbuiltinfunctions_p.h" #include <private/qv4jscall_p.h> #include <private/qv4vme_moth_p.h> +#include <private/qv4alloca_p.h> #include <QtCore/QDebug> #include <algorithm> -#include "qv4alloca_p.h" #include "qv4profiling_p.h" using namespace QV4; diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index ebd21b3543..c6a737b467 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -52,6 +52,7 @@ // #include <QtCore/qglobal.h> +#include <private/qv4compilerglobal_p.h> #include <QString> #ifdef QT_NO_DEBUG @@ -207,11 +208,6 @@ struct SetMapObject; struct PromiseObject; struct PromiseCapability; -// ReturnedValue is used to return values from runtime methods -// the type has to be a primitive type (no struct or union), so that the compiler -// will return it in a register on all platforms. -// It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm -typedef quint64 ReturnedValue; struct CallData; struct Scope; struct ScopedValue; @@ -341,13 +337,6 @@ struct Q_QML_EXPORT StackFrame { }; typedef QVector<StackFrame> StackTrace; -enum class ObjectLiteralArgument { - Value, - Method, - Getter, - Setter -}; - namespace JIT { enum class CallResultDestination { diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index becdc3bc55..bb81fb52d4 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -49,18 +49,14 @@ #include "qv4string_p.h" #include "qv4jscall_p.h" -#include <private/qqmljsengine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> -#include <qv4codegen_p.h> +#include <private/qv4codegen_p.h> +#include <private/qv4alloca_p.h> #include "private/qlocale_tools_p.h" #include "private/qtools_p.h" #include <QtCore/QDebug> #include <QtCore/QString> #include <iostream> -#include "qv4alloca_p.h" #include <wtf/MathExtras.h> diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index c0885a418c..92face6f94 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -167,7 +167,6 @@ void QV4Include::finished() QByteArray data = m_reply->readAll(); QString code = QString::fromUtf8(data); - QmlIR::Document::removeScriptPragmas(code); QV4::Scoped<QV4::QmlContext> qml(scope, m_qmlContext.value()); QV4::Script script(v4, qml, /*parse as QML binding*/false, code, m_url.toString()); diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index bca4c2ef66..6632d69c27 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -52,7 +52,7 @@ #include <qglobal.h> -#include <QtQml/private/qv4staticvalue_p.h> +#include <private/qv4staticvalue_p.h> #include <QtCore/qnumeric.h> #include <QtCore/private/qnumeric_p.h> #include <cmath> diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 8a4adfe69a..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> @@ -80,7 +81,9 @@ #include <QtCore/qtimer.h> #include <QtCore/qatomic.h> #include <QtCore/qmetaobject.h> +#if QT_CONFIG(qml_itemmodel) #include <QtCore/qabstractitemmodel.h> +#endif #include <QtCore/qloggingcategory.h> #include <vector> @@ -1233,7 +1236,9 @@ private: std::vector<bool> *stdVectorBoolPtr; std::vector<QString> *stdVectorQStringPtr; std::vector<QUrl> *stdVectorQUrlPtr; +#if QT_CONFIG(qml_itemmodel) std::vector<QModelIndex> *stdVectorQModelIndexPtr; +#endif char allocData[MaxSizeOf7<QVariant, QString, @@ -1693,8 +1698,10 @@ void *CallArgument::dataPtr() return stdVectorQStringPtr; else if (type == qMetaTypeId<std::vector<QUrl>>()) return stdVectorQUrlPtr; +#if QT_CONFIG(qml_itemmodel) else if (type == qMetaTypeId<std::vector<QModelIndex>>()) return stdVectorQModelIndexPtr; +#endif else if (type != 0) return (void *)&allocData; return nullptr; @@ -1845,7 +1852,10 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q || callType == qMetaTypeId<std::vector<bool>>() || callType == qMetaTypeId<std::vector<QString>>() || callType == qMetaTypeId<std::vector<QUrl>>() - || callType == qMetaTypeId<std::vector<QModelIndex>>()) { +#if QT_CONFIG(qml_itemmodel) + || callType == qMetaTypeId<std::vector<QModelIndex>>() +#endif + ) { queryEngine = true; const QV4::Object* object = value.as<QV4::Object>(); if (callType == qMetaTypeId<std::vector<int>>()) { @@ -1863,9 +1873,11 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q } else if (callType == qMetaTypeId<std::vector<QUrl>>()) { stdVectorQUrlPtr = nullptr; fromContainerValue<std::vector<QUrl>>(object, callType, &CallArgument::stdVectorQUrlPtr, queryEngine); +#if QT_CONFIG(qml_itemmodel) } else if (callType == qMetaTypeId<std::vector<QModelIndex>>()) { stdVectorQModelIndexPtr = nullptr; fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine); +#endif } #endif } else if (QMetaType::typeFlags(callType) diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 64aba1d85c..c1a42c4afa 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -56,7 +56,7 @@ #include <cassert> #include <typeinfo> #include <iostream> -#include "qv4alloca_p.h" +#include <private/qv4alloca_p.h> QT_BEGIN_NAMESPACE 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/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 13a73b7046..05ffb84d58 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -51,6 +51,7 @@ // #include <private/qv4global_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp index 8d324acbd0..162d75db63 100644 --- a/src/qml/jsruntime/qv4runtimecodegen.cpp +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -39,9 +39,10 @@ #include "qv4engine_p.h" #include "qv4runtimecodegen_p.h" -#include "qv4compilerscanfunctions_p.h" +#include <private/qv4compilerscanfunctions_p.h> using namespace QV4; +using namespace QQmlJS; void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, const QString &sourceCode, diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h index 006a6a3cde..71aaf1fb55 100644 --- a/src/qml/jsruntime/qv4runtimecodegen_p.h +++ b/src/qml/jsruntime/qv4runtimecodegen_p.h @@ -66,11 +66,11 @@ public: void generateFromFunctionExpression(const QString &fileName, const QString &sourceCode, - AST::FunctionExpression *ast, + QQmlJS::AST::FunctionExpression *ast, Compiler::Module *module); - void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override; - void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override; + void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override; + void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override; private: ExecutionEngine *engine; diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index c463812590..2fab9e4b7b 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -60,6 +60,7 @@ #include <QScopedValueRollback> using namespace QV4; +using namespace QQmlJS; Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit) : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) @@ -245,7 +246,6 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo QByteArray data = f.readAll(); QString sourceCode = QString::fromUtf8(data); - QmlIR::Document::removeScriptPragmas(sourceCode); auto result = new QV4::Script(engine, qmlContext, /*parseAsBinding*/false, sourceCode, originalUrl.toString()); result->contextType = QV4::Compiler::ContextType::ScriptImportedByQML; diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 1eef12a491..77a98247ac 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -49,8 +49,10 @@ #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" #include <private/qqmlvaluetypewrapper_p.h> +#if QT_CONFIG(qml_itemmodel) #include <private/qqmlmodelindexvaluetype_p.h> #include <QtCore/qabstractitemmodel.h> +#endif #include <algorithm> @@ -75,6 +77,16 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description } // F(elementType, elementTypeName, sequenceType, defaultValue) +#if QT_CONFIG(qml_itemmodel) +#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) \ + F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ + F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \ + F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ + F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) +#else +#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) +#endif + #define FOREACH_QML_SEQUENCE_TYPE(F) \ F(int, IntVector, QVector<int>, 0) \ F(qreal, RealVector, QVector<qreal>, 0.0) \ @@ -92,10 +104,7 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description F(QUrl, Url, QList<QUrl>, QUrl()) \ F(QUrl, UrlVector, QVector<QUrl>, QUrl()) \ F(QUrl, UrlStdVector, std::vector<QUrl>, QUrl()) \ - F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ - F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \ - F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ - F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) + FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) { @@ -112,6 +121,7 @@ static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, co return engine->newString(element.toString())->asReturnedValue(); } +#if QT_CONFIG(qml_itemmodel) static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QModelIndex &element) { const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(QMetaType::QModelIndex); @@ -124,6 +134,7 @@ static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, co const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(metaTypeId); return QV4::QQmlValueTypeWrapper::create(engine, QVariant::fromValue(element), vtmo, metaTypeId); } +#endif static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, qreal element) { @@ -150,6 +161,7 @@ static QString convertElementToString(const QUrl &element) return element.toString(); } +#if QT_CONFIG(qml_itemmodel) static QString convertElementToString(const QModelIndex &element) { return reinterpret_cast<const QQmlModelIndexValueType *>(&element)->toString(); @@ -159,6 +171,7 @@ static QString convertElementToString(const QItemSelectionRange &element) { return reinterpret_cast<const QQmlItemSelectionRangeValueType *>(&element)->toString(); } +#endif static QString convertElementToString(qreal element) { @@ -192,6 +205,7 @@ template <> QUrl convertValueToElement(const Value &value) return QUrl(value.toQString()); } +#if QT_CONFIG(qml_itemmodel) template <> QModelIndex convertValueToElement(const Value &value) { const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); @@ -207,6 +221,7 @@ template <> QItemSelectionRange convertValueToElement(const Value &value) return v->toVariant().value<QItemSelectionRange>(); return QItemSelectionRange(); } +#endif template <> qreal convertValueToElement(const Value &value) { @@ -667,6 +682,7 @@ typedef QQmlSequence<QVector<QUrl> > QQmlUrlVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlVectorList); typedef QQmlSequence<std::vector<QUrl> > QQmlUrlStdVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlStdVectorList); +#if QT_CONFIG(qml_itemmodel) typedef QQmlSequence<QModelIndexList> QQmlQModelIndexList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexList); typedef QQmlSequence<QVector<QModelIndex> > QQmlQModelIndexVectorList; @@ -675,6 +691,7 @@ typedef QQmlSequence<std::vector<QModelIndex> > QQmlQModelIndexStdVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexStdVectorList); typedef QQmlSequence<QItemSelection> QQmlQItemSelectionRangeList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQItemSelectionRangeList); +#endif typedef QQmlSequence<QList<bool> > QQmlBoolList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolList); typedef QQmlSequence<QList<qreal> > QQmlRealList; diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 227df4014e..9b4a2d575e 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -45,7 +45,7 @@ #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4symbol_p.h" -#include "qv4alloca_p.h" +#include <private/qv4alloca_p.h> #include "qv4jscall_p.h" #include "qv4stringiterator_p.h" #include <QtCore/QDateTime> diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index b2bbe985d3..b4c34d60fa 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -38,11 +38,11 @@ ****************************************************************************/ #include "qv4vme_moth_p.h" -#include "qv4instr_moth_p.h" #include <QtCore/qjsondocument.h> #include <QtCore/qjsonobject.h> +#include <private/qv4instr_moth_p.h> #include <private/qv4value_p.h> #include <private/qv4debugging_p.h> #include <private/qv4function_p.h> @@ -56,11 +56,10 @@ #include <private/qv4profiling_p.h> #include <private/qv4jscall_p.h> #include <private/qv4generatorobject_p.h> +#include <private/qv4alloca_p.h> #include <private/qqmljavascriptexpression_p.h> #include <iostream> -#include "qv4alloca_p.h" - #if QT_CONFIG(qml_jit) #include <private/qv4baselinejit_p.h> #endif diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 8a76e60f20..b3944f5454 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -52,6 +52,7 @@ // #include <private/qv4global_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 6ee36cbfcf..3465036c86 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -46,6 +46,7 @@ #include <QtCore/qalgorithms.h> #include <QtCore/private/qnumeric_p.h> #include <QtCore/qloggingcategory.h> +#include <private/qv4alloca_p.h> #include <qqmlengine.h> #include "PageReservation.h" #include "PageAllocation.h" @@ -59,7 +60,6 @@ #include <iostream> #include <cstdlib> #include <algorithm> -#include "qv4alloca_p.h" #include "qv4profiling_p.h" #include "qv4mapobject_p.h" #include "qv4setobject_p.h" diff --git a/src/qml/parser/parser.pri b/src/qml/parser/parser.pri index 2c0175c94b..e15730f5d1 100644 --- a/src/qml/parser/parser.pri +++ b/src/qml/parser/parser.pri @@ -4,11 +4,9 @@ HEADERS += \ $$PWD/qqmljsastvisitor_p.h \ $$PWD/qqmljsengine_p.h \ $$PWD/qqmljslexer_p.h \ - $$PWD/qqmljsmemorypool_p.h \ $$PWD/qqmljsglobal_p.h \ $$PWD/qqmljskeywords_p.h \ $$PWD/qqmljsengine_p.h \ - $$PWD/qqmljsglobal_p.h \ $$PWD/qqmljssourcelocation_p.h SOURCES += \ diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index daaa402ef1..bdc4a0bb46 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -299,6 +299,9 @@ public: AST::ExportsList *ExportsList; AST::ExportClause *ExportClause; AST::ExportDeclaration *ExportDeclaration; + AST::TypeAnnotation *TypeAnnotation; + AST::TypeArgumentList *TypeArgumentList; + AST::Type *Type; AST::UiProgram *UiProgram; AST::UiHeaderItemList *UiHeaderItemList; @@ -424,6 +427,8 @@ protected: diagnostic_messages.append(compileError(location, message)); } + bool ensureNoFunctionTypeAnnotations(AST::TypeAnnotation *returnTypeAnnotation, AST::FormalParameterList *formals); + protected: Engine *driver; MemoryPool *pool; @@ -594,6 +599,21 @@ int Parser::lookaheadToken(Lexer *lexer) return yytoken; } +bool Parser::ensureNoFunctionTypeAnnotations(AST::TypeAnnotation *returnValueAnnotation, AST::FormalParameterList *formals) +{ + for (auto formal = formals; formal; formal = formal->next) { + if (formal->element && formal->element->typeAnnotation) { + syntaxError(formal->element->typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in function parameters in JavaScript functions"); + return false; + } + } + if (returnValueAnnotation) { + syntaxError(returnValueAnnotation->firstSourceLocation(), "Type annotations are not permitted for the return value of JavaScript functions"); + return false; + } + return true; +} + //#define PARSER_DEBUG bool Parser::parse(int startToken) @@ -1086,6 +1106,17 @@ UiParameterListOpt: UiParameterList; } break; ./ +UiParameterList: QmlIdentifier T_COLON UiPropertyType; +/. + case $rule_number: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(3).UiQualifiedId->finish(), stringRef(1)); + node->identifierToken = loc(1); + node->colonToken = loc(2); + node->propertyTypeToken = loc(3); + sym(1).Node = node; + } break; +./ + UiParameterList: UiPropertyType QmlIdentifier; /. case $rule_number: { @@ -1096,6 +1127,18 @@ UiParameterList: UiPropertyType QmlIdentifier; } break; ./ +UiParameterList: UiParameterList T_COMMA QmlIdentifier T_COLON UiPropertyType; +/. + case $rule_number: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(5).UiQualifiedId->finish(), stringRef(3)); + node->propertyTypeToken = loc(5); + node->commaToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + sym(1).Node = node; + } break; +./ + UiParameterList: UiParameterList T_COMMA UiPropertyType QmlIdentifier; /. case $rule_number: { @@ -1355,7 +1398,7 @@ UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON Expre } break; ./ -UiObjectMember: FunctionDeclaration; +UiObjectMember: FunctionDeclarationWithTypes; /. case $rule_number: { sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); @@ -1471,6 +1514,63 @@ IdentifierReference: JsIdentifier; BindingIdentifier: IdentifierReference; -------------------------------------------------------------------------------------------------------- +-- Types +-------------------------------------------------------------------------------------------------------- + +TypeArguments: Type; +/. + case $rule_number: { + sym(1).TypeArgumentList = new (pool) AST::TypeArgumentList(sym(1).Type); + } break; +./ + +TypeArguments: TypeArguments T_COMMA Type; +/. + case $rule_number: { + sym(1).TypeArgumentList = new (pool) AST::TypeArgumentList(sym(1).TypeArgumentList, sym(3).Type); + } break; +./ + +Type: UiQualifiedId T_LT TypeArguments T_GT; +/. + case $rule_number: { + sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId, sym(3).TypeArgumentList->finish()); + } break; +./ + +Type: T_RESERVED_WORD; +/. + case $rule_number: { + AST::UiQualifiedId *id = new (pool) AST::UiQualifiedId(stringRef(1)); + id->identifierToken = loc(1); + sym(1).Type = new (pool) AST::Type(id->finish()); + } break; +./ + +Type: UiQualifiedId; +/. + case $rule_number: { + sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId); + } break; +./ + +TypeAnnotation: T_COLON Type; +/. + case $rule_number: { + sym(1).TypeAnnotation = new (pool) AST::TypeAnnotation(sym(2).Type); + sym(1).TypeAnnotation->colonToken = loc(1); + } break; +./ + +TypeAnnotationOpt: TypeAnnotation; +TypeAnnotationOpt: ; +/. + case $rule_number: { + sym(1).TypeAnnotation = nullptr; + } break; +./ + +-------------------------------------------------------------------------------------------------------- -- Expressions -------------------------------------------------------------------------------------------------------- @@ -2851,7 +2951,14 @@ VarDeclaration: Var VariableDeclarationList; VarDeclaration_In: Var VariableDeclarationList_In; /. case $rule_number: { - AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(sym(1).scope)); + AST::VariableDeclarationList *declarations = sym(2).VariableDeclarationList->finish(sym(1).scope); + for (auto it = declarations; it; it = it->next) { + if (it->declaration && it->declaration->typeAnnotation) { + syntaxError(it->declaration->typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in variable declarations"); + return false; + } + } + AST::VariableStatement *node = new (pool) AST::VariableStatement(declarations); node->declarationKindToken = loc(1); sym(1).Node = node; } break; @@ -2888,22 +2995,22 @@ VariableDeclarationList_In: VariableDeclarationList_In T_COMMA VariableDeclarati } break; ./ -LexicalBinding: BindingIdentifier InitializerOpt; +LexicalBinding: BindingIdentifier TypeAnnotationOpt InitializerOpt; /. case $rule_number: Q_FALLTHROUGH(); ./ -LexicalBinding_In: BindingIdentifier InitializerOpt_In; +LexicalBinding_In: BindingIdentifier TypeAnnotationOpt InitializerOpt_In; /. case $rule_number: Q_FALLTHROUGH(); ./ -VariableDeclaration: BindingIdentifier InitializerOpt; +VariableDeclaration: BindingIdentifier TypeAnnotationOpt InitializerOpt; /. case $rule_number: Q_FALLTHROUGH(); ./ -VariableDeclaration_In: BindingIdentifier InitializerOpt_In; +VariableDeclaration_In: BindingIdentifier TypeAnnotationOpt InitializerOpt_In; /. case $rule_number: { - auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression); + auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).TypeAnnotation, sym(3).Expression); node->identifierToken = loc(1); sym(1).Node = node; // if initializer is an anonymous function expression, we need to assign identifierref as it's name - if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression)) + if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) f->name = stringRef(1); - if (auto *c = asAnonymousClassDefinition(sym(2).Expression)) + if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) c->name = stringRef(1); } break; ./ @@ -3053,15 +3160,15 @@ BindingProperty: PropertyName T_COLON BindingPattern InitializerOpt_In; } break; ./ -BindingElement: BindingIdentifier InitializerOpt_In; +BindingElement: BindingIdentifier TypeAnnotationOpt InitializerOpt_In; /. case $rule_number: { - AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression); + AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).TypeAnnotation, sym(3).Expression); node->identifierToken = loc(1); // if initializer is an anonymous function expression, we need to assign identifierref as it's name - if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression)) + if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) f->name = stringRef(1); - if (auto *c = asAnonymousClassDefinition(sym(2).Expression)) + if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) c->name = stringRef(1); sym(1).Node = node; } break; @@ -3078,7 +3185,7 @@ BindingElement: BindingPattern InitializerOpt_In; BindingRestElement: T_ELLIPSIS BindingIdentifier; /. case $rule_number: { - AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), nullptr, AST::PatternElement::RestElement); + AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), /*type annotation*/nullptr, nullptr, AST::PatternElement::RestElement); node->identifierToken = loc(2); sym(1).Node = node; } break; @@ -3268,12 +3375,16 @@ IterationStatement: T_FOR T_LPAREN ForDeclaration InOrOf Expression_In T_RPAREN } break; ./ -ForDeclaration: LetOrConst BindingIdentifier; +ForDeclaration: LetOrConst BindingIdentifier TypeAnnotationOpt; /. case $rule_number: Q_FALLTHROUGH(); ./ -ForDeclaration: Var BindingIdentifier; +ForDeclaration: Var BindingIdentifier TypeAnnotationOpt; /. case $rule_number: { - auto *node = new (pool) AST::PatternElement(stringRef(2), nullptr); + if (auto typeAnnotation = sym(3).TypeAnnotation) { + syntaxError(typeAnnotation->firstSourceLocation(), "Type annotations are not permitted in variable declarations"); + return false; + } + auto *node = new (pool) AST::PatternElement(stringRef(2), sym(3).TypeAnnotation, nullptr); node->identifierToken = loc(2); node->scope = sym(1).scope; node->isForDeclaration = true; @@ -3560,59 +3671,85 @@ DebuggerStatement: T_DEBUGGER T_SEMICOLON; -- otherwise conflict. Function: T_FUNCTION %prec REDUCE_HERE; -FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace; /. case $rule_number: { - AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList); + if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList)) + return false; + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList, + /*type annotation*/nullptr); node->functionToken = loc(1); node->identifierToken = loc(2); node->lparenToken = loc(3); node->rparenToken = loc(5); - node->lbraceToken = loc(6); - node->rbraceToken = loc(8); + node->lbraceToken = loc(7); + node->rbraceToken = loc(9); sym(1).Node = node; } break; ./ +FunctionDeclarationWithTypes: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace; +/. + case $rule_number: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList, + sym(6).TypeAnnotation); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(7); + node->rbraceToken = loc(9); + sym(1).Node = node; + } break; +./ FunctionDeclaration_Default: FunctionDeclaration; -FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace; /. case $rule_number: { - AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList); + if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList)) + return false; + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(QStringRef(), sym(3).FormalParameterList, sym(7).StatementList, + /*type annotation*/nullptr); node->functionToken = loc(1); node->lparenToken = loc(2); node->rparenToken = loc(4); - node->lbraceToken = loc(5); - node->rbraceToken = loc(7); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); sym(1).Node = node; } break; ./ -FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace; /. case $rule_number: { - AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList); + if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList)) + return false; + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList, + /*type annotation*/nullptr); node->functionToken = loc(1); if (! stringRef(2).isNull()) node->identifierToken = loc(2); node->lparenToken = loc(3); node->rparenToken = loc(5); - node->lbraceToken = loc(6); - node->rbraceToken = loc(8); + node->lbraceToken = loc(7); + node->rbraceToken = loc(9); sym(1).Node = node; } break; ./ -FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace; /. case $rule_number: { - AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList); + if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList)) + return false; + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(7).StatementList, + /*type annotation*/nullptr); node->functionToken = loc(1); node->lparenToken = loc(2); node->rparenToken = loc(4); - node->lbraceToken = loc(5); - node->rbraceToken = loc(7); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); sym(1).Node = node; } break; ./ @@ -3722,7 +3859,7 @@ ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK Fun ArrowParameters: BindingIdentifier; /. case $rule_number: { - AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), nullptr, AST::PatternElement::Binding); + AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), /*type annotation*/nullptr, nullptr, AST::PatternElement::Binding); e->identifierToken = loc(1); sym(1).FormalParameterList = (new (pool) AST::FormalParameterList(nullptr, e))->finish(pool); } break; @@ -3756,30 +3893,34 @@ ConciseBodyLookahead: ; } break; ./ -MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace; /. case $rule_number: { - AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(6).StatementList); + if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, sym(3).FormalParameterList)) + return false; + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(7).StatementList); f->functionToken = sym(1).PropertyName->firstSourceLocation(); f->lparenToken = loc(2); f->rparenToken = loc(4); - f->lbraceToken = loc(5); - f->rbraceToken = loc(7); + f->lbraceToken = loc(6); + f->rbraceToken = loc(8); AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, f, AST::PatternProperty::Method); node->colonToken = loc(2); sym(1).Node = node; } break; ./ -MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace; +MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN TypeAnnotationOpt FunctionLBrace GeneratorBody GeneratorRBrace; /. case $rule_number: { - AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList); + if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList)) + return false; + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList); f->functionToken = sym(2).PropertyName->firstSourceLocation(); f->lparenToken = loc(3); f->rparenToken = loc(5); - f->lbraceToken = loc(6); - f->rbraceToken = loc(8); + f->lbraceToken = loc(7); + f->rbraceToken = loc(9); f->isGenerator = true; AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Method); node->colonToken = loc(2); @@ -3788,30 +3929,34 @@ MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_R ./ -MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace; /. case $rule_number: { - AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(6).StatementList); + if (!ensureNoFunctionTypeAnnotations(sym(5).TypeAnnotation, /*formals*/nullptr)) + return false; + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(7).StatementList); f->functionToken = sym(2).PropertyName->firstSourceLocation(); f->lparenToken = loc(3); f->rparenToken = loc(4); - f->lbraceToken = loc(5); - f->rbraceToken = loc(7); + f->lbraceToken = loc(6); + f->rbraceToken = loc(8); AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Getter); node->colonToken = loc(2); sym(1).Node = node; } break; ./ -MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; +MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN TypeAnnotationOpt FunctionLBrace FunctionBody FunctionRBrace; /. case $rule_number: { - AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList); + if (!ensureNoFunctionTypeAnnotations(sym(6).TypeAnnotation, sym(4).FormalParameterList)) + return false; + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(8).StatementList); f->functionToken = sym(2).PropertyName->firstSourceLocation(); f->lparenToken = loc(3); f->rparenToken = loc(5); - f->lbraceToken = loc(6); - f->rbraceToken = loc(8); + f->lbraceToken = loc(7); + f->rbraceToken = loc(9); AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Setter); node->colonToken = loc(2); sym(1).Node = node; diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 1bc0e6e364..700c191499 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -131,7 +131,7 @@ FormalParameterList *ExpressionNode::reparseAsFormalParameterList(MemoryPool *po } AST::PatternElement *binding = nullptr; if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) { - binding = new (pool) AST::PatternElement(idExpr->name, rhs); + binding = new (pool) AST::PatternElement(idExpr->name, /*type annotation*/nullptr, rhs); binding->identifierToken = idExpr->identifierToken; } else if (AST::Pattern *p = expr->patternCast()) { SourceLocation loc; @@ -961,6 +961,7 @@ void FunctionDeclaration::accept0(Visitor *visitor) { if (visitor->visit(this)) { accept(formals, visitor); + accept(typeAnnotation, visitor); accept(body, visitor); } @@ -971,6 +972,7 @@ void FunctionExpression::accept0(Visitor *visitor) { if (visitor->visit(this)) { accept(formals, visitor); + accept(typeAnnotation, visitor); accept(body, visitor); } @@ -982,9 +984,9 @@ FunctionExpression *FunctionExpression::asFunctionDefinition() return this; } -QStringList FormalParameterList::formals() const +BoundNames FormalParameterList::formals() const { - QStringList formals; + BoundNames formals; int i = 0; for (const FormalParameterList *it = this; it; it = it->next) { if (it->element) { @@ -992,18 +994,18 @@ QStringList FormalParameterList::formals() const int duplicateIndex = formals.indexOf(name); if (duplicateIndex >= 0) { // change the name of the earlier argument to enforce the lookup semantics from the spec - formals[duplicateIndex] += QLatin1String("#") + QString::number(i); + formals[duplicateIndex].id += QLatin1String("#") + QString::number(i); } - formals += name; + formals += {name, it->element->typeAnnotation}; } ++i; } return formals; } -QStringList FormalParameterList::boundNames() const +BoundNames FormalParameterList::boundNames() const { - QStringList names; + BoundNames names; for (const FormalParameterList *it = this; it; it = it->next) { if (it->element) it->element->boundNames(&names); @@ -1271,6 +1273,35 @@ void UiQualifiedId::accept0(Visitor *visitor) visitor->endVisit(this); } +void Type::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(typeId, visitor); + accept(typeArguments, visitor); + } + + visitor->endVisit(this); +} + +void TypeArgumentList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (TypeArgumentList *it = this; it; it = it->next) + accept(it->typeId, visitor); + } + + visitor->endVisit(this); +} + +void TypeAnnotation::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(type, visitor); + } + + visitor->endVisit(this); +} + void UiImport::accept0(Visitor *visitor) { if (visitor->visit(this)) { @@ -1339,13 +1370,14 @@ void PatternElement::accept0(Visitor *visitor) { if (visitor->visit(this)) { accept(bindingTarget, visitor); + accept(typeAnnotation, visitor); accept(initializer, visitor); } visitor->endVisit(this); } -void PatternElement::boundNames(QStringList *names) +void PatternElement::boundNames(BoundNames *names) { if (bindingTarget) { if (PatternElementList *e = elementList()) @@ -1353,7 +1385,7 @@ void PatternElement::boundNames(QStringList *names) else if (PatternPropertyList *p = propertyList()) p->boundNames(names); } else { - names->append(bindingIdentifier.toString()); + names->append({bindingIdentifier.toString(), typeAnnotation}); } } @@ -1369,7 +1401,7 @@ void PatternElementList::accept0(Visitor *visitor) visitor->endVisit(this); } -void PatternElementList::boundNames(QStringList *names) +void PatternElementList::boundNames(BoundNames *names) { for (PatternElementList *it = this; it; it = it->next) { if (it->element) @@ -1382,13 +1414,14 @@ void PatternProperty::accept0(Visitor *visitor) if (visitor->visit(this)) { accept(name, visitor); accept(bindingTarget, visitor); + accept(typeAnnotation, visitor); accept(initializer, visitor); } visitor->endVisit(this); } -void PatternProperty::boundNames(QStringList *names) +void PatternProperty::boundNames(BoundNames *names) { PatternElement::boundNames(names); } @@ -1404,7 +1437,7 @@ void PatternPropertyList::accept0(Visitor *visitor) visitor->endVisit(this); } -void PatternPropertyList::boundNames(QStringList *names) +void PatternPropertyList::boundNames(BoundNames *names) { for (PatternPropertyList *it = this; it; it = it->next) it->property->boundNames(names); @@ -1478,6 +1511,31 @@ void UiVersionSpecifier::accept0(Visitor *visitor) } visitor->endVisit(this); } + +QString Type::toString() const +{ + QString result; + toString(&result); + return result; +} + +void Type::toString(QString *out) const +{ + for (QQmlJS::AST::UiQualifiedId *it = typeId; it; it = it->next) { + out->append(it->name); + + if (it->next) + out->append(QLatin1Char('.')); + } + + if (typeArguments) { + out->append(QLatin1Char('<')); + if (auto subType = static_cast<TypeArgumentList*>(typeArguments)->typeId) + subType->toString(out); + out->append(QLatin1Char('>')); + }; +} + } } // namespace QQmlJS::AST QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 606137b67d..1502298d14 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -53,7 +53,8 @@ #include "qqmljsastvisitor_p.h" #include "qqmljsglobal_p.h" -#include "qqmljsmemorypool_p.h" + +#include <private/qqmljsmemorypool_p.h> #include <QtCore/qstring.h> @@ -233,6 +234,9 @@ public: Kind_PatternElementList, Kind_PatternProperty, Kind_PatternPropertyList, + Kind_Type, + Kind_TypeArgumentList, + Kind_TypeAnnotation, Kind_UiArrayBinding, Kind_UiImport, @@ -303,6 +307,131 @@ public: int kind = Kind_Undefined; }; + +class QML_PARSER_EXPORT UiQualifiedId: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiQualifiedId) + + UiQualifiedId(const QStringRef &name) + : next(this), name(name) + { kind = K; } + + UiQualifiedId(UiQualifiedId *previous, const QStringRef &name) + : name(name) + { + kind = K; + next = previous->next; + previous->next = this; + } + + UiQualifiedId *finish() + { + UiQualifiedId *head = next; + next = nullptr; + return head; + } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return identifierToken; } + + SourceLocation lastSourceLocation() const override + { return next ? next->lastSourceLocation() : identifierToken; } + +// attributes + UiQualifiedId *next; + QStringRef name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT Type: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Type) + + Type(UiQualifiedId *typeId, Node *typeArguments = nullptr) + : typeId(typeId) + , typeArguments(typeArguments) + { kind = K; } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return typeId->firstSourceLocation(); } + + SourceLocation lastSourceLocation() const override + { return typeArguments ? typeArguments->lastSourceLocation() : typeId->lastSourceLocation(); } + + QString toString() const; + void toString(QString *out) const; + +// attributes + UiQualifiedId *typeId; + Node *typeArguments; // TypeArgumentList +}; + + +class QML_PARSER_EXPORT TypeArgumentList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(TypeArgumentList) + + TypeArgumentList(Type *typeId) + : typeId(typeId) + , next(nullptr) + { kind = K; } + + TypeArgumentList(TypeArgumentList *previous, Type *typeId) + : typeId(typeId) + { + kind = K; + next = previous->next; + previous->next = this; + } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return typeId->firstSourceLocation(); } + + SourceLocation lastSourceLocation() const override + { return next ? next->lastSourceLocation() : typeId->lastSourceLocation(); } + + inline TypeArgumentList *finish() + { + TypeArgumentList *front = next; + next = nullptr; + return front; + } + +// attributes + Type *typeId; + TypeArgumentList *next; +}; + +class QML_PARSER_EXPORT TypeAnnotation: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(TypeAnnotation) + + TypeAnnotation(Type *type) + : type(type) + { kind = K; } + + void accept0(Visitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return colonToken; } + + SourceLocation lastSourceLocation() const override + { return type->lastSourceLocation(); } + +// attributes + Type *type; + SourceLocation colonToken; +}; class QML_PARSER_EXPORT ExpressionNode: public Node { public: @@ -704,6 +833,34 @@ public: SourceLocation propertyNameToken; }; +struct QML_PARSER_EXPORT BoundName +{ + QString id; + TypeAnnotation *typeAnnotation = nullptr; + BoundName(const QString &id, TypeAnnotation *typeAnnotation) + : id(id), typeAnnotation(typeAnnotation) + {} + BoundName() = default; + QString typeName() const { return typeAnnotation ? typeAnnotation->type->toString() : QString(); } +}; + +struct BoundNames : public QVector<BoundName> +{ + int indexOf(const QString &name, int from = 0) const + { + auto found = std::find_if(constBegin() + from, constEnd(), + [name](const BoundName &it) { return it.id == name; }); + if (found == constEnd()) + return -1; + return found - constBegin(); + } + + bool contains(const QString &name) const + { + return indexOf(name) != -1; + } +}; + class QML_PARSER_EXPORT PatternElement : public Node { public: @@ -728,8 +885,9 @@ public: : initializer(i), type(t) { kind = K; } - PatternElement(const QStringRef &n, ExpressionNode *i = nullptr, Type t = Binding) + PatternElement(const QStringRef &n, TypeAnnotation *typeAnnotation = nullptr, ExpressionNode *i = nullptr, Type t = Binding) : bindingIdentifier(n), initializer(i), type(t) + , typeAnnotation(typeAnnotation) { Q_ASSERT(t >= RestElement); kind = K; @@ -749,7 +907,7 @@ public: { return identifierToken.isValid() ? identifierToken : (bindingTarget ? bindingTarget->firstSourceLocation() : initializer->firstSourceLocation()); } SourceLocation lastSourceLocation() const override - { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : identifierToken); } + { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : (typeAnnotation ? typeAnnotation->lastSourceLocation() : identifierToken)); } ExpressionNode *destructuringTarget() const { return bindingTarget; } Pattern *destructuringPattern() const { return bindingTarget ? bindingTarget->patternCast() : nullptr; } @@ -759,7 +917,7 @@ public: bool isVariableDeclaration() const { return scope != VariableScope::NoScope; } bool isLexicallyScoped() const { return scope == VariableScope::Let || scope == VariableScope::Const; } - virtual void boundNames(QStringList *names); + virtual void boundNames(BoundNames *names); // attributes SourceLocation identifierToken; @@ -767,6 +925,7 @@ public: ExpressionNode *bindingTarget = nullptr; ExpressionNode *initializer = nullptr; Type type = Literal; + TypeAnnotation *typeAnnotation = nullptr; // when used in a VariableDeclarationList VariableScope scope = VariableScope::NoScope; bool isForDeclaration = false; @@ -796,7 +955,7 @@ public: void accept0(Visitor *visitor) override; - void boundNames(QStringList *names); + void boundNames(BoundNames *names); SourceLocation firstSourceLocation() const override { return elision ? elision->firstSourceLocation() : element->firstSourceLocation(); } @@ -819,7 +978,7 @@ public: { kind = K; } PatternProperty(PropertyName *name, const QStringRef &n, ExpressionNode *i = nullptr) - : PatternElement(n, i), name(name) + : PatternElement(n, /*type annotation*/nullptr, i), name(name) { kind = K; } PatternProperty(PropertyName *name, Pattern *pattern, ExpressionNode *i = nullptr) @@ -836,7 +995,7 @@ public: return loc.isValid() ? loc : name->lastSourceLocation(); } - void boundNames(QStringList *names) override; + void boundNames(BoundNames *names) override; bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override; // attributes @@ -864,7 +1023,7 @@ public: void accept0(Visitor *visitor) override; - void boundNames(QStringList *names); + void boundNames(BoundNames *names); inline PatternPropertyList *finish () { @@ -2154,8 +2313,9 @@ class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode public: QQMLJS_DECLARE_AST_NODE(FunctionExpression) - FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b): - name (n), formals (f), body (b) + FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b, TypeAnnotation *typeAnnotation = nullptr): + name (n), formals (f), body (b), + typeAnnotation(typeAnnotation) { kind = K; } void accept0(Visitor *visitor) override; @@ -2174,6 +2334,7 @@ public: bool isGenerator = false; FormalParameterList *formals; StatementList *body; + TypeAnnotation *typeAnnotation; SourceLocation functionToken; SourceLocation identifierToken; SourceLocation lparenToken; @@ -2187,8 +2348,8 @@ class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression public: QQMLJS_DECLARE_AST_NODE(FunctionDeclaration) - FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b): - FunctionExpression(n, f, b) + FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b, TypeAnnotation *typeAnnotation = nullptr): + FunctionExpression(n, f, b, typeAnnotation) { kind = K; } void accept0(Visitor *visitor) override; @@ -2258,9 +2419,9 @@ public: return false; } - QStringList formals() const; + BoundNames formals() const; - QStringList boundNames() const; + BoundNames boundNames() const; void accept0(Visitor *visitor) override; @@ -2809,44 +2970,6 @@ public: SourceLocation semicolonToken; }; -class QML_PARSER_EXPORT UiQualifiedId: public Node -{ -public: - QQMLJS_DECLARE_AST_NODE(UiQualifiedId) - - UiQualifiedId(const QStringRef &name) - : next(this), name(name) - { kind = K; } - - UiQualifiedId(UiQualifiedId *previous, const QStringRef &name) - : name(name) - { - kind = K; - next = previous->next; - previous->next = this; - } - - UiQualifiedId *finish() - { - UiQualifiedId *head = next; - next = nullptr; - return head; - } - - void accept0(Visitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { return identifierToken; } - - SourceLocation lastSourceLocation() const override - { return next ? next->lastSourceLocation() : identifierToken; } - -// attributes - UiQualifiedId *next; - QStringRef name; - SourceLocation identifierToken; -}; - class QML_PARSER_EXPORT UiImport: public Node { public: @@ -3114,10 +3237,10 @@ public: void accept0(Visitor *) override; SourceLocation firstSourceLocation() const override - { return propertyTypeToken; } + { return colonToken.isValid() ? identifierToken : propertyTypeToken; } SourceLocation lastSourceLocation() const override - { return next ? next->lastSourceLocation() : identifierToken; } + { return next ? next->lastSourceLocation() : (colonToken.isValid() ? propertyTypeToken : identifierToken); } inline UiParameterList *finish () { @@ -3133,6 +3256,7 @@ public: SourceLocation commaToken; SourceLocation propertyTypeToken; SourceLocation identifierToken; + SourceLocation colonToken; }; class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h index 6fe108e425..05226fd043 100644 --- a/src/qml/parser/qqmljsastfwd_p.h +++ b/src/qml/parser/qqmljsastfwd_p.h @@ -158,6 +158,9 @@ class NestedExpression; class ClassExpression; class ClassDeclaration; class ClassElementList; +class TypeArgumentList; +class Type; +class TypeAnnotation; // ui elements class UiProgram; diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h index f3732cbba8..7146cd00ac 100644 --- a/src/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/parser/qqmljsastvisitor_p.h @@ -403,6 +403,15 @@ public: virtual bool visit(DebuggerStatement *) { return true; } virtual void endVisit(DebuggerStatement *) {} + virtual bool visit(Type *) { return true; } + virtual void endVisit(Type *) {} + + virtual bool visit(TypeArgumentList *) { return true; } + virtual void endVisit(TypeArgumentList *) {} + + virtual bool visit(TypeAnnotation *) { return true; } + virtual void endVisit(TypeAnnotation *) {} + virtual void throwRecursionDepthError() = 0; quint16 recursionDepth() const { return m_recursionDepth; } diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h index 6a754fc236..8a3e2db6a1 100644 --- a/src/qml/parser/qqmljsengine_p.h +++ b/src/qml/parser/qqmljsengine_p.h @@ -52,9 +52,10 @@ // #include "qqmljsglobal_p.h" -#include "qqmljsmemorypool_p.h" #include "qqmljssourcelocation_p.h" +#include <private/qqmljsmemorypool_p.h> + #include <QtCore/qstring.h> #include <QtCore/qset.h> diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index 165925d2a2..1e0ac72bd1 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -39,10 +39,10 @@ #include "qqmljslexer_p.h" #include "qqmljsengine_p.h" -#include "qqmljsmemorypool_p.h" #include "qqmljskeywords_p.h" #include <private/qqmljsdiagnosticmessage_p.h> +#include <private/qqmljsmemorypool_p.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qvarlengtharray.h> diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 99ad6ace60..3c889244f4 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -19,31 +19,6 @@ gcc:isEqual(QT_ARCH, "mips"): QMAKE_CXXFLAGS += -fno-reorder-blocks DEFINES += QT_NO_FOREACH -!build_pass { - # Create a header containing a hash that describes this library. For a - # released version of Qt, we'll use the .tag file that is updated by git - # archive with the commit hash. For unreleased versions, we'll ask git - # describe. Note that it won't update unless qmake is run again, even if - # the commit change also changed something in this library. - tagFile = $$PWD/../../.tag - tag = - exists($$tagFile) { - tag = $$cat($$tagFile, singleline) - QMAKE_INTERNAL_INCLUDED_FILES += $$tagFile - } - !equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") { - QML_COMPILE_HASH = $$tag - } else:exists($$PWD/../../.git) { - commit = $$system(git rev-parse HEAD) - QML_COMPILE_HASH = $$commit - } - compile_hash_contents = \ - "// Generated file, DO NOT EDIT" \ - "$${LITERAL_HASH}define QML_COMPILE_HASH \"$$QML_COMPILE_HASH\"" \ - "$${LITERAL_HASH}define QML_COMPILE_HASH_LENGTH $$str_size($$QML_COMPILE_HASH)" - write_file("$$OUT_PWD/qml_compile_hash_p.h", compile_hash_contents)|error() -} - exists("qqml_enable_gcov") { QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors LIBS_PRIVATE += -lgcov @@ -64,7 +39,9 @@ greaterThan(QT_CLANG_MAJOR_VERSION, 3)|greaterThan(QT_CLANG_MINOR_VERSION, 3)| \ WERROR += -Wno-error=unused-const-variable HEADERS += qtqmlglobal.h \ - qtqmlglobal_p.h + qtqmlglobal_p.h \ + qtqmlcompilerglobal.h \ + qtqmlcompilerglobal_p.h #modules include(common/common.pri) diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 15ea12cbe7..08591b5237 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,10 +68,18 @@ 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/qqmlerror.h \ $$PWD/qqmlproperty.h \ $$PWD/qqmlcomponent.h \ $$PWD/qqmlcomponent_p.h \ @@ -163,5 +178,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/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 59b5cc2691..fefe2bc685 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -569,6 +569,12 @@ void QQmlComponent::setData(const QByteArray &data, const QUrl &url) { Q_D(QQmlComponent); + if (!d->engine) { + // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check + qWarning("QQmlComponent: Must provide an engine before calling setData"); + return; + } + d->clear(); d->url = url; @@ -774,6 +780,12 @@ QObject *QQmlComponent::create(QQmlContext *context) { Q_D(QQmlComponent); + if (!d->engine) { + // ###Qt6: In Qt 6, it should be impossible for users to create a QQmlComponent without an engine, and we can remove this check + qWarning("QQmlComponent: Must provide an engine before calling create"); + return nullptr; + } + if (!context) context = d->engine->rootContext(); diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 71275a2cd3..9a967501c9 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -60,6 +60,7 @@ #include "qqmlerror.h" #include "qqml.h" #include <private/qqmlobjectcreator_p.h> +#include <private/qqmltypedata_p.h> #include <QtCore/QString> #include <QtCore/QStringList> diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp index c477415ef7..a5f34dafdf 100644 --- a/src/qml/qml/qqmlcustomparser.cpp +++ b/src/qml/qml/qqmlcustomparser.cpp @@ -39,7 +39,7 @@ #include "qqmlcustomparser_p.h" -#include <private/qqmltypecompiler_p.h> +#include <private/qv4compileddata_p.h> #include <QtCore/qdebug.h> diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index 94671a4f35..df8cbc9072 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -53,7 +53,7 @@ #include "qqmlerror.h" #include "qqmlbinding_p.h" -#include <private/qqmltypecompiler_p.h> +#include <private/qv4compileddata_p.h> #include <QtCore/qbytearray.h> #include <QtCore/qxmlstream.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..00d2a6b502 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -51,14 +51,15 @@ #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" #include "qqmlcomponent_p.h" -#include "qqmldirparser_p.h" #include "qqmlextensioninterface.h" #include "qqmllist_p.h" #include "qqmltypenamecache_p.h" #include "qqmlnotifier_p.h" #include "qqmlincubator.h" #include "qqmlabstracturlinterceptor.h" +#include <private/qqmldirparser_p.h> #include <private/qqmlboundsignal_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> #include <QtCore/qstandardpaths.h> #include <QtCore/qsettings.h> #include <QtCore/qmetaobject.h> @@ -1790,7 +1791,7 @@ void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCo deferData->context = context; const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(objectIndex); - const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex); + const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex); const QV4::CompiledData::Binding *binding = compiledObject->bindingTable(); for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) { @@ -2115,10 +2116,11 @@ void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QList<QQmlError dumpwarning(error); } -QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(const QString &fileName, const QList<DiagnosticMessage> &diagnosticMessages) +QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics( + const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages) { QList<QQmlError> errors; - for (const DiagnosticMessage &m : diagnosticMessages) { + for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { if (m.isWarning()) { qWarning("%s:%d : %s", qPrintable(fileName), m.line, qPrintable(m.message)); continue; @@ -2325,6 +2327,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/qqmlinfo.h b/src/qml/qml/qqmlinfo.h index 673125632e..faa112d4af 100644 --- a/src/qml/qml/qqmlinfo.h +++ b/src/qml/qml/qqmlinfo.h @@ -40,6 +40,7 @@ #ifndef QQMLINFO_H #define QQMLINFO_H +#include <QtQml/qtqmlglobal.h> #include <QtCore/qdebug.h> #include <QtCore/qurl.h> #include <QtQml/qqmlerror.h> diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp index c2eb6da2fa..82cad8eba8 100644 --- a/src/qml/qml/qqmlirloader.cpp +++ b/src/qml/qml/qqmlirloader.cpp @@ -136,10 +136,10 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali QmlIR::Signal *s = pool->New<QmlIR::Signal>(); s->nameIndex = serializedSignal->nameIndex; s->location = serializedSignal->location; - s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >(); + s->parameters = pool->New<QmlIR::PoolList<QmlIR::Parameter> >(); for (uint i = 0; i < serializedSignal->nParameters; ++i) { - QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>(); + QmlIR::Parameter *p = pool->New<QmlIR::Parameter>(); *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); s->parameters->append(p); } @@ -188,11 +188,12 @@ QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *seriali f->index = functionIndices.count() - 1; f->location = compiledFunction->location; f->nameIndex = compiledFunction->nameIndex; + f->returnType = compiledFunction->returnType; f->formals.allocate(pool, int(compiledFunction->nFormals)); - const quint32_le *formalNameIdx = compiledFunction->formalsTable(); + const QV4::CompiledData::Parameter *formalNameIdx = compiledFunction->formalsTable(); for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) - f->formals[i] = *formalNameIdx; + *static_cast<QV4::CompiledData::Parameter*>(&f->formals[i]) = *formalNameIdx; object->functions->append(f); } 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 be40eeb58a..d5681b3449 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> @@ -735,7 +736,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) QQmlListProperty<void> savedList; qSwap(_currentList, savedList); - const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); + const QV4::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); if (_compiledObject->idNameIndex) { const QQmlPropertyData *idProperty = propertyData.last(); @@ -1445,8 +1446,12 @@ void QQmlObjectCreator::clear() return; Q_ASSERT(phase != Startup); - while (!sharedState->allCreatedObjects.isEmpty()) - delete sharedState->allCreatedObjects.pop(); + while (!sharedState->allCreatedObjects.isEmpty()) { + auto object = sharedState->allCreatedObjects.pop(); + if (engine->objectOwnership(object) != QQmlEngine::CppOwnership) { + delete object; + } + } while (sharedState->componentAttached) { QQmlComponentAttached *a = sharedState->componentAttached; diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 1cbad87920..ecdbcc56dd 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -53,17 +53,16 @@ #include <private/qqmlimport_p.h> #include <private/qqmltypenamecache_p.h> #include <private/qv4compileddata_p.h> -#include <private/qqmltypecompiler_p.h> #include <private/qfinitestack_p.h> #include <private/qrecursionwatcher_p.h> #include <private/qqmlprofiler_p.h> +#include <private/qv4qmlcontext_p.h> #include <qpointer.h> QT_BEGIN_NAMESPACE class QQmlAbstractBinding; -struct QQmlTypeCompiler; class QQmlInstantiationInterrupt; class QQmlIncubatorPrivate; diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 48c4216d54..69957ab282 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -348,17 +348,18 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag } void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flags flags, - int coreIndex, const QList<QByteArray> &names) + int coreIndex, int returnType, const QList<QByteArray> &names, + const QVector<int> ¶meterTypes) { int argumentCount = names.count(); QQmlPropertyData data; - data.setPropType(QMetaType::QVariant); + data.setPropType(returnType); data.setCoreIndex(coreIndex); QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); for (int ii = 0; ii < argumentCount; ++ii) - args->arguments[ii + 1] = QMetaType::QVariant; + args->arguments[ii + 1] = parameterTypes.at(ii); args->argumentsValid = true; data.setArguments(args); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 72692ee522..bfd78eef88 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -103,8 +103,8 @@ public: int propType, int revision, int notifyIndex); void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex, const int *types = nullptr, const QList<QByteArray> &names = QList<QByteArray>()); - void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, - const QList<QByteArray> &names = QList<QByteArray>()); + void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, int returnType, + const QList<QByteArray> &names, const QVector<int> ¶meterTypes); void appendEnum(const QString &, const QVector<QQmlEnumValue> &); const QMetaObject *metaObject() const; diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 14f734277d..9c7a69d571 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -54,9 +54,20 @@ #include <private/qqmlengine_p.h> #include <private/qqmlmetaobject_p.h> #include <private/qqmlpropertyresolver_p.h> +#include <private/qqmltypedata_p.h> QT_BEGIN_NAMESPACE +inline QQmlJS::DiagnosticMessage qQmlCompileError(const QV4::CompiledData::Location &location, + const QString &description) +{ + QQmlJS::DiagnosticMessage error; + error.line = location.line; + error.column = location.column; + error.message = description; + return error; +} + struct QQmlBindingInstantiationContext { QQmlBindingInstantiationContext() {} QQmlBindingInstantiationContext(int referencingObjectIndex, @@ -106,6 +117,8 @@ protected: QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlJS::DiagnosticMessage *error) const; QQmlJS::DiagnosticMessage createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache); + int metaTypeForParameter(const QV4::CompiledData::ParameterType ¶m, QString *customTypeName = nullptr); + QString stringAt(int index) const { return objectContainer->stringAt(index); } QQmlEnginePrivate * const enginePrivate; @@ -404,28 +417,13 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea auto end = s->parametersEnd(); for ( ; param != end; ++param, ++i) { names.append(stringAt(param->nameIndex).toUtf8()); - if (param->indexIsBuiltinType) { - // built-in type - paramTypes[i + 1] = metaTypeForPropertyType(static_cast<QV4::CompiledData::BuiltinType>(int(param->typeNameIndexOrBuiltinType))); - } else { - // lazily resolved type - const QString customTypeName = stringAt(param->typeNameIndexOrBuiltinType); - QQmlType qmltype; - if (!imports->resolveType(customTypeName, &qmltype, nullptr, nullptr, nullptr)) - return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); - - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - paramTypes[i + 1] = compilationUnit->metaTypeId; - } else { - paramTypes[i + 1] = qmltype.typeId(); - } - } + + QString customTypeName; + auto type = metaTypeForParameter(param->type, &customTypeName); + if (type == QMetaType::UnknownType) + return qQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); + + paramTypes[i + 1] = type; } } @@ -456,14 +454,23 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea // protect against overriding change signals or methods with properties. QList<QByteArray> parameterNames; + QVector<int> parameterTypes; auto formal = function->formalsBegin(); auto end = function->formalsEnd(); for ( ; formal != end; ++formal) { flags.hasArguments = true; - parameterNames << stringAt(*formal).toUtf8(); + parameterNames << stringAt(formal->nameIndex).toUtf8(); + int type = metaTypeForParameter(formal->type); + if (type == QMetaType::UnknownType) + type = QMetaType::QVariant; + parameterTypes << type; } - cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); + int returnType = metaTypeForParameter(function->returnType); + if (returnType == QMetaType::UnknownType) + returnType = QMetaType::QVariant; + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, returnType, parameterNames, parameterTypes); } @@ -542,6 +549,35 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheCreator<ObjectContainer>::crea } template <typename ObjectContainer> +inline int QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter(const QV4::CompiledData::ParameterType ¶m, + QString *customTypeName) +{ + if (param.indexIsBuiltinType) { + // built-in type + return metaTypeForPropertyType(static_cast<QV4::CompiledData::BuiltinType>(int(param.typeNameIndexOrBuiltinType))); + } + + // lazily resolved type + const QString typeName = stringAt(param.typeNameIndexOrBuiltinType); + if (customTypeName) + *customTypeName = typeName; + QQmlType qmltype; + if (!imports->resolveType(typeName, &qmltype, nullptr, nullptr, nullptr)) + return QMetaType::UnknownType; + + if (!qmltype.isComposite()) + return qmltype.typeId(); + + QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + auto compilationUnit = tdata->compilationUnit(); + + return compilationUnit->metaTypeId; +} + +template <typename ObjectContainer> class QQmlPropertyCacheAliasCreator { public: diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index 6bb00e438d..71964aca64 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -40,8 +40,11 @@ #include "qqmlpropertyvalidator_p.h" #include <private/qqmlcustomparser_p.h> +#include <private/qqmlirbuilder_p.h> #include <private/qqmlstringconverters_p.h> +#include <private/qqmlpropertycachecreator_p.h> #include <private/qqmlpropertyresolver_p.h> + #include <QtCore/qdatetime.h> QT_BEGIN_NAMESPACE @@ -136,7 +139,7 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( defaultProperty = propertyCache->defaultProperty(); } - QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings); + QV4::BindingPropertyData collectedBindingPropertyData(obj->nBindings); binding = obj->bindingTable(); for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { diff --git a/src/qml/qml/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h index f2b892e978..74a1281927 100644 --- a/src/qml/qml/qqmlpropertyvalidator_p.h +++ b/src/qml/qml/qqmlpropertyvalidator_p.h @@ -50,7 +50,13 @@ // We mean it. // -#include <private/qqmltypecompiler_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlimport_p.h> +#include <private/qqmljsdiagnosticmessage_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qv4compileddata_p.h> + +#include <QtCore/qcoreapplication.h> QT_BEGIN_NAMESPACE @@ -91,7 +97,7 @@ private: const QV4::CompiledData::Unit *qmlUnit; const QQmlPropertyCacheVector &propertyCaches; - QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject; + QVector<QV4::BindingPropertyData> * const bindingPropertyDataPerObject; }; QT_END_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/qqmltype.cpp b/src/qml/qml/qqmltype.cpp index 6b4e0a0734..2a6831d898 100644 --- a/src/qml/qml/qqmltype.cpp +++ b/src/qml/qml/qqmltype.cpp @@ -48,6 +48,7 @@ #include <private/qqmldata_p.h> #include <private/qqmlmetatypedata_p.h> #include <private/qqmlpropertycache_p.h> +#include <private/qqmltypedata_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index 6cbe81b4b8..e7633a1bba 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -39,7 +39,6 @@ #include "qqmltypecompiler_p.h" -#include <private/qqmlirbuilder_p.h> #include <private/qqmlobjectcreator_p.h> #include <private/qqmlcustomparser_p.h> #include <private/qqmlvmemetaobject_p.h> @@ -145,11 +144,11 @@ QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() document->jsModule.fileName = typeData->urlString(); document->jsModule.finalUrl = typeData->finalUrlString(); - QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, - document->program, &document->jsGenerator.stringTable, engine->v4engine()->illegalNames()); - QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); - if (!jsCodeGen.generateCodeForComponents()) + QmlIR::JSCodeGen v4CodeGenerator(document, engine->v4engine()->illegalNames()); + if (!v4CodeGenerator.generateCodeForComponents(componentRoots())) { + recordError(v4CodeGenerator.error()); return nullptr; + } document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false); } @@ -1309,76 +1308,6 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) return true; } -QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen) - : QQmlCompilePass(typeCompiler) - , customParsers(typeCompiler->customParserCache()) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) - , v4CodeGen(v4CodeGen) -{ -} - -bool QQmlJSCodeGenerator::generateCodeForComponents() -{ - const QVector<quint32> &componentRoots = compiler->componentRoots(); - for (int i = 0; i < componentRoots.count(); ++i) { - if (!compileComponent(componentRoots.at(i))) - return false; - } - - return compileComponent(/*root object*/0); -} - -bool QQmlJSCodeGenerator::compileComponent(int contextObject) -{ - const QmlIR::Object *obj = qmlObjects.at(contextObject); - if (obj->flags & QV4::CompiledData::Object::IsComponent) { - Q_ASSERT(obj->bindingCount() == 1); - const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - contextObject = componentBinding->value.objectIndex; - } - - if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject)) - return false; - - return true; -} - -bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) -{ - QmlIR::Object *object = qmlObjects.at(objectIndex); - if (object->flags & QV4::CompiledData::Object::IsComponent) - return true; - - if (object->functionsAndExpressions->count > 0) { - QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; - for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) - functionsToCompile << *foe; - const QVector<int> runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile); - if (v4CodeGen->hasError()) { - compiler->recordError(v4CodeGen->error()); - return false; - } - - QQmlJS::MemoryPool *pool = compiler->memoryPool(); - object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices); - } - - for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { - if (binding->type < QV4::CompiledData::Binding::Type_Object) - continue; - - int target = binding->value.objectIndex; - int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex; - - if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope)) - return false; - } - - return true; -} - QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) , qmlObjects(*typeCompiler->qmlObjects()) diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h index 615694d4bc..40b0337848 100644 --- a/src/qml/qml/qqmltypecompiler_p.h +++ b/src/qml/qml/qqmltypecompiler_p.h @@ -309,24 +309,6 @@ private: bool _seenObjectWithId; }; -// ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone. -class QQmlJSCodeGenerator : public QQmlCompilePass -{ -public: - QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen); - - bool generateCodeForComponents(); - -private: - bool compileComponent(int componentRoot); - bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); - - const QHash<int, QQmlCustomParser*> &customParsers; - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; - QmlIR::JSCodeGen * const v4CodeGen; -}; - class QQmlDefaultPropertyMerger : public QQmlCompilePass { public: diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp new file mode 100644 index 0000000000..12d4d8cbad --- /dev/null +++ b/src/qml/qml/qqmltypedata.cpp @@ -0,0 +1,844 @@ +/**************************************************************************** +** +** 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/qqmlirbuilder_p.h> +#include <private/qqmlirloader_p.h> +#include <private/qqmlscriptblob_p.h> +#include <private/qqmlscriptdata_p.h> +#include <private/qqmltypecompiler_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..4b6c09769a 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. @@ -1488,7 +625,11 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL bool incomplete = false; - QUrl qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir"))); + QUrl importUrl(importUri); + QString path = importUrl.path(); + path.append(QLatin1String(path.endsWith(QLatin1Char('/')) ? "qmldir" : "/qmldir")); + importUrl.setPath(path); + QUrl qmldirUrl = finalUrl().resolved(importUrl); if (!QQmlImports::isLocal(qmldirUrl)) { // This is a remote file; the import is currently incomplete incomplete = true; @@ -1532,6 +673,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 +715,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 +868,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 +1178,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/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index fce753cd26..88f7b69dc2 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -43,7 +43,9 @@ #include <private/qqmlglobal_p.h> #include <QtCore/qdebug.h> #include <private/qmetaobjectbuilder_p.h> +#if QT_CONFIG(qml_itemmodel) #include <private/qqmlmodelindexvaluetype_p.h> +#endif #include <private/qmetatype_p.h> QT_BEGIN_NAMESPACE @@ -69,8 +71,10 @@ QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl() { std::fill_n(valueTypes, int(QVariant::UserType), nullptr); +#if QT_CONFIG(qml_itemmodel) // See types wrapped in qqmlmodelindexvaluetype_p.h qRegisterMetaType<QItemSelectionRange>(); +#endif } QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl() @@ -120,13 +124,17 @@ const QMetaObject *QQmlValueTypeFactoryImpl::metaObjectForMetaType(int t) return &QQmlRectFValueType::staticMetaObject; case QVariant::EasingCurve: return &QQmlEasingValueType::staticMetaObject; +#if QT_CONFIG(qml_itemmodel) case QVariant::ModelIndex: return &QQmlModelIndexValueType::staticMetaObject; case QVariant::PersistentModelIndex: return &QQmlPersistentModelIndexValueType::staticMetaObject; +#endif default: +#if QT_CONFIG(qml_itemmodel) if (t == qMetaTypeId<QItemSelectionRange>()) return &QQmlItemSelectionRangeValueType::staticMetaObject; +#endif if (const QMetaObject *mo = QQml_valueTypeProvider()->metaObjectForMetaType(t)) return mo; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 458f26b465..b9d8fed243 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -58,6 +58,7 @@ #include <private/qv4jscall_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qqmlpropertycachecreator_p.h> +#include <private/qqmlpropertycachemethodarguments_p.h> QT_BEGIN_NAMESPACE @@ -917,21 +918,38 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * return -1; // The dynamic method with that id is not available. } - const unsigned int parameterCount = function->formalParameterCount(); + auto methodData = cache->method(_id); + auto arguments = methodData->hasArguments() ? methodData->arguments() : nullptr; + + const unsigned int parameterCount = (arguments && arguments->names) ? arguments->names->count() : 0; + Q_ASSERT(parameterCount == function->formalParameterCount()); + QV4::JSCallData jsCallData(scope, parameterCount); *jsCallData->thisObject = v4->global(); - for (uint ii = 0; ii < parameterCount; ++ii) - jsCallData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]); + for (uint ii = 0; ii < parameterCount; ++ii) { + jsCallData->args[ii] = scope.engine->metaTypeToJS(arguments->arguments[ii + 1], a[ii + 1]); + } + const int returnType = methodData->propType(); QV4::ScopedValue result(scope, function->call(jsCallData)); if (scope.hasException()) { QQmlError error = scope.engine->catchExceptionAsQmlError(); if (error.isValid()) ep->warning(error); - if (a[0]) *(QVariant *)a[0] = QVariant(); + if (a[0]) { + QMetaType::destruct(returnType, a[0]); + QMetaType::construct(returnType, a[0], nullptr); + } } else { - if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(result, 0); + if (a[0]) { + // When the return type is QVariant, JS objects are to be returned as QJSValue wrapped in + // QVariant. + if (returnType == QMetaType::QVariant) + *(QVariant *)a[0] = scope.engine->toVariant(result, 0); + else + scope.engine->metaTypeFromJS(result, returnType, a[0]); + } } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. 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/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h index 0957c8373a..20b92ab3c6 100644 --- a/src/qml/qmldirparser/qqmldirparser_p.h +++ b/src/qml/qmldirparser/qqmldirparser_p.h @@ -54,14 +54,14 @@ #include <QtCore/QUrl> #include <QtCore/QHash> #include <QtCore/QDebug> +#include <private/qtqmlcompilerglobal_p.h> #include <private/qqmljsengine_p.h> -#include <private/qv4global_p.h> #include <private/qqmljsdiagnosticmessage_p.h> QT_BEGIN_NAMESPACE class QQmlEngine; -class Q_QML_PRIVATE_EXPORT QQmlDirParser +class Q_QMLCOMPILER_PRIVATE_EXPORT QQmlDirParser { public: QQmlDirParser(); diff --git a/src/qml/qtqmlcompilerglobal.h b/src/qml/qtqmlcompilerglobal.h new file mode 100644 index 0000000000..850d413372 --- /dev/null +++ b/src/qml/qtqmlcompilerglobal.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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 QTQMLCOMPILERGLOBAL_H +#define QTQMLCOMPILERGLOBAL_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +#if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) || defined(QT_STATIC) +# define Q_QMLCOMPILER_EXPORT +#else +# if defined(QT_BUILD_QML_LIB) +# define Q_QMLCOMPILER_EXPORT Q_DECL_EXPORT +# else +# define Q_QMLCOMPILER_EXPORT Q_DECL_IMPORT +# endif +#endif + +QT_END_NAMESPACE +#endif // QTQMLCOMPILERGLOBAL_H diff --git a/src/qml/qtqmlcompilerglobal_p.h b/src/qml/qtqmlcompilerglobal_p.h new file mode 100644 index 0000000000..9c8bce23d3 --- /dev/null +++ b/src/qml/qtqmlcompilerglobal_p.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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 QTQMLCOMPILERGLOBAL_P_H +#define QTQMLCOMPILERGLOBAL_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 <QtCore/private/qglobal_p.h> +#include <qtqmlcompilerglobal.h> + +#define Q_QMLCOMPILER_PRIVATE_EXPORT Q_QMLCOMPILER_EXPORT + +#endif // QTQMLCOMPILERGLOBAL_P_H diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index c50273071b..54cd8710b6 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -1,12 +1,17 @@ SOURCES += \ $$PWD/qqmlbind.cpp \ - $$PWD/qqmlconnections.cpp \ - $$PWD/qqmlmodelindexvaluetype.cpp + $$PWD/qqmlconnections.cpp HEADERS += \ $$PWD/qqmlbind_p.h \ - $$PWD/qqmlconnections_p.h \ - $$PWD/qqmlmodelindexvaluetype_p.h + $$PWD/qqmlconnections_p.h + +qtConfig(qml-itemmodel) { + SOURCES += \ + $$PWD/qqmlmodelindexvaluetype.cpp + HEADERS += \ + $$PWD/qqmlmodelindexvaluetype_p.h +} qtConfig(qml-animation) { SOURCES += \ diff --git a/src/qmlmodels/configure.json b/src/qmlmodels/configure.json index bfe7d538ec..2b4fa4f6e2 100644 --- a/src/qmlmodels/configure.json +++ b/src/qmlmodels/configure.json @@ -16,19 +16,21 @@ "label": "QML list model", "purpose": "Provides the ListModel QML type.", "section": "QML", + "condition": "features.qml-itemmodel", "output": [ "privateFeature" ] }, "qml-delegate-model": { "label": "QML delegate model", "purpose": "Provides the DelegateModel QML type.", "section": "QML", - "condition": "features.qml-object-model", + "condition": "features.qml-object-model && features.qml-itemmodel", "output": [ "privateFeature" ] }, "qml-table-model": { "label": "QML table model", "purpose": "Provides the TableModel QML type.", "section": "QML", + "condition": "features.qml-itemmodel", "output": [ "privateFeature" ] } }, diff --git a/src/qmlmodels/qqmladaptormodel.cpp b/src/qmlmodels/qqmladaptormodel.cpp index 0bc67e1aad..48ff4e7d5b 100644 --- a/src/qmlmodels/qqmladaptormodel.cpp +++ b/src/qmlmodels/qqmladaptormodel.cpp @@ -161,8 +161,14 @@ public: signalIndexes.append(propertyId + signalOffset); } - for (int i = 0, c = items.count(); i < c; ++i) { - QQmlDelegateModelItem *item = items.at(i); + QVarLengthArray<QQmlGuard<QQmlDelegateModelItem>> guardedItems; + for (const auto item : items) + guardedItems.append(item); + + for (const auto &item : qAsConst(guardedItems)) { + if (item.isNull()) + continue; + const int idx = item->modelIndex(); if (idx >= index && idx < index + count) { for (int i = 0; i < signalIndexes.count(); ++i) 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/qmltest/doc/qtqmltest.qdocconf b/src/qmltest/doc/qtqmltest.qdocconf index 9a3e16b64d..25a7ae5f9b 100644 --- a/src/qmltest/doc/qtqmltest.qdocconf +++ b/src/qmltest/doc/qtqmltest.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtdeclarative.qdocconf) project = QtQmlTest description = Qt Quick Test Reference Documentation diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf index 6620650d4a..13746b13de 100644 --- a/src/quick/doc/qtquick.qdocconf +++ b/src/quick/doc/qtquick.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtdeclarative.qdocconf) project = QtQuick description = Qt Quick Reference Documentation diff --git a/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml b/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml index b40f517234..96f7fc1c7c 100644 --- a/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml +++ b/src/quick/doc/snippets/qml/qml-extending-types/signals/Button.qml @@ -55,7 +55,7 @@ Rectangle { id: rect width: 100; height: 100 - signal buttonClicked(int xPos, int yPos) + signal buttonClicked(xPos: int, yPos: int) MouseArea { anchors.fill: parent diff --git a/src/quick/doc/src/concepts/effects/sprites.qdoc b/src/quick/doc/src/concepts/effects/sprites.qdoc index 004db90eb3..159d9ca8a9 100644 --- a/src/quick/doc/src/concepts/effects/sprites.qdoc +++ b/src/quick/doc/src/concepts/effects/sprites.qdoc @@ -144,7 +144,7 @@ these situations, a warning will be output to the console containing the maximum There are several tools to help turn a set of images into sprite sheets. Here are some examples: \list \li Photoshop plugin: \l http://www.johnwordsworth.com/projects/photoshop-sprite-sheet-generator-script - \li Gimp plugin: \l http://registry.gimp.org/node/20943 + \li Gimp's SpriteSheet plugin \li Cmd-line tool: \l http://www.imagemagick.org/script/montage.php \endlist diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index bc87b2c43f..15a417a8b4 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -477,9 +477,11 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickTableView>(uri, 2, 12, "TableView"); #endif +#if QT_CONFIG(quick_itemview) qmlRegisterUncreatableType<QQuickItemView, 13>(uri, 2, 13, itemViewName, itemViewMessage); qmlRegisterType<QQuickPathView, 13>(uri, 2, 13, "PathView"); qmlRegisterType<QQuickGridView, 13>(uri, 2, 13, "GridView"); +#endif #if QT_CONFIG(quick_tableview) qmlRegisterType<QQuickTableView, 14>(uri, 2, 14, "TableView"); #endif diff --git a/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp index 0f9ca66ad7..b6b6f3b057 100644 --- a/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgopengldistancefieldglyphcache.cpp @@ -69,7 +69,8 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI QSGOpenGLDistanceFieldGlyphCache::QSGOpenGLDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) : QSGDistanceFieldGlyphCache(font) - , m_maxTextureSize(0) + , m_maxTextureWidth(0) + , m_maxTextureHeight(0) , m_maxTextureCount(3) , m_areaAllocator(nullptr) , m_blitProgram(nullptr) @@ -113,13 +114,23 @@ void QSGOpenGLDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs QList<GlyphPosition> glyphPositions; QVector<glyph_t> glyphsToRender; + const int padding = QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING; + const qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution); + + if (m_maxTextureHeight == 0) { + m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureWidth); + + // We need to add a buffer to avoid glyphs that overlap the border between two + // textures causing the height of the textures to extend beyond the limit. + m_maxTextureHeight = m_maxTextureWidth - (qCeil(m_referenceFont.pixelSize() * scaleFactor) + distanceFieldRadius() * 2 + padding * 2); + } + if (m_areaAllocator == nullptr) - m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize())); + m_areaAllocator = new QSGAreaAllocator(QSize(m_maxTextureWidth, m_maxTextureCount * m_maxTextureHeight)); for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) { glyph_t glyphIndex = *it; - int padding = QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING; QRectF boundingRect = glyphData(glyphIndex).boundingRect; int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2; int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2; @@ -152,8 +163,8 @@ void QSGOpenGLDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs continue; } - TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize()); - alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height()); + TextureInfo *tex = textureInfo(alloc.y() / m_maxTextureHeight); + alloc = QRect(alloc.x(), alloc.y() % m_maxTextureHeight, alloc.width(), alloc.height()); tex->allocatedArea |= alloc; Q_ASSERT(tex->padding == padding || tex->padding < 0); @@ -541,13 +552,6 @@ bool QSGOpenGLDistanceFieldGlyphCache::createFullSizeTextures() const return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT(); } -int QSGOpenGLDistanceFieldGlyphCache::maxTextureSize() const -{ - if (!m_maxTextureSize) - m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); - return m_maxTextureSize; -} - namespace { struct Qtdf { // We need these structs to be tightly packed, but some compilers we use do not @@ -646,7 +650,7 @@ bool QSGOpenGLDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fon } qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize)); - m_maxTextureSize = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize); + m_maxTextureWidth = m_maxTextureHeight = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize); m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1; padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding); @@ -655,7 +659,7 @@ bool QSGOpenGLDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fon return false; } - if (m_maxTextureSize <= 0) { + if (m_maxTextureWidth <= 0) { qWarning("Invalid texture size in '%s'", qPrintable(font.familyName())); return false; } @@ -663,11 +667,11 @@ bool QSGOpenGLDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fon int systemMaxTextureSize; m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &systemMaxTextureSize); - if (m_maxTextureSize > systemMaxTextureSize) { + if (m_maxTextureWidth > systemMaxTextureSize) { qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d", systemMaxTextureSize, qPrintable(font.familyName()), - m_maxTextureSize); + m_maxTextureWidth); } if (padding != QSG_OPENGL_DISTANCEFIELD_GLYPH_CACHE_PADDING) { @@ -690,12 +694,12 @@ bool QSGOpenGLDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &fon return false; } - if (m_areaAllocator->size().height() % m_maxTextureSize != 0) { + if (m_areaAllocator->size().height() % m_maxTextureHeight != 0) { qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName())); return false; } - textureCount = m_areaAllocator->size().height() / m_maxTextureSize; + textureCount = m_areaAllocator->size().height() / m_maxTextureHeight; m_maxTextureCount = qMax(m_maxTextureCount, textureCount); const char *textureRecord = allocatorData; diff --git a/src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h index 99cfe27b9c..66d1b52f86 100644 --- a/src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgopengldistancefieldglyphcache_p.h @@ -80,7 +80,6 @@ public: bool useTextureResizeWorkaround() const; bool useTextureUploadWorkaround() const; bool createFullSizeTextures() const; - int maxTextureSize() const; void setMaxTextureCount(int max) { m_maxTextureCount = max; } int maxTextureCount() const { return m_maxTextureCount; } @@ -98,7 +97,7 @@ private: QDistanceField image; int padding = -1; - TextureInfo(const QRect &preallocRect = QRect()) : texture(0), allocatedArea(preallocRect) { } + TextureInfo(const QRect &preallocRect = QRect(0, 0, 1, 1)) : texture(0), allocatedArea(preallocRect) { } }; void createTexture(TextureInfo * texInfo, int width, int height, const void *pixels); @@ -107,9 +106,10 @@ private: TextureInfo *textureInfo(int index) { + Q_ASSERT(m_maxTextureWidth > 0 && m_maxTextureHeight > 0); for (int i = m_textures.count(); i <= index; ++i) { if (createFullSizeTextures()) - m_textures.append(QRect(0, 0, maxTextureSize(), maxTextureSize())); + m_textures.append(QRect(0, 0, m_maxTextureWidth, m_maxTextureWidth)); else m_textures.append(TextureInfo()); } @@ -137,7 +137,8 @@ private: m_blitProgram->link(); } - mutable int m_maxTextureSize; + int m_maxTextureWidth; + int m_maxTextureHeight; int m_maxTextureCount; bool m_coreProfile; 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> |